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