blob: 5d5e9ae1162b8af3bb36a1e2abcf4e556aa62e84 [file] [log] [blame]
Alexandre Lision74dd47f2015-04-14 13:47:42 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision74dd47f2015-04-14 13:47:42 -04003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Alexandre Lision74dd47f2015-04-14 13:47:42 -040018 */
19
20#import "CallView.h"
Anthony Léonard14e7bf32017-06-08 08:13:16 -040021#import "CallLayer.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040022
Alexandre Lision74dd47f2015-04-14 13:47:42 -040023#import <QUrl>
24
Alexandre Lision74dd47f2015-04-14 13:47:42 -040025@interface CallView ()
26
27@property NSMenu *contextualMenu;
28
29@end
30
31@implementation CallView
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -040032
33NSString *currentDevice;
Alexandre Lision74dd47f2015-04-14 13:47:42 -040034@synthesize contextualMenu;
35@synthesize shouldAcceptInteractions;
36
37
38- (id)initWithFrame:(NSRect)frame
39{
40 self = [super initWithFrame:frame];
41 if (self)
42 {
43 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
Anthony Léonard14e7bf32017-06-08 08:13:16 -040044 [self setWantsLayer:YES];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -040045 [self setLayer:[CALayer layer]];
46 [self.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Alexandre Lision74dd47f2015-04-14 13:47:42 -040047 }
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -040048
49 [self.window setAcceptsMouseMovedEvents:YES];
50
51 NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
52
53 NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:frame
54 options:options
55 owner:self
56 userInfo:nil];
57
58 [self addTrackingArea:area];
Alexandre Lision74dd47f2015-04-14 13:47:42 -040059 return self;
60}
61
Alexandre Lision74dd47f2015-04-14 13:47:42 -040062#pragma mark - Destination Operations
63
64- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
65{
66 /*------------------------------------------------------
67 method called whenever a drag enters our drop zone
68 --------------------------------------------------------*/
69 NSLog(@"Dragging entered");
70
71 NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
Alexandre Lision81c97212015-06-17 15:51:53 -040072 CFStringRef fileExtension = (__bridge CFStringRef) [fileURL.path pathExtension];
Alexandre Lision74dd47f2015-04-14 13:47:42 -040073 CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
74
75 // Check if the pasteboard contains image data and source/user wants it copied
76 if ( [sender draggingSourceOperationMask] & NSDragOperationCopy &&
Alexandre Lisionba9b7082015-08-03 16:53:37 -040077 (UTTypeConformsTo(fileUTI, kUTTypeVideo)) ||
78 (UTTypeConformsTo(fileUTI, kUTTypeMovie)) ||
79 (UTTypeConformsTo(fileUTI, kUTTypeImage))) {
Alexandre Lision74dd47f2015-04-14 13:47:42 -040080
81 //highlight our drop zone
82 highlight=YES;
83
84 [self setNeedsDisplay: YES];
85
86 /* When an image from one window is dragged over another, we want to resize the dragging item to
87 * preview the size of the image as it would appear if the user dropped it in. */
88 [sender enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent
89 forView:self
90 classes:[NSArray arrayWithObject:[NSPasteboardItem class]]
91 searchOptions:nil
92 usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {
93 *stop = YES;
94 }];
Alexandre Lision81c97212015-06-17 15:51:53 -040095 CFRelease(fileUTI);
Alexandre Lision74dd47f2015-04-14 13:47:42 -040096 //accept data as a copy operation
97 return NSDragOperationCopy;
98 }
99
Alexandre Lision81c97212015-06-17 15:51:53 -0400100 CFRelease(fileUTI);
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400101 return NSDragOperationNone;
102}
103
104- (void)draggingExited:(id <NSDraggingInfo>)sender
105{
106 /*------------------------------------------------------
107 method called whenever a drag exits our drop zone
108 --------------------------------------------------------*/
109 //remove highlight of the drop zone
110 highlight=NO;
111
112 [self setNeedsDisplay: YES];
113}
114
115-(void)drawRect:(NSRect)rect
116{
117 /*------------------------------------------------------
118 draw method is overridden to do drop highlighing
119 --------------------------------------------------------*/
120 //do the usual draw operation to display the image
121 [super drawRect:rect];
122
123 if ( highlight ) {
124 //highlight by overlaying a gray border
125 [[NSColor blueColor] set];
126 [NSBezierPath setDefaultLineWidth: 5];
127 [NSBezierPath strokeRect: rect];
128 }
129}
130
131- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
132{
133 /*------------------------------------------------------
134 method to determine if we can accept the drop
135 --------------------------------------------------------*/
136 //finished with the drag so remove any highlighting
137 highlight=NO;
138
139 [self setNeedsDisplay: YES];
140
141 NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
Alexandre Lision81c97212015-06-17 15:51:53 -0400142 CFStringRef fileExtension = (__bridge CFStringRef) [fileURL.path pathExtension];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400143 CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
144
Alexandre Lisionba9b7082015-08-03 16:53:37 -0400145 BOOL conforms = (UTTypeConformsTo(fileUTI, kUTTypeVideo)) ||
146 (UTTypeConformsTo(fileUTI, kUTTypeMovie)) ||
147 UTTypeConformsTo(fileUTI, kUTTypeImage);
Alexandre Lision81c97212015-06-17 15:51:53 -0400148 CFRelease(fileUTI);
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400149 //check to see if we can accept the data
Alexandre Lision81c97212015-06-17 15:51:53 -0400150 return conforms;
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400151}
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400152#if 0
153// TODO: add file as a source
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400154- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
155{
156 /*------------------------------------------------------
157 method that should handle the drop data
158 --------------------------------------------------------*/
159 if ( [sender draggingSource] != self ) {
160 NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400161 Call* call = [self getCurrentCall];
162 if (call == nullptr) return;
163 if (auto outVideo = call->firstMedia<media::Video>(media::Media::Direction::OUT)) {
164 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(QString::fromUtf8([fileURL.path UTF8String])));
165 return YES;
Alexandre Lisione7a604c2015-12-10 09:29:02 -0500166 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400167 }
168
Alexandre Lisione7a604c2015-12-10 09:29:02 -0500169 return NO;
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400170}
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400171#endif
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400172
173- (void)showContextualMenu:(NSEvent *)theEvent {
174
175 contextualMenu = [[NSMenu alloc] initWithTitle:@"Switch camera"];
176
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400177 auto devices = [self.callDelegate getDeviceList];
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -0400178
179 currentDevice = [self.callDelegate getDefaultDeviceName];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400180
181 for(int i = 0 ; i < devices.size() ; ++i) {
182 std::string device = devices[i];
183 [contextualMenu insertItemWithTitle:@(device.c_str()) action:@selector(switchInput:) keyEquivalent:@"" atIndex:i];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400184 }
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400185 [contextualMenu insertItemWithTitle:NSLocalizedString(@"Share screen", @"Contextual menu entry")
186 action:@selector(captureScreen:)
187 keyEquivalent:@""
188 atIndex:contextualMenu.itemArray.count];
189#if 0
190// TODO: add file as a source
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400191 [contextualMenu addItem:[NSMenuItem separatorItem]];
Alexandre Lision7f01b072015-09-15 14:57:05 -0400192 [contextualMenu insertItemWithTitle:NSLocalizedString(@"Choose file", @"Contextual menu entry")
193 action:@selector(chooseFile:)
194 keyEquivalent:@""
195 atIndex:contextualMenu.itemArray.count];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400196#endif
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400197
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -0400198 auto menuItem = [contextualMenu itemWithTitle:currentDevice];
199 if(menuItem) {
200 [menuItem setState: NSControlStateValueOn];
201 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400202 [NSMenu popUpContextMenu:contextualMenu withEvent:theEvent forView:self];
203}
204
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400205- (void)mouseMoved:(NSEvent *)theEvent
206{
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400207 [NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel showContextualMenu
208 [self performSelector:@selector(mouseIdle:) withObject:theEvent afterDelay:3];
209 if (self.callDelegate && shouldAcceptInteractions)
210 [self.callDelegate mouseIsMoving:YES];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400211}
212
213- (void) mouseIdle:(NSEvent *)theEvent
214{
215 if (self.callDelegate && shouldAcceptInteractions)
216 [self.callDelegate mouseIsMoving:NO];
217}
218
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400219- (void) rightMouseUp: (NSEvent*) theEvent {
220 [NSObject cancelPreviousPerformRequestsWithTarget:self];
221 if([theEvent clickCount] == 1 && shouldAcceptInteractions) {
222 [self performSelector:@selector(showContextualMenu:) withObject:theEvent];
223 }
224}
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400225
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400226- (void)mouseUp:(NSEvent *)theEvent
227{
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400228 if([theEvent clickCount] == 2 && self.callDelegate) {
229 [self.callDelegate callShouldToggleFullScreen];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400230 }
231}
232
233- (void) switchInput:(NSMenuItem*) sender
234{
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -0400235 if([sender.title isEqualToString:currentDevice]) {
236 return;
237 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400238 int index = [contextualMenu indexOfItem:sender];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400239 [self.callDelegate switchToDevice: index];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400240}
241
242- (void) captureScreen:(NSMenuItem*) sender
243{
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -0400244 if([sender.title isEqualToString:currentDevice]) {
245 return;
246 }
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400247 [self.callDelegate screenShare];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400248}
249
250- (void) chooseFile:(NSMenuItem*) sender
251{
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -0400252 if([sender.title isEqualToString:currentDevice]) {
253 return;
254 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400255 NSOpenPanel *browsePanel = [[NSOpenPanel alloc] init];
256 [browsePanel setDirectoryURL:[NSURL URLWithString:NSHomeDirectory()]];
257 [browsePanel setCanChooseFiles:YES];
258 [browsePanel setCanChooseDirectories:NO];
259 [browsePanel setCanCreateDirectories:NO];
260
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400261 NSMutableArray* fileTypes = [[NSMutableArray alloc] initWithArray:[NSImage imageTypes]];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400262 [fileTypes addObject:(__bridge NSString *)kUTTypeVideo];
263 [fileTypes addObject:(__bridge NSString *)kUTTypeMovie];
Alexandre Lisionba9b7082015-08-03 16:53:37 -0400264 [fileTypes addObject:(__bridge NSString *)kUTTypeImage];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400265 [browsePanel setAllowedFileTypes:fileTypes];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400266 [browsePanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
267 if (result == NSFileHandlingPanelOKButton) {
268 NSURL* theDoc = [[browsePanel URLs] objectAtIndex:0];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400269 [self.callDelegate switchToFile: [theDoc.path UTF8String]];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400270 }
271 }];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400272}
273
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400274@end