ROOT  6.06/08
Reference Guide
QuartzWindow.mm
Go to the documentation of this file.
1 // @(#)root/graf2d:$Id$
2 // Author: Timur Pocheptsov 16/02/2012
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //#define DEBUG_ROOT_COCOA
13 
14 //#define NDEBUG
15 
16 #ifdef DEBUG_ROOT_COCOA
17 #include <iostream>
18 #include <fstream>
19 
20 #include "TClass.h"
21 #endif
22 
23 #include <algorithm>
24 #include <stdexcept>
25 #include <cassert>
26 #include <vector>
27 
28 #include <Availability.h>
29 
30 #include "ROOTOpenGLView.h"
31 #include "QuartzWindow.h"
32 #include "QuartzPixmap.h"
33 #include "QuartzUtils.h"
34 #include "CocoaUtils.h"
35 #include "RConfigure.h"
36 #include "X11Colors.h"
37 #include "X11Buffer.h"
38 #include "TGWindow.h"
39 #include "TGClient.h"
40 #include "TSystem.h"
41 #include "TGCocoa.h"
42 
43 
44 namespace ROOT {
45 namespace MacOSX {
46 namespace X11 {
47 
48 #pragma mark - Create a window or a view.
49 
50 //______________________________________________________________________________
52  UInt_t clss, void */*visual*/, SetWindowAttributes_t *attr, UInt_t)
53 {
54  NSRect winRect = {};
55  winRect.origin.x = GlobalXROOTToCocoa(x);
56  winRect.origin.y = GlobalYROOTToCocoa(y + h);
57  winRect.size.width = w;
58  winRect.size.height = h;
59 
60  const NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask |
61  NSMiniaturizableWindowMask | NSResizableWindowMask;
62 
63  QuartzWindow * const newWindow = [[QuartzWindow alloc] initWithContentRect : winRect
64  styleMask : styleMask
65  backing : NSBackingStoreBuffered
66  defer : YES
67  windowAttributes : attr];
68  if (!newWindow)
69  throw std::runtime_error("CreateTopLevelWindow failed");
70 
71  newWindow.fDepth = depth;
72  newWindow.fClass = clss;
73 
74  return newWindow;
75 }
76 
77 //______________________________________________________________________________
78 QuartzView *CreateChildView(QuartzView * /*parent*/, Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t /*border*/, Int_t /*depth*/,
79  UInt_t /*clss*/, void * /*visual*/, SetWindowAttributes_t *attr, UInt_t /*wtype*/)
80 {
81  NSRect viewRect = {};
82  viewRect.origin.x = x;
83  viewRect.origin.y = y;
84  viewRect.size.width = w;
85  viewRect.size.height = h;
86 
87  QuartzView * const view = [[QuartzView alloc] initWithFrame : viewRect windowAttributes : attr];
88  if (!view)
89  throw std::runtime_error("CreateChildView failed");
90 
91  return view;
92 }
93 
94 #pragma mark - root window (does not really exist, it's our desktop built of all screens).
95 
96 //______________________________________________________________________________
98 {
99  //'root' window does not exist, but we can request its attributes.
100  assert(attr != 0 && "GetRootWindowAttributes, parameter 'attr' is null");
101 
102 
103  NSArray * const screens = [NSScreen screens];
104  assert(screens != nil && "screens array is nil");
105  NSScreen * const mainScreen = [screens objectAtIndex : 0];
106  assert(mainScreen != nil && "screen with index 0 is nil");
107 
108  *attr = WindowAttributes_t();
109 
110  assert(dynamic_cast<TGCocoa *>(gVirtualX) &&
111  "GetRootWindowAttributes, gVirtualX is either null or has a wrong type");
112 
113  TGCocoa * const gCocoa = static_cast<TGCocoa *>(gVirtualX);
114 
115  const Rectangle &frame = gCocoa->GetDisplayGeometry();
116 
117  attr->fX = 0;
118  attr->fY = 0;
119  attr->fWidth = frame.fWidth;
120  attr->fHeight = frame.fHeight;
121  attr->fBorderWidth = 0;
122  attr->fYourEventMask = 0;
123  attr->fAllEventMasks = 0;//???
124 
125  attr->fDepth = NSBitsPerPixelFromDepth([mainScreen depth]);
126  attr->fVisual = 0;
127  attr->fRoot = 0;
128 }
129 
130 
131 #pragma mark - Coordinate conversions.
132 
133 //______________________________________________________________________________
134 NSPoint ConvertPointFromBaseToScreen(NSWindow *window, NSPoint windowPoint)
135 {
136  assert(window != nil && "ConvertPointFromBaseToScreen, parameter 'window' is nil");
137 
138  //I have no idea why apple deprecated function for a point conversion and requires rect conversion,
139  //point conversion seems to produce wrong results with HiDPI.
140 
141  NSRect tmpRect = {};
142  tmpRect.origin = windowPoint;
143  tmpRect.size = NSMakeSize(1., 1.);//This is strange size :) But if they require rect, 0,0 - will not work?
144  tmpRect = [window convertRectToScreen : tmpRect];
145 
146  return tmpRect.origin;
147 }
148 
149 //______________________________________________________________________________
150 NSPoint ConvertPointFromScreenToBase(NSPoint screenPoint, NSWindow *window)
151 {
152  assert(window != nil && "ConvertPointFromScreenToBase, parameter 'window' is nil");
153 
154  //I have no idea why apple deprecated function for a point conversion and requires rect conversion,
155  //point conversion seems to produce wrong results with HiDPI.
156 
157  NSRect tmpRect = {};
158  tmpRect.origin = screenPoint;
159  tmpRect.size = NSMakeSize(1., 1.);
160  tmpRect = [window convertRectFromScreen : tmpRect];
161 
162  return tmpRect.origin;
163 }
164 
165 //______________________________________________________________________________
166 int GlobalYCocoaToROOT(CGFloat yCocoa)
167 {
168  //We can have several physical displays and thus several NSScreens in some arbitrary order.
169  //With Cocoa, some screens can have negative coordinates - to the left ro down to the primary
170  //screen (whatever it means). With X11 (XQuartz) though it's always 0,0.
171 
172  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
173  "GlobalYCocoaToROOT, gVirtualX is either nul or has a wrong type");
174 
175  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
176 
177  return int(frame.fHeight - (yCocoa - frame.fY));
178 }
179 
180 //______________________________________________________________________________
181 int GlobalXCocoaToROOT(CGFloat xCocoa)
182 {
183  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
184  "GlobalXCocoaToROOT, gVirtualX is either nul or has a wrong type");
185  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
186  //With X11 coordinate space always starts from 0, 0
187  return int(xCocoa - frame.fX);
188 }
189 
190 //______________________________________________________________________________
191 int GlobalYROOTToCocoa(CGFloat yROOT)
192 {
193  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
194  "GlobalYROOTToCocoa, gVirtualX is either nul or has a wrong type");
195  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
196 
197  return int(frame.fY + (frame.fHeight - yROOT));
198 }
199 
200 //______________________________________________________________________________
201 int GlobalXROOTToCocoa(CGFloat xROOT)
202 {
203  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
204  "GlobalXROOTToCocoa, gVirtualX is either nul or has a wrong type");
205  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
206  //With X11 coordinate space always starts from 0, 0
207  return int(frame.fX + xROOT);
208 }
209 
210 //______________________________________________________________________________
211 int LocalYCocoaToROOT(NSView<X11Window> *parentView, CGFloat yCocoa)
212 {
213  assert(parentView != nil && "LocalYCocoaToROOT, parent view is nil");
214 
215  return int(parentView.frame.size.height - yCocoa);
216 }
217 
218 //______________________________________________________________________________
219 int LocalYROOTToCocoa(NSView<X11Window> *parentView, CGFloat yROOT)
220 {
221  //:)
222  assert(parentView != nil && "LocalYROOTToCocoa, parent view is nil");
223 
224  return int(parentView.frame.size.height - yROOT);
225 }
226 
227 
228 //______________________________________________________________________________
229 int LocalYROOTToCocoa(NSObject<X11Drawable> *drawable, CGFloat yROOT)
230 {
231  //:)
232  assert(drawable != nil && "LocalYROOTToCocoa, drawable is nil");
233 
234  return int(drawable.fHeight - yROOT);
235 }
236 
237 //______________________________________________________________________________
238 NSPoint TranslateToScreen(NSView<X11Window> *from, NSPoint point)
239 {
240  assert(from != nil && "TranslateToScreen, parameter 'from' is nil");
241 
242  const NSPoint winPoint = [from convertPoint : point toView : nil];
243 
244  NSPoint screenPoint = ConvertPointFromBaseToScreen([from window], winPoint);
245  screenPoint.x = GlobalXCocoaToROOT(screenPoint.x);
246  screenPoint.y = GlobalYCocoaToROOT(screenPoint.y);
247 
248  return screenPoint;
249 }
250 
251 //______________________________________________________________________________
252 NSPoint TranslateFromScreen(NSPoint point, NSView<X11Window> *to)
253 {
254  assert(to != nil && "TranslateFromScreen, parameter 'to' is nil");
255 
256  point.x = GlobalXROOTToCocoa(point.x);
257  point.y = GlobalYROOTToCocoa(point.y);
258  point = ConvertPointFromScreenToBase(point, [to window]);
259 
260  return [to convertPoint : point fromView : nil];
261 }
262 
263 //______________________________________________________________________________
264 NSPoint TranslateCoordinates(NSView<X11Window> *from, NSView<X11Window> *to, NSPoint sourcePoint)
265 {
266  //Both views are valid.
267  assert(from != nil && "TranslateCoordinates, parameter 'from' is nil");
268  assert(to != nil && "TranslateCoordinates, parameter 'to' is nil");
269 
270  if ([from window] == [to window]) {
271  //Both views are in the same window.
272  return [to convertPoint : sourcePoint fromView : from];
273  } else {
274  //May be, I can do it in one call, but it's not obvious for me
275  //what is 'pixel aligned backing store coordinates' and
276  //if they are the same as screen coordinates.
277 
278  //Many thanks to Apple for deprecated functions!!!
279 
280  const NSPoint win1Point = [from convertPoint : sourcePoint toView : nil];
281  const NSPoint screenPoint = ConvertPointFromBaseToScreen([from window], win1Point);
282  const NSPoint win2Point = ConvertPointFromScreenToBase(screenPoint, [to window]);
283 
284  return [to convertPoint : win2Point fromView : nil];
285  }
286 }
287 
288 //______________________________________________________________________________
289 bool ScreenPointIsInView(NSView<X11Window> *view, Int_t x, Int_t y)
290 {
291  assert(view != nil && "ScreenPointIsInView, parameter 'view' is nil");
292 
293  NSPoint point = {};
294  point.x = x, point.y = y;
295  point = TranslateFromScreen(point, view);
296  const NSRect viewFrame = view.frame;
297 
298  if (point.x < 0 || point.x > viewFrame.size.width)
299  return false;
300  if (point.y < 0 || point.y > viewFrame.size.height)
301  return false;
302 
303  return true;
304 }
305 
306 #pragma mark - Different FindView/Window functions iterating on the ROOT's windows/views.
307 
308 //______________________________________________________________________________
310 {
311  //array's counter is increased, all object in array are also retained.
312  const Util::AutoreleasePool pool;
313 
314  NSArray * const orderedWindows = [NSApp orderedWindows];
315  for (NSWindow *window in orderedWindows) {
316  if (![window isKindOfClass : [QuartzWindow class]])
317  continue;
318  QuartzWindow * const qw = (QuartzWindow *)window;
319  if (qw.fIsDeleted)//Because of reference counting this can happen.
320  continue;
321  //Check if point is inside.
322  if (ScreenPointIsInView(qw.fContentView, x, y))
323  return qw;
324  }
325 
326  return nil;
327 }
328 
329 //______________________________________________________________________________
330 NSView<X11Window> *FindDNDAwareViewInPoint(NSArray *children, Window_t dragWinID,
331  Window_t inputWinID, Int_t x, Int_t y, Int_t maxDepth)
332 {
333  assert(children != nil && "FindDNDAwareViewInPoint, parameter 'children' is nil");
334 
335  if (maxDepth <= 0)
336  return nil;
337 
338  NSEnumerator * const reverseEnumerator = [children reverseObjectEnumerator];
339  for (NSView<X11Window> *child in reverseEnumerator) {
340  if (!ScreenPointIsInView(child, x, y))
341  continue;
342  if (child.fIsDNDAware && child.fID != dragWinID && child.fID != inputWinID)
343  return child;//got it!
344 
345  NSView<X11Window> * const testView = FindDNDAwareViewInPoint([child subviews], dragWinID,
346  inputWinID, x, y, maxDepth - 1);
347  if (testView)
348  return testView;
349  }
350 
351  return nil;
352 }
353 
354 //______________________________________________________________________________
355 NSView<X11Window> *FindDNDAwareViewInPoint(NSView *parentView, Window_t dragWinID, Window_t inputWinID,
356  Int_t x, Int_t y, Int_t maxDepth)
357 {
358  //X and Y are ROOT's screen coordinates (Y is inverted).
359  if (maxDepth <= 0)
360  return nil;
361 
362  const Util::AutoreleasePool pool;
363 
364  if (!parentView) {//Start from the screen as a 'root' window.
365  NSArray * const orderedWindows = [NSApp orderedWindows];
366  for (NSWindow *window in orderedWindows) {
367  if (![window isKindOfClass : [QuartzWindow class]])
368  continue;
369  QuartzWindow * const qw = (QuartzWindow *)window;
370 
371  if (qw.fIsDeleted)//Because of reference counting this can happen.
372  continue;
373 
374  if (qw.fMapState != kIsViewable)
375  continue;
376 
377  //First, check this view itself, my be we found what we need already.
378  NSView<X11Window> *testView = qw.fContentView;
379  if (!ScreenPointIsInView(testView, x, y))
380  continue;
381 
382  if (testView.fIsDNDAware && testView.fID != dragWinID && testView.fID != inputWinID)
383  return testView;
384 
385  //Recursive part, check children.
386  NSArray * const children = [testView subviews];
387  testView = FindDNDAwareViewInPoint(children, dragWinID, inputWinID, x, y, maxDepth - 1);
388  if (testView)
389  return testView;
390  }
391 
392  //We did not find anything for 'root' window as parent.
393  return nil;
394  } else {
395  //Parent view is tested already (or should not be tested at all, check children.
396  return FindDNDAwareViewInPoint([parentView subviews], dragWinID, inputWinID, x, y, maxDepth);
397  }
398 }
399 
400 //______________________________________________________________________________
402 {
403  const Util::AutoreleasePool pool;
404 
405  NSArray * const orderedWindows = [NSApp orderedWindows];
406  for (NSWindow *nsWindow in orderedWindows) {
407  if (![nsWindow isKindOfClass : [QuartzWindow class]])
408  continue;
409 
410  QuartzWindow * const qWindow = (QuartzWindow *)nsWindow;
411 
412  if (qWindow.fIsDeleted)//Because of reference counting this can happen.
413  continue;
414 
415  if (qWindow.fMapState != kIsViewable)//Can it be false and still in this array???
416  continue;
417 
418  const NSPoint mousePosition = [qWindow mouseLocationOutsideOfEventStream];
419  const NSSize windowSize = qWindow.frame.size;
420  if (mousePosition.x >= 0 && mousePosition.x <= windowSize.width &&
421  mousePosition.y >= 0 && mousePosition.y <= windowSize.height)
422  return qWindow;
423  }
424 
425  return nil;
426 }
427 
428 //______________________________________________________________________________
429 NSView<X11Window> *FindViewUnderPointer()
430 {
431  //TODO: call FindViewInPoint using cursor screen coordiantes.
432  const Util::AutoreleasePool pool;
433 
434  if (QuartzWindow *topLevel = FindWindowUnderPointer()) {
435  const NSPoint mousePosition = [topLevel mouseLocationOutsideOfEventStream];
436  return (NSView<X11Window> *)[[topLevel contentView] hitTest : mousePosition];
437  }
438 
439  return nil;
440 }
441 
442 //______________________________________________________________________________
444 {
445  //FindWindowForPointerEvent is required because due to grabs
446  //the receiver of the event can be different from the actual
447  //window under cursor.
448 
449  assert(pointerEvent != nil &&
450  "FindWindowForPointerEvent, parameter 'pointerEvent' is nil");
451 
452  const Util::AutoreleasePool pool;
453 
454  NSArray * const orderedWindows = [NSApp orderedWindows];
455  for (NSWindow *nsWindow in orderedWindows) {
456  if (![nsWindow isKindOfClass : [QuartzWindow class]])
457  continue;
458 
459  QuartzWindow * const qWindow = (QuartzWindow *)nsWindow;
460 
461  if (qWindow.fIsDeleted)//Because of reference counting this can happen.
462  continue;
463 
464  //Can it be false and still in this array???
465  if (qWindow.fMapState != kIsViewable)
466  continue;
467 
468  NSPoint mousePosition = [pointerEvent locationInWindow];
469  //The event has a window, so position is in this window's coordinate system,
470  //convert it into screen point first.
471  if ([pointerEvent window]) {
472  //convertBaseToScreen is deprecated.
473  //mousePosition = [[pointerEvent window] convertBaseToScreen : mousePosition];
474  mousePosition = ConvertPointFromBaseToScreen([pointerEvent window], mousePosition);
475  }
476 
477  //convertScreenToBase is deprecated.
478  //mousePosition = [qWindow convertScreenToBase : mousePosition];
479  mousePosition = ConvertPointFromScreenToBase(mousePosition, qWindow);
480 
481  const NSSize windowSize = qWindow.frame.size;
482  if (mousePosition.x >= 0 && mousePosition.x <= windowSize.width &&
483  mousePosition.y >= 0 && mousePosition.y <= windowSize.height)
484  return qWindow;
485  }
486 
487  return nil;
488 }
489 
490 //______________________________________________________________________________
491 NSView<X11Window> *FindViewForPointerEvent(NSEvent *pointerEvent)
492 {
493  //FindViewForPointerEvent is required because of grabs - the receiver of the
494  //event can be different from the actual window under cursor.
495 
496  assert(pointerEvent != nil &&
497  "FindViewForPointerEvent, parameter 'pointerEvent' is nil");
498 
499  const Util::AutoreleasePool pool;
500 
501  if (QuartzWindow *topLevel = FindWindowForPointerEvent(pointerEvent)) {
502  NSPoint mousePosition = [pointerEvent locationInWindow];
503  if ([pointerEvent window])
504  mousePosition = ConvertPointFromBaseToScreen([pointerEvent window], mousePosition);
505 
506  //convertScreenToBase is deprecated.
507  //mousePosition = [topLevel convertScreenToBase : mousePosition];
508  mousePosition = ConvertPointFromScreenToBase(mousePosition, topLevel);
509 
510  return (NSView<X11Window> *)[[topLevel contentView] hitTest : mousePosition];
511  }
512 
513  return nil;
514 }
515 
516 #pragma mark - Downscale image ("reading color bits" on retina macs).
517 
518 //Hoho, we support C++11?? Let's return by value then!!!
519 std::vector<unsigned char> DownscaledImageData(unsigned w, unsigned h, CGImageRef image)
520 {
521  assert(w != 0 && h != 0 && "DownscaledImageData, invalid geometry");
522  assert(image != nullptr && "DonwscaledImageData, invalid parameter 'image'");
523 
524  std::vector<unsigned char> result;
525  try {
526  result.resize(w * h * 4);
527  } catch (const std::bad_alloc &) {
528  //TODO: check that 'resize' has no side effects in case of exception.
529  NSLog(@"DownscaledImageData, memory allocation failed");
530  return result;
531  }
532 
533  //TODO: device RGB? should it be generic?
534  const Util::CFScopeGuard<CGColorSpaceRef> colorSpace(CGColorSpaceCreateDeviceRGB());//[1]
535  if (!colorSpace.Get()) {
536  NSLog(@"DownscaledImageData, CGColorSpaceCreateDeviceRGB failed");
537  return result;
538  }
539 
540  Util::CFScopeGuard<CGContextRef> ctx(CGBitmapContextCreateWithData(&result[0], w, h, 8,
541  w * 4, colorSpace.Get(),
542  kCGImageAlphaPremultipliedLast, NULL, 0));
543  if (!ctx.Get()) {
544  NSLog(@"DownscaledImageData, CGBitmapContextCreateWithData failed");
545  return result;
546  }
547 
548  CGContextDrawImage(ctx.Get(), CGRectMake(0, 0, w, h), image);
549 
550  return result;
551 }
552 
553 #pragma mark - "Focus management" - just make another window key window.
554 
555 //______________________________________________________________________________
557 {
558  //XQuartz (and other X11
559  if (![NSApp isActive])
560  return;
561 
562  const Util::AutoreleasePool pool;
563 
564  NSArray * const orderedWindows = [NSApp orderedWindows];
565  for (NSWindow *nsWindow in orderedWindows) {
566  if (![nsWindow isKindOfClass : [QuartzWindow class]])
567  continue;
568 
569  QuartzWindow * const qWindow = (QuartzWindow *)nsWindow;
570 
571  if (qWindow.fIsDeleted || qWindow.fMapState != kIsViewable || qWindow.fID == winID)
572  continue;
573 
574  if (qWindow.fContentView.fOverrideRedirect)
575  continue;
576 
577  [qWindow makeKeyAndOrderFront : qWindow];
578  break;
579  }
580 }
581 
582 #pragma mark - 'shape mask' - to create a window with arbitrary (probably non-rectangle) shape.
583 
584 //______________________________________________________________________________
585 void ClipToShapeMask(NSView<X11Window> *view, CGContextRef ctx)
586 {
587  assert(view != nil && "ClipToShapeMask, parameter 'view' is nil");
588  assert(ctx != 0 && "ClipToShapeMask, parameter 'ctx' is null");
589 
590  QuartzWindow * const topLevelParent = view.fQuartzWindow;
591  assert(topLevelParent.fShapeCombineMask != nil &&
592  "ClipToShapeMask, fShapeCombineMask is nil on a top-level window");
593  assert(topLevelParent.fShapeCombineMask.fImage != 0 &&
594  "ClipToShapeMask, shape mask is null");
595 
596  //Important: shape mask should have the same width and height as
597  //a top-level window. In ROOT it does not :( Say hello to visual artifacts.
598 
599  //Attach clip mask to the context.
600  if (!view.fParentView) {
601  //'view' is a top-level view.
602  const CGRect clipRect = CGRectMake(0, 0, topLevelParent.fShapeCombineMask.fWidth,
603  topLevelParent.fShapeCombineMask.fHeight);
604  CGContextClipToMask(ctx, clipRect, topLevelParent.fShapeCombineMask.fImage);
605  } else {
606  NSRect clipRect = view.frame;
607  //More complex case: 'self' is a child view, we have to create a subimage from shape mask.
608  clipRect.origin = [view.fParentView convertPoint : clipRect.origin
609  toView : [view window].contentView];
610  clipRect.origin.y = X11::LocalYROOTToCocoa((NSView<X11Window> *)[view window].contentView,
611  clipRect.origin.y + clipRect.size.height);
612 
613  if (AdjustCropArea(topLevelParent.fShapeCombineMask, clipRect)) {
615  clipImageGuard(CGImageCreateWithImageInRect(topLevelParent.fShapeCombineMask.fImage,
616  NSRectToCGRect(clipRect)));
617  clipRect.origin = NSPoint();
618  CGContextClipToMask(ctx, NSRectToCGRect(clipRect), clipImageGuard.Get());
619  } else {
620  //View is invisible.
621  CGRect rect = {};
622  CGContextClipToRect(ctx, rect);
623  }
624  }
625 }
626 
627 #pragma mark - Window's geometry and attributes.
628 
629 //______________________________________________________________________________
630 void SetWindowAttributes(const SetWindowAttributes_t *attr, NSObject<X11Window> *window)
631 {
632  assert(attr != 0 && "SetWindowAttributes, parameter 'attr' is null");
633  assert(window != nil && "SetWindowAttributes, parameter 'window' is nil");
634 
635  const Mask_t mask = attr->fMask;
636 
637  if (mask & kWABackPixel)
638  window.fBackgroundPixel = attr->fBackgroundPixel;
639 
640  if (mask & kWAEventMask)
641  window.fEventMask = attr->fEventMask;
642 
643  if (mask & kWABitGravity)
644  window.fBitGravity = attr->fBitGravity;
645 
646  if (mask & kWAWinGravity)
647  window.fWinGravity = attr->fWinGravity;
648 
649  //TODO: More attributes to set -
650  //cursor for example, etc.
651  if (mask & kWAOverrideRedirect) {
652  //This is quite a special case.
653  //TODO: Must be checked yet, if I understand this correctly!
654  if ([(NSObject *)window isKindOfClass : [QuartzWindow class]]) {
655  QuartzWindow * const qw = (QuartzWindow *)window;
656  [qw setStyleMask : NSBorderlessWindowMask];
657  [qw setAlphaValue : 0.95];
658  }
659 
660  window.fOverrideRedirect = YES;
661  }
662 }
663 
664 //______________________________________________________________________________
665 void GetWindowGeometry(NSObject<X11Window> *win, WindowAttributes_t *dst)
666 {
667  assert(win != nil && "GetWindowGeometry, parameter 'win' is nil");
668  assert(dst != 0 && "GetWindowGeometry, parameter 'dst' is null");
669 
670  dst->fX = win.fX;
671  dst->fY = win.fY;
672 
673  dst->fWidth = win.fWidth;
674  dst->fHeight = win.fHeight;
675 }
676 
677 //______________________________________________________________________________
678 void GetWindowAttributes(NSObject<X11Window> *window, WindowAttributes_t *dst)
679 {
680  assert(window != nil && "GetWindowAttributes, parameter 'window' is nil");
681  assert(dst != 0 && "GetWindowAttributes, parameter 'attr' is null");
682 
683  *dst = WindowAttributes_t();
684 
685  //fX, fY, fWidth, fHeight.
686  GetWindowGeometry(window, dst);
687 
688  //Actually, most of them are not used by GUI.
689  dst->fBorderWidth = 0;
690  dst->fDepth = window.fDepth;
691  //Dummy value.
692  dst->fVisual = 0;
693  //Dummy value.
694  dst->fRoot = 0;
695  dst->fClass = window.fClass;
696  dst->fBitGravity = window.fBitGravity;
697  dst->fWinGravity = window.fWinGravity;
698  //Dummy value.
699  dst->fBackingStore = kAlways;//??? CHECK
700  dst->fBackingPlanes = 0;
701 
702  //Dummy value.
703  dst->fBackingPixel = 0;
704 
705  dst->fSaveUnder = 0;
706 
707  //Dummy value.
708  dst->fColormap = 0;
709  //Dummy value.
710  dst->fMapInstalled = kTRUE;
711 
712  dst->fMapState = window.fMapState;
713 
714  dst->fAllEventMasks = window.fEventMask;
715  dst->fYourEventMask = window.fEventMask;
716 
717  //Not used by GUI.
718  //dst->fDoNotPropagateMask
719 
720  dst->fOverrideRedirect = window.fOverrideRedirect;
721  //Dummy value.
722  dst->fScreen = 0;
723 }
724 
725 //With Apple's poor man's objective C/C++ + "brilliant" Cocoa you never know, what should be
726 //the linkage of callback functions, API + language dialects == MESS. I declare/define this comparators here
727 //as having "C++" linkage. If one good day clang will start to complane, I'll have to change this.
728 
729 #pragma mark - Comparators (I need them when changing a window's z-order).
730 
731 //______________________________________________________________________________
732 // SDK 10.11 and above ...
733 #ifdef MAC_OS_X_VERSION_10_11
734 NSComparisonResult CompareViewsToLower(__kindof NSView *view1, __kindof NSView *view2, void *context)
735 #else
736 NSComparisonResult CompareViewsToLower(id view1, id view2, void *context)
737 #endif
738 {
739  id topView = (id)context;
740  if (view1 == topView)
741  return NSOrderedAscending;
742  if (view2 == topView)
743  return NSOrderedDescending;
744 
745  return NSOrderedSame;
746 }
747 
748 //______________________________________________________________________________
749 // SDK 10.11 and above ...
750 #ifdef MAC_OS_X_VERSION_10_11
751 NSComparisonResult CompareViewsToRaise(__kindof NSView *view1, __kindof NSView *view2, void *context)
752 #else
753 NSComparisonResult CompareViewsToRaise(id view1, id view2, void *context)
754 #endif
755 {
756  id topView = (id)context;
757  if (view1 == topView)
758  return NSOrderedDescending;
759  if (view2 == topView)
760  return NSOrderedAscending;
761 
762  return NSOrderedSame;
763 }
764 
765 #pragma mark - Cursor's area.
766 
767 //______________________________________________________________________________
768 NSPoint GetCursorHotStop(NSImage *image, ECursor cursor)
769 {
770  assert(image != nil && "CursroHotSpot, parameter 'image' is nil");
771 
772  const NSSize imageSize = image.size;
773 
774  if (cursor == kArrowRight)
775  return NSMakePoint(imageSize.width, imageSize.height / 2);
776 
777  return NSMakePoint(imageSize.width / 2, imageSize.height / 2);
778 }
779 
780 //______________________________________________________________________________
781 NSCursor *CreateCustomCursor(ECursor currentCursor)
782 {
783  // Returns auto-released cursor object.
784  const char *pngFileName = 0;
785 
786  switch (currentCursor) {
787  case kMove:
788  pngFileName = "move_cursor.png";
789  break;
790  case kArrowHor:
791  pngFileName = "hor_arrow_cursor.png";
792  break;
793  case kArrowVer:
794  pngFileName = "ver_arrow_cursor.png";
795  break;
796  case kArrowRight:
797  pngFileName = "right_arrow_cursor.png";
798  break;
799  case kRotate:
800  pngFileName = "rotate.png";
801  break;
802  case kBottomLeft:
803  case kTopRight:
804  pngFileName = "top_right_cursor.png";
805  break;
806  case kTopLeft:
807  case kBottomRight:
808  pngFileName = "top_left_cursor.png";
809  break;
810  default:;
811  }
812 
813  if (pngFileName) {
814  const char * const path = gSystem->Which(TROOT::GetIconPath(), pngFileName, kReadPermission);
815  const Util::ScopedArray<const char> arrayGuard(path);
816 
817  if (!path || path[0] == 0) {
818  //File was not found.
819  return nil;
820  }
821 
822  NSString *nsPath = [NSString stringWithFormat : @"%s", path];//in autorelease pool.
823  NSImage * const cursorImage = [[NSImage alloc] initWithContentsOfFile : nsPath];
824 
825  if (!cursorImage)
826  return nil;
827 
828  const NSPoint hotSpot(X11::GetCursorHotStop(cursorImage, currentCursor));
829  NSCursor * const customCursor = [[[NSCursor alloc] initWithImage : cursorImage
830  hotSpot : hotSpot] autorelease];
831 
832  [cursorImage release];
833 
834  return customCursor;
835  }
836 
837  return nil;
838 }
839 
840 //______________________________________________________________________________
841 NSCursor *CreateCursor(ECursor currentCursor)
842 {
843  // Returns auto-released cursor object.
844 
845  //Cursors from TVirtaulX:
846  // kBottomLeft, kBottomRight, kTopLeft, kTopRight,
847  // kBottomSide, kLeftSide, kTopSide, kRightSide,
848  // kMove, kCross, kArrowHor, kArrowVer,
849  // kHand, kRotate, kPointer, kArrowRight,
850  // kCaret, kWatch
851 
852  NSCursor *cursor = nil;
853  switch (currentCursor) {
854  case kCross:
855  cursor = [NSCursor crosshairCursor];
856  break;
857  case kPointer:
858  cursor = [NSCursor arrowCursor];
859  break;
860  case kHand:
861  cursor = [NSCursor openHandCursor];
862  break;
863  case kLeftSide:
864  cursor = [NSCursor resizeLeftCursor];
865  break;
866  case kRightSide:
867  cursor = [NSCursor resizeRightCursor];
868  break;
869  case kTopSide:
870  cursor = [NSCursor resizeUpCursor];
871  break;
872  case kBottomSide:
873  cursor = [NSCursor resizeDownCursor];
874  break;
875  case kCaret:
876  cursor = [NSCursor IBeamCursor];
877  break;
878  case kRotate:
879  case kWatch:
880  default:
881  cursor = CreateCustomCursor(currentCursor);
882  }
883 
884  return cursor;
885 }
886 
887 //TGTextView/TGHtml is a very special window: it's a TGCompositeFrame,
888 //which has TGCompositeFrame inside (TGViewFrame). This TGViewFrame
889 //delegates Expose events to its parent, and parent tries to draw
890 //inside a TGViewFrame. This does not work with default
891 //QuartzView -drawRect/TGCocoa. So I need a trick to identify
892 //this special window.
893 
894 //TODO: possibly refactor these functions in a more generic way - not
895 //to have two separate versions for text and html.
896 
897 
898 #pragma mark - Workarounds for a text view and its descendants.
899 
900 //______________________________________________________________________________
901 bool ViewIsTextView(unsigned viewID)
902 {
903  const TGWindow * const window = gClient->GetWindowById(viewID);
904  if (!window)
905  return false;
906  return window->InheritsFrom("TGTextView");
907 }
908 
909 //______________________________________________________________________________
910 bool ViewIsTextView(NSView<X11Window> *view)
911 {
912  assert(view != nil && "ViewIsTextView, parameter 'view' is nil");
913 
914  return ViewIsTextView(view.fID);
915 }
916 
917 //______________________________________________________________________________
918 bool ViewIsTextViewFrame(NSView<X11Window> *view, bool checkParent)
919 {
920  assert(view != nil && "ViewIsTextViewFrame, parameter 'view' is nil");
921 
922  const TGWindow * const window = gClient->GetWindowById(view.fID);
923  if (!window)
924  return false;
925 
926  if (!window->InheritsFrom("TGViewFrame"))
927  return false;
928 
929  if (!checkParent)
930  return true;
931 
932  if (!view.fParentView)
933  return false;
934 
935  return ViewIsTextView(view.fParentView);
936 }
937 
938 //______________________________________________________________________________
939 bool ViewIsHtmlView(unsigned viewID)
940 {
941  const TGWindow * const window = gClient->GetWindowById(viewID);
942  if (!window)
943  return false;
944  return window->InheritsFrom("TGHtml");
945 }
946 
947 //______________________________________________________________________________
948 bool ViewIsHtmlView(NSView<X11Window> *view)
949 {
950  assert(view != nil && "ViewIsHtmlView, parameter 'view' is nil");
951 
952  return ViewIsHtmlView(view.fID);
953 }
954 
955 //______________________________________________________________________________
956 bool ViewIsHtmlViewFrame(NSView<X11Window> *view, bool checkParent)
957 {
958  //
959  assert(view != nil && "ViewIsHtmlViewFrame, parameter 'view' is nil");
960 
961  const TGWindow * const window = gClient->GetWindowById(view.fID);
962  if (!window)
963  return false;
964 
965  if (!window->InheritsFrom("TGViewFrame"))
966  return false;
967 
968  if (!checkParent)
969  return true;
970 
971  if (!view.fParentView)
972  return false;
973 
974  return ViewIsHtmlView(view.fParentView);
975 }
976 
977 //______________________________________________________________________________
978 NSView<X11Window> *FrameForTextView(NSView<X11Window> *textView)
979 {
980  assert(textView != nil && "FrameForTextView, parameter 'textView' is nil");
981 
982  for (NSView<X11Window> *child in [textView subviews]) {
983  if (ViewIsTextViewFrame(child, false))
984  return child;
985  }
986 
987  return nil;
988 }
989 
990 //______________________________________________________________________________
991 NSView<X11Window> *FrameForHtmlView(NSView<X11Window> *htmlView)
992 {
993  assert(htmlView != nil && "FrameForHtmlView, parameter 'htmlView' is nil");
994 
995  for (NSView<X11Window> *child in [htmlView subviews]) {
996  if (ViewIsHtmlViewFrame(child, false))
997  return child;
998  }
999 
1000  return nil;
1001 }
1002 
1003 #pragma mark - Workarounds for 'paint out of paint events'.
1004 
1005 //______________________________________________________________________________
1006 bool LockFocus(NSView<X11Window> *view)
1007 {
1008  assert(view != nil && "LockFocus, parameter 'view' is nil");
1009  assert([view isKindOfClass : [QuartzView class]] &&
1010  "LockFocus, QuartzView is expected");
1011 
1012  if ([view lockFocusIfCanDraw]) {
1013  NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
1014  assert(nsContext != nil && "LockFocus, currentContext is nil");
1015  CGContextRef currContext = (CGContextRef)[nsContext graphicsPort];
1016  assert(currContext != 0 && "LockFocus, graphicsPort is null");//remove this assert?
1017 
1018  ((QuartzView *)view).fContext = currContext;
1019 
1020  return true;
1021  }
1022 
1023  return false;
1024 }
1025 
1026 //______________________________________________________________________________
1027 void UnlockFocus(NSView<X11Window> *view)
1028 {
1029  assert(view != nil && "UnlockFocus, parameter 'view' is nil");
1030  assert([view isKindOfClass : [QuartzView class]] &&
1031  "UnlockFocus, QuartzView is expected");
1032 
1033  [view unlockFocus];
1034  ((QuartzView *)view).fContext = 0;
1035 }
1036 
1037 }//X11
1038 }//MacOSX
1039 }//ROOT
1040 
1041 namespace Quartz = ROOT::Quartz;
1042 namespace Util = ROOT::MacOSX::Util;
1043 namespace X11 = ROOT::MacOSX::X11;
1044 
1045 #ifdef DEBUG_ROOT_COCOA
1046 
1047 #pragma mark - 'loggers'.
1048 
1049 namespace {
1050 
1051 //______________________________________________________________________________
1052 void log_attributes(const SetWindowAttributes_t *attr, unsigned winID)
1053 {
1054  //This function is loggin requests, at the moment I can not set all
1055  //of these attributes, so I first have to check, what is actually
1056  //requested by ROOT.
1057  static std::ofstream logfile("win_attr.txt");
1058 
1059  const Mask_t mask = attr->fMask;
1060  if (mask & kWABackPixmap)
1061  logfile<<"win "<<winID<<": BackPixmap\n";
1062  if (mask & kWABackPixel)
1063  logfile<<"win "<<winID<<": BackPixel\n";
1064  if (mask & kWABorderPixmap)
1065  logfile<<"win "<<winID<<": BorderPixmap\n";
1066  if (mask & kWABorderPixel)
1067  logfile<<"win "<<winID<<": BorderPixel\n";
1068  if (mask & kWABorderWidth)
1069  logfile<<"win "<<winID<<": BorderWidth\n";
1070  if (mask & kWABitGravity)
1071  logfile<<"win "<<winID<<": BitGravity\n";
1072  if (mask & kWAWinGravity)
1073  logfile<<"win "<<winID<<": WinGravity\n";
1074  if (mask & kWABackingStore)
1075  logfile<<"win "<<winID<<": BackingStore\n";
1076  if (mask & kWABackingPlanes)
1077  logfile<<"win "<<winID<<": BackingPlanes\n";
1078  if (mask & kWABackingPixel)
1079  logfile<<"win "<<winID<<": BackingPixel\n";
1080  if (mask & kWAOverrideRedirect)
1081  logfile<<"win "<<winID<<": OverrideRedirect\n";
1082  if (mask & kWASaveUnder)
1083  logfile<<"win "<<winID<<": SaveUnder\n";
1084  if (mask & kWAEventMask)
1085  logfile<<"win "<<winID<<": EventMask\n";
1086  if (mask & kWADontPropagate)
1087  logfile<<"win "<<winID<<": DontPropagate\n";
1088  if (mask & kWAColormap)
1089  logfile<<"win "<<winID<<": Colormap\n";
1090  if (mask & kWACursor)
1091  logfile<<"win "<<winID<<": Cursor\n";
1092 }
1093 
1094 //______________________________________________________________________________
1095 void print_mask_info(ULong_t mask)
1096 {
1097  if (mask & kButtonPressMask)
1098  NSLog(@"button press mask");
1099  if (mask & kButtonReleaseMask)
1100  NSLog(@"button release mask");
1101  if (mask & kExposureMask)
1102  NSLog(@"exposure mask");
1103  if (mask & kPointerMotionMask)
1104  NSLog(@"pointer motion mask");
1105  if (mask & kButtonMotionMask)
1106  NSLog(@"button motion mask");
1107  if (mask & kEnterWindowMask)
1108  NSLog(@"enter notify mask");
1109  if (mask & kLeaveWindowMask)
1110  NSLog(@"leave notify mask");
1111 }
1112 
1113 }
1114 #endif
1115 
1116 
1117 @implementation QuartzWindow
1118 
1119 @synthesize fMainWindow;
1120 @synthesize fHasFocus;
1121 
1122 #pragma mark - QuartzWindow's life cycle.
1123 
1124 //______________________________________________________________________________
1125 - (id) initWithContentRect : (NSRect) contentRect styleMask : (NSUInteger) windowStyle
1126  backing : (NSBackingStoreType) bufferingType defer : (BOOL) deferCreation
1127  windowAttributes : (const SetWindowAttributes_t *) attr
1128 {
1129  self = [super initWithContentRect : contentRect styleMask : windowStyle
1130  backing : bufferingType defer : deferCreation];
1131 
1132  if (self) {
1133  //ROOT's not able to draw GUI concurrently, thanks to global variables and gVirtualX itself.
1134  [self setAllowsConcurrentViewDrawing : NO];
1135 
1136  self.delegate = self;
1137  //create content view here.
1138  NSRect contentViewRect = contentRect;
1139  contentViewRect.origin.x = 0.f;
1140  contentViewRect.origin.y = 0.f;
1141 
1142  //TODO: OpenGL view can not be content of our QuartzWindow, check if
1143  //this is a problem for ROOT.
1144  fContentView = [[QuartzView alloc] initWithFrame : contentViewRect windowAttributes : 0];
1145 
1146  [self setContentView : fContentView];
1147 
1148  [fContentView release];
1149  fDelayedTransient = NO;
1150 
1151  if (attr)
1152  X11::SetWindowAttributes(attr, self);
1153 
1154  fIsDeleted = NO;
1155  fHasFocus = NO;
1156  }
1157 
1158  return self;
1159 }
1160 
1161 //______________________________________________________________________________
1162 - (id) initWithGLView : (ROOTOpenGLView *) glView
1163 {
1164  assert(glView != nil && "-initWithGLView, parameter 'glView' is nil");
1165 
1166  const NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask |
1167  NSMiniaturizableWindowMask | NSResizableWindowMask;
1168 
1169  NSRect contentRect = glView.frame;
1170  contentRect.origin = NSPoint();
1171 
1172  self = [super initWithContentRect : contentRect styleMask : styleMask
1173  backing : NSBackingStoreBuffered defer : NO];
1174 
1175  if (self) {
1176  //ROOT's not able to draw GUI concurrently, thanks to global variables and gVirtualX itself.
1177  [self setAllowsConcurrentViewDrawing : NO];
1178  self.delegate = self;
1179  fContentView = glView;
1180  [self setContentView : fContentView];
1181  fDelayedTransient = NO;
1182  fIsDeleted = NO;
1183  fHasFocus = NO;
1184  }
1185 
1186  return self;
1187 }
1188 
1189 //______________________________________________________________________________
1191 {
1192  [fShapeCombineMask release];
1193  [super dealloc];
1194 }
1195 
1196 //______________________________________________________________________________
1197 - (BOOL) fIsDeleted
1198 {
1199  return fIsDeleted;
1200 }
1201 
1202 //______________________________________________________________________________
1203 - (void) setContentView:(NSView *)cv
1204 {
1205  [super setContentView:cv];
1206  if ([cv isKindOfClass:[QuartzView class]])
1207  fContentView = (QuartzView *)cv;
1208  else
1209  fContentView = nil;
1210 }
1211 
1212 //______________________________________________________________________________
1213 - (void) setFIsDeleted : (BOOL) deleted
1214 {
1215  fIsDeleted = deleted;
1216 }
1217 
1218 #pragma mark - Forwaring: I want to forward a lot of property setters/getters to the content view.
1219 
1220 //______________________________________________________________________________
1221 - (void) forwardInvocation : (NSInvocation *) anInvocation
1222 {
1223  if (!fContentView)
1224  return;
1225 
1226  if ([fContentView respondsToSelector : [anInvocation selector]]) {
1227  [anInvocation invokeWithTarget : fContentView];
1228  } else {
1229  [super forwardInvocation : anInvocation];
1230  }
1231 }
1232 
1233 //______________________________________________________________________________
1234 - (NSMethodSignature*) methodSignatureForSelector : (SEL) selector
1235 {
1236  NSMethodSignature *signature = [super methodSignatureForSelector : selector];
1237 
1238  if (!signature) {
1239  if (fContentView)
1240  signature = [fContentView methodSignatureForSelector : selector];
1241  }
1242 
1243  return signature;
1244 }
1245 
1246 //______________________________________________________________________________
1247 - (void) addTransientWindow : (QuartzWindow *) window
1248 {
1249  //Transient window: all the popups (menus, dialogs, popups, comboboxes, etc.)
1250  //always should be on the top of its 'parent' window.
1251  //To enforce this ordering, I have to connect such windows with parent/child
1252  //relation (it's not the same as a view hierarchy - both child and parent
1253  //windows are top-level windows).
1254 
1255  assert(window != nil && "-addTransientWindow:, parameter 'window' is nil");
1256 
1257  window.fMainWindow = self;
1258 
1259  if (window.fMapState != kIsViewable) {
1260  //If I add it as child, it'll immediately make a window visible
1261  //and this thing sucks.
1262  window.fDelayedTransient = YES;
1263  } else {
1264  [self addChildWindow : window ordered : NSWindowAbove];
1265  window.fDelayedTransient = NO;
1266  }
1267 }
1268 
1269 //______________________________________________________________________________
1270 - (void) makeKeyAndOrderFront : (id) sender
1271 {
1272 #pragma unused(sender)
1273 
1274  //The more I know Cocoa, the less I like it.
1275  //Window behavior between spaces is a total mess.
1276  //Set the window to join all spaces.
1277 #ifdef MAC_OS_X_VERSION_10_9
1278  [self setCollectionBehavior : NSWindowCollectionBehaviorMoveToActiveSpace];
1279 #else
1280  [self setCollectionBehavior : NSWindowCollectionBehaviorCanJoinAllSpaces];
1281 #endif
1282  //now bring it to the front, it will appear on the active space.
1283  [super makeKeyAndOrderFront : self];
1284  //then reset the collection behavior to default, so the window
1285  [self setCollectionBehavior : NSWindowCollectionBehaviorDefault];
1286 }
1287 
1288 //______________________________________________________________________________
1289 - (void) setFDelayedTransient : (BOOL) d
1290 {
1291  fDelayedTransient = d;
1292 }
1293 
1294 //______________________________________________________________________________
1296 {
1297  return fShapeCombineMask;
1298 }
1299 
1300 //______________________________________________________________________________
1301 - (void) setFShapeCombineMask : (QuartzImage *) mask
1302 {
1303  if (mask != fShapeCombineMask) {
1304  [fShapeCombineMask release];
1305  if (mask) {
1306  fShapeCombineMask = [mask retain];
1307 
1308  //TODO: Check window's shadow???
1309  }
1310  }
1311 }
1312 
1313 #pragma mark - X11Drawable's protocol.
1314 
1315 //______________________________________________________________________________
1316 - (BOOL) fIsPixmap
1317 {
1318  //Never.
1319  return NO;
1320 }
1321 
1322 //______________________________________________________________________________
1324 {
1325  //Never.
1326  return NO;
1327 }
1328 
1329 //______________________________________________________________________________
1330 - (int) fX
1331 {
1332  return X11::GlobalXCocoaToROOT(self.frame.origin.x);
1333 }
1334 
1335 //______________________________________________________________________________
1336 - (int) fY
1337 {
1338  return X11::GlobalYCocoaToROOT(self.frame.origin.y + self.frame.size.height);
1339 }
1340 
1341 //______________________________________________________________________________
1342 - (unsigned) fWidth
1343 {
1344  return self.frame.size.width;
1345 }
1346 
1347 //______________________________________________________________________________
1348 - (unsigned) fHeight
1349 {
1350  //NSWindow's frame (height component) also includes title-bar.
1351  //So I have to use content view's height.
1352  //Obviously, there is a "hole" == 22 pixels.
1353  assert(fContentView != nil && "-fHeight:, content view is nil");
1354 
1355  return fContentView.frame.size.height;
1356 }
1357 
1358 //______________________________________________________________________________
1359 - (void) setDrawableSize : (NSSize) newSize
1360 {
1361  //Can not simply do self.frame.size = newSize.
1362  assert(!(newSize.width < 0) && "-setDrawableSize:, width is negative");
1363  assert(!(newSize.height < 0) && "-setDrawableSize:, height is negative");
1364 
1365  NSRect frame = self.frame;
1366  //dY is potentially a titlebar height.
1367  const CGFloat dY = fContentView ? frame.size.height - fContentView.frame.size.height : 0.;
1368  //Adjust the frame.
1369  frame.origin.y = frame.origin.y + frame.size.height - newSize.height - dY;
1370  frame.size = newSize;
1371  frame.size.height += dY;
1372  [self setFrame : frame display : YES];
1373 }
1374 
1375 //______________________________________________________________________________
1376 - (void) setX : (int) x Y : (int) y width : (unsigned) w height : (unsigned) h
1377 {
1378  NSSize newSize = {};
1379  newSize.width = w;
1380  newSize.height = h;
1381  [self setContentSize : newSize];
1382 
1383  //Check how this is affected by title bar's height.
1384  NSPoint topLeft = {};
1385  topLeft.x = X11::GlobalXROOTToCocoa(x);
1386  topLeft.y = X11::GlobalYROOTToCocoa(y);
1387 
1388  [self setFrameTopLeftPoint : topLeft];
1389 }
1390 
1391 //______________________________________________________________________________
1392 - (void) setX : (int) x Y : (int) y
1393 {
1394  NSPoint topLeft = {};
1395  topLeft.x = X11::GlobalXROOTToCocoa(x);
1396  topLeft.y = X11::GlobalYROOTToCocoa(y);
1397 
1398  [self setFrameTopLeftPoint : topLeft];
1399 }
1400 
1401 //______________________________________________________________________________
1402 - (void) copy : (NSObject<X11Drawable> *) src area : (X11::Rectangle) area withMask : (QuartzImage *) mask
1403  clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
1404 {
1405  if (!fContentView)
1406  return;
1407 
1408  [fContentView copy : src area : area withMask : mask clipOrigin : clipXY toPoint : dstPoint];
1409 }
1410 
1411 //______________________________________________________________________________
1412 - (unsigned char *) readColorBits : (X11::Rectangle) area
1413 {
1414  if (!fContentView)
1415  return nullptr;
1416 
1417  return [fContentView readColorBits : area];
1418 }
1419 
1420 #pragma mark - X11Window protocol's implementation.
1421 
1422 //______________________________________________________________________________
1423 - (QuartzView *) fParentView
1424 {
1425  return nil;
1426 }
1427 
1428 //______________________________________________________________________________
1429 - (void) setFParentView : (QuartzView *) parent
1430 {
1431 #pragma unused(parent)
1432 }
1433 
1434 //______________________________________________________________________________
1435 - (NSView<X11Window> *) fContentView
1436 {
1437  return fContentView;
1438 }
1439 
1440 //______________________________________________________________________________
1442 {
1443  return self;
1444 }
1445 
1446 //... many forwards to fContentView.
1447 
1448 //______________________________________________________________________________
1449 - (void) setFBackgroundPixel : (unsigned long) backgroundColor
1450 {
1451  if (!fContentView)
1452  return;
1453 
1454  if (!fShapeCombineMask) {
1455  CGFloat rgba[] = {0., 0., 0., 1.};
1456  X11::PixelToRGB(backgroundColor, rgba);
1457 
1458  [self setBackgroundColor : [NSColor colorWithColorSpace : [NSColorSpace deviceRGBColorSpace] components : rgba count : 4]];
1459  }
1460 
1461  fContentView.fBackgroundPixel = backgroundColor;
1462 }
1463 
1464 //______________________________________________________________________________
1465 - (unsigned long) fBackgroundPixel
1466 {
1467  if (!fContentView)
1468  return 0;
1469 
1471 }
1472 
1473 //______________________________________________________________________________
1474 - (int) fMapState
1475 {
1476  //Top-level window can be only kIsViewable or kIsUnmapped (not unviewable).
1477  if (!fContentView)
1478  return kIsUnmapped;
1479 
1480  if ([fContentView isHidden])
1481  return kIsUnmapped;
1482 
1483  return kIsViewable;
1484 }
1485 
1486 //______________________________________________________________________________
1487 - (void) addChild : (NSView<X11Window> *) child
1488 {
1489  assert(child != nil && "-addChild:, parameter 'child' is nil");
1490 
1491  if (!fContentView) {
1492  //This can happen only in case of re-parent operation.
1493  assert([child isKindOfClass : [QuartzView class]] &&
1494  "-addChild: gl view in a top-level window as content view is not supported");
1495 
1496  fContentView = (QuartzView *)child;
1497  [self setContentView : child];
1498  fContentView.fParentView = nil;
1499  } else
1500  [fContentView addChild : child];
1501 }
1502 
1503 //______________________________________________________________________________
1504 - (void) getAttributes : (WindowAttributes_t *) attr
1505 {
1506  if (!fContentView)
1507  return;
1508 
1509  assert(attr && "-getAttributes:, parameter 'attr' is nil");
1510 
1511  X11::GetWindowAttributes(self, attr);
1512 }
1513 
1514 //______________________________________________________________________________
1515 - (void) setAttributes : (const SetWindowAttributes_t *) attr
1516 {
1517  assert(attr != 0 && "-setAttributes:, parameter 'attr' is null");
1518 
1519 #ifdef DEBUG_ROOT_COCOA
1520  log_attributes(attr, self.fID);
1521 #endif
1522 
1523  X11::SetWindowAttributes(attr, self);
1524 }
1525 
1526 //______________________________________________________________________________
1528 {
1529  if (!fContentView)
1530  return;
1531 
1532  const Util::AutoreleasePool pool;
1533 
1534  [fContentView setHidden : NO];
1535  [self makeKeyAndOrderFront : self];
1537 
1538  if (fDelayedTransient) {
1539  fDelayedTransient = NO;
1540  [fMainWindow addChildWindow : self ordered : NSWindowAbove];
1541  }
1542 }
1543 
1544 //______________________________________________________________________________
1546 {
1547  if (!fContentView)
1548  return;
1549 
1550  const Util::AutoreleasePool pool;
1551 
1552  [fContentView setHidden : NO];
1553  [self makeKeyAndOrderFront : self];
1555 
1556  if (fDelayedTransient) {
1557  fDelayedTransient = NO;
1558  [fMainWindow addChildWindow : self ordered : NSWindowAbove];
1559  }
1560 }
1561 
1562 //______________________________________________________________________________
1564 {
1565  if (!fContentView)
1566  return;
1567 
1568  const Util::AutoreleasePool pool;
1569 
1572 }
1573 
1574 //______________________________________________________________________________
1576 {
1577  if (!fContentView)
1578  return;
1579 
1580  [fContentView setHidden : YES];
1581  [self orderOut : self];
1582 
1583  if (fMainWindow && !fDelayedTransient) {
1584  [fMainWindow removeChildWindow : self];
1585  fMainWindow = nil;
1586  }
1587 }
1588 
1589 #pragma mark - Events.
1590 
1591 //______________________________________________________________________________
1592 - (void) sendEvent : (NSEvent *) theEvent
1593 {
1594  //With XQuartz, if you open a menu and try to move a window without closing this menu,
1595  //window does not move, menu closes, and after that you can start draggin a window again.
1596  //With Cocoa I can not do such a thing (window WILL move), but still can report button release event
1597  //to close a menu.
1598  if (!fContentView)
1599  return;
1600 
1601  if (theEvent.type == NSLeftMouseDown || theEvent.type == NSRightMouseDown) {
1602  bool generateFakeRelease = false;
1603 
1604  const NSPoint windowPoint = [theEvent locationInWindow];
1605 
1606  if (windowPoint.x <= 4 || windowPoint.x >= self.fWidth - 4)
1607  generateFakeRelease = true;
1608 
1609  if (windowPoint.y <= 4 || windowPoint.y >= self.fHeight - 4)
1610  generateFakeRelease = true;
1611 
1612  const NSPoint viewPoint = [fContentView convertPoint : windowPoint fromView : nil];
1613 
1614  if (viewPoint.y <= 0 && windowPoint.y >= 0)
1615  generateFakeRelease = true;
1616 
1617  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
1618  "-sendEvent:, gVirtualX is either null or not of TGCocoa type");
1619 
1620  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
1621  if (vx->GetEventTranslator()->HasPointerGrab() && generateFakeRelease) {
1623  theEvent.type == NSLeftMouseDown ?
1624  kButton1 : kButton3);
1625  //Yes, ignore this event completely (this means, you are not able to immediately start
1626  //resizing a window, if some popup is open. Actually, this is more or less
1627  //the same as with XQuartz and X11 version.
1628  return;
1629  }
1630  }
1631 
1632  [super sendEvent : theEvent];
1633 }
1634 
1635 #pragma mark - NSWindowDelegate's methods.
1636 
1637 //______________________________________________________________________________
1638 - (BOOL) windowShouldClose : (id) sender
1639 {
1640 #pragma unused(sender)
1641  if (!fContentView)
1642  return NO;
1643 
1644  //TODO: check this!!! Children are
1645  //transient windows and ROOT does not handle
1646  //such a deletion properly, noop then:
1647  //you can not close some window, if there is a
1648  //modal dialog above.
1649  if ([[self childWindows] count])
1650  return NO;
1651 
1652  //Prepare client message for a window.
1653  Event_t closeEvent = {};
1654  closeEvent.fWindow = fContentView.fID;
1655  closeEvent.fType = kClientMessage;
1656  closeEvent.fFormat = 32;//Taken from GUI classes.
1657  closeEvent.fHandle = TGCocoa::fgDeleteWindowAtom;
1658  closeEvent.fUser[0] = TGCocoa::fgDeleteWindowAtom;
1659  //Place it into the queue.
1660  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
1661  "-windowShouldClose:, gVirtualX is either null or has a type different from TGCocoa");
1662  ((TGCocoa *)gVirtualX)->SendEvent(fContentView.fID, &closeEvent);
1663 
1664  //Do not let AppKit to close a window,
1665  //ROOT will do.
1666  return NO;
1667 }
1668 
1669 //______________________________________________________________________________
1670 - (void) windowDidBecomeKey : (NSNotification *) aNotification
1671 {
1672 #pragma unused(aNotification)
1673 
1674  if (!fContentView)
1675  return;
1676 
1678  fHasFocus = YES;
1679  //
1680  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
1681  "-windowDidBecomeKey:, gVirtualX is null or not of TGCocoa type");
1682  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
1684  }
1685 }
1686 
1687 
1688 //______________________________________________________________________________
1689 - (void) windowDidResignKey : (NSNotification *) aNotification
1690 {
1691 #pragma unused(aNotification)
1692  fHasFocus = NO;
1693 }
1694 
1695 @end
1696 
1697 #pragma mark - Passive key grab info.
1698 
1699 @implementation PassiveKeyGrab
1700 
1701 //______________________________________________________________________________
1702 - (id) initWithKey : (unichar) keyCode modifiers : (NSUInteger) modifiers
1703 {
1704  if (self = [super init]) {
1705  fKeyCode = keyCode;
1706  fModifiers = modifiers;
1707  }
1708 
1709  return self;
1710 }
1711 
1712 //______________________________________________________________________________
1713 - (BOOL) matchKey : (unichar) keyCode modifiers : (NSUInteger) modifiers
1714 {
1715  return keyCode == fKeyCode && modifiers == fModifiers;
1716 }
1717 
1718 //______________________________________________________________________________
1719 - (BOOL) matchKey : (unichar) keyCode
1720 {
1721  return keyCode == fKeyCode;
1722 }
1723 
1724 //______________________________________________________________________________
1725 - (unichar) fKeyCode
1726 {
1727  return fKeyCode;
1728 }
1729 
1730 //______________________________________________________________________________
1731 - (NSUInteger) fModifiers
1732 {
1733  return fModifiers;
1734 }
1735 
1736 @end
1737 
1738 #pragma mark - X11 property emulation.
1739 
1740 @interface QuartzWindowProperty : NSObject {
1741  NSData *fPropertyData;
1742  Atom_t fType;
1743  unsigned fFormat;
1744 }
1745 
1746 @property (nonatomic, readonly) Atom_t fType;
1747 
1748 @end
1749 
1750 @implementation QuartzWindowProperty
1751 
1752 @synthesize fType;
1753 
1754 //______________________________________________________________________________
1755 - (id) initWithData : (unsigned char *) data size : (unsigned) dataSize type : (Atom_t) type format : (unsigned) format
1756 {
1757  if (self = [super init]) {
1758  //Memory is zero-initialized, but just to make it explicit:
1759  fPropertyData = nil;
1760  fType = 0;
1761  fFormat = 0;
1762 
1763  [self resetPropertyData : data size : dataSize type : type format : format];
1764  }
1765 
1766  return self;
1767 }
1768 
1769 //______________________________________________________________________________
1770 - (void) dealloc
1771 {
1772  [fPropertyData release];
1773 
1774  [super dealloc];
1775 }
1776 
1777 //______________________________________________________________________________
1778 - (void) resetPropertyData : (unsigned char *) data size : (unsigned) dataSize
1779  type : (Atom_t) type format : (unsigned) format
1780 {
1781  [fPropertyData release];
1782 
1783  fFormat = format;
1784  if (format == 16)
1785  dataSize *= 2;
1786  else if (format == 32)
1787  dataSize *= 4;
1788 
1789  fPropertyData = [[NSData dataWithBytes : data length : dataSize] retain];
1790 
1791  fType = type;
1792 }
1793 
1794 //______________________________________________________________________________
1795 - (NSData *) fPropertyData
1796 {
1797  return fPropertyData;
1798 }
1799 
1800 //______________________________________________________________________________
1801 - (unsigned) fFormat
1802 {
1803  return fFormat;
1804 }
1805 
1806 @end
1807 
1808 #pragma mark - QuartzView.
1809 
1810 //
1811 //QuartzView is a children view (also is a content view for a top-level QuartzWindow).
1812 //
1813 
1814 @implementation QuartzView
1815 
1816 @synthesize fID;
1817 @synthesize fContext;
1818 /////////////////////
1819 //SetWindowAttributes_t/WindowAttributes_t
1820 @synthesize fEventMask;
1821 @synthesize fClass;
1822 @synthesize fDepth;
1823 @synthesize fBitGravity;
1824 @synthesize fWinGravity;
1825 @synthesize fBackgroundPixel;
1826 @synthesize fOverrideRedirect;
1827 //SetWindowAttributes_t/WindowAttributes_t
1828 /////////////////////
1829 @synthesize fHasFocus;
1830 @synthesize fParentView;
1831 
1832 @synthesize fPassiveGrabButton;
1833 @synthesize fPassiveGrabEventMask;
1834 @synthesize fPassiveGrabKeyModifiers;
1835 @synthesize fActiveGrabEventMask;
1836 @synthesize fPassiveGrabOwnerEvents;
1837 @synthesize fSnapshotDraw;
1838 @synthesize fCurrentCursor;
1839 @synthesize fIsDNDAware;
1840 
1841 #pragma mark - Lifetime.
1842 
1843 //______________________________________________________________________________
1844 - (id) initWithFrame : (NSRect) frame windowAttributes : (const SetWindowAttributes_t *)attr
1845 {
1846  if (self = [super initWithFrame : frame]) {
1847  //Make this explicit (though memory is zero initialized).
1848  fBackBuffer = nil;
1849  fID = 0;
1850 
1851  //Passive grab parameters.
1852  fPassiveGrabButton = -1;//0 is kAnyButton.
1855 
1856  fPassiveKeyGrabs = [[NSMutableArray alloc] init];
1857 
1858  [self setCanDrawConcurrently : NO];
1859 
1860  [self setHidden : YES];
1861  //Actually, check if view need this.
1862  //
1863  if (attr)
1864  X11::SetWindowAttributes(attr, self);
1865 
1867  fX11Properties = [[NSMutableDictionary alloc] init];
1868 
1871  fActiveGrabOwnerEvents = YES;
1872  }
1873 
1874  return self;
1875 }
1876 
1877 //______________________________________________________________________________
1878 - (void) dealloc
1879 {
1880  [fBackBuffer release];
1881  [fPassiveKeyGrabs release];
1882  [fX11Properties release];
1883  [fBackgroundPixmap release];
1884  [super dealloc];
1885 }
1886 
1887 #pragma mark - Tracking area.
1888 
1889 //Tracking area is required to ... track mouse motion events inside a view.
1890 
1891 //______________________________________________________________________________
1892 - (void) updateTrackingAreas
1893 {
1894  [super updateTrackingAreas];
1895 
1896  if (!fID)
1897  return;
1898 
1899  const Util::AutoreleasePool pool;
1900 
1901  if (NSArray *trackingArray = [self trackingAreas]) {
1902  const NSUInteger size = [trackingArray count];
1903  for (NSUInteger i = 0; i < size; ++i) {
1904  NSTrackingArea * const t = [trackingArray objectAtIndex : i];
1905  [self removeTrackingArea : t];
1906  }
1907  }
1908 
1909  const NSUInteger trackerOptions = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited |
1910  NSTrackingActiveInActiveApp | NSTrackingInVisibleRect |
1911  NSTrackingEnabledDuringMouseDrag | NSTrackingCursorUpdate;
1912 
1913  NSRect frame = {};
1914  frame.size.width = self.fWidth;
1915  frame.size.height = self.fHeight;
1916 
1917  NSTrackingArea * const tracker = [[NSTrackingArea alloc] initWithRect : frame
1918  options : trackerOptions owner : self userInfo : nil];
1919  [self addTrackingArea : tracker];
1920  [tracker release];
1921 }
1922 
1923 //______________________________________________________________________________
1924 - (void) updateTrackingAreasAfterRaise
1925 {
1926  [self updateTrackingAreas];
1927 
1928  for (QuartzView *childView in [self subviews])
1929  [childView updateTrackingAreasAfterRaise];
1930 }
1931 
1932 #pragma mark - X11Drawable protocol.
1933 
1934 //______________________________________________________________________________
1935 - (BOOL) fIsPixmap
1936 {
1937  return NO;
1938 }
1939 
1940 //______________________________________________________________________________
1942 {
1943  return NO;
1944 }
1945 
1946 //______________________________________________________________________________
1947 - (int) fX
1948 {
1949  return self.frame.origin.x;
1950 }
1951 
1952 //______________________________________________________________________________
1953 - (int) fY
1954 {
1955  return self.frame.origin.y;
1956 }
1957 
1958 //______________________________________________________________________________
1959 - (unsigned) fWidth
1960 {
1961  return self.frame.size.width;
1962 }
1963 
1964 //______________________________________________________________________________
1965 - (unsigned) fHeight
1966 {
1967  return self.frame.size.height;
1968 }
1969 
1970 //______________________________________________________________________________
1971 - (void) setDrawableSize : (NSSize) newSize
1972 {
1973  assert(!(newSize.width < 0) && "-setDrawableSize, width is negative");
1974  assert(!(newSize.height < 0) && "-setDrawableSize, height is negative");
1975 
1976  //This will cause redraw(?)
1977 
1978  //In X11, resize changes the size, but upper-left corner is not changed.
1979  //In Cocoa, bottom-left is fixed.
1980  NSRect frame = self.frame;
1981  frame.size = newSize;
1982 
1983  self.frame = frame;
1984 }
1985 
1986 //______________________________________________________________________________
1987 - (void) setX : (int) x Y : (int) y width : (unsigned) w height : (unsigned) h
1988 {
1989  NSRect newFrame = {};
1990  newFrame.origin.x = x;
1991  newFrame.origin.y = y;
1992  newFrame.size.width = w;
1993  newFrame.size.height = h;
1994 
1995  self.frame = newFrame;
1996 }
1997 
1998 //______________________________________________________________________________
1999 - (void) setX : (int) x Y : (int) y
2000 {
2001  NSRect newFrame = self.frame;
2002  newFrame.origin.x = x;
2003  newFrame.origin.y = y;
2004 
2005  self.frame = newFrame;
2006 }
2007 
2008 //______________________________________________________________________________
2009 - (void) copyImage : (QuartzImage *) srcImage area : (X11::Rectangle) area
2010  withMask : (QuartzImage *) mask clipOrigin : (X11::Point) clipXY
2011  toPoint : (X11::Point) dstPoint
2012 {
2013  //Check parameters.
2014  assert(srcImage != nil &&
2015  "-copyImage:area:withMask:clipOrigin:toPoint:, parameter 'srcImage' is nil");
2016  assert(srcImage.fImage != nil &&
2017  "-copyImage:area:withMask:clipOrigin:toPoint:, srcImage.fImage is nil");
2018 
2019  //Check self.
2020  assert(self.fContext != 0 &&
2021  "-copyImage:area:withMask:clipOrigin:toPoint:, self.fContext is null");
2022 
2023  if (!X11::AdjustCropArea(srcImage, area)) {
2024  NSLog(@"QuartzView: -copyImage:area:withMask:clipOrigin:toPoint:,"
2025  " srcRect and copyRect do not intersect");
2026  return;
2027  }
2028 
2029  //No RAII for subImage, since it can be really subimage or image itself and
2030  //in these cases there is no need to release image.
2031  CGImageRef subImage = 0;
2032  bool needSubImage = false;
2033  if (area.fX || area.fY || area.fWidth != srcImage.fWidth || area.fHeight != srcImage.fHeight) {
2034  needSubImage = true;
2035  subImage = X11::CreateSubImage(srcImage, area);
2036  if (!subImage) {
2037  NSLog(@"QuartzView: -copyImage:area:withMask:clipOrigin:toPoint:,"
2038  " subimage creation failed");
2039  return;
2040  }
2041  } else
2042  subImage = srcImage.fImage;
2043 
2044  //Save context state.
2045  const Quartz::CGStateGuard ctxGuard(self.fContext);
2046 
2047  //Scale and translate to undo isFlipped.
2048  CGContextTranslateCTM(self.fContext, 0., self.fHeight);
2049  CGContextScaleCTM(self.fContext, 1., -1.);
2050  //Set clip mask on a context.
2051 
2052  if (mask) {
2053  assert(mask.fImage != nil &&
2054  "-copyImage:area:withMask:clipOrigin:toPoint:, mask.fImage is nil");
2055  assert(CGImageIsMask(mask.fImage) == true &&
2056  "-copyImage:area:withMask:clipOrigin:toPoint:, mask.fImage is not a mask");
2057  //clipXY.fY = X11::LocalYROOTToCocoa(self, clipXY.fY + mask.fHeight);
2058  const CGFloat clipY = X11::LocalYROOTToCocoa(self, CGFloat(clipXY.fY) + mask.fHeight);
2059  //const CGRect clipRect = CGRectMake(clipXY.fX, clipXY.fY, mask.fWidth, mask.fHeight);
2060  const CGRect clipRect = CGRectMake(clipXY.fX, clipY, mask.fWidth, mask.fHeight);
2061  CGContextClipToMask(self.fContext, clipRect, mask.fImage);
2062  }
2063 
2064  //Convert from X11 to Cocoa (as soon as we scaled y * -1).
2065  //dstPoint.fY = X11::LocalYROOTToCocoa(self, dstPoint.fY + area.fHeight);
2066  const CGFloat dstY = X11::LocalYROOTToCocoa(self, CGFloat(dstPoint.fY) + area.fHeight);
2067  //const CGRect imageRect = CGRectMake(dstPoint.fX, dstPoint.fY, area.fWidth, area.fHeight);
2068  const CGRect imageRect = CGRectMake(dstPoint.fX, dstY, area.fWidth, area.fHeight);
2069  CGContextDrawImage(self.fContext, imageRect, subImage);
2070 
2071  if (needSubImage)
2072  CGImageRelease(subImage);
2073 }
2074 
2075 //______________________________________________________________________________
2076 - (void) copyView : (QuartzView *) srcView area : (X11::Rectangle) area toPoint : (X11::Point) dstPoint
2077 {
2078  //To copy one "window" to another "window", I have to ask source QuartzView to draw intself into
2079  //bitmap, and copy this bitmap into the destination view.
2080 
2081  //TODO: this code must be tested, with all possible cases.
2082 
2083  assert(srcView != nil && "-copyView:area:toPoint:, parameter 'srcView' is nil");
2084 
2085  const NSRect frame = [srcView frame];
2086  //imageRep is in autorelease pool now.
2087  NSBitmapImageRep * const imageRep = [srcView bitmapImageRepForCachingDisplayInRect : frame];
2088  if (!imageRep) {
2089  NSLog(@"QuartzView: -copyView:area:toPoint failed");
2090  return;
2091  }
2092 
2093  assert(srcView != nil && "-copyView:area:toPoint:, parameter 'srcView' is nil");
2094  assert(self.fContext != 0 && "-copyView:area:toPoint, self.fContext is null");
2095 
2096  //It can happen, that src and self are the same.
2097  //cacheDisplayInRect calls drawRect with bitmap context
2098  //(and this will reset self.fContext: I have to save/restore it.
2099  CGContextRef ctx = srcView.fContext;
2100  srcView.fSnapshotDraw = YES;
2101  [srcView cacheDisplayInRect : frame toBitmapImageRep : imageRep];
2102  srcView.fSnapshotDraw = NO;
2103  srcView.fContext = ctx;
2104 
2105  const CGRect subImageRect = CGRectMake(area.fX, area.fY, area.fWidth, area.fHeight);
2106  const Util::CFScopeGuard<CGImageRef> subImage(CGImageCreateWithImageInRect(imageRep.CGImage, subImageRect));
2107 
2108  if (!subImage.Get()) {
2109  NSLog(@"QuartzView: -copyView:area:toPoint, CGImageCreateWithImageInRect failed");
2110  return;
2111  }
2112 
2113  const Quartz::CGStateGuard ctxGuard(self.fContext);
2114  const CGRect imageRect = CGRectMake(dstPoint.fX,
2115  [self visibleRect].size.height - (CGFloat(dstPoint.fY) + area.fHeight),
2116  area.fWidth, area.fHeight);
2117 
2118  CGContextTranslateCTM(self.fContext, 0., [self visibleRect].size.height);
2119  CGContextScaleCTM(self.fContext, 1., -1.);
2120 
2121  CGContextDrawImage(self.fContext, imageRect, subImage.Get());
2122 }
2123 
2124 //______________________________________________________________________________
2125 - (void) copyPixmap : (QuartzPixmap *) srcPixmap area : (X11::Rectangle) area
2126  withMask : (QuartzImage *) mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
2127 {
2128  //Check parameters.
2129  assert(srcPixmap != nil && "-copyPixmap:area:withMask:clipOrigin:toPoint:, parameter 'srcPixmap' is nil");
2130 
2131  if (!X11::AdjustCropArea(srcPixmap, area)) {
2132  NSLog(@"QuartzView: -copyPixmap:area:withMask:clipOrigin:toPoint,"
2133  " no intersection between pixmap rectangle and cropArea");
2134  return;
2135  }
2136 
2137  //Check self.
2138  assert(self.fContext != 0 &&
2139  "-copyPixmap:area:withMask:clipOrigin:toPoint:, self.fContext is null");
2140 
2141  //Save context state.
2142  const Quartz::CGStateGuard ctxGuard(self.fContext);
2143 
2144  CGContextTranslateCTM(self.fContext, 0., self.frame.size.height);//???
2145  CGContextScaleCTM(self.fContext, 1., -1.);
2146 
2147  const Util::CFScopeGuard<CGImageRef> imageFromPixmap([srcPixmap createImageFromPixmap]);
2148  assert(imageFromPixmap.Get() != 0 &&
2149  "-copyPixmap:area:withMask:clipOrigin:toPoint:, createImageFromPixmap failed");
2150 
2151  CGImageRef subImage = 0;
2152  bool needSubImage = false;
2153  if (area.fX || area.fY || area.fWidth != srcPixmap.fWidth || area.fHeight != srcPixmap.fHeight) {
2154  needSubImage = true;
2155  const CGRect subImageRect = CGRectMake(area.fX, area.fY, area.fHeight, area.fWidth);
2156  subImage = CGImageCreateWithImageInRect(imageFromPixmap.Get(), subImageRect);
2157  if (!subImage) {
2158  NSLog(@"QuartzView: -copyImage:area:withMask:clipOrigin:toPoint:,"
2159  " subimage creation failed");
2160  return;
2161  }
2162  } else
2163  subImage = imageFromPixmap.Get();
2164 
2165  if (mask) {
2166  assert(mask.fImage != nil &&
2167  "-copyPixmap:area:withMask:clipOrigin:toPoint:, mask.fImage is nil");
2168  assert(CGImageIsMask(mask.fImage) == true &&
2169  "-copyPixmap:area:withMask:clipOrigin:toPoint:, mask.fImage is not a mask");
2170 
2171  //clipXY.fY = X11::LocalYROOTToCocoa(self, clipXY.fY + mask.fHeight);
2172  const CGFloat clipY = X11::LocalYROOTToCocoa(self, CGFloat(clipXY.fY) + mask.fHeight);
2173  //const CGRect clipRect = CGRectMake(clipXY.fX, clipXY.fY, mask.fWidth, mask.fHeight);
2174  const CGRect clipRect = CGRectMake(clipXY.fX, clipY, mask.fWidth, mask.fHeight);
2175  CGContextClipToMask(self.fContext, clipRect, mask.fImage);
2176  }
2177 
2178  //dstPoint.fY = X11::LocalYCocoaToROOT(self, dstPoint.fY + area.fHeight);
2179  const CGFloat dstY = X11::LocalYCocoaToROOT(self, CGFloat(dstPoint.fY) + area.fHeight);
2180  const CGRect imageRect = CGRectMake(dstPoint.fX, dstY, area.fWidth, area.fHeight);
2181  CGContextDrawImage(self.fContext, imageRect, imageFromPixmap.Get());
2182 
2183  if (needSubImage)
2184  CGImageRelease(subImage);
2185 }
2186 
2187 
2188 //______________________________________________________________________________
2189 - (void) copyImage : (QuartzImage *) srcImage area : (X11::Rectangle) area
2190  toPoint : (X11::Point) dstPoint
2191 {
2192  assert(srcImage != nil && "-copyImage:area:toPoint:, parameter 'srcImage' is nil");
2193  assert(srcImage.fImage != nil && "-copyImage:area:toPoint:, srcImage.fImage is nil");
2194  assert(self.fContext != 0 && "-copyImage:area:toPoint:, fContext is null");
2195 
2196  if (!X11::AdjustCropArea(srcImage, area)) {
2197  NSLog(@"QuartzView: -copyImage:area:toPoint, image and copy area do not intersect");
2198  return;
2199  }
2200 
2201  CGImageRef subImage = 0;
2202  bool needSubImage = false;
2203  if (area.fX || area.fY || area.fWidth != srcImage.fWidth || area.fHeight != srcImage.fHeight) {
2204  needSubImage = true;
2205  subImage = X11::CreateSubImage(srcImage, area);
2206  if (!subImage) {
2207  NSLog(@"QuartzView: -copyImage:area:toPoint:, subimage creation failed");
2208  return;
2209  }
2210  } else
2211  subImage = srcImage.fImage;
2212 
2213  const Quartz::CGStateGuard ctxGuard(self.fContext);
2214 
2215  CGContextTranslateCTM(self.fContext, 0., self.fHeight);
2216  CGContextScaleCTM(self.fContext, 1., -1.);
2217 
2218  //dstPoint.fY = X11::LocalYCocoaToROOT(self, dstPoint.fY + area.fHeight);
2219  const CGFloat dstY = X11::LocalYCocoaToROOT(self, CGFloat(dstPoint.fY) + area.fHeight);
2220  //const CGRect imageRect = CGRectMake(dstPoint.fX, dstPoint.fY, area.fWidth, area.fHeight);
2221  const CGRect imageRect = CGRectMake(dstPoint.fX, dstY, area.fWidth, area.fHeight);
2222  CGContextDrawImage(self.fContext, imageRect, subImage);
2223 
2224  if (needSubImage)
2225  CGImageRelease(subImage);
2226 }
2227 
2228 //______________________________________________________________________________
2229 - (void) copy : (NSObject<X11Drawable> *) src area : (X11::Rectangle) area
2230  withMask : (QuartzImage *)mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
2231 {
2232  assert(src != nil && "-copy:area:withMask:clipOrigin:toPoint:, parameter 'src' is nil");
2233  assert(area.fWidth && area.fHeight && "-copy:area:withMask:clipOrigin:toPoint:, area to copy is empty");
2234 
2235  if ([src isKindOfClass : [QuartzWindow class]]) {
2236  //Forget about mask (can I have it???)
2237  QuartzWindow * const qw = (QuartzWindow *)src;
2238  //Will not work with OpenGL.
2239  [self copyView : (QuartzView *)qw.fContentView area : area toPoint : dstPoint];
2240  } else if ([src isKindOfClass : [QuartzView class]]) {
2241  //Forget about mask (can I have it???)
2242  [self copyView : (QuartzView *)src area : area toPoint : dstPoint];
2243  } else if ([src isKindOfClass : [QuartzPixmap class]]) {
2244  [self copyPixmap : (QuartzPixmap *)src area : area withMask : mask clipOrigin : clipXY toPoint : dstPoint];
2245  } else if ([src isKindOfClass : [QuartzImage class]]) {
2246  [self copyImage : (QuartzImage *)src area : area withMask : mask clipOrigin : clipXY toPoint : dstPoint];
2247  } else {
2248  assert(0 && "-copy:area:withMask:clipOrigin:toPoint:, src is of unknown type");
2249  }
2250 }
2251 
2252 //______________________________________________________________________________
2253 - (unsigned char *) readColorBits : (X11::Rectangle) area
2254 {
2255  //This is quite a bad idea - to read pixels back from a view,
2256  //but our GUI does exactly this. In case of Cocoa it's expensive
2257  //and not guaranteed to work.
2258 
2259  assert(area.fWidth && area.fHeight && "-readColorBits:, area to copy is empty");
2260 
2261  //int, not unsigned or something - to keep it simple.
2262  const NSRect visRect = [self visibleRect];
2263  const X11::Rectangle srcRect(int(visRect.origin.x), int(visRect.origin.y),
2264  unsigned(visRect.size.width), unsigned(visRect.size.height));
2265 
2266  if (!X11::AdjustCropArea(srcRect, area)) {
2267  NSLog(@"QuartzView: -readColorBits:, visible rect of view and copy area do not intersect");
2268  return nullptr;
2269  }
2270 
2271  //imageRep is autoreleased.
2272  NSBitmapImageRep * const imageRep = [self bitmapImageRepForCachingDisplayInRect : visRect];
2273  if (!imageRep) {
2274  NSLog(@"QuartzView: -readColorBits:, bitmapImageRepForCachingDisplayInRect failed");
2275  return nullptr;
2276  }
2277 
2278  CGContextRef ctx = self.fContext; //Save old context if any.
2279  [self cacheDisplayInRect : visRect toBitmapImageRep : imageRep];
2280  self.fContext = ctx; //Restore old context.
2281  //
2282  const NSInteger bitsPerPixel = [imageRep bitsPerPixel];
2283  //TODO: ohhh :(((
2284  assert(bitsPerPixel == 32 && "-readColorBits:, no alpha channel???");
2285  const NSInteger bytesPerRow = [imageRep bytesPerRow];
2286  unsigned dataWidth = bytesPerRow / (bitsPerPixel / 8);//assume an octet :(
2287 
2288  unsigned char *srcData = nullptr;
2289  std::vector<unsigned char> downscaled;
2290  if ([[NSScreen mainScreen] backingScaleFactor] > 1 && imageRep.CGImage) {
2291  downscaled = X11::DownscaledImageData(area.fWidth, area.fHeight, imageRep.CGImage);
2292  if (downscaled.size())
2293  srcData = &downscaled[0];
2294  dataWidth = area.fWidth;
2295  } else
2296  srcData = [imageRep bitmapData];
2297 
2298  if (!srcData) {
2299  NSLog(@"QuartzView: -readColorBits:, failed to obtain backing store contents");
2300  return nullptr;
2301  }
2302 
2303  //We have a source data now. Let's allocate buffer for ROOT's GUI and convert source data.
2304  unsigned char *data = nullptr;
2305 
2306  try {
2307  data = new unsigned char[area.fWidth * area.fHeight * 4];//bgra?
2308  } catch (const std::bad_alloc &) {
2309  NSLog(@"QuartzView: -readColorBits:, memory allocation failed");
2310  return nullptr;
2311  }
2312 
2313  unsigned char *dstPixel = data;
2314  const unsigned char *line = srcData + area.fY * dataWidth * 4;
2315  const unsigned char *srcPixel = line + area.fX * 4;
2316 
2317  for (unsigned i = 0; i < area.fHeight; ++i) {
2318  for (unsigned j = 0; j < area.fWidth; ++j, srcPixel += 4, dstPixel += 4) {
2319  dstPixel[0] = srcPixel[2];
2320  dstPixel[1] = srcPixel[1];
2321  dstPixel[2] = srcPixel[0];
2322  dstPixel[3] = srcPixel[3];
2323  }
2324 
2325  line += dataWidth * 4;
2326  srcPixel = line + area.fX * 4;
2327  }
2328 
2329  return data;
2330 }
2331 
2332 //______________________________________________________________________________
2333 - (void) setFBackgroundPixmap : (QuartzImage *) pixmap
2334 {
2335  if (fBackgroundPixmap != pixmap) {
2336  [fBackgroundPixmap release];
2337  if (pixmap)
2338  fBackgroundPixmap = [pixmap retain];
2339  else
2340  fBackgroundPixmap = nil;
2341  }
2342 }
2343 
2344 //______________________________________________________________________________
2346 {
2347  //I do not autorelease, screw this idiom!
2348 
2349  return fBackgroundPixmap;
2350 }
2351 
2352 //______________________________________________________________________________
2353 - (int) fMapState
2354 {
2355  if ([self isHidden])
2356  return kIsUnmapped;
2357 
2358  for (QuartzView *parent = fParentView; parent; parent = parent.fParentView) {
2359  if ([parent isHidden])
2360  return kIsUnviewable;
2361  }
2362 
2363  return kIsViewable;
2364 }
2365 
2366 //______________________________________________________________________________
2367 - (BOOL) fHasFocus
2368 {
2369  //With the latest update clang became a bit more stupid.
2370  //Let's write a stupid useless cargo cult code
2371  //to make IT SHUT THE F... UP.
2372  (void)fHasFocus;
2373  return NO;
2374 }
2375 
2376 //______________________________________________________________________________
2377 - (void) setFHasFocus : (BOOL) focus
2378 {
2379 #pragma unused(focus)
2380  //With the latest update clang became a bit more stupid.
2381  //Let's write a stupid useless cargo cult code
2382  //to make IT SHUT THE F... UP.
2383  (void)fHasFocus;
2384 }
2385 
2386 //______________________________________________________________________________
2388 {
2389  return fBackBuffer;//No autorelease, I know the object's lifetime myself.
2390 }
2391 
2392 //______________________________________________________________________________
2393 - (void) setFBackBuffer : (QuartzPixmap *) backBuffer
2394 {
2395  if (fBackBuffer != backBuffer) {
2396  [fBackBuffer release];
2397 
2398  if (backBuffer)
2399  fBackBuffer = [backBuffer retain];
2400  else
2401  fBackBuffer = nil;
2402  }
2403 }
2404 
2405 //______________________________________________________________________________
2406 - (NSView<X11Window> *) fContentView
2407 {
2408  return self;
2409 }
2410 
2411 //______________________________________________________________________________
2413 {
2414  return (QuartzWindow *)[self window];
2415 }
2416 
2417 //______________________________________________________________________________
2419 {
2421 }
2422 
2423 //______________________________________________________________________________
2425 {
2427 }
2428 
2429 //______________________________________________________________________________
2430 - (void) activateGrab : (unsigned) eventMask ownerEvents : (BOOL) ownerEvents
2431 {
2433  fActiveGrabEventMask = eventMask;
2434  fActiveGrabOwnerEvents = ownerEvents;
2435 }
2436 
2437 //______________________________________________________________________________
2439 {
2442  fActiveGrabOwnerEvents = YES;
2443 }
2444 
2445 //______________________________________________________________________________
2446 - (BOOL) acceptsCrossingEvents : (unsigned) eventMask
2447 {
2448  bool accepts = fEventMask & eventMask;
2449 
2450  //In ROOT passive grabs are always with owner_events == true.
2452  accepts = accepts || (fPassiveGrabEventMask & eventMask);
2453 
2456  accepts = accepts || (fActiveGrabOwnerEvents & eventMask);
2457  else
2458  accepts = fActiveGrabOwnerEvents & eventMask;
2459  }
2460 
2461  return accepts;
2462 }
2463 
2464 //______________________________________________________________________________
2465 - (void) addChild : (NSView<X11Window> *) child
2466 {
2467  assert(child != nil && "-addChild:, parameter 'child' is nil");
2468 
2469  [self addSubview : child];
2470  child.fParentView = self;
2471 }
2472 
2473 //______________________________________________________________________________
2474 - (void) getAttributes : (WindowAttributes_t *) attr
2475 {
2476  assert(attr != 0 && "-getAttributes:, parameter 'attr' is null");
2477 
2478  X11::GetWindowAttributes(self, attr);
2479 }
2480 
2481 //______________________________________________________________________________
2482 - (void) setAttributes : (const SetWindowAttributes_t *)attr
2483 {
2484  assert(attr != 0 && "-setAttributes:, parameter 'attr' is null");
2485 
2486 #ifdef DEBUG_ROOT_COCOA
2487  log_attributes(attr, fID);
2488 #endif
2489 
2490  X11::SetWindowAttributes(attr, self);
2491 }
2492 
2493 //______________________________________________________________________________
2495 {
2496  //Move view to the top of subviews.
2497  QuartzView * const parent = fParentView;
2498  [self removeFromSuperview];
2499  [parent addSubview : self];
2500  [self setHidden : NO];
2501 }
2502 
2503 //______________________________________________________________________________
2505 {
2506  [self setHidden : NO];
2507 }
2508 
2509 //______________________________________________________________________________
2511 {
2512  for (QuartzView * v in [self subviews])
2513  [v setHidden : NO];
2514 }
2515 
2516 //______________________________________________________________________________
2518 {
2519  [self setHidden : YES];
2520 }
2521 
2522 //______________________________________________________________________________
2524 {
2525  return fIsOverlapped;
2526 }
2527 
2528 //______________________________________________________________________________
2529 - (void) setOverlapped : (BOOL) overlap
2530 {
2531  fIsOverlapped = overlap;
2532  for (NSView<X11Window> *child in [self subviews])
2533  [child setOverlapped : overlap];
2534 }
2535 
2536 //______________________________________________________________________________
2538 {
2539  //Now, I can not remove window and add it ...
2540  //For example, if you click on a tab, this:
2541  //1. Creates (potentially) a passive button grab
2542  //2. Raises this tab - changes the window order.
2543  //3. On a button release - grab is release.
2544  //The tough problem is, if I remove a view from subviews
2545  //and add it ... it will never receve the
2546  //release event thus a grab will 'hang' on
2547  //view leading to bugs and artifacts.
2548  //So instead I have to ... SORT!!!!!
2549 
2550  using namespace X11;//Comparators.
2551 
2552  for (QuartzView *sibling in [fParentView subviews]) {
2553  if (self == sibling)
2554  continue;
2555  if ([sibling isHidden])
2556  continue;
2557 
2558  if (NSEqualRects(sibling.frame, self.frame)) {
2559  [sibling setOverlapped : YES];
2560  [sibling setHidden : YES];
2561  }
2562  }
2563 
2564  [self setOverlapped : NO];
2565  //
2566  [self setHidden : NO];
2567  //
2568  [fParentView sortSubviewsUsingFunction : CompareViewsToRaise context : (void *)self];
2569  //
2570  [self updateTrackingAreasAfterRaise];
2571  //
2572  [self setNeedsDisplay : YES];
2573 }
2574 
2575 //______________________________________________________________________________
2577 {
2578  //See comment about sorting in -raiseWindow.
2579 
2580  using namespace X11;
2581 
2582  NSEnumerator * const reverseEnumerator = [[fParentView subviews] reverseObjectEnumerator];
2583  for (QuartzView *sibling in reverseEnumerator) {
2584  if (sibling == self)
2585  continue;
2586 
2587  //TODO: equal test is not good :) I have a baaad feeling about this ;)
2588  if (NSEqualRects(sibling.frame, self.frame)) {
2589  [sibling setOverlapped : NO];
2590  //
2591  [sibling setHidden : NO];
2592  //
2593  [sibling setNeedsDisplay : YES];
2594  [self setOverlapped : YES];
2595  //
2596  [self setHidden : YES];
2597  //
2598  break;
2599  }
2600  }
2601 
2602  [fParentView sortSubviewsUsingFunction : CompareViewsToLower context : (void*)self];
2603 }
2604 
2605 //______________________________________________________________________________
2606 - (BOOL) isFlipped
2607 {
2608  //Now view's placement, geometry, moving and resizing can be
2609  //done with ROOT's (X11) coordinates without conversion - we're are 'flipped'.
2610  return YES;
2611 }
2612 
2613 //______________________________________________________________________________
2615 {
2616  if (self.fMapState == kIsViewable || fIsOverlapped == YES) {
2618  assert(dynamic_cast<TGCocoa *>(gVirtualX) &&
2619  "-configureNotifyTree, gVirtualX is either null or has type different from TGCocoa");
2620  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2621  vx->GetEventTranslator()->GenerateConfigureNotifyEvent(self, self.frame);
2622  }
2623 
2624  for (NSView<X11Window> *v in [self subviews])
2625  [v configureNotifyTree];
2626  }
2627 }
2628 
2629 #pragma mark - Key grabs.
2630 
2631 //______________________________________________________________________________
2632 - (void) addPassiveKeyGrab : (unichar) keyCode modifiers : (NSUInteger) modifiers
2633 {
2634  [self removePassiveKeyGrab : keyCode modifiers : modifiers];
2635  PassiveKeyGrab * const newGrab = [[PassiveKeyGrab alloc] initWithKey : keyCode
2636  modifiers : modifiers];
2637  [fPassiveKeyGrabs addObject : newGrab];
2638  [newGrab release];
2639 }
2640 
2641 //______________________________________________________________________________
2642 - (void) removePassiveKeyGrab : (unichar) keyCode modifiers : (NSUInteger) modifiers
2643 {
2644  const NSUInteger count = [fPassiveKeyGrabs count];
2645  for (NSUInteger i = 0; i < count; ++i) {
2646  PassiveKeyGrab *grab = [fPassiveKeyGrabs objectAtIndex : i];
2647  if ([grab matchKey : keyCode modifiers : modifiers]) {
2648  [fPassiveKeyGrabs removeObjectAtIndex : i];
2649  break;
2650  }
2651  }
2652 }
2653 
2654 //______________________________________________________________________________
2655 - (PassiveKeyGrab *) findPassiveKeyGrab : (unichar) keyCode modifiers : (NSUInteger) modifiers
2656 {
2657  NSEnumerator * const enumerator = [fPassiveKeyGrabs objectEnumerator];
2658  while (PassiveKeyGrab *grab = (PassiveKeyGrab *)[enumerator nextObject]) {
2659  if ([grab matchKey : keyCode modifiers : modifiers])
2660  return grab;
2661  }
2662 
2663  return nil;
2664 }
2665 
2666 //______________________________________________________________________________
2667 - (PassiveKeyGrab *) findPassiveKeyGrab : (unichar) keyCode
2668 {
2669  //Do not check modifiers.
2670  NSEnumerator * const enumerator = [fPassiveKeyGrabs objectEnumerator];
2671  while (PassiveKeyGrab *grab = (PassiveKeyGrab *)[enumerator nextObject]) {
2672  if ([grab matchKey : keyCode])
2673  return grab;
2674  }
2675 
2676  return nil;
2677 }
2678 
2679 #pragma mark - Painting mechanics.
2680 
2681 //______________________________________________________________________________
2682 - (void) drawRect : (NSRect) dirtyRect
2683 {
2684 #pragma unused(dirtyRect)
2685 
2686  using namespace X11;
2687 
2688  if (fID) {
2689  if (TGWindow * const window = gClient->GetWindowById(fID)) {
2690  //It's never painted, parent renders child. true == check the parent also.
2691  if (ViewIsTextViewFrame(self, true) ||ViewIsHtmlViewFrame(self, true))
2692  return;
2693 
2694  NSGraphicsContext * const nsContext = [NSGraphicsContext currentContext];
2695  assert(nsContext != nil && "-drawRect:, currentContext returned nil");
2696 
2697  TGCocoa * const vx = (TGCocoa *)gVirtualX;
2698  vx->CocoaDrawON();
2699 
2700  fContext = (CGContextRef)[nsContext graphicsPort];
2701  assert(fContext != 0 && "-drawRect:, graphicsPort returned null");
2702 
2703  const Quartz::CGStateGuard ctxGuard(fContext);
2704 
2705  //Non-rectangular windows.
2708 
2709  if (window->InheritsFrom("TGContainer"))//It always has an ExposureMask.
2710  vx->GetEventTranslator()->GenerateExposeEvent(self, [self visibleRect]);
2711 
2712  if (fEventMask & kExposureMask) {
2713  if (ViewIsTextView(self)) {
2714  //Send Expose event, using child view (this is how it's done in GUI :( ).
2715  NSView<X11Window> * const viewFrame = FrameForTextView(self);
2716  if (viewFrame)//Now we set fExposedRegion for TGView.
2717  vx->GetEventTranslator()->GenerateExposeEvent(viewFrame, [viewFrame visibleRect]);
2718  }
2719 
2720  if (ViewIsHtmlView(self)) {
2721  NSView<X11Window> *const viewFrame = FrameForHtmlView(self);
2722  if (viewFrame)
2723  vx->GetEventTranslator()->GenerateExposeEvent(viewFrame, [viewFrame visibleRect]);
2724  }
2725 
2726  //Ask ROOT's widget/window to draw itself.
2727  gClient->NeedRedraw(window, kTRUE);
2728 
2729  if (!fSnapshotDraw && !ViewIsTextView(self) && !ViewIsHtmlView(self)) {
2730  //If Cocoa repaints widget, cancel all ROOT's "outside of paint event"
2731  //rendering into this widget ... Except it's a text view :)
2732  gClient->CancelRedraw(window);
2734  }
2735  }
2736 
2737  if (fBackBuffer) {
2738  //Very "special" window.
2739  const X11::Rectangle copyArea(0, 0, fBackBuffer.fWidth, fBackBuffer.fHeight);
2740  [self copy : fBackBuffer area : copyArea withMask : nil
2741  clipOrigin : X11::Point() toPoint : X11::Point()];
2742  }
2743 
2744  vx->CocoaDrawOFF();
2745 #ifdef DEBUG_ROOT_COCOA
2746  CGContextSetRGBStrokeColor(fContext, 1., 0., 0., 1.);
2747  CGContextStrokeRect(fContext, dirtyRect);
2748 #endif
2749 
2750  fContext = 0;
2751  } else {
2752 #ifdef DEBUG_ROOT_COCOA
2753  NSLog(@"QuartzView: -drawRect: method, no window for id %u was found", fID);
2754 #endif
2755  }
2756  }
2757 }
2758 
2759 #pragma mark - Geometry.
2760 
2761 //______________________________________________________________________________
2762 - (void) setFrame : (NSRect) newFrame
2763 {
2764  //In case of TBrowser, setFrame started infinite recursion:
2765  //HandleConfigure for embedded main frame emits signal, slot
2766  //calls layout, layout calls setFrame -> HandleConfigure and etc. etc.
2767  if (NSEqualRects(newFrame, self.frame))
2768  return;
2769 
2770  [super setFrame : newFrame];
2771 }
2772 
2773 //______________________________________________________________________________
2774 - (void) setFrameSize : (NSSize) newSize
2775 {
2776  //Check, if setFrameSize calls setFrame.
2777 
2778  [super setFrameSize : newSize];
2779 
2780  if ((fEventMask & kStructureNotifyMask) && (self.fMapState == kIsViewable || fIsOverlapped == YES)) {
2781  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2782  "setFrameSize:, gVirtualX is either null or has a type, different from TGCocoa");
2783  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2784  vx->GetEventTranslator()->GenerateConfigureNotifyEvent(self, self.frame);
2785  }
2786 
2787  [self setNeedsDisplay : YES];//?
2788 }
2789 
2790 #pragma mark - Event handling.
2791 
2792 //______________________________________________________________________________
2793 - (void) mouseDown : (NSEvent *) theEvent
2794 {
2795  assert(fID != 0 && "-mouseDown:, fID is 0");
2796 
2797  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2798  "-mouseDown:, gVirtualX is either null or has a type, different from TGCocoa");
2799  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2800  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton1);
2801 }
2802 
2803 //______________________________________________________________________________
2804 - (void) scrollWheel : (NSEvent*) theEvent
2805 {
2806  assert(fID != 0 && "-scrollWheel:, fID is 0");
2807 
2808 
2809  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2810  "-scrollWheel:, gVirtualX is either null or has a type, different from TGCocoa");
2811 
2812  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2813  const CGFloat deltaY = [theEvent deltaY];
2814  if (deltaY < 0) {
2815  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton5);
2816  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton5);
2817  } else if (deltaY > 0) {
2818  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton4);
2819  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton4);
2820  }
2821 }
2822 
2823 #ifdef DEBUG_ROOT_COCOA
2824 //______________________________________________________________________________
2825 - (void) printViewInformation
2826 {
2827  assert(fID != 0 && "-printWindowInformation, fID is 0");
2828  const TGWindow * const window = gClient->GetWindowById(fID);
2829  assert(window != 0 && "printWindowInformation, window not found");
2830 
2831  NSLog(@"-----------------View %u info:---------------------", fID);
2832  NSLog(@"ROOT's window class is %s", window->IsA()->GetName());
2833  NSLog(@"event mask is:");
2834  print_mask_info(fEventMask);
2835  NSLog(@"grab mask is:");
2836  print_mask_info(fPassiveGrabEventMask);
2837  NSLog(@"view's geometry: x == %g, y == %g, w == %g, h == %g", self.frame.origin.x,
2838  self.frame.origin.y, self.frame.size.width, self.frame.size.height);
2839  NSLog(@"----------------End of view info------------------");
2840 }
2841 #endif
2842 
2843 //______________________________________________________________________________
2844 - (void) rightMouseDown : (NSEvent *) theEvent
2845 {
2846  assert(fID != 0 && "-rightMouseDown:, fID is 0");
2847 
2848 #ifdef DEBUG_ROOT_COCOA
2849  [self printViewInformation];
2850 #endif
2851 
2852  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2853  "-rightMouseDown:, gVirtualX is either null or has type different from TGCocoa");
2854  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2855  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton3);
2856 }
2857 
2858 //______________________________________________________________________________
2859 - (void) otherMouseDown : (NSEvent *) theEvent
2860 {
2861  assert(fID != 0 && "-otherMouseDown:, fID is 0");
2862 
2863  //Funny enough, [theEvent buttonNumber] is not the same thing as button masked in [NSEvent pressedMouseButtons],
2864  //button number actually is a kind of right operand for bitshift for pressedMouseButtons.
2865  if ([theEvent buttonNumber] == 2) {//this '2' will correspond to '4' in pressedMouseButtons.
2866  //I do not care about mouse buttons after left/right/wheel - ROOT does not have
2867  //any code for this.
2868  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2869  "-otherMouseDown:, gVirtualX is either null or has type different from TGCocoa");
2870  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2871  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton2);
2872  }
2873 }
2874 
2875 //______________________________________________________________________________
2876 - (void) mouseUp : (NSEvent *) theEvent
2877 {
2878  assert(fID != 0 && "-mouseUp:, fID is 0");
2879 
2880  assert(dynamic_cast<TGCocoa *>(gVirtualX) &&
2881  "-mouseUp:, gVirtualX is either null or has type different from TGCocoa");
2882  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2883  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton1);
2884 }
2885 
2886 //______________________________________________________________________________
2887 - (void) rightMouseUp : (NSEvent *) theEvent
2888 {
2889 
2890  assert(fID != 0 && "-rightMouseUp:, fID is 0");
2891 
2892  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2893  "-rightMouseUp:, gVirtualX is either null or has type different from TGCocoa");
2894 
2895  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2896  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton3);
2897 }
2898 
2899 //______________________________________________________________________________
2900 - (void) otherMouseUp : (NSEvent *) theEvent
2901 {
2902  assert(fID != 0 && "-otherMouseUp:, fID is 0");
2903 
2904  //Here I assume it's always kButton2.
2905  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2906  "-otherMouseUp:, gVirtualX is either null or has type different from TGCocoa");
2907  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2908  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton2);
2909 }
2910 
2911 //______________________________________________________________________________
2912 - (void) mouseEntered : (NSEvent *) theEvent
2913 {
2914  assert(fID != 0 && "-mouseEntered:, fID is 0");
2915  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2916  "-mouseEntered:, gVirtualX is null or not of TGCocoa type");
2917 
2918  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2919  vx->GetEventTranslator()->GenerateCrossingEvent(theEvent);
2920 }
2921 
2922 //______________________________________________________________________________
2923 - (void) mouseExited : (NSEvent *) theEvent
2924 {
2925  assert(fID != 0 && "-mouseExited:, fID is 0");
2926 
2927  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2928  "-mouseExited:, gVirtualX is null or not of TGCocoa type");
2929 
2930  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2931  vx->GetEventTranslator()->GenerateCrossingEvent(theEvent);
2932 }
2933 
2934 //______________________________________________________________________________
2935 - (void) mouseMoved : (NSEvent *) theEvent
2936 {
2937  assert(fID != 0 && "-mouseMoved:, fID is 0");
2938 
2939  if (fParentView)//Suppress events in all views, except the top-level one.
2940  return; //TODO: check, that it does not create additional problems.
2941 
2942  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2943  "-mouseMoved:, gVirtualX is null or not of TGCocoa type");
2944 
2945  TGCocoa *vx = static_cast<TGCocoa *>(gVirtualX);
2947 }
2948 
2949 //______________________________________________________________________________
2950 - (void) mouseDragged : (NSEvent *) theEvent
2951 {
2952  assert(fID != 0 && "-mouseDragged:, fID is 0");
2953 
2954  TGCocoa * const vx = dynamic_cast<TGCocoa *>(gVirtualX);
2955  assert(vx != 0 && "-mouseDragged:, gVirtualX is null or not of TGCocoa type");
2956 
2958 }
2959 
2960 //______________________________________________________________________________
2961 - (void) rightMouseDragged : (NSEvent *) theEvent
2962 {
2963  assert(fID != 0 && "-rightMouseDragged:, fID is 0");
2964 
2965  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2966  "-rightMouseDragged:, gVirtualX is null or not of TGCocoa type");
2967 
2968  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2970 }
2971 
2972 //______________________________________________________________________________
2973 - (void) otherMouseDragged : (NSEvent *) theEvent
2974 {
2975  assert(fID != 0 && "-otherMouseDragged:, fID is 0");
2976 
2977  if ([theEvent buttonNumber] == 2) {
2978  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2979  "-otherMouseDragged:, gVirtualX is null or not of TGCocoa type");
2980  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2982  }
2983 }
2984 
2985 //______________________________________________________________________________
2986 - (void) keyDown : (NSEvent *) theEvent
2987 {
2988  assert(fID != 0 && "-keyDown:, fID is 0");
2989 
2990  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2991  "-keyDown:, gVirtualX is null or not of TGCocoa type");
2992 
2993  NSView<X11Window> *eventView = self;
2994  if (NSView<X11Window> *pointerView = X11::FindViewUnderPointer())
2995  eventView = pointerView;
2996 
2997  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2998  vx->GetEventTranslator()->GenerateKeyPressEvent(eventView, theEvent);
2999 }
3000 
3001 //______________________________________________________________________________
3002 - (void) keyUp : (NSEvent *) theEvent
3003 {
3004  assert(fID != 0 && "-keyUp:, fID is 0");
3005 
3006  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3007  "-keyUp:, gVirtualX is null or not of TGCocoa type");
3008 
3009  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3010  NSView<X11Window> *eventView = self;
3011  if (NSView<X11Window> *pointerView = X11::FindViewUnderPointer())
3012  eventView = pointerView;
3013 
3014  vx->GetEventTranslator()->GenerateKeyReleaseEvent(eventView, theEvent);
3015 }
3016 
3017 #pragma mark - First responder stuff.
3018 
3019 //______________________________________________________________________________
3020 - (BOOL) acceptsFirstMouse : (NSEvent *) theEvent
3021 {
3022 #pragma unused(theEvent)
3023  return YES;
3024 }
3025 
3026 //______________________________________________________________________________
3027 - (BOOL) acceptsFirstResponder
3028 {
3029  return YES;
3030 }
3031 
3032 #pragma mark - Cursors.
3033 
3034 //______________________________________________________________________________
3035 - (void) setFCurrentCursor : (ECursor) cursor
3036 {
3037  if (cursor != fCurrentCursor) {
3038  fCurrentCursor = cursor;
3039  [self.fQuartzWindow invalidateCursorRectsForView : self];
3040  }
3041 }
3042 
3043 //______________________________________________________________________________
3044 - (NSCursor *) createCustomCursor
3045 {
3046  const char *pngFileName = 0;
3047 
3048  switch (fCurrentCursor) {
3049  case kMove:
3050  pngFileName = "move_cursor.png";
3051  break;
3052  case kArrowHor:
3053  pngFileName = "hor_arrow_cursor.png";
3054  break;
3055  case kArrowVer:
3056  pngFileName = "ver_arrow_cursor.png";
3057  break;
3058  case kArrowRight:
3059  pngFileName = "right_arrow_cursor.png";
3060  break;
3061  case kRotate:
3062  pngFileName = "rotate.png";
3063  break;
3064  case kBottomLeft:
3065  case kTopRight:
3066  pngFileName = "top_right_cursor.png";
3067  break;
3068  case kTopLeft:
3069  case kBottomRight:
3070  pngFileName = "top_left_cursor.png";
3071  break;
3072  default:;
3073  }
3074 
3075  if (pngFileName) {
3076  const char * const path = gSystem->Which(TROOT::GetIconPath(), pngFileName, kReadPermission);
3077  const Util::ScopedArray<const char> arrayGuard(path);
3078 
3079  if (!path || path[0] == 0) {
3080  //File was not found.
3081  return nil;
3082  }
3083 
3084  NSString *nsPath = [NSString stringWithFormat : @"%s", path];//in autorelease pool.
3085  NSImage * const cursorImage = [[NSImage alloc] initWithContentsOfFile : nsPath];
3086 
3087  if (!cursorImage)
3088  return nil;
3089 
3090  NSPoint hotSpot = X11::GetCursorHotStop(cursorImage, fCurrentCursor);
3091  NSCursor * const customCursor = [[[NSCursor alloc] initWithImage : cursorImage
3092  hotSpot : hotSpot] autorelease];
3093 
3094  [cursorImage release];
3095 
3096  return customCursor;
3097  }
3098 
3099  return nil;
3100 }
3101 
3102 //______________________________________________________________________________
3103 - (void) resetCursorRects
3104 {
3105  if (NSCursor * const cursor = X11::CreateCursor(fCurrentCursor))
3106  [self addCursorRect : self.visibleRect cursor : cursor];
3107 }
3108 
3109 //______________________________________________________________________________
3110 - (void) cursorUpdate
3111 {
3112  if (NSCursor * const cursor = X11::CreateCursor(fCurrentCursor)) {
3113  // NB: [window invalidateCursorRectsForView] called here has the
3114  // same problem as commented below in -cursorUpdate:.
3115  [cursor set];
3116  }
3117 }
3118 
3119 //______________________________________________________________________________
3120 - (void) cursorUpdate : (NSEvent *) event
3121 {
3122 #pragma unused(event)
3123  // It looks like [NSCursor set] method does not work properly when called from
3124  // cursorUpdate:, having, say, a parent frame with 'arrow' cursor and a child (completely
3125  // filling its parent's area) with 'cross', it happens the 'cross' cursor is not always
3126  // set correctly, for example:
3127  // if we have a TCanvas and resize it, cursor is 'arrow' inside this canvas,
3128  // though it must be 'cross'. This all, as it always happesn with "thinking different"
3129  // Apple is somehow related to run loop or something. As always, it's not documented,
3130  // so Apple can continue to think different. The idea with performSelector comes from:
3131  // http://stackoverflow.com/questions/8430236/nscursor-set-method-has-no-effect
3132  // Or may be it's just a bug:
3133  // http://stackoverflow.com/questions/13901232/nscursor-set-not-working-on-unfocused-window
3134  [self performSelector : @selector(cursorUpdate) withObject : nil afterDelay : 0.05f];
3135 }
3136 
3137 #pragma mark - Emulated X11 properties.
3138 
3139 //______________________________________________________________________________
3140 - (void) setProperty : (const char *) propName data : (unsigned char *) propData
3141  size : (unsigned) dataSize forType : (Atom_t) dataType format : (unsigned) format
3142 {
3143  assert(propName != 0 && "-setProperty:data:size:forType:, parameter 'propName' is null");
3144  assert(propData != 0 && "-setProperty:data:size:forType:, parameter 'propData' is null");
3145  assert(dataSize != 0 && "-setProperty:data:size:forType:, parameter 'dataSize' is 0");
3146 
3147  NSString * const key = [NSString stringWithCString : propName encoding : NSASCIIStringEncoding];
3148  QuartzWindowProperty * property = (QuartzWindowProperty *)[fX11Properties valueForKey : key];
3149 
3150  //At the moment (and I think this will never change) TGX11 always calls XChangeProperty with PropModeReplace.
3151  if (property)
3152  [property resetPropertyData : propData size : dataSize type : dataType format : format];
3153  else {
3154  //No property found, add a new one.
3155  property = [[QuartzWindowProperty alloc] initWithData : propData size : dataSize
3156  type : dataType format : format];
3157  [fX11Properties setObject : property forKey : key];
3158  [property release];
3159  }
3160 }
3161 
3162 //______________________________________________________________________________
3163 - (BOOL) hasProperty : (const char *) propName
3164 {
3165  assert(propName != 0 && "-hasProperty:, propName parameter is null");
3166 
3167  NSString * const key = [NSString stringWithCString : propName encoding : NSASCIIStringEncoding];
3168  QuartzWindowProperty * const property = (QuartzWindowProperty *)[fX11Properties valueForKey : key];
3169 
3170  return property != nil;
3171 }
3172 
3173 //______________________________________________________________________________
3174 - (unsigned char *) getProperty : (const char *) propName returnType : (Atom_t *) type
3175  returnFormat : (unsigned *) format nElements : (unsigned *) nElements
3176 {
3177  assert(propName != 0 &&
3178  "-getProperty:returnType:returnFormat:nElements:, parameter 'propName' is null");
3179  assert(type != 0 &&
3180  "-getProperty:returnType:returnFormat:nElements:, parameter 'type' is null");
3181  assert(format != 0 &&
3182  "-getProperty:returnType:returnFormat:nElements:, parameter 'format' is null");
3183  assert(nElements != 0 &&
3184  "-getProperty:returnType:returnFormat:nElements:, parameter 'nElements' is null");
3185 
3186  NSString * const key = [NSString stringWithCString : propName encoding : NSASCIIStringEncoding];
3187  QuartzWindowProperty * const property = (QuartzWindowProperty *)[fX11Properties valueForKey : key];
3188  assert(property != 0 &&
3189  "-getProperty:returnType:returnFormat:nElements, property not found");
3190 
3191  NSData * const propData = property.fPropertyData;
3192 
3193  const NSUInteger dataSize = [propData length];
3194  unsigned char *buff = 0;
3195  try {
3196  buff = new unsigned char[dataSize]();
3197  } catch (const std::bad_alloc &) {
3198  //Hmm, can I log, if new failed? :)
3199  NSLog(@"QuartzWindow: -getProperty:returnType:returnFormat:nElements:,"
3200  " memory allocation failed");
3201  return 0;
3202  }
3203 
3204  [propData getBytes : buff length : dataSize];
3205  *format = property.fFormat;
3206 
3207  *nElements = dataSize;
3208 
3209  if (*format == 16)
3210  *nElements= dataSize / 2;
3211  else if (*format == 32)
3212  *nElements = dataSize / 4;
3213 
3214  *type = property.fType;
3215 
3216  return buff;
3217 }
3218 
3219 //______________________________________________________________________________
3220 - (void) removeProperty : (const char *) propName
3221 {
3222  assert(propName != 0 && "-removeProperty:, parameter 'propName' is null");
3223 
3224  NSString * const key = [NSString stringWithCString : propName
3225  encoding : NSASCIIStringEncoding];
3226  [fX11Properties removeObjectForKey : key];
3227 }
3228 
3229 //DND
3230 //______________________________________________________________________________
3231 - (NSDragOperation) draggingEntered : (id<NSDraggingInfo>) sender
3232 {
3233  NSPasteboard * const pasteBoard = [sender draggingPasteboard];
3234  const NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
3235 
3236  if ([[pasteBoard types] containsObject : NSFilenamesPboardType] && (sourceDragMask & NSDragOperationCopy))
3237  return NSDragOperationCopy;
3238 
3239  return NSDragOperationNone;
3240 }
3241 
3242 //______________________________________________________________________________
3243 - (BOOL) performDragOperation : (id<NSDraggingInfo>) sender
3244 {
3245  //We can drag some files (images, pdfs, source code files) from
3246  //finder to ROOT's window (mainly TCanvas or text editor).
3247  //The logic is totally screwed here :((( - ROOT will try to
3248  //read a property of some window (not 'self', unfortunately) -
3249  //this works since on Window all data is in a global clipboard
3250  //(on X11 it simply does not work at all).
3251  //I'm attaching the file name as a property for the top level window,
3252  //there is no other way to make this data accessible for ROOT.
3253 
3254  NSPasteboard * const pasteBoard = [sender draggingPasteboard];
3255  const NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
3256 
3257  if ([[pasteBoard types] containsObject : NSFilenamesPboardType] && (sourceDragMask & NSDragOperationCopy)) {
3258 
3259  //Here I try to put string ("file://....") into window's property to make
3260  //it accesible from ROOT's GUI.
3261  const Atom_t textUriAtom = gVirtualX->InternAtom("text/uri-list", kFALSE);
3262 
3263  NSArray * const files = [pasteBoard propertyListForType : NSFilenamesPboardType];
3264  for (NSString *path in files) {
3265  //ROOT can not process several files, use the first one.
3266  NSString * const item = [@"file://" stringByAppendingString : path];
3267  //Yes, only ASCII encoding, but after all, ROOT's not able to work with NON-ASCII strings.
3268  const NSUInteger len = [item lengthOfBytesUsingEncoding : NSASCIIStringEncoding] + 1;
3269  try {
3270  std::vector<unsigned char> propertyData(len);
3271  [item getCString : (char *)&propertyData[0] maxLength : propertyData.size()
3272  encoding : NSASCIIStringEncoding];
3273  //There is no any guarantee, that this will ever work, logic in TGDNDManager is totally crazy.
3274  NSView<X11Window> * const targetView = self.fQuartzWindow.fContentView;
3275  [targetView setProperty : "_XC_DND_DATA" data : &propertyData[0]
3276  size : propertyData.size() forType : textUriAtom format : 8];
3277  } catch (const std::bad_alloc &) {
3278  //Hehe, can I log something in case of bad_alloc??? ;)
3279  NSLog(@"QuartzView: -performDragOperation:, memory allocation failed");
3280  return NO;
3281  }
3282 
3283  break;
3284  }
3285 
3286  //Property is attached now.
3287 
3288  //Gdk on windows creates three events on file drop (WM_DROPFILES): XdndEnter, XdndPosition, XdndDrop.
3289  //1. Dnd enter.
3290  Event_t event1 = {};
3291  event1.fType = kClientMessage;
3292  event1.fWindow = fID;
3293  event1.fHandle = gVirtualX->InternAtom("XdndEnter", kFALSE);
3294  event1.fUser[0] = long(fID);
3295  event1.fUser[2] = textUriAtom;//gVirtualX->InternAtom("text/uri-list", kFALSE);
3296  //
3297  gVirtualX->SendEvent(fID, &event1);
3298 
3299  //2. Dnd position.
3300  Event_t event2 = {};
3301  event2.fType = kClientMessage;
3302  event2.fWindow = fID;
3303  event2.fHandle = gVirtualX->InternAtom("XdndPosition", kFALSE);
3304  event2.fUser[0] = long(fID);
3305  event2.fUser[2] = 0;//Here I have to pack x and y for drop coordinates, shifting by 16 bits.
3306  NSPoint dropPoint = [sender draggingLocation];
3307  //convertPointFromBase is deprecated.
3308  //dropPoint = [self convertPointFromBase : dropPoint];
3309  dropPoint = [self convertPoint : dropPoint fromView : nil];
3310  //
3311  dropPoint = X11::TranslateToScreen(self, dropPoint);
3312  event2.fUser[2] = UShort_t(dropPoint.y) | (UShort_t(dropPoint.x) << 16);
3313 
3314  gVirtualX->SendEvent(fID, &event2);
3315 
3316  Event_t event3 = {};
3317  event3.fType = kClientMessage;
3318  event3.fWindow = fID;
3319  event3.fHandle = gVirtualX->InternAtom("XdndDrop", kFALSE);
3320 
3321  gVirtualX->SendEvent(fID, &event3);
3322  }
3323 
3324  return YES;//Always ok, even if file type is not supported - no need in "animation".
3325 }
3326 
3327 @end
std::vector< unsigned char > DownscaledImageData(unsigned w, unsigned h, CGImageRef image)
int GlobalXCocoaToROOT(CGFloat xCocoa)
BOOL fOverrideRedirect
Definition: QuartzWindow.h:177
void GenerateConfigureNotifyEvent(NSView< X11Window > *view, const NSRect &newFrame)
Definition: X11Events.mm:1152
QuartzWindow * CreateTopLevelWindow(Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t border, Int_t depth, UInt_t clss, void *visual, SetWindowAttributes_t *attr, UInt_t)
Definition: QuartzWindow.mm:51
NSView< X11Window > * FindViewForPointerEvent(NSEvent *pointerEvent)
void GetRootWindowAttributes(WindowAttributes_t *attr)
Definition: QuartzWindow.mm:97
unsigned long fBackgroundPixel
Definition: QuartzWindow.h:111
void GeneratePointerMotionEvent(NSEvent *theEvent)
Definition: X11Events.mm:1250
void configureNotifyTree()
unsigned fPassiveGrabEventMask
Definition: QuartzWindow.h:183
bool LockFocus(NSView< X11Window > *view)
ROOT::MacOSX::Util::CFScopeGuard< CGImageRef > fImage
Definition: QuartzPixmap.h:105
Namespace for new ROOT classes and functions.
Definition: ROOT.py:1
void GetWindowAttributes(NSObject< X11Window > *window, WindowAttributes_t *dst)
TLine * line
QuartzWindow * fQuartzWindow
Definition: QuartzWindow.h:122
UInt_t Mask_t
Definition: GuiTypes.h:42
ECursor fCurrentCursor
Definition: QuartzWindow.h:188
ULong_t fBackingPixel
Definition: GuiTypes.h:127
QuartzWindow * FindWindowInPoint(Int_t x, Int_t y)
void mapSubwindows()
void GenerateKeyReleaseEvent(NSView< X11Window > *eventView, NSEvent *theEvent)
Definition: X11Events.mm:1307
static const TString & GetIconPath()
Get the icon path in the installation. Static utility function.
Definition: TROOT.cxx:2717
const Mask_t kButtonMotionMask
Definition: GuiTypes.h:165
const Mask_t kWACursor
Definition: GuiTypes.h:155
#define assert(cond)
Definition: unittest.h:542
unsigned short UShort_t
Definition: RtypesCore.h:36
NSView< X11Window > * FindDNDAwareViewInPoint(NSView *parentView, Window_t dragWinID, Window_t inputWinID, Int_t x, Int_t y, Int_t maxDepth)
bool AdjustCropArea(const Rectangle &srcRect, Rectangle &cropArea)
TH1 * h
Definition: legend2.C:5
const Mask_t kLeaveWindowMask
Definition: GuiTypes.h:169
const Mask_t kWABackPixmap
Definition: GuiTypes.h:140
BOOL fIsDNDAware
Definition: QuartzWindow.h:189
const Mask_t kWABorderPixel
Definition: GuiTypes.h:143
const Mask_t kWABitGravity
Definition: GuiTypes.h:145
int LocalYCocoaToROOT(NSView< X11Window > *parentView, CGFloat yCocoa)
QuartzImage * fShapeCombineMask
Definition: QuartzWindow.h:48
void unmapWindow()
#define gClient
Definition: TGClient.h:174
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: Rtypes.h:92
const Mask_t kWABackingStore
Definition: GuiTypes.h:147
BOOL fIsOpenGLWidget()
virtual char * Which(const char *search, const char *file, EAccessMode mode=kFileExists)
Find location of file in a search path.
Definition: TSystem.cxx:1510
NSView< X11Window > * fContentView
Definition: QuartzWindow.h:255
NSUInteger fModifiers()
unsigned fWidth()
unsigned char * readColorBits:(ROOT::MacOSX::X11::Rectangle area)
QuartzPixmap * fBackBuffer
Definition: QuartzWindow.h:195
static std::string format(double x, double y, int digits, int width)
ECursor
Definition: TVirtualX.h:56
NSCursor * CreateCustomCursor(ECursor currentCursor)
Window_t fWindow
Definition: GuiTypes.h:177
BOOL fIsOpenGLWidget()
int GlobalYROOTToCocoa(CGFloat yROOT)
void GenerateButtonPressEvent(NSView< X11Window > *eventView, NSEvent *theEvent, EMouseButton btn)
Definition: X11Events.mm:1263
const Mask_t kWABorderPixmap
Definition: GuiTypes.h:142
unsigned fHeight
Definition: QuartzPixmap.h:103
ULong_t fBackgroundPixel
Definition: GuiTypes.h:96
const Mask_t kPointerMotionMask
Definition: GuiTypes.h:164
void addChild:(NSView< X11Window > *child)
Bool_t fMapInstalled
Definition: GuiTypes.h:130
void SetWindowAttributes(const SetWindowAttributes_t *attr, NSObject< X11Window > *window)
QuartzView * fParentView
Definition: QuartzWindow.h:180
NSMutableDictionary * fX11Properties
Definition: QuartzWindow.h:199
Double_t x[n]
Definition: legend1.C:17
NSPoint GetCursorHotStop(NSImage *image, ECursor cursor)
NSPoint TranslateCoordinates(NSView< X11Window > *fromView, NSView< X11Window > *toView, NSPoint sourcePoint)
QuartzImage * fBackgroundPixmap
Definition: QuartzWindow.h:200
NSPoint ConvertPointFromBaseToScreen(NSWindow *window, NSPoint windowPoint)
ULong_t fBackingPlanes
Definition: GuiTypes.h:126
Handle_t fHandle
Definition: GuiTypes.h:186
QuartzWindow * FindWindowForPointerEvent(NSEvent *pointerEvent)
Handle_t Atom_t
Definition: GuiTypes.h:38
Colormap_t fColormap
Definition: GuiTypes.h:129
unsigned fHeight
Definition: QuartzPixmap.h:46
ROOT::MacOSX::X11::PointerGrab fCurrentGrabType
Definition: QuartzWindow.h:202
QuartzWindow * fQuartzWindow
Definition: QuartzWindow.h:256
CGImageRef CreateSubImage(QuartzImage *image, const Rectangle &area)
ROOT::MacOSX::X11::Rectangle GetDisplayGeometry() const
Definition: TGCocoa.mm:541
const Mask_t kWAColormap
Definition: GuiTypes.h:154
void lowerWindow()
XFontStruct * id
Definition: TGX11.cxx:108
NSPoint TranslateFromScreen(NSPoint point, NSView< X11Window > *to)
void CocoaDrawOFF()
Definition: TGCocoa.mm:4411
NSPoint ConvertPointFromScreenToBase(NSPoint screenPoint, NSWindow *window)
const Mask_t kWABackingPlanes
Definition: GuiTypes.h:148
int GlobalXROOTToCocoa(CGFloat xROOT)
const Mask_t kButtonPressMask
Definition: GuiTypes.h:162
NSView< X11Window > * FindViewUnderPointer()
BOOL fHasFocus
Definition: QuartzWindow.h:179
NSPoint TranslateToScreen(NSView< X11Window > *from, NSPoint point)
NSView< X11Window > * FrameForTextView(NSView< X11Window > *textView)
Long_t fAllEventMasks
Definition: GuiTypes.h:132
int GlobalYCocoaToROOT(CGFloat yCocoa)
bool ViewIsHtmlViewFrame(NSView< X11Window > *view, bool checkParent)
void activatePassiveGrab()
void GenerateExposeEvent(NSView< X11Window > *view, const NSRect &exposedRect)
Definition: X11Events.mm:1179
void setOverlapped:(BOOL overlap)
const Mask_t kWAEventMask
Definition: GuiTypes.h:152
R__EXTERN TSystem * gSystem
Definition: TSystem.h:549
const Mask_t kWASaveUnder
Definition: GuiTypes.h:151
SVector< double, 2 > v
Definition: Dict.h:5
static Atom_t fgDeleteWindowAtom
Definition: TGCocoa.h:475
EGEventType fType
Definition: GuiTypes.h:176
QuartzWindow * FindWindowUnderPointer()
unsigned fHeight()
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition: TObject.cxx:487
virtual const char * GetName() const
Return unique name, used in SavePrimitive methods.
Definition: TGWindow.cxx:221
unsigned fWidth
Definition: QuartzPixmap.h:102
const Mask_t kExposureMask
Definition: GuiTypes.h:166
unsigned int UInt_t
Definition: RtypesCore.h:42
QuartzView * fParentView
Definition: QuartzWindow.h:120
QuartzView * CreateChildView(QuartzView *parent, Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t border, Int_t depth, UInt_t clss, void *visual, SetWindowAttributes_t *attr, UInt_t wtype)
Definition: QuartzWindow.mm:78
BOOL fIsOverlapped()
void UnlockFocus(NSView< X11Window > *view)
void PixelToRGB(Pixel_t pixelColor, CGFloat *rgb)
Definition: X11Colors.mm:920
long fEventMask
Definition: QuartzWindow.h:171
void RemoveGraphicsOperationsForWindow(Window_t wid)
Definition: X11Buffer.mm:675
unsigned fID
Definition: QuartzWindow.h:169
#define gVirtualX
Definition: TVirtualX.h:362
int LocalYROOTToCocoa(NSView< X11Window > *parentView, CGFloat yROOT)
bool ViewIsTextView(unsigned viewID)
unsigned fWidth
Definition: QuartzPixmap.h:45
PyObject * fType
const Mask_t kEnterWindowMask
Definition: GuiTypes.h:168
BOOL fPassiveGrabOwnerEvents
Definition: QuartzWindow.h:186
CGContextRef fContext
Definition: QuartzWindow.h:170
unsigned fWidth()
QuartzWindow * fMainWindow
Definition: QuartzWindow.h:42
BOOL fSnapshotDraw
Definition: QuartzWindow.h:187
const Mask_t kStructureNotifyMask
Definition: GuiTypes.h:167
static Int_t init()
bool ViewIsHtmlView(unsigned viewID)
NSView< X11Window > * FrameForHtmlView(NSView< X11Window > *htmlView)
bool ScreenPointIsInView(NSView< X11Window > *view, Int_t x, Int_t y)
const Mask_t kButtonReleaseMask
Definition: GuiTypes.h:163
int type
Definition: TGX11.cxx:120
const Mask_t kWAOverrideRedirect
Definition: GuiTypes.h:150
unsigned long ULong_t
Definition: RtypesCore.h:51
void GenerateCrossingEvent(NSEvent *theEvent)
Definition: X11Events.mm:1197
Window_t fRoot
Definition: GuiTypes.h:121
Double_t y[n]
Definition: legend1.C:17
const Mask_t kWAWinGravity
Definition: GuiTypes.h:146
void WindowLostFocus(Window_t winID)
void ClipToShapeMask(NSView< X11Window > *view, CGContextRef ctx)
NSCursor * CreateCursor(ECursor currentCursor)
Long_t fUser[5]
Definition: GuiTypes.h:188
unsigned long fBackgroundPixel
Definition: QuartzWindow.h:176
unsigned fPassiveGrabKeyModifiers
Definition: QuartzWindow.h:184
ROOT::MacOSX::X11::CommandBuffer * GetCommandBuffer() const
Definition: TGCocoa.mm:4399
BOOL fDelayedTransient
Definition: QuartzWindow.h:47
typedef void((*Func_t)())
Bool_t fOverrideRedirect
Definition: GuiTypes.h:135
Handle_t Window_t
Definition: GuiTypes.h:30
Int_t fFormat
Definition: GuiTypes.h:187
This class implements TVirtualX interface for MacOS X, using Cocoa and Quartz 2D. ...
Definition: TGCocoa.h:64
NSComparisonResult CompareViewsToLower(id view1, id view2, void *context)
QuartzView * fContentView
Definition: QuartzWindow.h:46
const Mask_t kWABorderWidth
Definition: GuiTypes.h:144
#define NULL
Definition: Rtypes.h:82
void GenerateKeyPressEvent(NSView< X11Window > *eventView, NSEvent *theEvent)
Definition: X11Events.mm:1291
NSComparisonResult CompareViewsToRaise(id view1, id view2, void *context)
BOOL fActiveGrabOwnerEvents
Definition: QuartzWindow.h:204
const Mask_t kWABackPixel
Definition: GuiTypes.h:141
double result[121]
const Mask_t kWABackingPixel
Definition: GuiTypes.h:149
const Mask_t kWADontPropagate
Definition: GuiTypes.h:153
bool ViewIsTextViewFrame(NSView< X11Window > *view, bool checkParent)
void raiseWindow()
const Bool_t kTRUE
Definition: Rtypes.h:91
void CocoaDrawON()
Definition: TGCocoa.mm:4405
Long_t fYourEventMask
Definition: GuiTypes.h:133
unsigned fActiveGrabEventMask
Definition: QuartzWindow.h:185
ROOT::MacOSX::X11::EventTranslator * GetEventTranslator() const
Definition: TGCocoa.mm:4393
void GenerateFocusChangeEvent(NSView< X11Window > *eventView)
Definition: X11Events.mm:1327
unsigned fHeight()
int fPassiveGrabButton
Definition: QuartzWindow.h:182
void GetWindowGeometry(NSObject< X11Window > *win, WindowAttributes_t *dst)
NSMutableArray * fPassiveKeyGrabs
Definition: QuartzWindow.h:196
void activateImplicitGrab()
void GenerateButtonReleaseEvent(NSView< X11Window > *eventView, NSEvent *theEvent, EMouseButton btn)
Definition: X11Events.mm:1276