47584124702d007fc679b2997c6926477228afcd
[occt.git] / src / Draw / Draw_Window_1.mm
1 // Copyright (c) 2013-2014 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #if defined(__APPLE__) && !defined(HAVE_XLIB)
15
16 #import <Cocoa/Cocoa.h>
17
18 #include <Draw_Window.hxx>
19 #include <Cocoa_LocalPool.hxx>
20
21 #if !defined(MAC_OS_X_VERSION_10_12) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)
22   // replacements for macOS versions before 10.12
23   #define NSEventTypeLeftMouseDown    NSLeftMouseDown
24   #define NSEventTypeRightMouseDown   NSRightMouseDown
25   #define NSEventTypeLeftMouseDragged NSLeftMouseDragged
26   #define NSEventTypeMouseMoved       NSMouseMoved
27
28   #define NSEventMaskLeftMouseDragged NSLeftMouseDraggedMask
29   #define NSEventMaskMouseMoved       NSMouseMovedMask
30   #define NSEventMaskLeftMouseDown    NSLeftMouseDownMask
31   #define NSEventMaskRightMouseDown   NSRightMouseDownMask
32
33   #define NSWindowStyleMaskResizable  NSResizableWindowMask
34   #define NSWindowStyleMaskClosable   NSClosableWindowMask
35   #define NSWindowStyleMaskTitled     NSTitledWindowMask
36
37   #define NSCompositingOperationSourceOver NSCompositeSourceOver
38 #endif
39 #if !defined(MAC_OS_X_VERSION_10_14) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_14)
40   #define NSBitmapImageFileTypePNG  NSPNGFileType
41   #define NSBitmapImageFileTypeBMP  NSBMPFileType
42   #define NSBitmapImageFileTypeJPEG NSJPEGFileType
43   #define NSBitmapImageFileTypeGIF  NSGIFFileType
44 #endif
45
46 @interface Draw_CocoaView : NSView
47 {
48   NSImage* myImage;
49 }
50
51 - (void )setImage: (NSImage* )theImage;
52 - (void )redraw;
53 @end
54
55 @implementation Draw_CocoaView
56
57 - (void )setImage: (NSImage* )theImage
58 {
59   [theImage retain];
60   [myImage release];
61   myImage = theImage;
62 }
63
64 - (BOOL )isFlipped
65 {
66   return YES; // for drawing image from left-top corner
67 }
68
69 - (void )redraw
70 {
71   [self setNeedsDisplay: YES];
72 }
73
74 - (void )drawRect: (NSRect )theRect
75 {
76   (void )theRect;
77   NSRect aBounds = NSMakeRect (0.0, 0.0, myImage.size.width, myImage.size.height);
78
79   [myImage drawInRect: aBounds
80              fromRect: NSZeroRect
81             operation: NSCompositingOperationSourceOver
82              fraction: 1
83        respectFlipped: YES
84                 hints: nil];
85 }
86
87 - (void )dealloc
88 {
89   [myImage release];
90   [super dealloc];
91 }
92 @end
93
94 static Standard_Integer getScreenBottom()
95 {
96   NSRect aRect = [[[NSScreen screens] objectAtIndex:0] frame];
97   Standard_Integer aScreenBottom = Standard_Integer(aRect.size.height + aRect.origin.y);
98   return aScreenBottom;
99 }
100
101 extern Standard_Boolean Draw_VirtualWindows;
102 static Standard_Boolean Draw_IsInZoomingMode = Standard_False;
103
104 Standard_Real Draw_RGBColorsArray[MAXCOLOR][3] = {{1.0,  1.0,  1.0},
105                                                   {1.0,  0.0,  0.0},
106                                                   {0.0,  1.0,  0.0},
107                                                   {0.0,  0.0,  1.0},
108                                                   {0.0,  1.0,  1.0},
109                                                   {1.0,  0.84, 0.0},
110                                                   {1.0,  0.0,  1.0},
111                                                   {1.0,  0.2,  0.7},
112                                                   {1.0,  0.65, 0.0},
113                                                   {1.0,  0.89, 0.88},
114                                                   {1.0,  0.63, 0.48},
115                                                   {0.78, 0.08, 0.52},
116                                                   {1.0,  1.0,  0.0},
117                                                   {0.94, 0.9,  0.55},
118                                                   {1.0,  0.5,  0.31}};
119
120 //=======================================================================
121 //function : Draw_Window
122 //purpose  :
123 //=======================================================================
124 Draw_Window::Draw_Window (const char* theTitle,
125                           const NCollection_Vec2<int>& theXY,
126                           const NCollection_Vec2<int>& theSize,
127                           Aspect_Drawable theParent,
128                           Aspect_Drawable theWindow)
129 : myWindow (NULL),
130   myView (NULL),
131   myImageBuffer (NULL),
132   myCurrentColor (0),
133   myUseBuffer (Standard_False)
134 {
135   (void )theParent;
136   if (theWindow != 0)
137   {
138     myWindow = [(NSWindow* )theWindow retain];
139   }
140   init (theXY, theSize);
141   SetTitle (theTitle);
142 }
143
144 //=======================================================================
145 //function : ~Draw_Window
146 //purpose  :
147 //=======================================================================
148 Draw_Window::~Draw_Window()
149 {
150   if (myWindow != NULL)
151   { 
152     [myWindow release];
153     myWindow = NULL;
154   }
155
156   if (myView != NULL)
157   {
158     [myView release];
159     myView = NULL;
160   }
161
162   if (myImageBuffer != NULL)
163   {
164     [myImageBuffer release];
165     myImageBuffer = NULL;
166   }
167 }
168
169 //=======================================================================
170 //function : init
171 //purpose  :
172 //=======================================================================
173 void Draw_Window::init (const NCollection_Vec2<int>& theXY,
174                         const NCollection_Vec2<int>& theSize)
175 {
176   Cocoa_LocalPool aLocalPool;
177
178   // converting left-bottom coordinate to left-top coordinate
179   Standard_Integer anYTop = getScreenBottom() - theXY.y() - theSize.y();
180
181   if (myWindow == NULL)
182   {
183     NSRect     aRectNs   = NSMakeRect (theXY.x(), anYTop, theSize.x(), theSize.y());
184     NSUInteger aWinStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
185
186     myWindow = [[NSWindow alloc] initWithContentRect: aRectNs
187                                            styleMask: aWinStyle
188                                              backing: NSBackingStoreBuffered
189                                                defer: NO];
190   }
191
192   if (myView == NULL)
193   {
194     NSRect aBounds = [[myWindow contentView] bounds];
195     
196     myView = [[Draw_CocoaView alloc] initWithFrame: aBounds];
197     [myWindow setContentView: myView];
198   }
199
200   if (myImageBuffer == NULL)
201   {
202     NSRect aRectNs = [myView bounds];
203     myImageBuffer  = [[NSImage alloc] initWithSize: aRectNs.size];
204   }
205
206   [myView setImage: myImageBuffer];
207
208   myUseBuffer = Draw_VirtualWindows;
209
210   myCurrentColor = 3;
211
212   [myWindow setBackgroundColor: NSColor.blackColor];
213   [myWindow setReleasedWhenClosed: NO];
214 }
215
216 //=======================================================================
217 //function : InitBuffer
218 //purpose  :
219 //=======================================================================
220 void Draw_Window::InitBuffer()
221 {
222   //
223 }
224
225 //=======================================================================
226 //function : SetPosition
227 //purpose  :
228 //=======================================================================
229 void Draw_Window::SetPosition (Standard_Integer theNewXpos,
230                                Standard_Integer theNewYpos)
231 {
232   NSPoint aNewPosition = NSMakePoint (theNewXpos, theNewYpos);
233   [myWindow setFrameTopLeftPoint: aNewPosition];
234 }
235
236 //=======================================================================
237 //function : SetDimension
238 //purpose  :
239 //=======================================================================
240 void Draw_Window::SetDimension (Standard_Integer theNewWidth,
241                                 Standard_Integer theNewHeight)
242 {
243   NSRect aWindowRect = [myWindow frame];
244   Standard_Integer aNewY = aWindowRect.origin.y + aWindowRect.size.height - theNewHeight;
245   NSRect aNewContentRect = NSMakeRect (aWindowRect.origin.x, aNewY,
246                                        theNewWidth, theNewHeight);
247   [myWindow setFrame: aNewContentRect display: YES];
248 }
249
250 //=======================================================================
251 //function : GetPosition
252 //purpose  :
253 //=======================================================================
254 void Draw_Window::GetPosition (Standard_Integer &thePosX,
255                                Standard_Integer &thePosY)
256 {
257   NSRect aWindowRect = [myWindow frame];
258   thePosX = aWindowRect.origin.x;
259   thePosY = getScreenBottom() - aWindowRect.origin.y - aWindowRect.size.height;
260 }
261
262 //=======================================================================
263 //function : HeightWin
264 //purpose  :
265 //=======================================================================
266 Standard_Integer Draw_Window::HeightWin() const
267 {
268   NSRect aViewBounds = [myView bounds];
269   return aViewBounds.size.height;
270 }
271
272 //=======================================================================
273 //function : WidthWin
274 //purpose  :
275 //=======================================================================
276 Standard_Integer Draw_Window::WidthWin() const
277 {
278   NSRect aViewBounds = [myView bounds];
279   return aViewBounds.size.width;
280 }
281
282 //=======================================================================
283 //function : SetTitle
284 //purpose  :
285 //=======================================================================
286 void Draw_Window::SetTitle (const TCollection_AsciiString& theTitle)
287 {
288   NSString* aTitleNs = [[NSString alloc] initWithUTF8String: theTitle.ToCString()];
289   [myWindow setTitle: aTitleNs];
290   [aTitleNs release];
291 }
292
293 //=======================================================================
294 //function : GetTitle
295 //purpose  :
296 //=======================================================================
297 TCollection_AsciiString Draw_Window::GetTitle() const
298 {
299   Standard_CString aTitle = [[myWindow title] UTF8String];
300   return TCollection_AsciiString (aTitle);
301 }
302
303 //=======================================================================
304 //function :DefineColor
305 //purpose  :
306 //=======================================================================
307 Standard_Boolean Draw_Window::DefineColor (const Standard_Integer , Standard_CString )
308 {
309   return Standard_True; // unused
310 }
311
312 //=======================================================================
313 //function : IsMapped
314 //purpose  :
315 //=======================================================================
316 bool Draw_Window::IsMapped() const
317 {
318   if (Draw_VirtualWindows
319    || myWindow == NULL)
320   {
321     return false;
322   }
323
324   return [myWindow isVisible];
325 }
326
327 //=======================================================================
328 //function : DisplayWindow
329 //purpose  :
330 //=======================================================================
331 void Draw_Window::DisplayWindow()
332 {
333   if (Draw_VirtualWindows)
334   {
335     return;
336   }
337
338   if (myWindow != NULL)
339   {
340     [myWindow orderFront: NULL];
341   }
342 }
343
344 //=======================================================================
345 //function : Hide
346 //purpose  :
347 //=======================================================================
348 void Draw_Window::Hide()
349 {
350   if (myWindow != NULL)
351   {
352     [myWindow orderOut: NULL];
353   }
354 }
355
356 //=======================================================================
357 //function : Destroy
358 //purpose  :
359 //=======================================================================
360 void Draw_Window::Destroy()
361 {  
362   if (myWindow != NULL)
363   { 
364     [myWindow release];
365     myWindow = NULL;
366   }
367
368   if (myView != NULL)
369   {
370     [myView release];
371     myView = NULL;
372   }
373
374   if (myImageBuffer != NULL)
375   {
376     [myImageBuffer release];
377     myImageBuffer = NULL;
378   }
379 }
380
381 //=======================================================================
382 //function : Clear
383 //purpose  :
384 //=======================================================================
385 void Draw_Window::Clear()
386 {
387   [myImageBuffer lockFocus];
388   [[NSColor blackColor] set];
389   NSRect anImageBounds = NSMakeRect (0.0, 0.0, myImageBuffer.size.width, myImageBuffer.size.height);
390   NSRectFill (anImageBounds);
391   [myImageBuffer unlockFocus];
392
393   if (!myUseBuffer)
394   {
395     [myView redraw];
396   }
397 }
398
399 //=======================================================================
400 //function : Flush
401 //purpose  :
402 //=======================================================================
403 void Draw_Window::Flush()
404 {
405   //
406 }
407
408 //=======================================================================
409 //function : DrawString
410 //purpose  :
411 //=======================================================================
412 void Draw_Window::DrawString (Standard_Integer theXLeft, Standard_Integer theYTop,
413                               const char* theText)
414 {
415   Cocoa_LocalPool aLocalPool;
416
417   NSString* aTextNs = [[[NSString alloc] initWithUTF8String: theText] autorelease];
418   NSColor*  aColor  = [NSColor colorWithDeviceRed: Draw_RGBColorsArray[myCurrentColor][0]
419                                             green: Draw_RGBColorsArray[myCurrentColor][1]
420                                              blue: Draw_RGBColorsArray[myCurrentColor][2]
421                                             alpha: 1.0f];
422   NSDictionary* anAttributes = [[[NSDictionary alloc] initWithObjectsAndKeys: aColor, NSForegroundColorAttributeName, nil] autorelease];
423
424   [myImageBuffer lockFocus];
425   [aTextNs drawAtPoint: NSMakePoint (theXLeft, myImageBuffer.size.height - theYTop) withAttributes: anAttributes];
426   [myImageBuffer unlockFocus];
427
428   if (!myUseBuffer)
429   {
430     [myView redraw];
431   }
432 }
433
434 //=======================================================================
435 //function : DrawSegments
436 //purpose  :
437 //=======================================================================
438 void Draw_Window::DrawSegments (const Draw_XSegment* theSegments,
439                                 Standard_Integer theNumberOfElements)
440 {
441   Cocoa_LocalPool aLocalPool;
442
443   NSBezierPath* aPath = [[[NSBezierPath alloc] init] autorelease];
444
445   NSImage* anImage;
446   Standard_Integer anIter = 0;
447   
448   if (Draw_IsInZoomingMode)
449   {
450     // workaround for rectangle drawing when zooming
451     anImage = [[myImageBuffer copy] autorelease];
452     anIter  = 4;
453   }
454   else
455   {
456     anImage = myImageBuffer;
457   }
458
459
460   for (; anIter < theNumberOfElements; ++anIter)
461   {
462     const Draw_XSegment& aSeg = theSegments[anIter];
463     NSPoint aPoint = NSMakePoint (aSeg[0].x(), myImageBuffer.size.height - aSeg[0].y());
464     [aPath moveToPoint: aPoint];
465     aPoint = NSMakePoint (aSeg[1].x(), myImageBuffer.size.height - aSeg[1].y());
466     [aPath lineToPoint: aPoint];
467   }
468
469   [anImage lockFocus];
470   NSColor* aColor = [NSColor colorWithDeviceRed: Draw_RGBColorsArray[myCurrentColor][0]
471                                           green: Draw_RGBColorsArray[myCurrentColor][1]
472                                            blue: Draw_RGBColorsArray[myCurrentColor][2]
473                                           alpha: 1.0f];
474   [aColor set];
475   [aPath stroke];
476   [anImage unlockFocus];
477
478   if (!myUseBuffer)
479   {
480     [myView setImage: anImage];
481     [myView redraw];
482   }
483   
484   Draw_IsInZoomingMode = Standard_False;
485 }
486
487 //=======================================================================
488 //function : Redraw
489 //purpose  :
490 //=======================================================================
491 void Draw_Window::Redraw()
492 {
493   if (myUseBuffer)
494   {
495     [myView redraw];
496   }
497 }
498
499 //=======================================================================
500 //function : SetColor
501 //purpose  :
502 //=======================================================================
503 void Draw_Window::SetColor (Standard_Integer theColor)
504 {
505   myCurrentColor = theColor;
506 }
507
508 //=======================================================================
509 //function : SetMode
510 //purpose  :
511 //=======================================================================
512 void Draw_Window::SetMode (Standard_Integer theMode)
513 {
514   // unsupported
515   (void )theMode;
516 }
517
518 //=======================================================================
519 //function : Save
520 //purpose  :
521 //=======================================================================
522 Standard_Boolean Draw_Window::Save (Standard_CString theFileName) const
523 {
524   Cocoa_LocalPool aLocalPool;
525
526   NSString* aFileName = [[[NSString alloc] initWithUTF8String: theFileName] autorelease];
527   NSString* aFileExtension = [[aFileName pathExtension] lowercaseString];
528
529   NSDictionary* aFileTypeDict = [NSDictionary dictionaryWithObjectsAndKeys:
530                                   [NSNumber numberWithInt: NSBitmapImageFileTypePNG],  @"png",
531                                   [NSNumber numberWithInt: NSBitmapImageFileTypeBMP],  @"bmp",
532                                   [NSNumber numberWithInt: NSBitmapImageFileTypeJPEG], @"jpg",
533                                   [NSNumber numberWithInt: NSBitmapImageFileTypeGIF],  @"gif",
534                                   nil];
535   if ([aFileTypeDict valueForKey: aFileExtension] == NULL)
536   {
537     return Standard_False; // unsupported image extension
538   }
539
540   NSBitmapImageFileType aFileType = (NSBitmapImageFileType )[[aFileTypeDict valueForKey: aFileExtension] intValue];
541   NSBitmapImageRep* anImageRep = [NSBitmapImageRep imageRepWithData: [myImageBuffer TIFFRepresentation]];
542
543   NSDictionary* anImgProps = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat: 0.8]
544                                                          forKey: NSImageCompressionFactor];
545
546   NSData* aData = [anImageRep representationUsingType: aFileType 
547                                            properties: anImgProps];
548
549   Standard_Boolean isSuccess = [aData writeToFile: aFileName
550                                        atomically: NO];
551
552   return isSuccess;
553 }
554
555 Standard_Boolean Draw_Window::IsEqualWindows (const Standard_Integer& theWindowNumber)
556 {
557   return ([myWindow windowNumber] == theWindowNumber);
558 }
559
560 void Draw_Window::GetNextEvent (Standard_Boolean  theWait,
561                                 Standard_Integer& theWindowNumber,
562                                 Standard_Integer& theX,
563                                 Standard_Integer& theY,
564                                 Standard_Integer& theButton)
565 {
566   Cocoa_LocalPool aLocalPool;
567
568   unsigned int anEventMatchMask = NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown;
569
570   if (!theWait)
571   {
572     anEventMatchMask = anEventMatchMask | NSEventMaskMouseMoved | NSEventMaskLeftMouseDragged;
573     Draw_IsInZoomingMode = Standard_True;
574   }
575
576   NSEvent* anEvent = [NSApp nextEventMatchingMask: anEventMatchMask
577                                         untilDate: [NSDate distantFuture]
578                                            inMode: NSEventTrackingRunLoopMode
579                                           dequeue: YES];
580
581   NSWindow* aWindow = [anEvent window];
582   NSView*   aView   = [aWindow contentView];
583   theWindowNumber   = [aWindow windowNumber];
584
585   NSPoint aMouseLoc = [aView convertPoint: [anEvent locationInWindow] fromView: nil];
586
587   theX = Standard_Integer (aMouseLoc.x);
588   theY = Standard_Integer (aMouseLoc.y);
589
590   NSEventType anEventType = [anEvent type];
591
592   if (anEventType == NSEventTypeLeftMouseDown)
593   {
594     theButton = 1;
595   }
596   else if (anEventType == NSEventTypeRightMouseDown)
597   {
598     theButton = 3;
599   }
600   else if ((anEventType == NSEventTypeMouseMoved || anEventType == NSEventTypeLeftMouseDragged) && !theWait)
601   {
602     theButton = 0;
603   }
604 }
605
606 #endif // __APPLE__