Back to index

texmacs  1.0.7.15
TMView.mm
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : TMView.mm
00004 * DESCRIPTION: Main TeXmacs view
00005 * COPYRIGHT  : (C) 2007  Massimiliano Gubinelli
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #import "TMView.h"
00013 #include "converter.hpp"
00014 #include "message.hpp"
00015 #include "aqua_renderer.h"
00016 #include "aqua_gui.h"
00017 
00018 #define PIXEL 256
00019 
00020 extern bool aqua_update_flag;
00021 extern int time_credit;
00022 extern int timeout_time;
00023 
00024 hashmap<int,string> nskeymap("");
00025 
00026 inline void scale (NSPoint &point)
00027 {      
00028        point.x *= PIXEL; point.y *= -PIXEL;
00029 }
00030 
00031 inline void scaleSize (NSSize &point)
00032 {      
00033        point.width *= PIXEL; point.height *= PIXEL;
00034 }
00035 
00036 inline void unscaleSize (NSSize &point)
00037 {      
00038        point.width /= PIXEL; point.height /= PIXEL;
00039 }
00040 
00041 
00042 
00043 @interface TMRect : NSObject
00044 {
00045        NSRect rect;
00046 }
00047 - initWithRect:(NSRect)_rect;
00048 - (NSRect)rect;
00049 @end
00050 
00051 @implementation TMRect
00052 - initWithRect:(NSRect)_rect
00053 {
00054        [super init];
00055        rect = _rect;
00056        return self;
00057 }
00058 - (NSRect)rect { return rect; }
00059 @end
00060 
00061 
00062 @interface TMView (Private)
00063 - (void)setNeedsDisplayInTMRect:(TMRect*)r;
00064 - (void)delayedUpdate;
00065 - (void) focusIn;
00066 - (void) focusOut;
00067 @end
00068 
00069 
00070 
00071 
00072 @implementation TMView
00073 
00074 
00075 inline void map(int code, string name)
00076 {
00077   nskeymap(code) = name;
00078 }
00079 
00080 void initkeymap()
00081 {
00082   map(0x0d,"return");
00083   map(0x09,"tab");
00084   map(0xf728,"backspace");
00085   map(0xf003,"enter");
00086   map(0x1b,"escape");
00087   map(0x0003,"K-enter");
00088   map(0x7f,"backspace");
00089   
00090   map( NSUpArrowFunctionKey       ,"up" );
00091   map( NSDownArrowFunctionKey     ,"down" );
00092   map( NSLeftArrowFunctionKey     ,"left" );
00093   map( NSRightArrowFunctionKey    ,"right" );
00094   map( NSF1FunctionKey    ,"F1" );
00095   map( NSF2FunctionKey    ,"F2" );
00096   map( NSF3FunctionKey    ,"F3" );
00097   map( NSF4FunctionKey    ,"F4" );
00098   map( NSF5FunctionKey    ,"F5" );
00099   map( NSF6FunctionKey    ,"F6" );
00100   map( NSF7FunctionKey    ,"F7" );
00101   map( NSF8FunctionKey    ,"F8" );
00102   map( NSF9FunctionKey    ,"F9" );
00103   map( NSF10FunctionKey   ,"F10" );
00104   map( NSF11FunctionKey   ,"F11" );
00105   map( NSF12FunctionKey   ,"F12" );
00106   map( NSF13FunctionKey   ,"F13" );
00107   map( NSF14FunctionKey   ,"F14" );
00108   map( NSF15FunctionKey   ,"F15" );
00109   map( NSF16FunctionKey   ,"F16" );
00110   map( NSF17FunctionKey   ,"F17" );
00111   map( NSF18FunctionKey   ,"F18" );
00112   map( NSF19FunctionKey   ,"F19" );
00113   map( NSF20FunctionKey   ,"F20" );
00114   map( NSF21FunctionKey   ,"F21" );
00115   map( NSF22FunctionKey   ,"F22" );
00116   map( NSF23FunctionKey   ,"F23" );
00117   map( NSF24FunctionKey   ,"F24" );
00118   map( NSF25FunctionKey   ,"F25" );
00119   map( NSF26FunctionKey   ,"F26" );
00120   map( NSF27FunctionKey   ,"F27" );
00121   map( NSF28FunctionKey   ,"F28" );
00122   map( NSF29FunctionKey   ,"F29" );
00123   map( NSF30FunctionKey   ,"F30" );
00124   map( NSF31FunctionKey   ,"F31" );
00125   map( NSF32FunctionKey   ,"F32" );
00126   map( NSF33FunctionKey   ,"F33" );
00127   map( NSF34FunctionKey   ,"F34" );
00128   map( NSF35FunctionKey   ,"F35" );
00129   map( NSInsertFunctionKey        ,"insert" );
00130   map( NSDeleteFunctionKey        ,"delete" );
00131   map( NSHomeFunctionKey  ,"home" );
00132   map( NSBeginFunctionKey         ,"begin" );
00133   map( NSEndFunctionKey   ,"end" );
00134   map( NSPageUpFunctionKey        ,"pageup" );
00135   map( NSPageDownFunctionKey      ,"pagedown" );
00136   map( NSPrintScreenFunctionKey   ,"printscreen" );
00137   map( NSScrollLockFunctionKey    ,"scrolllock" );
00138   map( NSPauseFunctionKey         ,"pause" );
00139   map( NSSysReqFunctionKey        ,"sysreq" );
00140   map( NSBreakFunctionKey         ,"break" );
00141   map( NSResetFunctionKey         ,"reset" );
00142   map( NSStopFunctionKey  ,"stop" );
00143   map( NSMenuFunctionKey  ,"menu" );
00144   map( NSUserFunctionKey  ,"user" );
00145   map( NSSystemFunctionKey        ,"system" );
00146   map( NSPrintFunctionKey         ,"print" );
00147   map( NSClearLineFunctionKey     ,"clear" );
00148   map( NSClearDisplayFunctionKey  ,"cleardisplay" );
00149   map( NSInsertLineFunctionKey    ,"insertline" );
00150   map( NSDeleteLineFunctionKey    ,"deleteline" );
00151   map( NSInsertCharFunctionKey    ,"insert" );
00152   map( NSDeleteCharFunctionKey    ,"delete" );
00153   map( NSPrevFunctionKey  ,"prev" );
00154   map( NSNextFunctionKey  ,"next" );
00155   map( NSSelectFunctionKey        ,"select" );
00156   map( NSExecuteFunctionKey       ,"execute" );
00157   map( NSUndoFunctionKey  ,"undo" );
00158   map( NSRedoFunctionKey  ,"redo" );
00159   map( NSFindFunctionKey  ,"find" );
00160   map( NSHelpFunctionKey  ,"help" );
00161   map( NSModeSwitchFunctionKey    ,"modeswitch" );  
00162 }
00163 
00164 
00165 - (id)initWithFrame:(NSRect)frame {
00166   self = [super initWithFrame:frame];
00167   if (self) {
00168     // Initialization code here.
00169     wid = NULL;
00170     processingCompose = NO;
00171     workingText = nil;
00172     delayed_rects = [[NSMutableArray arrayWithCapacity:100] retain];
00173     
00174     
00175     
00176   }
00177   return self;
00178 }
00179 
00180 -(void) dealloc
00181 {
00182   [delayed_rects release];
00183   [self deleteWorkingText];
00184   [[NSNotificationCenter defaultCenter] removeObserver: self
00185                                                   name: @"NSWindowDidBecomeKeyNotification"
00186                                                 object: nil];
00187   [[NSNotificationCenter defaultCenter] removeObserver: self
00188                                                   name: @"NSWindowDidBecomeKeyNotification"
00189                                                 object: nil];
00190   
00191   [super dealloc];
00192 }
00193 
00194 - (void) setWidget:(widget_rep*) w
00195 {
00196        wid = (simple_widget_rep*)w;
00197 }
00198 
00199 - (widget_rep*)widget
00200 {
00201        return  (widget_rep*)wid;
00202 }
00203 
00204 - (void)setNeedsDisplayInTMRect:(TMRect*)r
00205 {
00206   [self setNeedsDisplayInRect:[r rect]];
00207 }
00208 
00209 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
00210 {
00211   // query widget preferred size
00212   SI w,h;
00213   wid->handle_get_size_hint (w,h);
00214   NSSize s = NSMakeSize(w,h);
00215   unscaleSize(s);
00216   [self setFrameSize:s];
00217   
00218   // register to receive focus in/out notifications  
00219   [[NSNotificationCenter defaultCenter] removeObserver: self
00220                                                   name: @"NSWindowDidBecomeKeyNotification"
00221                                                 object: nil];
00222   [[NSNotificationCenter defaultCenter] removeObserver: self
00223                                                   name: @"NSWindowDidBecomeKeyNotification"
00224                                                 object: nil];
00225   
00226   [[NSNotificationCenter defaultCenter] addObserver: self
00227                                            selector: @selector(focusIn)
00228                                                name: @"NSWindowDidBecomeKeyNotification"
00229                                              object: newWindow];
00230   
00231   [[NSNotificationCenter defaultCenter] addObserver: self
00232                                            selector: @selector(focusOut)
00233                                                name: @"NSWindowDidResignKeyNotification"
00234                                              object: newWindow];
00235   
00236 }
00237 
00238 
00239 
00240 - (void) focusIn
00241 {
00242   if (DEBUG_EVENTS) cout << "FOCUSIN" << LF;
00243   if (wid) {
00244     wid -> handle_keyboard_focus (true, texmacs_time ());
00245   }
00246 }
00247 
00248 - (void) focusOut
00249 {
00250   if (DEBUG_EVENTS)   cout << "FOCUSOUT" << LF;
00251   if (wid) {
00252     wid -> handle_keyboard_focus (false, texmacs_time ());
00253   }
00254 }
00255 
00256 - (void)delayedUpdate
00257 {
00258   NSMutableArray *arr = delayed_rects;
00259   NSEnumerator *enumerator = [arr objectEnumerator];
00260   TMRect *anObject;
00261   delayed_rects = [[NSMutableArray arrayWithCapacity:10] retain];
00262   while ((anObject = [enumerator nextObject])) {
00263     [self displayRect:[anObject rect]];
00264   }
00265   [arr release];
00266 }
00267 
00268 
00269 
00270 - (void)drawRect:(NSRect)rect 
00271 {
00272   if (aqua_update_flag) {
00273     [delayed_rects addObject:[[[TMRect alloc] initWithRect:rect] autorelease]];
00274     return;
00275   }
00276     
00277        // Drawing code here.
00278        if ([self inLiveResize])
00279        {
00280               NSRect bounds = [self bounds];
00281               [[NSColor blackColor] set];
00282               [NSBezierPath strokeRect:NSInsetRect(bounds,1,1)];
00283               //    return;
00284        }
00285 //     cout << "DRAWING : " << rect.origin.x << ","<< rect.origin.x << ","<< rect.size.width<< "," << rect.size.height <<  "\n";
00286 //     NSRect bounds = [self bounds];
00287        
00288   {
00289               basic_renderer r = the_aqua_renderer();
00290     int x1 = rect.origin.x;
00291     int y1 = rect.origin.y+rect.size.height;
00292     int x2 = rect.origin.x+rect.size.width;
00293     int y2 = rect.origin.y;
00294     
00295     r -> begin([NSGraphicsContext currentContext]);
00296   //  r -> set_origin(0,0);
00297     r -> encode (x1,y1);
00298     r -> encode (x2,y2);
00299  //   cout << "DRAWING RECT " << x1 << "," << y1 << "," << x2 << "," << y2 << LF;
00300     r -> set_clipping (x1,y1,x2,y2);
00301     wid->handle_repaint (x1,y1,x2,y2);
00302               r->end();
00303     if (r->interrupted())
00304       aqua_update_flag= true;
00305        }
00306 //     cout << "END DRAWING" << "\n";
00307  
00308   if (aqua_update_flag) {
00309     if (DEBUG_EVENTS)
00310       cout << "Postponed redrawing\n"; 
00311     [self performSelector:@selector(delayedUpdate) withObject: nil afterDelay: 10];
00312   }
00313   
00314 }
00315 
00316 
00317 #if 0
00318 - (void)keyDown:(NSEvent *)theEvent
00319 {
00320   if (!wid) return;
00321   
00322   {
00323     char str[256];
00324     string r;
00325     NSString *nss = [theEvent charactersIgnoringModifiers];
00326     unsigned int mods = [theEvent modifierFlags];
00327     
00328     
00329     
00330     if (([nss length]==1)&& (!processingCompose))
00331       
00332     {
00333       int key = [nss characterAtIndex:0];
00334       if (nskeymap->contains(key)) {
00335         r = nskeymap[key];
00336         r = ((mods & NSShiftKeyMask)? "S-" * r: r);
00337       }
00338       else
00339       {
00340         [nss getCString:str maxLength:256 encoding:NSUTF8StringEncoding];
00341         string rr (str, strlen(str));
00342         r= utf8_to_cork (rr);          
00343       } 
00344       
00345       
00346       string s (r);
00347       if (! contains_unicode_char (s))     
00348       {
00349         //      string s= ((mods & NSShiftKeyMask)? "S-" * r: r);
00350         /* other keyboard modifiers */
00351         if (N(s)!=0) {
00352           if (mods & NSControlKeyMask ) s= "C-" * s;
00353           if (mods & NSAlternateKeyMask) s= "A-" * s;
00354           if (mods & NSCommandKeyMask) s= "M-" * s;
00355           // if (mods & NSNumericPadKeyMask) s= "K-" * s;
00356          // if (mods & NSHelpKeyMask) s= "H-" * s;
00357           // if (mods & NSFunctionKeyMask) s= "F-" * s;
00358         }
00359         cout << "key press: " << s << LF;
00360         wid -> handle_keypress (s, texmacs_time());    
00361       }
00362     }
00363     else {
00364       processingCompose = YES;
00365       static NSMutableArray *nsEvArray = nil;
00366       if (nsEvArray == nil)
00367         nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
00368       
00369       [nsEvArray addObject: theEvent];
00370       [self interpretKeyEvents: nsEvArray];
00371       [nsEvArray removeObject: theEvent];
00372     }
00373   }    
00374   
00375   
00376 }
00377 #else
00378 - (void)keyDown:(NSEvent *)theEvent
00379 {
00380   if (!wid) return;
00381 
00382   time_credit= 25;
00383   timeout_time= texmacs_time () + time_credit;
00384   static bool fInit = false;
00385   if (!fInit) {
00386     if (DEBUG_EVENTS)
00387       cout << "Initializing keymap\n";
00388     initkeymap();
00389     fInit= true;
00390   }
00391   
00392   {
00393     // char str[256];
00394     string r;
00395     NSString *nss = [theEvent charactersIgnoringModifiers];
00396     unsigned int mods = [theEvent modifierFlags];
00397     
00398     string modstr;
00399     
00400     if (mods & NSControlKeyMask ) modstr= "C-" * modstr;
00401     if (mods & NSAlternateKeyMask) modstr= "A-" * modstr;
00402     if (mods & NSCommandKeyMask) modstr= "M-" * modstr;
00403     // if (mods & NSNumericPadKeyMask) modstr= "K-" * modstr;
00404     // if (mods & NSHelpKeyMask) modstr= "H-" * modstr;
00405     // if (mods & NSFunctionKeyMask) modstr= "F-" * modstr;
00406     
00407     //    if (!processingCompose)
00408     {
00409       if ([nss length]>0) {
00410         int key = [nss characterAtIndex:0];
00411         if (nskeymap->contains(key)) {
00412           r = nskeymap[key];
00413           r = ((mods & NSShiftKeyMask)? "S-" * modstr: modstr) * r;          
00414           cout << "function key press: " << r << LF;
00415           [self deleteWorkingText];
00416           wid -> handle_keypress (r, texmacs_time());    
00417           return;
00418         } else if (mods & (NSControlKeyMask  | NSCommandKeyMask | NSHelpKeyMask))
00419         {
00420           static char str[256];
00421           [nss getCString:str maxLength:256 encoding:NSUTF8StringEncoding];
00422           string rr (str, strlen(str));
00423           r= utf8_to_cork (rr);          
00424           
00425           string s ( modstr * r);
00426           cout << "modified  key press: " << s << LF;
00427           [self deleteWorkingText];
00428           wid -> handle_keypress (s, texmacs_time());    
00429           the_gui->update (); // FIXME: remove this line when
00430           // edit_typeset_rep::get_env_value will be faster
00431 
00432           return;
00433         }
00434       }
00435     }
00436     
00437     processingCompose = YES;
00438     static NSMutableArray *nsEvArray = nil;
00439     if (nsEvArray == nil)
00440       nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
00441     
00442     [nsEvArray addObject: theEvent];
00443     [self interpretKeyEvents: nsEvArray];
00444     [nsEvArray removeObject: theEvent];
00445   }
00446 }
00447 
00448 #endif
00449 
00450 static unsigned int
00451 mouse_state (NSEvent* event, bool flag) {
00452   unsigned int i= 0;
00453   i += 1 << min([event buttonNumber],4);
00454   unsigned int mods = [event modifierFlags];
00455   if (mods & NSAlternateKeyMask) i = 2;  
00456   if (mods & NSCommandKeyMask) i = 4;  
00457   if (mods & NSShiftKeyMask) i += 256;  
00458   if (mods & NSControlKeyMask) i += 2048;  
00459   return i;
00460 }
00461 
00462 static string
00463 mouse_decode (unsigned int mstate) {
00464   if      (mstate & 1 ) return "left";
00465   else if (mstate & 2 ) return "middle";
00466   else if (mstate & 4 ) return "right";
00467   else if (mstate & 8 ) return "up";
00468   else if (mstate & 16) return "down";
00469   return "unknown";
00470 }
00471 
00472 
00473 
00474 - (void)mouseDown:(NSEvent *)theEvent
00475 {
00476   if (wid) {
00477     NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
00478        scale(point);
00479     unsigned int mstate= mouse_state (theEvent, false);
00480     string s= "press-" * mouse_decode (mstate);
00481     wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ());
00482     if (DEBUG_EVENTS)
00483       cout << "mouse event: " << s << " at "
00484       << point.x << ", " << point.y  << LF;
00485   }
00486 }
00487 
00488 - (void)mouseUp:(NSEvent *)theEvent
00489 {
00490   if (wid) {
00491     NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
00492        scale(point);
00493     unsigned int mstate= mouse_state (theEvent, true);
00494     string s= "release-" * mouse_decode (mstate);
00495     wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ());
00496     if (DEBUG_EVENTS)
00497       cout << "mouse event: " << s << " at "
00498       << point.x  << ", " << point.y  << LF;
00499   }
00500 }
00501 
00502 - (void)mouseDragged:(NSEvent *)theEvent
00503 {
00504   if (wid) {
00505     NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
00506               scale(point);
00507     unsigned int mstate= mouse_state (theEvent, false);
00508     string s= "move";
00509     wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ());
00510     if (DEBUG_EVENTS)
00511       cout << "mouse event: " << s << " at "
00512       << point.x  << ", " << point.y  << LF;
00513   }  
00514 }
00515 
00516 - (void)mouseMoved:(NSEvent *)theEvent
00517 {
00518   if (wid) {
00519     NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
00520               scale(point);
00521     unsigned int mstate= mouse_state (theEvent, false);
00522     string s= "move";
00523     wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ());
00524     if (DEBUG_EVENTS)
00525       cout << "mouse event: " << s << " at "
00526       << point.x  << ", " << point.y  << LF;
00527   }  
00528 }
00529 
00530 - (BOOL)isFlipped
00531 {
00532   return YES;
00533 }
00534 
00535 - (BOOL)isOpaque
00536 {
00537   return YES;
00538 }
00539 
00540 - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize
00541 {
00542   [super resizeWithOldSuperviewSize:oldBoundsSize];
00543   if (wid)  {
00544     NSSize size = [self bounds].size;
00545               scaleSize(size);
00546               wid-> handle_notify_resize (size.width, size.height);
00547   }
00548        
00549 }
00550 
00551 - (BOOL)acceptsFirstResponder
00552 {
00553        return YES;
00554 }
00555 
00556 - (void) deleteWorkingText
00557 { 
00558   if (workingText == nil)
00559     return;
00560   [workingText release];
00561   workingText = nil;
00562   processingCompose = NO;
00563   
00564   
00565 }
00566 
00567 #pragma mark NSTextInput protocol implementation
00568 
00569 
00570 - (void) insertText:(id)aString
00571 // instead of keyDown: aString can be NSString or NSAttributedString
00572 {
00573   processingCompose = NO;
00574   NSLog(@"insertText: <%@>",aString);
00575   
00576   NSString *str = [aString respondsToSelector: @selector(string)] ?
00577   [aString string] : aString;
00578   
00579   static char buf[256];
00580   for(unsigned int i=0; i<[str length]; i++) {
00581     [[str substringWithRange:NSMakeRange(i, 1)] getCString:buf maxLength:256 encoding:NSUTF8StringEncoding];
00582     string rr (buf, strlen(buf));
00583     string s= utf8_to_cork (rr);          
00584     cout << "key press: " << s << LF;
00585     wid -> handle_keypress (s, texmacs_time());        
00586   }
00587 }
00588 
00589 - (void) doCommandBySelector:(SEL)aSelector
00590 {
00591 }
00592 
00593 // setMarkedText: cannot take a nil first argument. aString can be NSString or NSAttributedString
00594 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
00595 {
00596   NSString *str = [aString respondsToSelector: @selector(string)] ?
00597   [aString string] : aString;
00598   
00599   if (workingText != nil)
00600     [self deleteWorkingText];
00601   if ([str length] == 0)
00602     return;
00603   workingText = [str copy];
00604   processingCompose = YES;
00605   NSLog(@"setMarkedText: <%@>",workingText);
00606   
00607 }
00608 
00609 - (void) unmarkText
00610 {
00611   [self deleteWorkingText];  
00612 }
00613 - (BOOL) hasMarkedText
00614 {
00615   return workingText != nil;
00616   
00617 }
00618 - (NSInteger) conversationIdentifier
00619 {
00620   return (NSInteger)self;
00621 }
00622 
00623 /* Returns attributed string at the range.  This allows input mangers to query any range in backing-store.  May return nil.
00624  */
00625 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
00626 {
00627   static NSAttributedString *str = nil;
00628   if (str == nil) str = [NSAttributedString new];
00629   return str;
00630 }
00631 
00632 /* This method returns the range for marked region.  If hasMarkedText == false, it'll return NSNotFound location & 0 length range.
00633  */
00634 - (NSRange) markedRange
00635 {
00636   NSRange rng = workingText != nil
00637   ? NSMakeRange(0, [workingText length]) : NSMakeRange(NSNotFound, 0);
00638   return rng;
00639   
00640 }
00641 
00642 /* This method returns the range for selected region.  Just like markedRange method, its location field contains char index from the text beginning.
00643  */
00644 - (NSRange) selectedRange
00645 {
00646   return NSMakeRange(NSNotFound, 0);
00647 }
00648 /* This method returns the first frame of rects for theRange in screen coordindate system.
00649  */
00650 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
00651 {
00652   return NSMakeRect(0,0,50,50);
00653 }
00654 
00655 /* This method returns the index for character that is nearest to thePoint.  thePoint is in screen coordinate system.
00656  */
00657 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
00658 {
00659   return 0;
00660 }
00661 
00662 /* This method is the key to attribute extension.  We could add new attributes through this method. NSInputServer examines the return value of this method & constructs appropriate attributed string.
00663  */
00664 - (NSArray*) validAttributesForMarkedText
00665 {
00666   static NSArray *arr = nil;
00667   if (arr == nil) arr = [NSArray new];
00668   return arr;
00669 }
00670 
00671 
00672 @end