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