blob: 1c37d9ae8a439bbf4092e3bd82d5105f3f9e0d08 [file] [log] [blame]
Alexandre Lisionc5148052015-03-04 15:10:35 -05001/*
2 * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
3 * 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.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30#import "CurrentCallVC.h"
31
32#import <QuartzCore/QuartzCore.h>
33
34#import <call.h>
35#import <callmodel.h>
36#import <useractionmodel.h>
37#import <contactmethod.h>
38#import <qabstractitemmodel.h>
39#import <QItemSelectionModel>
40#import <QItemSelection>
Alexandre Lisionc5148052015-03-04 15:10:35 -050041#import <video/previewmanager.h>
42#import <video/renderer.h>
Alexandre Lision58cab672015-06-09 15:25:40 -040043#import <media/text.h>
Alexandre Lision2db8f472015-07-22 15:05:46 -040044#import <person.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050045
Alexandre Lisionf47a2562015-06-15 15:48:29 -040046#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040047#import "views/CallView.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040048#import "PersonLinkerVC.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040049
Alexandre Lisionc5148052015-03-04 15:10:35 -050050@interface RendererConnectionsHolder : NSObject
51
52@property QMetaObject::Connection frameUpdated;
53@property QMetaObject::Connection started;
54@property QMetaObject::Connection stopped;
55
56@end
57
58@implementation RendererConnectionsHolder
59
60@end
61
Alexandre Lision2db8f472015-07-22 15:05:46 -040062@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate>
Alexandre Lisionc5148052015-03-04 15:10:35 -050063
Alexandre Lision58cab672015-06-09 15:25:40 -040064@property (unsafe_unretained) IBOutlet NSTextField *personLabel;
65@property (unsafe_unretained) IBOutlet NSTextField *stateLabel;
66@property (unsafe_unretained) IBOutlet NSButton *holdOnOffButton;
67@property (unsafe_unretained) IBOutlet NSButton *hangUpButton;
68@property (unsafe_unretained) IBOutlet NSButton *recordOnOffButton;
69@property (unsafe_unretained) IBOutlet NSButton *pickUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040070@property (unsafe_unretained) IBOutlet NSButton *muteAudioButton;
71@property (unsafe_unretained) IBOutlet NSButton *muteVideoButton;
Alexandre Lision2db8f472015-07-22 15:05:46 -040072@property (unsafe_unretained) IBOutlet NSButton *addContactButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040073
Alexandre Lisionf47a2562015-06-15 15:48:29 -040074@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040075
Alexandre Lision58cab672015-06-09 15:25:40 -040076@property (unsafe_unretained) IBOutlet NSTextField *timeSpentLabel;
77@property (unsafe_unretained) IBOutlet NSView *controlsPanel;
78@property (unsafe_unretained) IBOutlet NSSplitView *splitView;
79@property (unsafe_unretained) IBOutlet NSButton *chatButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -050080
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -040081@property (strong) IBOutlet NSPopover *qualityPopOver;
Alexandre Lision2db8f472015-07-22 15:05:46 -040082@property (strong) NSPopover* addToContactPopover;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -040083
Alexandre Lisionc5148052015-03-04 15:10:35 -050084@property QHash<int, NSButton*> actionHash;
85
86// Video
Alexandre Lision58cab672015-06-09 15:25:40 -040087@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lisionc5148052015-03-04 15:10:35 -050088@property CALayer* videoLayer;
Alexandre Lision58cab672015-06-09 15:25:40 -040089@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -050090@property CALayer* previewLayer;
91
92@property RendererConnectionsHolder* previewHolder;
93@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -040094@property QMetaObject::Connection videoStarted;
Alexandre Lisionb65c0272015-07-22 15:51:29 -040095@property QMetaObject::Connection messageConnection;
96@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -050097
98@end
99
100@implementation CurrentCallVC
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400101@synthesize personLabel, actionHash, stateLabel, holdOnOffButton, hangUpButton,
102 recordOnOffButton, pickUpButton, chatButton, timeSpentLabel,
103 muteVideoButton, muteAudioButton, controlsPanel, videoView,
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400104 videoLayer, previewLayer, previewView, splitView, loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500105
106@synthesize previewHolder;
107@synthesize videoHolder;
108
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400109- (void) updateAllActions
Alexandre Lisionc5148052015-03-04 15:10:35 -0500110{
111 for(int i = 0 ; i <= CallModel::instance()->userActionModel()->rowCount() ; i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400112 [self updateActionAtIndex:i];
113 }
114}
115
116- (void) updateActionAtIndex:(int) row
117{
118 const QModelIndex& idx = CallModel::instance()->userActionModel()->index(row,0);
119 UserActionModel::Action action = qvariant_cast<UserActionModel::Action>(idx.data(UserActionModel::Role::ACTION));
120 NSButton* a = actionHash[(int) action];
121 if (a != nil) {
122 [a setEnabled:(idx.flags() & Qt::ItemIsEnabled)];
123 [a setState:(idx.data(Qt::CheckStateRole) == Qt::Checked) ? NSOnState : NSOffState];
124
125 if(action == UserActionModel::Action::HOLD) {
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400126 NSString* imgName = (a.state == NSOnState ? @"ic_action_holdoff" : @"ic_action_hold");
127 [a setImage:[NSImage imageNamed:imgName]];
128
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400129 }
130 if(action == UserActionModel::Action::RECORD) {
131 [a setTitle:(a.state == NSOnState ? @"Record off" : @"Record")];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500132 }
133 }
134}
135
136-(void) updateCall
137{
138 QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400139 if (!callIdx.isValid()) {
140 return;
141 }
Alexandre Lision58cab672015-06-09 15:25:40 -0400142 [personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()];
143 [timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500144
Alexandre Lision2db8f472015-07-22 15:05:46 -0400145 auto contactmethod = qvariant_cast<Call*>(callIdx.data(static_cast<int>(Call::Role::Object)))->peerContactMethod();
146 BOOL shouldShow = (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder());
147 [self.addContactButton setHidden:!shouldShow];
148
Alexandre Lision58cab672015-06-09 15:25:40 -0400149 Call::State state = callIdx.data((int)Call::Role::State).value<Call::State>();
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400150 [loadingIndicator setHidden:YES];
151 [stateLabel setStringValue:callIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500152 switch (state) {
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400153 case Call::State::DIALING:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400154 [loadingIndicator setHidden:NO];
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400155 break;
156 case Call::State::NEW:
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400157 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500158 case Call::State::INITIALIZATION:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400159 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400160 [loadingIndicator setHidden:NO];
161 break;
162 case Call::State::CONNECTED:
163 [videoView setShouldAcceptInteractions:NO];
164 [loadingIndicator setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500165 break;
166 case Call::State::RINGING:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400167 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500168 break;
169 case Call::State::CURRENT:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400170 [videoView setShouldAcceptInteractions:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500171 break;
172 case Call::State::HOLD:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400173 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500174 break;
175 case Call::State::BUSY:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400176 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500177 break;
178 case Call::State::OVER:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400179 [videoView setShouldAcceptInteractions:NO];
180 if(videoView.isInFullScreenMode)
181 [videoView exitFullScreenModeWithOptions:nil];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500182 break;
183 case Call::State::FAILURE:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400184 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500185 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500186 }
187
188}
189
190- (void)awakeFromNib
191{
192 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400193 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500194 [self.view setLayer:[CALayer layer]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500195
196 [controlsPanel setWantsLayer:YES];
197 [controlsPanel setLayer:[CALayer layer]];
198 [controlsPanel.layer setZPosition:2.0];
199 [controlsPanel.layer setBackgroundColor:[NSColor whiteColor].CGColor];
200
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400201 actionHash[ (int)UserActionModel::Action::ACCEPT] = pickUpButton;
202 actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton;
203 actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton;
204 actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400205 actionHash[ (int)UserActionModel::Action::MUTE_AUDIO] = muteAudioButton;
206 actionHash[ (int)UserActionModel::Action::MUTE_VIDEO] = muteVideoButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500207
Alexandre Lisionc5148052015-03-04 15:10:35 -0500208 videoLayer = [CALayer layer];
209 [videoView setWantsLayer:YES];
210 [videoView setLayer:videoLayer];
211 [videoView.layer setBackgroundColor:[NSColor blackColor].CGColor];
212 [videoView.layer setFrame:videoView.frame];
213 [videoView.layer setContentsGravity:kCAGravityResizeAspect];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500214
215 previewLayer = [CALayer layer];
216 [previewView setWantsLayer:YES];
217 [previewView setLayer:previewLayer];
218 [previewLayer setBackgroundColor:[NSColor blackColor].CGColor];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400219 [previewLayer setContentsGravity:kCAGravityResizeAspectFill];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500220 [previewLayer setFrame:previewView.frame];
221
222 [controlsPanel setWantsLayer:YES];
223 [controlsPanel setLayer:[CALayer layer]];
224 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
225 [controlsPanel.layer setFrame:controlsPanel.frame];
226
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400227 previewHolder = [[RendererConnectionsHolder alloc] init];
228 videoHolder = [[RendererConnectionsHolder alloc] init];
229
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400230 [loadingIndicator setColor:[NSColor whiteColor]];
231 [loadingIndicator setNumberOfLines:100];
232 [loadingIndicator setWidthOfLine:2];
233 [loadingIndicator setLengthOfLine:2];
234 [loadingIndicator setInnerMargin:30];
235
Alexandre Lision58cab672015-06-09 15:25:40 -0400236 [self.videoView setFullScreenDelegate:self];
237
Alexandre Lisionc5148052015-03-04 15:10:35 -0500238 [self connect];
239}
240
241- (void) connect
242{
243 QObject::connect(CallModel::instance()->selectionModel(),
244 &QItemSelectionModel::currentChanged,
245 [=](const QModelIndex &current, const QModelIndex &previous) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500246 if(!current.isValid()) {
247 [self animateOut];
248 return;
249 }
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400250
251 auto call = CallModel::instance()->getCall(current);
252 if (call->state() == Call::State::HOLD) {
253 call << Call::Action::HOLD;
254 }
255
Alexandre Lision58cab672015-06-09 15:25:40 -0400256 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500257 [self updateCall];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400258 [self updateAllActions];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500259 [self animateOut];
260 });
261
262 QObject::connect(CallModel::instance(),
263 &QAbstractItemModel::dataChanged,
264 [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500265 [self updateCall];
266 });
267
268 QObject::connect(CallModel::instance()->userActionModel(),
269 &QAbstractItemModel::dataChanged,
270 [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500271 const int first(topLeft.row()),last(bottomRight.row());
272 for(int i = first; i <= last;i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400273 [self updateActionAtIndex:i];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500274 }
275 });
276
277 QObject::connect(CallModel::instance(),
278 &CallModel::callStateChanged,
279 [self](Call* c, Call::State state) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500280 [self updateCall];
281 });
282}
283
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400284- (void) monitorIncomingTextMessages:(Media::Text*) media
285{
286 /* connect to incoming chat messages to open the chat view */
287 QObject::disconnect(self.messageConnection);
288 self.messageConnection = QObject::connect(media,
289 &Media::Text::messageReceived,
290 [self] (const QMap<QString,QString>& m) {
291 if([[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]])
292 [self uncollapseRightView];
293 });
294}
295
Alexandre Lisionc5148052015-03-04 15:10:35 -0500296-(void) connectVideoSignals
297{
298 QModelIndex idx = CallModel::instance()->selectionModel()->currentIndex();
299 Call* call = CallModel::instance()->getCall(idx);
Alexandre Lision58cab672015-06-09 15:25:40 -0400300 self.videoStarted = QObject::connect(call,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500301 &Call::videoStarted,
302 [=](Video::Renderer* renderer) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400303 NSLog(@"Video started!");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400304 [self connectVideoRenderer:renderer];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500305 });
306
307 if(call->videoRenderer())
308 {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500309 [self connectVideoRenderer:call->videoRenderer()];
310 }
311
312 [self connectPreviewRenderer];
313
314}
315
316-(void) connectPreviewRenderer
317{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400318 QObject::disconnect(previewHolder.frameUpdated);
319 QObject::disconnect(previewHolder.stopped);
320 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500321 previewHolder.started = QObject::connect(Video::PreviewManager::instance(),
322 &Video::PreviewManager::previewStarted,
323 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500324 QObject::disconnect(previewHolder.frameUpdated);
325 previewHolder.frameUpdated = QObject::connect(renderer,
326 &Video::Renderer::frameUpdated,
327 [=]() {
328 [self renderer:Video::PreviewManager::instance()->previewRenderer()
329 renderFrameForView:previewView];
330 });
331 });
332
333 previewHolder.stopped = QObject::connect(Video::PreviewManager::instance(),
334 &Video::PreviewManager::previewStopped,
335 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500336 QObject::disconnect(previewHolder.frameUpdated);
337 [previewView.layer setContents:nil];
338 });
339
340 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance()->previewRenderer(),
341 &Video::Renderer::frameUpdated,
342 [=]() {
343 [self renderer:Video::PreviewManager::instance()->previewRenderer()
344 renderFrameForView:previewView];
345 });
346}
347
348-(void) connectVideoRenderer: (Video::Renderer*)renderer
349{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400350 QObject::disconnect(videoHolder.frameUpdated);
351 QObject::disconnect(videoHolder.started);
352 QObject::disconnect(videoHolder.stopped);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500353 videoHolder.frameUpdated = QObject::connect(renderer,
354 &Video::Renderer::frameUpdated,
355 [=]() {
356 [self renderer:renderer renderFrameForView:videoView];
357 });
358
359 videoHolder.started = QObject::connect(renderer,
360 &Video::Renderer::started,
361 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500362 QObject::disconnect(videoHolder.frameUpdated);
363 videoHolder.frameUpdated = QObject::connect(renderer,
364 &Video::Renderer::frameUpdated,
365 [=]() {
366 [self renderer:renderer renderFrameForView:videoView];
367 });
368 });
369
370 videoHolder.stopped = QObject::connect(renderer,
371 &Video::Renderer::stopped,
372 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500373 QObject::disconnect(videoHolder.frameUpdated);
374 [videoView.layer setContents:nil];
375 });
376}
377
378-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
379{
Alexandre Lision911e0072015-07-13 16:19:08 -0400380 auto data = renderer->currentSmartFrame();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500381 QSize res = renderer->size();
382
Alexandre Lision911e0072015-07-13 16:19:08 -0400383 auto buf = reinterpret_cast<const unsigned char*>(data->data());
Alexandre Lisionc5148052015-03-04 15:10:35 -0500384
385 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
386 CGContextRef newContext = CGBitmapContextCreate((void *)buf,
387 res.width(),
388 res.height(),
389 8,
390 4*res.width(),
391 colorSpace,
392 kCGImageAlphaPremultipliedLast);
393
394
395 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
396
397 /*We release some components*/
398 CGContextRelease(newContext);
399 CGColorSpaceRelease(colorSpace);
400
401 [CATransaction begin];
402 view.layer.contents = (__bridge id)newImage;
403 [CATransaction commit];
404
405 CFRelease(newImage);
406}
407
408- (void) initFrame
409{
410 [self.view setFrame:self.view.superview.bounds];
411 [self.view setHidden:YES];
412 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400413 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500414}
415
416# pragma private IN/OUT animations
417
418-(void) animateIn
419{
420 NSLog(@"animateIn");
421 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
422 [self.view setHidden:NO];
423
424 [CATransaction begin];
425 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
426 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
427 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
428 [animation setDuration:0.2f];
429 [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
430 [CATransaction setCompletionBlock:^{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500431 [self connectVideoSignals];
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400432 /* check if text media is already present */
433 if (CallModel::instance()->selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) {
434 Media::Text *text = CallModel::instance()->selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::IN);
435 [self monitorIncomingTextMessages:text];
436 } else if (CallModel::instance()->selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) {
437 Media::Text *text = CallModel::instance()->selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::OUT);
438 [self monitorIncomingTextMessages:text];
439 } else {
440 /* monitor media for messaging text messaging */
441 self.mediaAddedConnection = QObject::connect(CallModel::instance()->selectedCall(),
442 &Call::mediaAdded,
443 [self] (Media::Media* media) {
444 if (media->type() == Media::Media::Type::TEXT) { [self monitorIncomingTextMessages:(Media::Text*)media];
445 QObject::disconnect(self.mediaAddedConnection);
446 }
447 });
448 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500449 }];
450 [self.view.layer addAnimation:animation forKey:animation.keyPath];
451
452 [CATransaction commit];
453}
454
455-(void) cleanUp
456{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400457 QObject::disconnect(videoHolder.frameUpdated);
458 QObject::disconnect(videoHolder.started);
459 QObject::disconnect(videoHolder.stopped);
460 QObject::disconnect(previewHolder.frameUpdated);
461 QObject::disconnect(previewHolder.stopped);
462 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500463 [videoView.layer setContents:nil];
464 [previewView.layer setContents:nil];
465}
466
467-(void) animateOut
468{
469 NSLog(@"animateOut");
470 if(self.view.frame.origin.x < 0) {
471 NSLog(@"Already hidden");
472 if (CallModel::instance()->selectionModel()->currentIndex().isValid()) {
473 [self animateIn];
474 }
475 return;
476 }
477
478 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
479 [CATransaction begin];
480 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
481 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
482 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
483 [animation setDuration:0.2f];
484 [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
485
486 [CATransaction setCompletionBlock:^{
487 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400488 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500489 [self cleanUp];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500490 if (CallModel::instance()->selectionModel()->currentIndex().isValid()) {
491 [self animateIn];
492 }
493 }];
494 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400495
496 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500497 [CATransaction commit];
498}
499
500/**
501 * Debug purpose
502 */
503-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
504{
505 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
506}
507
Alexandre Lision58cab672015-06-09 15:25:40 -0400508-(void)collapseRightView
509{
510 NSView *right = [[splitView subviews] objectAtIndex:1];
511 NSView *left = [[splitView subviews] objectAtIndex:0];
512 NSRect leftFrame = [left frame];
513 [right setHidden:YES];
514 [splitView display];
515}
Alexandre Lisionc5148052015-03-04 15:10:35 -0500516
Alexandre Lision58cab672015-06-09 15:25:40 -0400517-(void)uncollapseRightView
518{
519 NSView *left = [[splitView subviews] objectAtIndex:0];
520 NSView *right = [[splitView subviews] objectAtIndex:1];
521 [right setHidden:NO];
522
523 CGFloat dividerThickness = [splitView dividerThickness];
524
525 // get the different frames
526 NSRect leftFrame = [left frame];
527 NSRect rightFrame = [right frame];
528
529 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
530 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
531 [left setFrameSize:leftFrame.size];
532 [right setFrame:rightFrame];
533 [splitView display];
534}
535
536
537#pragma mark - Button methods
538
Alexandre Lision2db8f472015-07-22 15:05:46 -0400539- (IBAction)addToContact:(NSButton*) sender {
540 auto contactmethod = CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex())->peerContactMethod();
541
542 if (self.addToContactPopover != nullptr) {
543 [self.addToContactPopover performClose:self];
544 self.addToContactPopover = NULL;
545 } else if (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder()) {
546 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
547 [editorVC setMethodToLink:contactmethod];
548 [editorVC setContactLinkedDelegate:self];
549 self.addToContactPopover = [[NSPopover alloc] init];
550 [self.addToContactPopover setContentSize:editorVC.view.frame.size];
551 [self.addToContactPopover setContentViewController:editorVC];
552 [self.addToContactPopover setAnimates:YES];
553 [self.addToContactPopover setBehavior:NSPopoverBehaviorTransient];
554 [self.addToContactPopover setDelegate:self];
555
556 [self.addToContactPopover showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMaxXEdge];
557 }
558}
559
Alexandre Lisionc5148052015-03-04 15:10:35 -0500560- (IBAction)hangUp:(id)sender {
561 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::REFUSE;
562}
563
564- (IBAction)accept:(id)sender {
565 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::ACCEPT;
566}
567
568- (IBAction)toggleRecording:(id)sender {
Alexandre Lision66643432015-06-04 11:59:36 -0400569 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::RECORD_AUDIO;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500570}
571
572- (IBAction)toggleHold:(id)sender {
573 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::HOLD;
574}
575
Alexandre Lision58cab672015-06-09 15:25:40 -0400576-(IBAction)toggleChat:(id)sender;
577{
578 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
579 if (rightViewCollapsed) {
580 [self uncollapseRightView];
581 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex())->addOutgoingMedia<Media::Text>();
582 } else {
583 [self collapseRightView];
584 }
585 [chatButton setState:rightViewCollapsed];
586}
587
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400588- (IBAction)muteAudio:(id)sender
589{
590 UserActionModel* uam = CallModel::instance()->userActionModel();
591 uam << UserActionModel::Action::MUTE_AUDIO;
592}
593
594- (IBAction)muteVideo:(id)sender
595{
596 UserActionModel* uam = CallModel::instance()->userActionModel();
597 uam << UserActionModel::Action::MUTE_VIDEO;
598}
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400599- (IBAction)displayQualityPopUp:(id)sender {
600 [self.qualityPopOver showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxXEdge];
601}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400602
Alexandre Lision2db8f472015-07-22 15:05:46 -0400603#pragma mark - NSPopOverDelegate
604
605- (void)popoverDidClose:(NSNotification *)notification
606{
607 if (self.addToContactPopover != nullptr) {
608 [self.addToContactPopover performClose:self];
609 self.addToContactPopover = NULL;
610 }
611}
612
613#pragma mark - ContactLinkedDelegate
614
615- (void)contactLinked
616{
617 if (self.addToContactPopover != nullptr) {
618 [self.addToContactPopover performClose:self];
619 self.addToContactPopover = NULL;
620 }
621}
622
Alexandre Lision58cab672015-06-09 15:25:40 -0400623#pragma mark - NSSplitViewDelegate
624
625/* Return YES if the subview should be collapsed because the user has double-clicked on an adjacent divider. If a split view has a delegate, and the delegate responds to this message, it will be sent once for the subview before a divider when the user double-clicks on that divider, and again for the subview after the divider, but only if the delegate returned YES when sent -splitView:canCollapseSubview: for the subview in question. When the delegate indicates that both subviews should be collapsed NSSplitView's behavior is undefined.
626 */
627- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
628{
629 NSView* rightView = [[splitView subviews] objectAtIndex:1];
630 return ([subview isEqual:rightView]);
631}
632
633
634- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
635{
636 NSView* rightView = [[splitView subviews] objectAtIndex:1];
637 return ([subview isEqual:rightView]);
638}
639
640
641# pragma mark - FullScreenDelegate
642
643- (void) callShouldToggleFullScreen
644{
645 if(self.splitView.isInFullScreenMode)
646 [self.splitView exitFullScreenModeWithOptions:nil];
647 else {
648 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
649 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
650 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
651 NSFullScreenModeApplicationPresentationOptions, nil];
652
653 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
654 }
655}
656
Alexandre Lisionc5148052015-03-04 15:10:35 -0500657@end