1 /* GdkQuartzView.m
2  *
3  * Copyright (C) 2005-2007 Imendio AB
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 #import "GdkQuartzView.h"
22 #include "gdkregion.h"
23 #include "gdkregion-generic.h"
24 #include "gdkwindow-quartz.h"
25 #include "gdkprivate-quartz.h"
26 #include "gdkquartz.h"
27 
28 @implementation GdkQuartzView
29 
30 -(id)initWithFrame: (NSRect)frameRect
31 {
32   if ((self = [super initWithFrame: frameRect]))
33     {
34       markedRange = NSMakeRange (NSNotFound, 0);
35       selectedRange = NSMakeRange (NSNotFound, 0);
36     }
37 
38   return self;
39 }
40 
41 -(BOOL)acceptsFirstResponder
42 {
43   GDK_NOTE (EVENTS, g_print ("acceptsFirstResponder\n"));
44   return YES;
45 }
46 
47 -(BOOL)becomeFirstResponder
48 {
49   GDK_NOTE (EVENTS, g_print ("becomeFirstResponder\n"));
50   return YES;
51 }
52 
53 -(BOOL)resignFirstResponder
54 {
55   GDK_NOTE (EVENTS, g_print ("resignFirstResponder\n"));
56   return YES;
57 }
58 
59 -(void) keyDown: (NSEvent *) theEvent
60 {
61   GDK_NOTE (EVENTS, g_print ("keyDown\n"));
62   [self interpretKeyEvents: [NSArray arrayWithObject: theEvent]];
63 }
64 
65 -(void)flagsChanged: (NSEvent *) theEvent
66 {
67 }
68 
69 -(NSUInteger)characterIndexForPoint: (NSPoint)aPoint
70 {
71   GDK_NOTE (EVENTS, g_print ("characterIndexForPoint\n"));
72   return 0;
73 }
74 
75 -(NSRect)firstRectForCharacterRange: (NSRange)aRange actualRange: (NSRangePointer)actualRange
76 {
77   GDK_NOTE (EVENTS, g_print ("firstRectForCharacterRange\n"));
78   gint ns_x, ns_y;
79   GdkRectangle *rect;
80 
81   rect = g_object_get_data (G_OBJECT (gdk_window), GIC_CURSOR_RECT);
82   if (rect)
83     {
84       _gdk_quartz_window_gdk_xy_to_xy (rect->x, rect->y + rect->height,
85 				       &ns_x, &ns_y);
86 
87       return NSMakeRect (ns_x, ns_y, rect->width, rect->height);
88     }
89   else
90     {
91       return NSMakeRect (0, 0, 0, 0);
92     }
93 }
94 
95 -(NSArray *)validAttributesForMarkedText
96 {
97   GDK_NOTE (EVENTS, g_print ("validAttributesForMarkedText\n"));
98   return [NSArray arrayWithObjects: NSUnderlineStyleAttributeName, nil];
99 }
100 
101 -(NSAttributedString *)attributedSubstringForProposedRange: (NSRange)aRange actualRange: (NSRangePointer)actualRange
102 {
103   GDK_NOTE (EVENTS, g_print ("attributedSubstringForProposedRange\n"));
104   return nil;
105 }
106 
107 -(BOOL)hasMarkedText
108 {
109   GDK_NOTE (EVENTS, g_print ("hasMarkedText\n"));
110   return markedRange.location != NSNotFound && markedRange.length != 0;
111 }
112 
113 -(NSRange)markedRange
114 {
115   GDK_NOTE (EVENTS, g_print ("markedRange\n"));
116   return markedRange;
117 }
118 
119 -(NSRange)selectedRange
120 {
121   GDK_NOTE (EVENTS, g_print ("selectedRange\n"));
122   return selectedRange;
123 }
124 
125 -(void)unmarkText
126 {
127   GDK_NOTE (EVENTS, g_print ("unmarkText\n"));
128   gchar *prev_str;
129   markedRange = selectedRange = NSMakeRange (NSNotFound, 0);
130 
131   prev_str = g_object_get_data (G_OBJECT (gdk_window), TIC_MARKED_TEXT);
132   if (prev_str)
133     g_free (prev_str);
134   g_object_set_data (G_OBJECT (gdk_window), TIC_MARKED_TEXT, NULL);
135 }
136 
137 -(void)setMarkedText: (id)aString selectedRange: (NSRange)newSelection replacementRange: (NSRange)replacementRange
138 {
139   GDK_NOTE (EVENTS, g_print ("setMarkedText\n"));
140   const char *str;
141   gchar *prev_str;
142 
143   if (replacementRange.location == NSNotFound)
144     {
145       markedRange = NSMakeRange (newSelection.location, [aString length]);
146       selectedRange = NSMakeRange (newSelection.location, newSelection.length);
147     }
148   else {
149       markedRange = NSMakeRange (replacementRange.location, [aString length]);
150       selectedRange = NSMakeRange (replacementRange.location + newSelection.location, newSelection.length);
151     }
152 
153   if ([aString isKindOfClass: [NSAttributedString class]])
154     {
155       str = [[aString string] UTF8String];
156     }
157   else {
158       str = [aString UTF8String];
159     }
160 
161   prev_str = g_object_get_data (G_OBJECT (gdk_window), TIC_MARKED_TEXT);
162   if (prev_str)
163     g_free (prev_str);
164   g_object_set_data (G_OBJECT (gdk_window), TIC_MARKED_TEXT, g_strdup (str));
165   g_object_set_data (G_OBJECT (gdk_window), TIC_SELECTED_POS,
166 		     GUINT_TO_POINTER (selectedRange.location));
167   g_object_set_data (G_OBJECT (gdk_window), TIC_SELECTED_LEN,
168 		     GUINT_TO_POINTER (selectedRange.length));
169 
170   GDK_NOTE (EVENTS, g_print ("setMarkedText: set %s (%p, nsview %p): %s\n",
171 			     TIC_MARKED_TEXT, gdk_window, self,
172 			     str ? str : "(empty)"));
173 
174   /* handle text input changes by mouse events */
175   if (!GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (gdk_window),
176                                             TIC_IN_KEY_DOWN)))
177     {
178       _gdk_quartz_synthesize_null_key_event(gdk_window);
179     }
180 }
181 
182 -(void)doCommandBySelector: (SEL)aSelector
183 {
184   GDK_NOTE (EVENTS, g_print ("doCommandBySelector\n"));
185   if ([self respondsToSelector: aSelector])
186     [self performSelector: aSelector];
187 }
188 
189 /* This gets called on OS X 10.6 and upwards from interpretKeyEvents */
190 -(void)insertText: (id)aString replacementRange: (NSRange)replacementRange
191 {
192   [self insertText:aString];
193 }
194 
195 /* This gets called on OS X 10.5 from interpretKeyEvents, although 10.5
196  * is supposed to support NSTextInputClient  */
197 -(void)insertText: (id)aString
198 {
199   GDK_NOTE (EVENTS, g_print ("insertText\n"));
200   const char *str;
201   NSString *string;
202   gchar *prev_str;
203 
204   if ([self hasMarkedText])
205     [self unmarkText];
206 
207   if ([aString isKindOfClass: [NSAttributedString class]])
208       string = [aString string];
209   else
210       string = aString;
211 
212   NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
213   NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
214   if ([string rangeOfCharacterFromSet:ctrlChars].length &&
215       [string rangeOfCharacterFromSet:wsnlChars].length == 0)
216     {
217       /* discard invalid text input with Chinese input methods */
218       str = "";
219       [self unmarkText];
220       NSInputManager *currentInputManager = [NSInputManager currentInputManager];
221       [currentInputManager markedTextAbandoned:self];
222     }
223   else
224    {
225       str = [string UTF8String];
226    }
227 
228   prev_str = g_object_get_data (G_OBJECT (gdk_window), TIC_INSERT_TEXT);
229   if (prev_str)
230     g_free (prev_str);
231   g_object_set_data (G_OBJECT (gdk_window), TIC_INSERT_TEXT, g_strdup (str));
232   GDK_NOTE (EVENTS, g_print ("insertText: set %s (%p, nsview %p): %s\n",
233 			     TIC_INSERT_TEXT, gdk_window, self,
234 			     str ? str : "(empty)"));
235 
236   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
237 		     GUINT_TO_POINTER (GIC_FILTER_FILTERED));
238 
239   /* handle text input changes by mouse events */
240   if (!GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (gdk_window),
241                                             TIC_IN_KEY_DOWN)))
242     {
243       _gdk_quartz_synthesize_null_key_event(gdk_window);
244     }
245 }
246 
247 -(void)deleteBackward: (id)sender
248 {
249   GDK_NOTE (EVENTS, g_print ("deleteBackward\n"));
250   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
251 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
252 }
253 
254 -(void)deleteForward: (id)sender
255 {
256   GDK_NOTE (EVENTS, g_print ("deleteForward\n"));
257   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
258 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
259 }
260 
261 -(void)deleteToBeginningOfLine: (id)sender
262 {
263   GDK_NOTE (EVENTS, g_print ("deleteToBeginningOfLine\n"));
264   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
265 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
266 }
267 
268 -(void)deleteToEndOfLine: (id)sender
269 {
270   GDK_NOTE (EVENTS, g_print ("deleteToEndOfLine\n"));
271   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
272 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
273 }
274 
275 -(void)deleteWordBackward: (id)sender
276 {
277   GDK_NOTE (EVENTS, g_print ("deleteWordBackward\n"));
278   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
279 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
280 }
281 
282 -(void)deleteWordForward: (id)sender
283 {
284   GDK_NOTE (EVENTS, g_print ("deleteWordForward\n"));
285   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
286 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
287 }
288 
289 -(void)insertBacktab: (id)sender
290 {
291   GDK_NOTE (EVENTS, g_print ("insertBacktab\n"));
292   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
293 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
294 }
295 
296 -(void)insertNewline: (id)sender
297 {
298   GDK_NOTE (EVENTS, g_print ("insertNewline\n"));
299   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY, GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
300 }
301 
302 -(void)insertTab: (id)sender
303 {
304   GDK_NOTE (EVENTS, g_print ("insertTab\n"));
305   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
306 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
307 }
308 
309 -(void)moveBackward: (id)sender
310 {
311   GDK_NOTE (EVENTS, g_print ("moveBackward\n"));
312   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
313 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
314 }
315 
316 -(void)moveBackwardAndModifySelection: (id)sender
317 {
318   GDK_NOTE (EVENTS, g_print ("moveBackwardAndModifySelection\n"));
319   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
320 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
321 }
322 
323 -(void)moveDown: (id)sender
324 {
325   GDK_NOTE (EVENTS, g_print ("moveDown\n"));
326   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
327 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
328 }
329 
330 -(void)moveDownAndModifySelection: (id)sender
331 {
332   GDK_NOTE (EVENTS, g_print ("moveDownAndModifySelection\n"));
333   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
334 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
335 }
336 
337 -(void)moveForward: (id)sender
338 {
339   GDK_NOTE (EVENTS, g_print ("moveForward\n"));
340   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
341 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
342 }
343 
344 -(void)moveForwardAndModifySelection: (id)sender
345 {
346   GDK_NOTE (EVENTS, g_print ("moveForwardAndModifySelection\n"));
347   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
348 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
349 }
350 
351 -(void)moveLeft: (id)sender
352 {
353   GDK_NOTE (EVENTS, g_print ("moveLeft\n"));
354   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
355 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
356 }
357 
358 -(void)moveLeftAndModifySelection: (id)sender
359 {
360   GDK_NOTE (EVENTS, g_print ("moveLeftAndModifySelection\n"));
361   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
362 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
363 }
364 
365 -(void)moveRight: (id)sender
366 {
367   GDK_NOTE (EVENTS, g_print ("moveRight\n"));
368   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
369 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
370 }
371 
372 -(void)moveRightAndModifySelection: (id)sender
373 {
374   GDK_NOTE (EVENTS, g_print ("moveRightAndModifySelection\n"));
375   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
376 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
377 }
378 
379 -(void)moveToBeginningOfDocument: (id)sender
380 {
381   GDK_NOTE (EVENTS, g_print ("moveToBeginningOfDocument\n"));
382   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
383 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
384 }
385 
386 -(void)moveToBeginningOfDocumentAndModifySelection: (id)sender
387 {
388   GDK_NOTE (EVENTS, g_print ("moveToBeginningOfDocumentAndModifySelection\n"));
389   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
390 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
391 }
392 
393 -(void)moveToBeginningOfLine: (id)sender
394 {
395   GDK_NOTE (EVENTS, g_print ("moveToBeginningOfLine\n"));
396   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
397 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
398 }
399 
400 -(void)moveToBeginningOfLineAndModifySelection: (id)sender
401 {
402   GDK_NOTE (EVENTS, g_print ("moveToBeginningOfLineAndModifySelection\n"));
403   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
404 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
405 }
406 
407 -(void)moveToEndOfDocument: (id)sender
408 {
409   GDK_NOTE (EVENTS, g_print ("moveToEndOfDocument\n"));
410   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
411 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
412 }
413 
414 -(void)moveToEndOfDocumentAndModifySelection: (id)sender
415 {
416   GDK_NOTE (EVENTS, g_print ("moveToEndOfDocumentAndModifySelection\n"));
417   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
418 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
419 }
420 
421 -(void)moveToEndOfLine: (id)sender
422 {
423   GDK_NOTE (EVENTS, g_print ("moveToEndOfLine\n"));
424   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
425 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
426 }
427 
428 -(void)moveToEndOfLineAndModifySelection: (id)sender
429 {
430   GDK_NOTE (EVENTS, g_print ("moveToEndOfLineAndModifySelection\n"));
431   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
432 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
433 }
434 
435 -(void)moveUp: (id)sender
436 {
437   GDK_NOTE (EVENTS, g_print ("moveUp\n"));
438   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
439 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
440 }
441 
442 -(void)moveUpAndModifySelection: (id)sender
443 {
444   GDK_NOTE (EVENTS, g_print ("moveUpAndModifySelection\n"));
445   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
446 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
447 }
448 
449 -(void)moveWordBackward: (id)sender
450 {
451   GDK_NOTE (EVENTS, g_print ("moveWordBackward\n"));
452   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
453 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
454 }
455 
456 -(void)moveWordBackwardAndModifySelection: (id)sender
457 {
458   GDK_NOTE (EVENTS, g_print ("moveWordBackwardAndModifySelection\n"));
459   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
460 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
461 }
462 
463 -(void)moveWordForward: (id)sender
464 {
465   GDK_NOTE (EVENTS, g_print ("moveWordForward\n"));
466   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
467 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
468 }
469 
470 -(void)moveWordForwardAndModifySelection: (id)sender
471 {
472   GDK_NOTE (EVENTS, g_print ("moveWordForwardAndModifySelection\n"));
473   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
474 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
475 }
476 
477 -(void)moveWordLeft: (id)sender
478 {
479   GDK_NOTE (EVENTS, g_print ("moveWordLeft\n"));
480   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
481 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
482 }
483 
484 -(void)moveWordLeftAndModifySelection: (id)sender
485 {
486   GDK_NOTE (EVENTS, g_print ("moveWordLeftAndModifySelection\n"));
487   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
488 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
489 }
490 
491 -(void)moveWordRight: (id)sender
492 {
493   GDK_NOTE (EVENTS, g_print ("moveWordRight\n"));
494   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
495 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
496 }
497 
498 -(void)moveWordRightAndModifySelection: (id)sender
499 {
500   GDK_NOTE (EVENTS, g_print ("moveWordRightAndModifySelection\n"));
501   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
502 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
503 }
504 
505 -(void)pageDown: (id)sender
506 {
507   GDK_NOTE (EVENTS, g_print ("pageDown\n"));
508   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
509 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
510 }
511 
512 -(void)pageDownAndModifySelection: (id)sender
513 {
514   GDK_NOTE (EVENTS, g_print ("pageDownAndModifySelection\n"));
515   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
516 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
517 }
518 
519 -(void)pageUp: (id)sender
520 {
521   GDK_NOTE (EVENTS, g_print ("pageUp\n"));
522   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
523 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
524 }
525 
526 -(void)pageUpAndModifySelection: (id)sender
527 {
528   GDK_NOTE (EVENTS, g_print ("pageUpAndModifySelection\n"));
529   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
530 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
531 }
532 
533 -(void)selectAll: (id)sender
534 {
535   GDK_NOTE (EVENTS, g_print ("selectAll\n"));
536   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
537 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
538 }
539 
540 -(void)selectLine: (id)sender
541 {
542   GDK_NOTE (EVENTS, g_print ("selectLine\n"));
543   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
544 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
545 }
546 
547 -(void)selectWord: (id)sender
548 {
549   GDK_NOTE (EVENTS, g_print ("selectWord\n"));
550   g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
551 		     GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
552 }
553 
554 -(void)noop: (id)sender
555 {
556   GDK_NOTE (EVENTS, g_print ("noop\n"));
557 }
558 
559 /* --------------------------------------------------------------- */
560 
561 -(void)dealloc
562 {
563   if (trackingRect)
564     {
565       [self removeTrackingRect: trackingRect];
566       trackingRect = 0;
567     }
568 
569   [super dealloc];
570 }
571 
572 -(void)setGdkWindow: (GdkWindow *)window
573 {
574   gdk_window = window;
575 }
576 
577 -(GdkWindow *)gdkWindow
578 {
579   return gdk_window;
580 }
581 
582 -(NSTrackingRectTag)trackingRect
583 {
584   return trackingRect;
585 }
586 
587 -(BOOL)isFlipped
588 {
589   return YES;
590 }
591 
592 -(BOOL)isOpaque
593 {
594   if (GDK_WINDOW_DESTROYED (gdk_window))
595     return YES;
596 
597   /* A view is opaque if its GdkWindow doesn't have the RGBA colormap */
598   return gdk_drawable_get_colormap (gdk_window) !=
599     gdk_screen_get_rgba_colormap (_gdk_screen);
600 }
601 
602 -(void)drawRect: (NSRect)rect
603 {
604   GdkRectangle gdk_rect;
605   GdkWindowObject *private = GDK_WINDOW_OBJECT (gdk_window);
606   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
607   const NSRect *drawn_rects;
608   NSInteger count;
609   int i;
610   GdkRegion *region;
611 
612   if (GDK_WINDOW_DESTROYED (gdk_window))
613     return;
614 
615   if (! (private->event_mask & GDK_EXPOSURE_MASK))
616     return;
617 
618   if (NSEqualRects (rect, NSZeroRect))
619     return;
620 
621   if (!GDK_WINDOW_IS_MAPPED (gdk_window))
622     {
623       /* If the window is not yet mapped, clip_region_with_children
624        * will be empty causing the usual code below to draw nothing.
625        * To not see garbage on the screen, we draw an aesthetic color
626        * here. The garbage would be visible if any widget enabled
627        * the NSView's CALayer in order to add sublayers for custom
628        * native rendering.
629        */
630       [NSGraphicsContext saveGraphicsState];
631 
632       [[NSColor windowBackgroundColor] setFill];
633       [NSBezierPath fillRect: rect];
634 
635       [NSGraphicsContext restoreGraphicsState];
636 
637       return;
638     }
639 
640   /* Clear our own bookkeeping of regions that need display */
641   if (impl->needs_display_region)
642     {
643       gdk_region_destroy (impl->needs_display_region);
644       impl->needs_display_region = NULL;
645     }
646 
647   [self getRectsBeingDrawn: &drawn_rects count: &count];
648   region = gdk_region_new ();
649 
650   for (i = 0; i < count; i++)
651     {
652       gdk_rect.x = drawn_rects[i].origin.x;
653       gdk_rect.y = drawn_rects[i].origin.y;
654       gdk_rect.width = drawn_rects[i].size.width;
655       gdk_rect.height = drawn_rects[i].size.height;
656 
657       gdk_region_union_with_rect (region, &gdk_rect);
658     }
659 
660   impl->in_paint_rect_count++;
661   _gdk_window_process_updates_recurse (gdk_window, region);
662   impl->in_paint_rect_count--;
663 
664   gdk_region_destroy (region);
665 
666   if (needsInvalidateShadow)
667     {
668       [[self window] invalidateShadow];
669       needsInvalidateShadow = NO;
670     }
671 }
672 
673 -(void)setNeedsInvalidateShadow: (BOOL)invalidate
674 {
675   needsInvalidateShadow = invalidate;
676 }
677 
678 /* For information on setting up tracking rects properly, see here:
679  * http://developer.apple.com/documentation/Cocoa/Conceptual/EventOverview/EventOverview.pdf
680  */
681 -(void)updateTrackingRect
682 {
683   GdkWindowObject *private = GDK_WINDOW_OBJECT (gdk_window);
684   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
685   NSRect rect;
686 
687   if (!impl || !impl->toplevel)
688     return;
689 
690   if (trackingRect)
691     {
692       [self removeTrackingRect: trackingRect];
693       trackingRect = 0;
694     }
695 
696   if (!impl->toplevel)
697     return;
698 
699   /* Note, if we want to set assumeInside we can use:
700    * NSPointInRect ([[self window] convertScreenToBase:[NSEvent mouseLocation]], rect)
701    */
702 
703   rect = [self bounds];
704   trackingRect = [self addTrackingRect: rect
705 		  owner: self
706 		  userData: nil
707 		  assumeInside: NO];
708 }
709 
710 -(void)viewDidMoveToWindow
711 {
712   if (![self window]) /* We are destroyed already */
713     return;
714 
715   [self updateTrackingRect];
716 }
717 
718 -(void)viewWillMoveToWindow: (NSWindow *)newWindow
719 {
720   if (newWindow == nil && trackingRect)
721     {
722       [self removeTrackingRect: trackingRect];
723       trackingRect = 0;
724     }
725 }
726 
727 -(void)setFrame: (NSRect)frame
728 {
729   [super setFrame: frame];
730 
731   if ([self window])
732     [self updateTrackingRect];
733 }
734 
735 @end
736