1 2/****************************************************************************** 3* MODULE : TMView.mm 4* DESCRIPTION: Main TeXmacs view 5* COPYRIGHT : (C) 2007 Massimiliano Gubinelli 6******************************************************************************* 7* This software falls under the GNU general public license version 3 or later. 8* It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE 9* in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>. 10******************************************************************************/ 11 12#import "TMView.h" 13#include "converter.hpp" 14#include "message.hpp" 15#include "aqua_renderer.h" 16#include "aqua_gui.h" 17 18extern bool aqua_update_flag; 19extern int time_credit; 20extern int timeout_time; 21 22hashmap<int,string> nskeymap(""); 23 24inline void scale (NSPoint &point) 25{ 26 point.x *= PIXEL; point.y *= -PIXEL; 27} 28 29inline void scaleSize (NSSize &point) 30{ 31 point.width *= PIXEL; point.height *= PIXEL; 32} 33 34inline void unscaleSize (NSSize &point) 35{ 36 point.width /= PIXEL; point.height /= PIXEL; 37} 38 39 40 41@interface TMRect : NSObject 42{ 43 NSRect rect; 44} 45- initWithRect:(NSRect)_rect; 46- (NSRect)rect; 47@end 48 49@implementation TMRect 50- initWithRect:(NSRect)_rect 51{ 52 [super init]; 53 rect = _rect; 54 return self; 55} 56- (NSRect)rect { return rect; } 57@end 58 59 60@interface TMView (Private) 61- (void)setNeedsDisplayInTMRect:(TMRect*)r; 62- (void)delayedUpdate; 63- (void) focusIn; 64- (void) focusOut; 65@end 66 67 68 69 70@implementation TMView 71 72 73inline void map(int code, string name) 74{ 75 nskeymap(code) = name; 76} 77 78void initkeymap() 79{ 80 map(0x0d,"return"); 81 map(0x09,"tab"); 82 map(0xf728,"backspace"); 83 map(0xf003,"enter"); 84 map(0x1b,"escape"); 85 map(0x0003,"K-enter"); 86 map(0x7f,"backspace"); 87 88 map( NSUpArrowFunctionKey ,"up" ); 89 map( NSDownArrowFunctionKey ,"down" ); 90 map( NSLeftArrowFunctionKey ,"left" ); 91 map( NSRightArrowFunctionKey ,"right" ); 92 map( NSF1FunctionKey ,"F1" ); 93 map( NSF2FunctionKey ,"F2" ); 94 map( NSF3FunctionKey ,"F3" ); 95 map( NSF4FunctionKey ,"F4" ); 96 map( NSF5FunctionKey ,"F5" ); 97 map( NSF6FunctionKey ,"F6" ); 98 map( NSF7FunctionKey ,"F7" ); 99 map( NSF8FunctionKey ,"F8" ); 100 map( NSF9FunctionKey ,"F9" ); 101 map( NSF10FunctionKey ,"F10" ); 102 map( NSF11FunctionKey ,"F11" ); 103 map( NSF12FunctionKey ,"F12" ); 104 map( NSF13FunctionKey ,"F13" ); 105 map( NSF14FunctionKey ,"F14" ); 106 map( NSF15FunctionKey ,"F15" ); 107 map( NSF16FunctionKey ,"F16" ); 108 map( NSF17FunctionKey ,"F17" ); 109 map( NSF18FunctionKey ,"F18" ); 110 map( NSF19FunctionKey ,"F19" ); 111 map( NSF20FunctionKey ,"F20" ); 112 map( NSF21FunctionKey ,"F21" ); 113 map( NSF22FunctionKey ,"F22" ); 114 map( NSF23FunctionKey ,"F23" ); 115 map( NSF24FunctionKey ,"F24" ); 116 map( NSF25FunctionKey ,"F25" ); 117 map( NSF26FunctionKey ,"F26" ); 118 map( NSF27FunctionKey ,"F27" ); 119 map( NSF28FunctionKey ,"F28" ); 120 map( NSF29FunctionKey ,"F29" ); 121 map( NSF30FunctionKey ,"F30" ); 122 map( NSF31FunctionKey ,"F31" ); 123 map( NSF32FunctionKey ,"F32" ); 124 map( NSF33FunctionKey ,"F33" ); 125 map( NSF34FunctionKey ,"F34" ); 126 map( NSF35FunctionKey ,"F35" ); 127 map( NSInsertFunctionKey ,"insert" ); 128 map( NSDeleteFunctionKey ,"delete" ); 129 map( NSHomeFunctionKey ,"home" ); 130 map( NSBeginFunctionKey ,"begin" ); 131 map( NSEndFunctionKey ,"end" ); 132 map( NSPageUpFunctionKey ,"pageup" ); 133 map( NSPageDownFunctionKey ,"pagedown" ); 134 map( NSPrintScreenFunctionKey ,"printscreen" ); 135 map( NSScrollLockFunctionKey ,"scrolllock" ); 136 map( NSPauseFunctionKey ,"pause" ); 137 map( NSSysReqFunctionKey ,"sysreq" ); 138 map( NSBreakFunctionKey ,"break" ); 139 map( NSResetFunctionKey ,"reset" ); 140 map( NSStopFunctionKey ,"stop" ); 141 map( NSMenuFunctionKey ,"menu" ); 142 map( NSUserFunctionKey ,"user" ); 143 map( NSSystemFunctionKey ,"system" ); 144 map( NSPrintFunctionKey ,"print" ); 145 map( NSClearLineFunctionKey ,"clear" ); 146 map( NSClearDisplayFunctionKey ,"cleardisplay" ); 147 map( NSInsertLineFunctionKey ,"insertline" ); 148 map( NSDeleteLineFunctionKey ,"deleteline" ); 149 map( NSInsertCharFunctionKey ,"insert" ); 150 map( NSDeleteCharFunctionKey ,"delete" ); 151 map( NSPrevFunctionKey ,"prev" ); 152 map( NSNextFunctionKey ,"next" ); 153 map( NSSelectFunctionKey ,"select" ); 154 map( NSExecuteFunctionKey ,"execute" ); 155 map( NSUndoFunctionKey ,"undo" ); 156 map( NSRedoFunctionKey ,"redo" ); 157 map( NSFindFunctionKey ,"find" ); 158 map( NSHelpFunctionKey ,"help" ); 159 map( NSModeSwitchFunctionKey ,"modeswitch" ); 160} 161 162 163- (id)initWithFrame:(NSRect)frame { 164 self = [super initWithFrame:frame]; 165 if (self) { 166 // Initialization code here. 167 wid = NULL; 168 processingCompose = NO; 169 workingText = nil; 170 delayed_rects = [[NSMutableArray arrayWithCapacity:100] retain]; 171 172 173 174 } 175 return self; 176} 177 178-(void) dealloc 179{ 180 [delayed_rects release]; 181 [self deleteWorkingText]; 182 [[NSNotificationCenter defaultCenter] removeObserver: self 183 name: @"NSWindowDidBecomeKeyNotification" 184 object: nil]; 185 [[NSNotificationCenter defaultCenter] removeObserver: self 186 name: @"NSWindowDidBecomeKeyNotification" 187 object: nil]; 188 189 [super dealloc]; 190} 191 192- (void) setWidget:(widget_rep*) w 193{ 194 wid = (simple_widget_rep*)w; 195} 196 197- (widget_rep*)widget 198{ 199 return (widget_rep*)wid; 200} 201 202- (void)setNeedsDisplayInTMRect:(TMRect*)r 203{ 204 [self setNeedsDisplayInRect:[r rect]]; 205} 206 207- (void)viewWillMoveToWindow:(NSWindow *)newWindow 208{ 209 // query widget preferred size 210 SI w,h; 211 wid->handle_get_size_hint (w,h); 212 NSSize s = NSMakeSize(w,h); 213 unscaleSize(s); 214 [self setFrameSize:s]; 215 216 // register to receive focus in/out notifications 217 [[NSNotificationCenter defaultCenter] removeObserver: self 218 name: @"NSWindowDidBecomeKeyNotification" 219 object: nil]; 220 [[NSNotificationCenter defaultCenter] removeObserver: self 221 name: @"NSWindowDidBecomeKeyNotification" 222 object: nil]; 223 224 [[NSNotificationCenter defaultCenter] addObserver: self 225 selector: @selector(focusIn) 226 name: @"NSWindowDidBecomeKeyNotification" 227 object: newWindow]; 228 229 [[NSNotificationCenter defaultCenter] addObserver: self 230 selector: @selector(focusOut) 231 name: @"NSWindowDidResignKeyNotification" 232 object: newWindow]; 233 234} 235 236 237 238- (void) focusIn 239{ 240 if (DEBUG_EVENTS) debug_events << "FOCUSIN" << LF; 241 if (wid) { 242 wid -> handle_keyboard_focus (true, texmacs_time ()); 243 } 244} 245 246- (void) focusOut 247{ 248 if (DEBUG_EVENTS) debug_events << "FOCUSOUT" << LF; 249 if (wid) { 250 wid -> handle_keyboard_focus (false, texmacs_time ()); 251 } 252} 253 254- (void)delayedUpdate 255{ 256 NSMutableArray *arr = delayed_rects; 257 NSEnumerator *enumerator = [arr objectEnumerator]; 258 TMRect *anObject; 259 delayed_rects = [[NSMutableArray arrayWithCapacity:10] retain]; 260 while ((anObject = [enumerator nextObject])) { 261 [self displayRect:[anObject rect]]; 262 } 263 [arr release]; 264} 265 266 267 268- (void)drawRect:(NSRect)rect 269{ 270 if (aqua_update_flag) { 271 [delayed_rects addObject:[[[TMRect alloc] initWithRect:rect] autorelease]]; 272 return; 273 } 274 275 // Drawing code here. 276 if ([self inLiveResize]) 277 { 278 NSRect bounds = [self bounds]; 279 [[NSColor blackColor] set]; 280 [NSBezierPath strokeRect:NSInsetRect(bounds,1,1)]; 281 // return; 282 } 283// debug_events << "DRAWING : " << rect.origin.x << ","<< rect.origin.x << ","<< rect.size.width<< "," << rect.size.height << "\n"; 284// NSRect bounds = [self bounds]; 285 286 { 287 basic_renderer r = the_aqua_renderer(); 288 int x1 = rect.origin.x; 289 int y1 = rect.origin.y+rect.size.height; 290 int x2 = rect.origin.x+rect.size.width; 291 int y2 = rect.origin.y; 292 293 r -> begin([NSGraphicsContext currentContext]); 294 // r -> set_origin(0,0); 295 r -> encode (x1,y1); 296 r -> encode (x2,y2); 297 // debug_events << "DRAWING RECT " << x1 << "," << y1 << "," << x2 << "," << y2 << LF; 298 r -> set_clipping (x1,y1,x2,y2); 299 wid->handle_repaint (x1,y1,x2,y2); 300 r->end(); 301 if (gui_interrupted()) 302 aqua_update_flag= true; 303 } 304// debug_events << "END DRAWING" << "\n"; 305 306 if (aqua_update_flag) { 307 if (DEBUG_EVENTS) 308 debug_events << "Postponed redrawing\n"; 309 [self performSelector:@selector(delayedUpdate) withObject: nil afterDelay: 10]; 310 } 311 312} 313 314 315#if 0 316- (void)keyDown:(NSEvent *)theEvent 317{ 318 if (!wid) return; 319 320 { 321 char str[256]; 322 string r; 323 NSString *nss = [theEvent charactersIgnoringModifiers]; 324 unsigned int mods = [theEvent modifierFlags]; 325 326 327 328 if (([nss length]==1)&& (!processingCompose)) 329 330 { 331 int key = [nss characterAtIndex:0]; 332 if (nskeymap->contains(key)) { 333 r = nskeymap[key]; 334 r = ((mods & NSShiftKeyMask)? "S-" * r: r); 335 } 336 else 337 { 338 [nss getCString:str maxLength:256 encoding:NSUTF8StringEncoding]; 339 string rr (str, strlen(str)); 340 r= utf8_to_cork (rr); 341 } 342 343 344 string s (r); 345 if (! contains_unicode_char (s)) 346 { 347 // string s= ((mods & NSShiftKeyMask)? "S-" * r: r); 348 /* other keyboard modifiers */ 349 if (N(s)!=0) { 350 if (mods & NSControlKeyMask ) s= "C-" * s; 351 if (mods & NSAlternateKeyMask) s= "A-" * s; 352 if (mods & NSCommandKeyMask) s= "M-" * s; 353 // if (mods & NSNumericPadKeyMask) s= "K-" * s; 354 // if (mods & NSHelpKeyMask) s= "H-" * s; 355 // if (mods & NSFunctionKeyMask) s= "F-" * s; 356 } 357 debug_events << "key press: " << s << LF; 358 wid -> handle_keypress (s, texmacs_time()); 359 } 360 } 361 else { 362 processingCompose = YES; 363 static NSMutableArray *nsEvArray = nil; 364 if (nsEvArray == nil) 365 nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; 366 367 [nsEvArray addObject: theEvent]; 368 [self interpretKeyEvents: nsEvArray]; 369 [nsEvArray removeObject: theEvent]; 370 } 371 } 372 373 374} 375#else 376- (void)keyDown:(NSEvent *)theEvent 377{ 378 if (!wid) return; 379 380 time_credit= 25; 381 timeout_time= texmacs_time () + time_credit; 382 static bool fInit = false; 383 if (!fInit) { 384 if (DEBUG_EVENTS) 385 debug_events << "Initializing keymap\n"; 386 initkeymap(); 387 fInit= true; 388 } 389 390 { 391 // char str[256]; 392 string r; 393 NSString *nss = [theEvent charactersIgnoringModifiers]; 394 unsigned int mods = [theEvent modifierFlags]; 395 396 string modstr; 397 398 if (mods & NSControlKeyMask ) modstr= "C-" * modstr; 399 if (mods & NSAlternateKeyMask) modstr= "A-" * modstr; 400 if (mods & NSCommandKeyMask) modstr= "M-" * modstr; 401 // if (mods & NSNumericPadKeyMask) modstr= "K-" * modstr; 402 // if (mods & NSHelpKeyMask) modstr= "H-" * modstr; 403 // if (mods & NSFunctionKeyMask) modstr= "F-" * modstr; 404 405 // if (!processingCompose) 406 { 407 if ([nss length]>0) { 408 int key = [nss characterAtIndex:0]; 409 if (nskeymap->contains(key)) { 410 r = nskeymap[key]; 411 r = ((mods & NSShiftKeyMask)? "S-" * modstr: modstr) * r; 412 debug_events << "function key press: " << r << LF; 413 [self deleteWorkingText]; 414 wid -> handle_keypress (r, texmacs_time()); 415 return; 416 } else if (mods & (NSControlKeyMask | NSCommandKeyMask | NSHelpKeyMask)) 417 { 418 static char str[256]; 419 [nss getCString:str maxLength:256 encoding:NSUTF8StringEncoding]; 420 string rr (str, strlen(str)); 421 r= utf8_to_cork (rr); 422 423 string s ( modstr * r); 424 debug_events << "modified key press: " << s << LF; 425 [self deleteWorkingText]; 426 wid -> handle_keypress (s, texmacs_time()); 427 the_gui->update (); // FIXME: remove this line when 428 // edit_typeset_rep::get_env_value will be faster 429 430 return; 431 } 432 } 433 } 434 435 processingCompose = YES; 436 static NSMutableArray *nsEvArray = nil; 437 if (nsEvArray == nil) 438 nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; 439 440 [nsEvArray addObject: theEvent]; 441 [self interpretKeyEvents: nsEvArray]; 442 [nsEvArray removeObject: theEvent]; 443 } 444} 445 446#endif 447 448static unsigned int 449mouse_state (NSEvent* event, bool flag) { 450 unsigned int i= 0; 451 i += 1 << min([event buttonNumber],4); 452 unsigned int mods = [event modifierFlags]; 453 if (mods & NSAlternateKeyMask) i = 2; 454 if (mods & NSCommandKeyMask) i = 4; 455 if (mods & NSShiftKeyMask) i += 256; 456 if (mods & NSControlKeyMask) i += 2048; 457 return i; 458} 459 460static string 461mouse_decode (unsigned int mstate) { 462 if (mstate & 1 ) return "left"; 463 else if (mstate & 2 ) return "middle"; 464 else if (mstate & 4 ) return "right"; 465 else if (mstate & 8 ) return "up"; 466 else if (mstate & 16) return "down"; 467 return "unknown"; 468} 469 470 471 472- (void)mouseDown:(NSEvent *)theEvent 473{ 474 if (wid) { 475 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 476 scale(point); 477 unsigned int mstate= mouse_state (theEvent, false); 478 string s= "press-" * mouse_decode (mstate); 479 wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ()); 480 if (DEBUG_EVENTS) 481 debug_events << "mouse event: " << s << " at " 482 << point.x << ", " << point.y << LF; 483 } 484} 485 486- (void)mouseUp:(NSEvent *)theEvent 487{ 488 if (wid) { 489 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 490 scale(point); 491 unsigned int mstate= mouse_state (theEvent, true); 492 string s= "release-" * mouse_decode (mstate); 493 wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ()); 494 if (DEBUG_EVENTS) 495 debug_events << "mouse event: " << s << " at " 496 << point.x << ", " << point.y << LF; 497 } 498} 499 500- (void)mouseDragged:(NSEvent *)theEvent 501{ 502 if (wid) { 503 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 504 scale(point); 505 unsigned int mstate= mouse_state (theEvent, false); 506 string s= "move"; 507 wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ()); 508 if (DEBUG_EVENTS) 509 debug_events << "mouse event: " << s << " at " 510 << point.x << ", " << point.y << LF; 511 } 512} 513 514- (void)mouseMoved:(NSEvent *)theEvent 515{ 516 if (wid) { 517 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 518 scale(point); 519 unsigned int mstate= mouse_state (theEvent, false); 520 string s= "move"; 521 wid -> handle_mouse (s, point.x , point.y , mstate, texmacs_time ()); 522 if (DEBUG_EVENTS) 523 debug_events << "mouse event: " << s << " at " 524 << point.x << ", " << point.y << LF; 525 } 526} 527 528- (BOOL)isFlipped 529{ 530 return YES; 531} 532 533- (BOOL)isOpaque 534{ 535 return YES; 536} 537 538- (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize 539{ 540 [super resizeWithOldSuperviewSize:oldBoundsSize]; 541 if (wid) { 542 NSSize size = [self bounds].size; 543 scaleSize(size); 544 wid-> handle_notify_resize (size.width, size.height); 545 } 546 547} 548 549- (BOOL)acceptsFirstResponder 550{ 551 return YES; 552} 553 554- (void) deleteWorkingText 555{ 556 if (workingText == nil) 557 return; 558 [workingText release]; 559 workingText = nil; 560 processingCompose = NO; 561 562 563} 564 565#pragma mark NSTextInput protocol implementation 566 567 568- (void) insertText:(id)aString 569// instead of keyDown: aString can be NSString or NSAttributedString 570{ 571 processingCompose = NO; 572 NSLog(@"insertText: <%@>",aString); 573 574 NSString *str = [aString respondsToSelector: @selector(string)] ? 575 [aString string] : aString; 576 577 static char buf[256]; 578 for(unsigned int i=0; i<[str length]; i++) { 579 [[str substringWithRange:NSMakeRange(i, 1)] getCString:buf maxLength:256 encoding:NSUTF8StringEncoding]; 580 string rr (buf, strlen(buf)); 581 string s= utf8_to_cork (rr); 582 debug_events << "key press: " << s << LF; 583 wid -> handle_keypress (s, texmacs_time()); 584 } 585} 586 587- (void) doCommandBySelector:(SEL)aSelector 588{ 589} 590 591// setMarkedText: cannot take a nil first argument. aString can be NSString or NSAttributedString 592- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange 593{ 594 NSString *str = [aString respondsToSelector: @selector(string)] ? 595 [aString string] : aString; 596 597 if (workingText != nil) 598 [self deleteWorkingText]; 599 if ([str length] == 0) 600 return; 601 workingText = [str copy]; 602 processingCompose = YES; 603 NSLog(@"setMarkedText: <%@>",workingText); 604 605} 606 607- (void) unmarkText 608{ 609 [self deleteWorkingText]; 610} 611- (BOOL) hasMarkedText 612{ 613 return workingText != nil; 614 615} 616- (NSInteger) conversationIdentifier 617{ 618 return (NSInteger)self; 619} 620 621/* Returns attributed string at the range. This allows input mangers to query any range in backing-store. May return nil. 622 */ 623- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange 624{ 625 static NSAttributedString *str = nil; 626 if (str == nil) str = [NSAttributedString new]; 627 return str; 628} 629 630/* This method returns the range for marked region. If hasMarkedText == false, it'll return NSNotFound location & 0 length range. 631 */ 632- (NSRange) markedRange 633{ 634 NSRange rng = workingText != nil 635 ? NSMakeRange(0, [workingText length]) : NSMakeRange(NSNotFound, 0); 636 return rng; 637 638} 639 640/* This method returns the range for selected region. Just like markedRange method, its location field contains char index from the text beginning. 641 */ 642- (NSRange) selectedRange 643{ 644 return NSMakeRange(NSNotFound, 0); 645} 646/* This method returns the first frame of rects for theRange in screen coordindate system. 647 */ 648- (NSRect) firstRectForCharacterRange:(NSRange)theRange 649{ 650 return NSMakeRect(0,0,50,50); 651} 652 653/* This method returns the index for character that is nearest to thePoint. thePoint is in screen coordinate system. 654 */ 655- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint 656{ 657 return 0; 658} 659 660/* 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. 661 */ 662- (NSArray*) validAttributesForMarkedText 663{ 664 static NSArray *arr = nil; 665 if (arr == nil) arr = [NSArray new]; 666 return arr; 667} 668 669 670@end 671