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