blob: c70338a841b4cd249058399ad8fde8a06b53d061 [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
23#import <QItemSelectionModel>
24#import <QAbstractProxyModel>
25#import <QUrl>
26
27#import <video/configurationproxy.h>
28#import <video/sourcemodel.h>
Alexandre Lisione7a604c2015-12-10 09:29:02 -050029#import <media/video.h>
30#import <callmodel.h>
Alexandre Lision74dd47f2015-04-14 13:47:42 -040031#import <video/previewmanager.h>
32#import <video/renderer.h>
33#import <video/device.h>
34#import <video/devicemodel.h>
35
36@interface CallView ()
37
38@property NSMenu *contextualMenu;
39
40@end
41
42@implementation CallView
43@synthesize contextualMenu;
44@synthesize shouldAcceptInteractions;
45
46
47- (id)initWithFrame:(NSRect)frame
48{
49 self = [super initWithFrame:frame];
50 if (self)
51 {
52 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
Anthony Léonard14e7bf32017-06-08 08:13:16 -040053 [self setWantsLayer:YES];
Alexandre Lision74dd47f2015-04-14 13:47:42 -040054 }
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -040055
56 [self.window setAcceptsMouseMovedEvents:YES];
57
58 NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
59
60 NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:frame
61 options:options
62 owner:self
63 userInfo:nil];
64
65 [self addTrackingArea:area];
Alexandre Lision74dd47f2015-04-14 13:47:42 -040066 return self;
67}
68
Anthony Léonard14e7bf32017-06-08 08:13:16 -040069- (CALayer *)makeBackingLayer
70{
71 return (CALayer*) [[CallLayer alloc] init];
72}
73
Alexandre Lision74dd47f2015-04-14 13:47:42 -040074
75#pragma mark - Destination Operations
76
77- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
78{
79 /*------------------------------------------------------
80 method called whenever a drag enters our drop zone
81 --------------------------------------------------------*/
82 NSLog(@"Dragging entered");
83
84 NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
Alexandre Lision81c97212015-06-17 15:51:53 -040085 CFStringRef fileExtension = (__bridge CFStringRef) [fileURL.path pathExtension];
Alexandre Lision74dd47f2015-04-14 13:47:42 -040086 CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
87
88 // Check if the pasteboard contains image data and source/user wants it copied
89 if ( [sender draggingSourceOperationMask] & NSDragOperationCopy &&
Alexandre Lisionba9b7082015-08-03 16:53:37 -040090 (UTTypeConformsTo(fileUTI, kUTTypeVideo)) ||
91 (UTTypeConformsTo(fileUTI, kUTTypeMovie)) ||
92 (UTTypeConformsTo(fileUTI, kUTTypeImage))) {
Alexandre Lision74dd47f2015-04-14 13:47:42 -040093
94 //highlight our drop zone
95 highlight=YES;
96
97 [self setNeedsDisplay: YES];
98
99 /* When an image from one window is dragged over another, we want to resize the dragging item to
100 * preview the size of the image as it would appear if the user dropped it in. */
101 [sender enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent
102 forView:self
103 classes:[NSArray arrayWithObject:[NSPasteboardItem class]]
104 searchOptions:nil
105 usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {
106 *stop = YES;
107 }];
Alexandre Lision81c97212015-06-17 15:51:53 -0400108 CFRelease(fileUTI);
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400109 //accept data as a copy operation
110 return NSDragOperationCopy;
111 }
112
Alexandre Lision81c97212015-06-17 15:51:53 -0400113 CFRelease(fileUTI);
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400114 return NSDragOperationNone;
115}
116
117- (void)draggingExited:(id <NSDraggingInfo>)sender
118{
119 /*------------------------------------------------------
120 method called whenever a drag exits our drop zone
121 --------------------------------------------------------*/
122 //remove highlight of the drop zone
123 highlight=NO;
124
125 [self setNeedsDisplay: YES];
126}
127
128-(void)drawRect:(NSRect)rect
129{
130 /*------------------------------------------------------
131 draw method is overridden to do drop highlighing
132 --------------------------------------------------------*/
133 //do the usual draw operation to display the image
134 [super drawRect:rect];
135
136 if ( highlight ) {
137 //highlight by overlaying a gray border
138 [[NSColor blueColor] set];
139 [NSBezierPath setDefaultLineWidth: 5];
140 [NSBezierPath strokeRect: rect];
141 }
142}
143
144- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
145{
146 /*------------------------------------------------------
147 method to determine if we can accept the drop
148 --------------------------------------------------------*/
149 //finished with the drag so remove any highlighting
150 highlight=NO;
151
152 [self setNeedsDisplay: YES];
153
154 NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
Alexandre Lision81c97212015-06-17 15:51:53 -0400155 CFStringRef fileExtension = (__bridge CFStringRef) [fileURL.path pathExtension];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400156 CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
157
Alexandre Lisionba9b7082015-08-03 16:53:37 -0400158 BOOL conforms = (UTTypeConformsTo(fileUTI, kUTTypeVideo)) ||
159 (UTTypeConformsTo(fileUTI, kUTTypeMovie)) ||
160 UTTypeConformsTo(fileUTI, kUTTypeImage);
Alexandre Lision81c97212015-06-17 15:51:53 -0400161 CFRelease(fileUTI);
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400162 //check to see if we can accept the data
Alexandre Lision81c97212015-06-17 15:51:53 -0400163 return conforms;
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400164}
165
166- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
167{
168 /*------------------------------------------------------
169 method that should handle the drop data
170 --------------------------------------------------------*/
171 if ( [sender draggingSource] != self ) {
172 NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
Alexandre Lisione7a604c2015-12-10 09:29:02 -0500173 if (auto current = CallModel::instance().selectedCall()) {
174 if (auto outVideo = current->firstMedia<Media::Video>(Media::Media::Direction::OUT)) {
175 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(QString::fromUtf8([fileURL.path UTF8String])));
176 return YES;
177 }
178 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400179 }
180
Alexandre Lisione7a604c2015-12-10 09:29:02 -0500181 return NO;
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400182}
183
184- (void)showContextualMenu:(NSEvent *)theEvent {
185
186 contextualMenu = [[NSMenu alloc] initWithTitle:@"Switch camera"];
187
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400188 for(int i = 0 ; i < Video::DeviceModel::instance().devices().size() ; ++i) {
189 Video::Device* device = Video::DeviceModel::instance().devices()[i];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400190 [contextualMenu insertItemWithTitle:device->name().toNSString() action:@selector(switchInput:) keyEquivalent:@"" atIndex:i];
191 }
192
193 [contextualMenu addItem:[NSMenuItem separatorItem]];
Alexandre Lision7f01b072015-09-15 14:57:05 -0400194 [contextualMenu insertItemWithTitle:NSLocalizedString(@"Choose file", @"Contextual menu entry")
195 action:@selector(chooseFile:)
196 keyEquivalent:@""
197 atIndex:contextualMenu.itemArray.count];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400198
199 [NSMenu popUpContextMenu:contextualMenu withEvent:theEvent forView:self];
200}
201
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400202- (void)mouseMoved:(NSEvent *)theEvent
203{
204 [NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel showContextualMenu
205 [self performSelector:@selector(mouseIdle:) withObject:theEvent afterDelay:3];
206 if (self.callDelegate)
207 [self.callDelegate mouseIsMoving:YES];
208}
209
210- (void) mouseIdle:(NSEvent *)theEvent
211{
212 if (self.callDelegate && shouldAcceptInteractions)
213 [self.callDelegate mouseIsMoving:NO];
214}
215
216
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400217- (void)mouseUp:(NSEvent *)theEvent
218{
219 if([theEvent clickCount] == 1 && shouldAcceptInteractions) {
220 if(!contextualMenu)
221 [self performSelector:@selector(showContextualMenu:) withObject:theEvent afterDelay:[NSEvent doubleClickInterval]];
222 else
223 contextualMenu = nil;
224 }
225 else if([theEvent clickCount] == 2)
226 {
227 [NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel showContextualMenu
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400228 if(self.callDelegate)
229 [self.callDelegate callShouldToggleFullScreen];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400230 }
231}
232
233- (void) switchInput:(NSMenuItem*) sender
234{
235 int index = [contextualMenu indexOfItem:sender];
Alexandre Lisione7a604c2015-12-10 09:29:02 -0500236 if (auto current = CallModel::instance().selectedCall()) {
237 if (auto outVideo = current->firstMedia<Media::Video>(Media::Media::Direction::OUT)) {
238 outVideo->sourceModel()->switchTo(Video::DeviceModel::instance().devices()[index]);
239 }
240 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400241}
242
243- (void) chooseFile:(NSMenuItem*) sender
244{
245 NSOpenPanel *browsePanel = [[NSOpenPanel alloc] init];
246 [browsePanel setDirectoryURL:[NSURL URLWithString:NSHomeDirectory()]];
247 [browsePanel setCanChooseFiles:YES];
248 [browsePanel setCanChooseDirectories:NO];
249 [browsePanel setCanCreateDirectories:NO];
250
251 //NSMutableArray* fileTypes = [[NSMutableArray alloc] initWithArray:[NSImage imageTypes]];
252 NSMutableArray* fileTypes = [NSMutableArray array];
253 [fileTypes addObject:(__bridge NSString *)kUTTypeVideo];
254 [fileTypes addObject:(__bridge NSString *)kUTTypeMovie];
Alexandre Lisionba9b7082015-08-03 16:53:37 -0400255 [fileTypes addObject:(__bridge NSString *)kUTTypeImage];
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400256 [browsePanel setAllowedFileTypes:fileTypes];
257
258 [browsePanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
259 if (result == NSFileHandlingPanelOKButton) {
260 NSURL* theDoc = [[browsePanel URLs] objectAtIndex:0];
Alexandre Lisione7a604c2015-12-10 09:29:02 -0500261 if (auto current = CallModel::instance().selectedCall()) {
262 if (auto outVideo = current->firstMedia<Media::Video>(Media::Media::Direction::OUT)) {
263 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(QString::fromUtf8([theDoc.path UTF8String])));
264 }
265 }
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400266 }
267 }];
268
269}
270
271@end