]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/src/highgui/window_cocoa.mm
disallow user to resize highgui windows with autoresize flag (cocoa bindings) - ticke...
[opencv.git] / opencv / src / highgui / window_cocoa.mm
1 /* The file is the modified version of window_cocoa.mm from opencv-cocoa project by Andre Cohen */
2
3 /*M///////////////////////////////////////////////////////////////////////////////////////
4 //
5 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
6 //
7 //  By downloading, copying, installing or using the software you agree to this license.
8 //  If you do not agree to this license, do not download, install,
9 //  copy or use the software.
10 //
11 //
12 //                         License Agreement
13 //                For Open Source Computer Vision Library
14 //
15 // Copyright (C) 2010, Willow Garage Inc., all rights reserved.
16 // Third party copyrights are property of their respective owners.
17 //
18 // Redistribution and use in source and binary forms, with or without modification,
19 // are permitted provided that the following conditions are met:
20 //
21 //   * Redistribution's of source code must retain the above copyright notice,
22 //     this list of conditions and the following disclaimer.
23 //
24 //   * Redistribution's in binary form must reproduce the above copyright notice,
25 //     this list of conditions and the following disclaimer in the documentation
26 //     and/or other materials provided with the distribution.
27 //
28 //   * The name of Intel Corporation may not be used to endorse or promote products
29 //     derived from this software without specific prior written permission.
30 //
31 // This software is provided by the copyright holders and contributors "as is" and
32 // any express or implied warranties, including, but not limited to, the implied
33 // warranties of merchantability and fitness for a particular purpose are disclaimed.
34 // In no event shall the Intel Corporation or contributors be liable for any direct,
35 // indirect, incidental, special, exemplary, or consequential damages
36 // (including, but not limited to, procurement of substitute goods or services;
37 // loss of use, data, or profits; or business interruption) however caused
38 // and on any theory of liability, whether in contract, strict liability,
39 // or tort (including negligence or otherwise) arising in any way out of
40 // the use of this software, even if advised of the possibility of such damage.
41 //
42 //M*/
43
44 #import <Cocoa/Cocoa.h>
45 #include "_highgui.h"
46
47 const int TOP_BORDER  = 7;
48
49 static NSApplication *application = nil;
50 static NSAutoreleasePool *pool = nil;
51 static NSMutableDictionary *windows = nil;
52 static bool wasInitialized = false;
53
54 @interface CVView : NSView {
55         NSImage *image;
56 }
57 @property(assign) NSImage *image;
58 - (void)setImageData:(CvArr *)arr;
59 @end
60
61 @interface CVSlider : NSView {
62         NSSlider *slider;
63         NSTextField *name;
64         int *value;
65         void *userData;
66         CvTrackbarCallback callback;
67         CvTrackbarCallback2 callback2;
68 }
69 @property(assign) NSSlider *slider;
70 @property(assign) NSTextField *name;
71 @property(assign) int *value;
72 @property(assign) void *userData;
73 @property(assign) CvTrackbarCallback callback;
74 @property(assign) CvTrackbarCallback2 callback2;
75 @end
76
77 @interface CVWindow : NSWindow {
78         NSMutableDictionary *sliders;
79         CvMouseCallback mouseCallback;
80         void *mouseParam;
81         BOOL autosize;
82 }
83 @property(assign) CvMouseCallback mouseCallback;
84 @property(assign) void *mouseParam;
85 @property(assign) BOOL autosize;
86 @property(assign) NSMutableDictionary *sliders;
87 - (CVView *)contentView;
88 - (void)cvSendMouseEvent:(NSEvent *)event type:(int)type flags:(int)flags;
89 - (void)cvMouseEvent:(NSEvent *)event;
90 - (void)createSliderWithName:(const char *)name maxValue:(int)max value:(int *)value callback:(CvTrackbarCallback)callback;
91 @end
92
93 static void icvCocoaCleanup(void)
94 {
95     if( application )
96     {
97         [application terminate:nil];
98         [pool release];
99     }
100 }
101
102 CV_IMPL int cvInitSystem( int argc, char** argv) 
103 {
104         wasInitialized = true;
105         
106         pool = [[NSAutoreleasePool alloc] init];
107         application = [NSApplication sharedApplication];
108         windows = [[NSMutableDictionary alloc] init];
109
110 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
111     if( floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5 )
112         [application setActivationPolicy:0/*NSApplicationActivationPolicyRegular*/];
113 #endif
114     [application finishLaunching];
115     atexit(icvCocoaCleanup);
116         
117     return 0;
118 }
119
120 CVWindow *cvGetWindow(const char *name) {
121         NSString *cvname = [NSString stringWithFormat:@"%s", name];
122         return (CVWindow *)[windows valueForKey:cvname];
123 }
124
125 CV_IMPL int cvStartWindowThread()
126 {
127     return 0;
128 }
129
130 CV_IMPL void cvDestroyWindow( const char* name)
131 {
132         CVWindow *window = cvGetWindow(name);
133         if(window) {
134                 [window performClose:nil];
135                 [windows removeObjectForKey:[NSString stringWithFormat:@"%s", name]];
136         }
137 }
138
139
140 CV_IMPL void cvDestroyAllWindows( void )
141 {
142         for(NSString *key in windows) {
143                 [[windows valueForKey:key] performClose:nil];
144         }
145         [windows removeAllObjects];
146 }
147
148
149 CV_IMPL void cvShowImage( const char* name, const CvArr* arr)
150 {
151         CVWindow *window = cvGetWindow(name);
152         if(window) {
153         bool empty = [[window contentView] image] == nil;
154                 [[window contentView] setImageData:(CvArr *)arr];
155                 if([window autosize] || empty) {
156                         NSRect rect = [window frame];
157                         rect.size = [[[window contentView] image] size];
158                         [window setFrame:rect display:YES];
159                 } else {
160                         [window display];
161                 }
162         }
163         
164 }
165
166 CV_IMPL void cvResizeWindow( const char* name, int width, int height)
167 {
168         CVWindow *window = cvGetWindow(name);
169         if(window) {
170                 NSRect frame = [window frame];
171                 frame.size.width = width;
172                 frame.size.height = height;
173                 [window setFrame:frame display:YES];
174         }
175 }
176
177 CV_IMPL void cvMoveWindow( const char* name, int x, int y)
178 {
179         CV_FUNCNAME("cvMoveWindow");
180         __BEGIN__;
181         
182         CVWindow *window = nil;
183         
184         if(name == NULL)
185                 CV_ERROR( CV_StsNullPtr, "NULL window name" );
186         
187         window = cvGetWindow(name);
188         if(window) {
189                 y = [[window screen] frame].size.height - y;
190                 [window setFrameTopLeftPoint:NSMakePoint(x, y)];
191         }
192         
193         __END__;
194 }
195
196 CV_IMPL int cvCreateTrackbar (const char* trackbar_name,
197                               const char* window_name,
198                               int* val, int count,
199                               CvTrackbarCallback on_notify)
200 {
201         CV_FUNCNAME("cvCreateTrackbar");
202
203         int result = 0;
204         CVWindow *window = nil;
205         
206         __BEGIN__;
207         
208         if(window_name == NULL)
209                 CV_ERROR( CV_StsNullPtr, "NULL window name" );
210                 
211         window = cvGetWindow(window_name);
212         if(window) {
213                 [window createSliderWithName:trackbar_name
214                                                         maxValue:count
215                                                            value:val 
216                                                         callback:on_notify];
217                 result = 1;
218         }
219         
220         __END__;
221         return result;
222 }
223
224
225 CV_IMPL int cvCreateTrackbar2(const char* trackbar_name,
226                               const char* window_name,
227                               int* val, int count,
228                               CvTrackbarCallback2 on_notify2,
229                               void* userdata)
230 {
231         int res = cvCreateTrackbar(trackbar_name, window_name, val, count, NULL);
232         if(res) {
233                 CVSlider *slider = [[cvGetWindow(window_name) sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
234                 [slider setCallback2:on_notify2];
235                 [slider setUserData:userdata];
236         }
237         return res;
238 }
239
240
241 CV_IMPL void
242 cvSetMouseCallback( const char* name, CvMouseCallback function, void* info)
243 {
244         CV_FUNCNAME("cvSetMouseCallback");
245         
246         CVWindow *window = nil;
247         
248         __BEGIN__;
249         
250         if(name == NULL)
251                 CV_ERROR( CV_StsNullPtr, "NULL window name" );
252                 
253         window = cvGetWindow(name);
254         if(window) {
255                 [window setMouseCallback:function];
256                 [window setMouseParam:info];
257         }
258         
259         __END__;
260 }
261
262  CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
263 {
264         CV_FUNCNAME("cvGetTrackbarPos");
265         
266         CVWindow *window = nil;
267         int pos = -1;
268         
269         __BEGIN__;
270         
271         if(trackbar_name == NULL || window_name == NULL)
272                 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
273         
274         window = cvGetWindow(window_name);
275         if(window) {
276                 CVSlider *slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
277                 if(slider) {
278                         pos = [[slider slider] intValue];
279                 }
280         }
281         
282         __END__;
283         return pos;
284 }
285
286 CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name, int pos)
287 {
288         CV_FUNCNAME("cvSetTrackbarPos");
289         
290         CVWindow *window = nil;
291         CVSlider *slider = nil;
292         
293         __BEGIN__;
294         
295         if(trackbar_name == NULL || window_name == NULL)
296                 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
297         
298         if(pos <= 0)
299         CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
300         
301         window = cvGetWindow(window_name);
302         if(window) {
303                 slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
304                 if(slider) {
305                         [[slider slider] setIntValue:pos];
306                 }
307         }
308         
309         __END__;
310 }
311
312 CV_IMPL void* cvGetWindowHandle( const char* name )
313 {
314         return cvGetWindow(name);
315 }
316
317
318 CV_IMPL const char* cvGetWindowName( void* window_handle )
319 {       
320         for(NSString *key in windows) {
321                 if([windows valueForKey:key] == window_handle)
322                         return [key UTF8String];
323         }
324         return 0;
325 }
326
327 CV_IMPL int cvNamedWindow( const char* name, int flags )
328 {
329     if( !wasInitialized )
330         cvInitSystem(0, 0);
331     
332         CVWindow *window = [[CVWindow alloc] initWithContentRect:NSMakeRect(0,0,200,200)
333         styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|
334         (!(flags & CV_WND_PROP_AUTOSIZE) ? NSResizableWindowMask : 0)
335         backing:NSBackingStoreBuffered  
336             defer:NO];
337         
338         NSString *windowName = [NSString stringWithFormat:@"%s", name];
339         
340         [window setContentView:[[CVView alloc] init]];
341         
342         [window setHasShadow:YES];
343         [window setAcceptsMouseMovedEvents:YES];
344         [window useOptimizedDrawing:YES];
345         [window setTitle:windowName];
346         [window makeKeyAndOrderFront:nil];
347
348         [window setAutosize:(flags == CV_WINDOW_AUTOSIZE)];     
349         
350         [windows setValue:window forKey:windowName];
351         
352         return [windows count]-1;
353 }
354
355 CV_IMPL int cvWaitKey (int maxWait)
356 {
357     int returnCode = -1;
358         double start = [[NSDate date] timeIntervalSince1970];
359     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
360
361         while(true) {
362                 if(([[NSDate date] timeIntervalSince1970] - start) * 1000 >= maxWait && maxWait>0)
363                         break;
364                 
365                 //event = [application currentEvent];
366         [pool release];
367         pool = [[NSAutoreleasePool alloc] init];
368         
369         NSEvent *event =
370         [application
371          nextEventMatchingMask:NSAnyEventMask
372          untilDate://[NSDate dateWithTimeIntervalSinceNow: 1./100]
373          [NSDate distantPast]
374          inMode:NSDefaultRunLoopMode
375          dequeue:YES];
376         
377                 if([event type] == NSKeyDown) {
378                         returnCode = [[event characters] characterAtIndex:0];
379             break;
380                 }
381         
382         [application sendEvent:event];
383         [application updateWindows];
384         
385                 [NSThread sleepForTimeInterval:1/100.];
386         }
387     [pool release];
388
389         return returnCode;
390 }
391
392 @implementation CVWindow 
393
394 @synthesize mouseCallback;
395 @synthesize mouseParam;
396 @synthesize autosize;
397 @synthesize sliders;
398
399 - (void)cvSendMouseEvent:(NSEvent *)event type:(int)type flags:(int)flags {
400         NSPoint mp = [NSEvent mouseLocation];
401         NSRect visible = [[self contentView] frame];
402     mp = [self convertScreenToBase: mp];
403     double viewHeight = [self contentView].frame.size.height;
404     double viewWidth = [self contentView].frame.size.width;
405     CVWindow *window = (CVWindow *)[[self contentView] window];
406     for(NSString *key in [window sliders]) {
407         NSSlider *slider = [[window sliders] valueForKey:key];
408         viewHeight = std::min(viewHeight, (double)([slider frame].origin.y));
409     }
410     viewHeight -= TOP_BORDER;
411     mp.y = viewHeight - mp.y;
412     
413     NSImage* image = ((CVView*)[self contentView]).image;
414     NSSize imageSize = [image size];
415     mp.x = mp.x * imageSize.width / std::max(viewWidth, 1.);
416     mp.y = mp.y * imageSize.height / std::max(viewHeight, 1.);
417     
418     if( mp.x >= 0 && mp.y >= 0 && mp.x < imageSize.width && mp.y < imageSize.height )
419         mouseCallback(type, mp.x, mp.y, flags, mouseParam);
420 }
421
422 - (void)cvMouseEvent:(NSEvent *)event {
423         if(!mouseCallback) 
424                 return;
425                 
426         int flags = 0;
427         if([event modifierFlags] & NSShiftKeyMask)              flags |= CV_EVENT_FLAG_SHIFTKEY;
428         if([event modifierFlags] & NSControlKeyMask)    flags |= CV_EVENT_FLAG_CTRLKEY;
429         if([event modifierFlags] & NSAlternateKeyMask)  flags |= CV_EVENT_FLAG_ALTKEY;
430                 
431         if([event type] == NSLeftMouseDown)     {[self cvSendMouseEvent:event type:CV_EVENT_LBUTTONDOWN flags:flags | CV_EVENT_FLAG_LBUTTON];}
432         if([event type] == NSLeftMouseUp)       {[self cvSendMouseEvent:event type:CV_EVENT_LBUTTONUP   flags:flags | CV_EVENT_FLAG_LBUTTON];}
433         if([event type] == NSRightMouseDown){[self cvSendMouseEvent:event type:CV_EVENT_RBUTTONDOWN flags:flags | CV_EVENT_FLAG_RBUTTON];}
434         if([event type] == NSRightMouseUp)      {[self cvSendMouseEvent:event type:CV_EVENT_RBUTTONUP   flags:flags | CV_EVENT_FLAG_RBUTTON];}
435         if([event type] == NSOtherMouseDown){[self cvSendMouseEvent:event type:CV_EVENT_MBUTTONDOWN flags:flags];}
436         if([event type] == NSOtherMouseUp)      {[self cvSendMouseEvent:event type:CV_EVENT_MBUTTONUP   flags:flags];}
437         if([event type] == NSMouseMoved)        {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags];}
438         if([event type] == NSLeftMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags | CV_EVENT_FLAG_LBUTTON];}
439         if([event type] == NSRightMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags | CV_EVENT_FLAG_RBUTTON];}
440         if([event type] == NSOtherMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags | CV_EVENT_FLAG_MBUTTON];}
441 }
442 - (void)keyDown:(NSEvent *)theEvent {
443         [super keyDown:theEvent];
444 }
445 - (void)rightMouseDragged:(NSEvent *)theEvent {
446         [self cvMouseEvent:theEvent];
447 }
448 - (void)rightMouseUp:(NSEvent *)theEvent {
449         [self cvMouseEvent:theEvent];
450 }
451 - (void)rightMouseDown:(NSEvent *)theEvent {
452         // Does not seem to work?
453         [self cvMouseEvent:theEvent];
454 }
455 - (void)mouseMoved:(NSEvent *)theEvent {
456         [self cvMouseEvent:theEvent];
457 }
458 - (void)otherMouseDragged:(NSEvent *)theEvent {
459         [self cvMouseEvent:theEvent];
460 }
461 - (void)otherMouseUp:(NSEvent *)theEvent {
462         [self cvMouseEvent:theEvent];
463 }
464 - (void)otherMouseDown:(NSEvent *)theEvent {
465         [self cvMouseEvent:theEvent];
466 }
467 - (void)mouseDragged:(NSEvent *)theEvent {
468         [self cvMouseEvent:theEvent];
469 }
470 - (void)mouseUp:(NSEvent *)theEvent {
471         [self cvMouseEvent:theEvent];
472 }
473 - (void)mouseDown:(NSEvent *)theEvent {
474         [self cvMouseEvent:theEvent];
475 }
476
477 - (void)createSliderWithName:(const char *)name maxValue:(int)max value:(int *)value callback:(CvTrackbarCallback)callback {
478         if(sliders == nil)
479                 sliders = [[NSMutableDictionary alloc] init];
480         
481         NSString *cvname = [NSString stringWithFormat:@"%s", name];
482         
483         // Avoid overwriting slider
484         if([sliders valueForKey:cvname]!=nil)
485                 return;
486         
487         // Create slider
488         CVSlider *slider = [[CVSlider alloc] init];
489         [[slider name] setStringValue:cvname];
490         [[slider slider] setMaxValue:max];
491         if(value)
492     {
493                 [[slider slider] setIntValue:*value];
494         [slider setValue:value];
495     }
496         if(callback)
497                 [slider setCallback:callback];
498         
499         // Save slider
500         [sliders setValue:slider forKey:cvname];
501         [[self contentView] addSubview:slider];
502         
503         // Update slider sizes
504         [[self contentView] setFrameSize:[[self contentView] frame].size];
505         [[self contentView] setNeedsDisplay:YES];
506 }
507
508 - (CVView *)contentView {
509         return (CVView*)[super contentView];
510 }
511
512 @end
513
514 @implementation CVView 
515
516 @synthesize image;
517
518 - (id)init {
519         [super init];
520         image = nil;
521         return self;
522 }
523
524 - (void)setImageData:(CvArr *)arr {
525         CvMat *arrMat, *cvimage, stub;
526          
527         arrMat = cvGetMat(arr, &stub);
528         
529         cvimage = cvCreateMat(arrMat->rows, arrMat->cols, CV_8UC3);
530         cvConvertImage(arrMat, cvimage, CV_CVTIMG_SWAP_RB);
531         
532         CGColorSpaceRef colorspace = NULL;
533     CGDataProviderRef provider = NULL;
534         int width = cvimage->width;
535     int height = cvimage->height;
536
537     colorspace = CGColorSpaceCreateDeviceRGB();
538
539     int size = 8;
540     int nbChannels = 3;
541       
542     provider = CGDataProviderCreateWithData(NULL, cvimage->data.ptr, width * height , NULL );
543       
544         CGImageRef imageRef = CGImageCreate(width, height, size , size*nbChannels , cvimage->step, colorspace,  kCGImageAlphaNone , provider, NULL, true, kCGRenderingIntentDefault);
545         
546         NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:imageRef] autorelease];
547         if(image) {
548                 [image release];
549         }
550         image = [[NSImage alloc] init];
551         [image addRepresentation:bitmap];
552         
553     CGDataProviderRelease(provider);
554         cvReleaseMat(&cvimage);
555         
556         [self setNeedsDisplay:YES];
557 }
558
559 - (void)setFrameSize:(NSSize)size {
560         [super setFrameSize:size];
561         int height = size.height;
562         
563         CVWindow *window = (CVWindow *)[self window];
564         for(NSString *key in [window sliders]) {
565                 NSSlider *slider = [[window sliders] valueForKey:key];
566                 NSRect r = [slider frame];
567                 r.origin.y = height - r.size.height;
568                 [slider setFrame:r];
569                 height -= r.size.height;
570         }
571 }
572
573 - (void)drawRect:(NSRect)rect {
574         CVWindow *window = (CVWindow *)[self window];
575         int height = 0;
576         for(NSString *key in [window sliders]) {
577                 height += [[[window sliders] valueForKey:key] frame].size.height;
578         }
579         
580         [super drawRect:rect];
581
582         NSRect imageRect = {{0,0}, {self.frame.size.width, self.frame.size.height-height-6}};
583         
584         if(image != nil) {
585                 [image drawInRect: imageRect
586                                       fromRect: NSZeroRect
587                                      operation: NSCompositeSourceOver
588                                       fraction: 1.0];
589         }
590
591 }
592
593 @end
594
595 @implementation CVSlider 
596
597 @synthesize slider;
598 @synthesize name;
599 @synthesize value;
600 @synthesize userData;
601 @synthesize callback;
602 @synthesize callback2;
603
604 - (id)init {
605         [super init];
606
607         callback = NULL;
608         value = NULL;
609         userData = NULL;
610
611         [self setFrame:NSMakeRect(0,0,200,25)];
612
613         name = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0,120, 20)];
614         [name setEditable:NO];
615     [name setSelectable:NO];
616     [name setBezeled:NO];
617     [name setBordered:NO];
618         [name setDrawsBackground:NO];
619         [[name cell] setLineBreakMode:NSLineBreakByTruncatingTail];
620         [self addSubview:name];
621         
622         slider = [[NSSlider alloc] initWithFrame:NSMakeRect(120, 0, 76, 20)];
623         [slider setAutoresizingMask:NSViewWidthSizable];
624         [slider setMinValue:0];
625         [slider setMaxValue:100];
626         [slider setContinuous:YES];
627         [slider setTarget:self];
628         [slider setAction:@selector(sliderChanged:)];
629         [self addSubview:slider];
630         
631         [self setAutoresizingMask:NSViewWidthSizable];
632         
633         [self setFrame:NSMakeRect(12, 0, 182, 30)];
634         
635         return self;
636 }
637
638 - (void)sliderChanged:(NSNotification *)notification {
639     int pos = [slider intValue];
640     if(value)
641         *value = pos;
642         if(callback)
643                 callback(pos);
644         if(callback2)
645                 callback2(pos, userData);
646 }
647
648 @end
649
650 /* End of file. */