0030619: Draw Harness, ViewerTest - add continuous rendering option to vrepaint command
[occt.git] / src / Cocoa / Cocoa_Window.mm
1 // Created on: 2012-11-12
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2012-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #import <TargetConditionals.h>
17
18 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
19   #import <UIKit/UIKit.h>
20 #else
21   #import <Cocoa/Cocoa.h>
22 #endif
23
24 #include <Cocoa_Window.hxx>
25
26 #include <Cocoa_LocalPool.hxx>
27
28 #include <Image_AlienPixMap.hxx>
29 #include <Aspect_Convert.hxx>
30 #include <Aspect_WindowDefinitionError.hxx>
31
32 IMPLEMENT_STANDARD_RTTIEXT(Cocoa_Window,Aspect_Window)
33
34 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
35   //
36 #else
37
38 #if !defined(MAC_OS_X_VERSION_10_12) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)
39   // replacements for macOS versions before 10.12
40   #define NSWindowStyleMaskResizable NSResizableWindowMask
41   #define NSWindowStyleMaskClosable  NSClosableWindowMask
42   #define NSWindowStyleMaskTitled    NSTitledWindowMask
43 #endif
44
45 static Standard_Integer getScreenBottom()
46 {
47   Cocoa_LocalPool aLocalPool;
48   NSArray* aScreens = [NSScreen screens];
49   if (aScreens == NULL || [aScreens count] == 0)
50   {
51     return 0;
52   }
53
54   NSScreen* aScreen = (NSScreen* )[aScreens objectAtIndex: 0];
55   NSDictionary* aDict = [aScreen deviceDescription];
56   NSNumber* aNumber = [aDict objectForKey: @"NSScreenNumber"];
57   if (aNumber == NULL
58   || [aNumber isKindOfClass: [NSNumber class]] == NO)
59   {
60     return 0;
61   }
62
63   CGDirectDisplayID aDispId = [aNumber unsignedIntValue];
64   CGRect aRect = CGDisplayBounds(aDispId);
65   return Standard_Integer(aRect.origin.y + aRect.size.height);
66 }
67 #endif
68
69 //! Extension for Cocoa_Window::InvalidateContent().
70 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
71   @interface UIView (UIViewOcctAdditions)
72   - (void )invalidateContentOcct: (id )theSender;
73   @end
74   @implementation UIView (UIViewOcctAdditions)
75   - (void )invalidateContentOcct: (id )theSender
76   {
77     (void )theSender;
78     [self setNeedsDisplay];
79   }
80   @end
81 #else
82   @interface NSView (NSViewOcctAdditions)
83   - (void )invalidateContentOcct: (id )theSender;
84   @end
85   @implementation NSView (NSViewOcctAdditions)
86   - (void )invalidateContentOcct: (id )theSender
87   {
88     (void )theSender;
89     [self setNeedsDisplay: YES];
90   }
91   @end
92 #endif
93
94 // =======================================================================
95 // function : Cocoa_Window
96 // purpose  :
97 // =======================================================================
98 Cocoa_Window::Cocoa_Window (const Standard_CString theTitle,
99                             const Standard_Integer thePxLeft,
100                             const Standard_Integer thePxTop,
101                             const Standard_Integer thePxWidth,
102                             const Standard_Integer thePxHeight)
103 : Aspect_Window (),
104 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
105   myHWindow (NULL),
106 #endif
107   myHView   (NULL),
108   myXLeft   (thePxLeft),
109   myYTop    (thePxTop),
110   myXRight  (thePxLeft + thePxWidth),
111   myYBottom (thePxTop + thePxHeight)
112 {
113 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
114   //
115 #else
116   if (thePxWidth <= 0 || thePxHeight <= 0)
117   {
118     throw Aspect_WindowDefinitionError("Coordinate(s) out of range");
119   }
120   else if (NSApp == NULL)
121   {
122     throw Aspect_WindowDefinitionError("Cocoa application should be instantiated before window");
123     return;
124   }
125
126   // convert top-bottom coordinates to bottom-top (Cocoa)
127   myYTop    = getScreenBottom() - myYBottom;
128   myYBottom = myYTop + thePxHeight;
129
130   Cocoa_LocalPool aLocalPool;
131   NSUInteger aWinStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
132   NSRect aRectNs = NSMakeRect (float(myXLeft), float(myYTop), float(thePxWidth), float(thePxHeight));
133   myHWindow = [[NSWindow alloc] initWithContentRect: aRectNs
134                                           styleMask: aWinStyle
135                                             backing: NSBackingStoreBuffered
136                                               defer: NO];
137   if (myHWindow == NULL)
138   {
139     throw Aspect_WindowDefinitionError("Unable to create window");
140   }
141   myHView = [[myHWindow contentView] retain];
142
143   NSString* aTitleNs = [[NSString alloc] initWithUTF8String: theTitle];
144   [myHWindow setTitle: aTitleNs];
145   [aTitleNs release];
146
147   // do not destroy NSWindow on close - we didn't handle it!
148   [myHWindow setReleasedWhenClosed: NO];
149 #endif
150 }
151
152 // =======================================================================
153 // function : Cocoa_Window
154 // purpose  :
155 // =======================================================================
156 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
157 Cocoa_Window::Cocoa_Window (UIView* theViewNS)
158 : Aspect_Window(),
159 #else
160 Cocoa_Window::Cocoa_Window (NSView* theViewNS)
161 : Aspect_Window(),
162   myHWindow (NULL),
163 #endif
164   myHView   (NULL),
165   myXLeft   (0),
166   myYTop    (0),
167   myXRight  (512),
168   myYBottom (512)
169 {
170 #if defined(HAVE_OBJC_ARC)
171   myHView = theViewNS;
172 #else
173   myHView = [theViewNS retain];
174 #endif
175   DoResize();
176 }
177
178 // =======================================================================
179 // function : ~Cocoa_Window
180 // purpose  :
181 // =======================================================================
182 Cocoa_Window::~Cocoa_Window()
183 {
184 #if !defined(HAVE_OBJC_ARC)
185   Cocoa_LocalPool aLocalPool;
186 #endif
187 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
188   if (myHWindow != NULL)
189   {
190   #if !defined(HAVE_OBJC_ARC)
191     //[myHWindow close];
192     [myHWindow release];
193   #endif
194     myHWindow = NULL;
195   }
196 #endif
197   if (myHView != NULL)
198   {
199   #if !defined(HAVE_OBJC_ARC)
200     [myHView release];
201   #endif
202     myHView = NULL;
203   }
204 }
205
206 // =======================================================================
207 // function : SetHView
208 // purpose  :
209 // =======================================================================
210 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
211 void Cocoa_Window::SetHView (UIView* theView)
212 {
213 #else
214 void Cocoa_Window::SetHView (NSView* theView)
215 {
216   if (myHWindow != NULL)
217   {
218     [myHWindow setContentView: theView];
219   }
220 #endif
221
222 #if defined(HAVE_OBJC_ARC)
223   myHView = theView;
224 #else
225   if (myHView != NULL)
226   {
227     [myHView release];
228     myHView = NULL;
229   }
230   myHView = [theView retain];
231 #endif
232 }
233
234 // =======================================================================
235 // function : IsMapped
236 // purpose  :
237 // =======================================================================
238 Standard_Boolean Cocoa_Window::IsMapped() const
239 {
240   if (IsVirtual())
241   {
242     return Standard_True;
243   }
244
245 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
246   return myHView != NULL;
247 #else
248   return myHView != NULL
249    &&  [[myHView window] isVisible];
250 #endif
251 }
252
253 // =======================================================================
254 // function : Map
255 // purpose  :
256 // =======================================================================
257 void Cocoa_Window::Map() const
258 {
259   if (IsVirtual())
260   {
261     return;
262   }
263
264   if (myHView != NULL)
265   {
266   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
267     //
268   #else
269     [[myHView window] orderFront: NULL];
270   #endif
271   }
272 }
273
274 // =======================================================================
275 // function : Unmap
276 // purpose  :
277 // =======================================================================
278 void Cocoa_Window::Unmap() const
279 {
280   if (myHView != NULL)
281   {
282   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
283     //
284   #else
285     [[myHView window] orderOut: NULL];
286   #endif
287   }
288 }
289
290 // =======================================================================
291 // function : DoResize
292 // purpose  :
293 // =======================================================================
294 Aspect_TypeOfResize Cocoa_Window::DoResize() const
295 {
296   if (myHView == NULL)
297   {
298     return Aspect_TOR_UNKNOWN;
299   }
300
301 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
302   CGRect aBounds = [myHView bounds];
303 #else
304   NSRect aBounds = [myHView bounds];
305 #endif
306   Standard_Integer aMask = 0;
307   Aspect_TypeOfResize aMode = Aspect_TOR_UNKNOWN;
308
309   if (Abs ((Standard_Integer )aBounds.origin.x                         - myXLeft  ) > 2) aMask |= 1;
310   if (Abs ((Standard_Integer )(aBounds.origin.x + aBounds.size.width)  - myXRight ) > 2) aMask |= 2;
311   if (Abs ((Standard_Integer )aBounds.origin.y                         - myYTop   ) > 2) aMask |= 4;
312   if (Abs ((Standard_Integer )(aBounds.origin.y + aBounds.size.height) - myYBottom) > 2) aMask |= 8;
313   switch (aMask)
314   {
315     case 0:  aMode = Aspect_TOR_NO_BORDER;               break;
316     case 1:  aMode = Aspect_TOR_LEFT_BORDER;             break;
317     case 2:  aMode = Aspect_TOR_RIGHT_BORDER;            break;
318     case 4:  aMode = Aspect_TOR_TOP_BORDER;              break;
319     case 5:  aMode = Aspect_TOR_LEFT_AND_TOP_BORDER;     break;
320     case 6:  aMode = Aspect_TOR_TOP_AND_RIGHT_BORDER;    break;
321     case 8:  aMode = Aspect_TOR_BOTTOM_BORDER;           break;
322     case 9:  aMode = Aspect_TOR_BOTTOM_AND_LEFT_BORDER;  break;
323     case 10: aMode = Aspect_TOR_RIGHT_AND_BOTTOM_BORDER; break;
324     default: break;
325   }
326
327   *((Standard_Integer* )&myXLeft   ) = (Standard_Integer )aBounds.origin.x;
328   *((Standard_Integer* )&myXRight  ) = (Standard_Integer )(aBounds.origin.x + aBounds.size.width);
329   *((Standard_Integer* )&myYTop    ) = (Standard_Integer )aBounds.origin.y;
330   *((Standard_Integer* )&myYBottom ) = (Standard_Integer )(aBounds.origin.y + aBounds.size.height);
331   return aMode;
332 }
333
334 // =======================================================================
335 // function : DoMapping
336 // purpose  :
337 // =======================================================================
338 Standard_Boolean Cocoa_Window::DoMapping() const
339 {
340   return Standard_True;
341 }
342
343 // =======================================================================
344 // function : Ratio
345 // purpose  :
346 // =======================================================================
347 Standard_Real Cocoa_Window::Ratio() const
348 {
349   if (myHView == NULL)
350   {
351     return 1.0;
352   }
353
354 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
355   CGRect aBounds = [myHView bounds];
356 #else
357   NSRect aBounds = [myHView bounds];
358 #endif
359   return Standard_Real (aBounds.size.width / aBounds.size.height);
360 }
361
362 // =======================================================================
363 // function : Position
364 // purpose  :
365 // =======================================================================
366 void Cocoa_Window::Position (Standard_Integer& X1, Standard_Integer& Y1,
367                              Standard_Integer& X2, Standard_Integer& Y2) const
368 {
369 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
370   CGRect aBounds = [myHView bounds];
371   X1 = 0;
372   Y1 = 0;
373   X2 = (Standard_Integer )aBounds.size.width;
374   Y2 = (Standard_Integer )aBounds.size.height;
375 #else
376   NSWindow* aWindow = [myHView window];
377   NSRect aWindowRect = [aWindow frame];
378   X1 = (Standard_Integer) aWindowRect.origin.x;
379   Y1 = getScreenBottom() - (Standard_Integer) aWindowRect.origin.y - (Standard_Integer) aWindowRect.size.height;
380   X2 = X1 + (Standard_Integer) aWindowRect.size.width;
381   Y2 = Y1 + (Standard_Integer) aWindowRect.size.height;
382 #endif
383 }
384
385 // =======================================================================
386 // function : Size
387 // purpose  :
388 // =======================================================================
389 void Cocoa_Window::Size (Standard_Integer& theWidth,
390                          Standard_Integer& theHeight) const
391 {
392   if (myHView == NULL)
393   {
394     return;
395   }
396
397 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
398   CGRect aBounds = [myHView bounds];
399 #else
400   NSRect aBounds = [myHView bounds];
401 #endif
402   theWidth  = (Standard_Integer )aBounds.size.width;
403   theHeight = (Standard_Integer )aBounds.size.height;
404 }
405
406 // =======================================================================
407 // function : InvalidateContent
408 // purpose  :
409 // =======================================================================
410 void Cocoa_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
411 {
412   if (myHView == NULL)
413   {
414     return;
415   }
416
417   if ([NSThread isMainThread])
418   {
419   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
420     [myHView setNeedsDisplay];
421   #else
422     [myHView setNeedsDisplay: YES];
423   #endif
424   }
425   else
426   {
427     [myHView performSelectorOnMainThread: @selector(invalidateContentOcct:)
428                               withObject: NULL
429                            waitUntilDone: NO];
430   }
431 }