blob: fdb0a9d9b93ed7789469be972b3675b3b3207483 [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 Lision16d9c0a2015-08-10 12:05:15 -040049#import "ChatVC.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040050
Alexandre Lisionc5148052015-03-04 15:10:35 -050051@interface RendererConnectionsHolder : NSObject
52
53@property QMetaObject::Connection frameUpdated;
54@property QMetaObject::Connection started;
55@property QMetaObject::Connection stopped;
56
57@end
58
59@implementation RendererConnectionsHolder
60
61@end
62
Alexandre Lision2db8f472015-07-22 15:05:46 -040063@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate>
Alexandre Lisionc5148052015-03-04 15:10:35 -050064
Alexandre Lision58cab672015-06-09 15:25:40 -040065@property (unsafe_unretained) IBOutlet NSTextField *personLabel;
66@property (unsafe_unretained) IBOutlet NSTextField *stateLabel;
67@property (unsafe_unretained) IBOutlet NSButton *holdOnOffButton;
68@property (unsafe_unretained) IBOutlet NSButton *hangUpButton;
69@property (unsafe_unretained) IBOutlet NSButton *recordOnOffButton;
70@property (unsafe_unretained) IBOutlet NSButton *pickUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040071@property (unsafe_unretained) IBOutlet NSButton *muteAudioButton;
72@property (unsafe_unretained) IBOutlet NSButton *muteVideoButton;
Alexandre Lision2db8f472015-07-22 15:05:46 -040073@property (unsafe_unretained) IBOutlet NSButton *addContactButton;
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -040074@property (unsafe_unretained) IBOutlet NSView *headerContainer;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040075
Alexandre Lisionf47a2562015-06-15 15:48:29 -040076@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040077
Alexandre Lision58cab672015-06-09 15:25:40 -040078@property (unsafe_unretained) IBOutlet NSTextField *timeSpentLabel;
79@property (unsafe_unretained) IBOutlet NSView *controlsPanel;
80@property (unsafe_unretained) IBOutlet NSSplitView *splitView;
81@property (unsafe_unretained) IBOutlet NSButton *chatButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -050082
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -040083@property (strong) IBOutlet NSPopover *qualityPopOver;
Alexandre Lision2db8f472015-07-22 15:05:46 -040084@property (strong) NSPopover* addToContactPopover;
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040085@property (strong) IBOutlet ChatVC *chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -040086
Alexandre Lisionc5148052015-03-04 15:10:35 -050087@property QHash<int, NSButton*> actionHash;
88
89// Video
Alexandre Lision58cab672015-06-09 15:25:40 -040090@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lisionc5148052015-03-04 15:10:35 -050091@property CALayer* videoLayer;
Alexandre Lision58cab672015-06-09 15:25:40 -040092@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -050093@property CALayer* previewLayer;
94
95@property RendererConnectionsHolder* previewHolder;
96@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -040097@property QMetaObject::Connection videoStarted;
Alexandre Lisionb65c0272015-07-22 15:51:29 -040098@property QMetaObject::Connection messageConnection;
99@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500100
101@end
102
103@implementation CurrentCallVC
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400104@synthesize personLabel, actionHash, stateLabel, holdOnOffButton, hangUpButton,
105 recordOnOffButton, pickUpButton, chatButton, timeSpentLabel,
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400106 muteVideoButton, muteAudioButton, controlsPanel, headerContainer, videoView, videoLayer, previewLayer, previewView, splitView, loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500107
108@synthesize previewHolder;
109@synthesize videoHolder;
110
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400111- (void) updateAllActions
Alexandre Lisionc5148052015-03-04 15:10:35 -0500112{
113 for(int i = 0 ; i <= CallModel::instance()->userActionModel()->rowCount() ; i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400114 [self updateActionAtIndex:i];
115 }
116}
117
118- (void) updateActionAtIndex:(int) row
119{
120 const QModelIndex& idx = CallModel::instance()->userActionModel()->index(row,0);
121 UserActionModel::Action action = qvariant_cast<UserActionModel::Action>(idx.data(UserActionModel::Role::ACTION));
122 NSButton* a = actionHash[(int) action];
123 if (a != nil) {
124 [a setEnabled:(idx.flags() & Qt::ItemIsEnabled)];
125 [a setState:(idx.data(Qt::CheckStateRole) == Qt::Checked) ? NSOnState : NSOffState];
126
127 if(action == UserActionModel::Action::HOLD) {
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400128 NSString* imgName = (a.state == NSOnState ? @"ic_action_holdoff" : @"ic_action_hold");
129 [a setImage:[NSImage imageNamed:imgName]];
130
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400131 }
132 if(action == UserActionModel::Action::RECORD) {
133 [a setTitle:(a.state == NSOnState ? @"Record off" : @"Record")];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500134 }
135 }
136}
137
138-(void) updateCall
139{
140 QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400141 if (!callIdx.isValid()) {
142 return;
143 }
Alexandre Lision58cab672015-06-09 15:25:40 -0400144 [personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()];
145 [timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500146
Alexandre Lision2db8f472015-07-22 15:05:46 -0400147 auto contactmethod = qvariant_cast<Call*>(callIdx.data(static_cast<int>(Call::Role::Object)))->peerContactMethod();
148 BOOL shouldShow = (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder());
149 [self.addContactButton setHidden:!shouldShow];
150
Alexandre Lision58cab672015-06-09 15:25:40 -0400151 Call::State state = callIdx.data((int)Call::Role::State).value<Call::State>();
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400152 [loadingIndicator setHidden:YES];
153 [stateLabel setStringValue:callIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500154 switch (state) {
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400155 case Call::State::DIALING:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400156 [loadingIndicator setHidden:NO];
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400157 break;
158 case Call::State::NEW:
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400159 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500160 case Call::State::INITIALIZATION:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400161 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400162 [loadingIndicator setHidden:NO];
163 break;
164 case Call::State::CONNECTED:
165 [videoView setShouldAcceptInteractions:NO];
166 [loadingIndicator setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500167 break;
168 case Call::State::RINGING:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400169 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500170 break;
171 case Call::State::CURRENT:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400172 [videoView setShouldAcceptInteractions:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500173 break;
174 case Call::State::HOLD:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400175 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500176 break;
177 case Call::State::BUSY:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400178 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500179 break;
180 case Call::State::OVER:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400181 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400182 if(self.splitView.isInFullScreenMode)
183 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500184 break;
185 case Call::State::FAILURE:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400186 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500187 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500188 }
189
190}
191
192- (void)awakeFromNib
193{
194 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400195 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500196 [self.view setLayer:[CALayer layer]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500197
198 [controlsPanel setWantsLayer:YES];
199 [controlsPanel setLayer:[CALayer layer]];
200 [controlsPanel.layer setZPosition:2.0];
201 [controlsPanel.layer setBackgroundColor:[NSColor whiteColor].CGColor];
202
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400203 actionHash[ (int)UserActionModel::Action::ACCEPT] = pickUpButton;
204 actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton;
205 actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton;
206 actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400207 actionHash[ (int)UserActionModel::Action::MUTE_AUDIO] = muteAudioButton;
208 actionHash[ (int)UserActionModel::Action::MUTE_VIDEO] = muteVideoButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500209
Alexandre Lisionc5148052015-03-04 15:10:35 -0500210 videoLayer = [CALayer layer];
211 [videoView setWantsLayer:YES];
212 [videoView setLayer:videoLayer];
213 [videoView.layer setBackgroundColor:[NSColor blackColor].CGColor];
214 [videoView.layer setFrame:videoView.frame];
215 [videoView.layer setContentsGravity:kCAGravityResizeAspect];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500216
217 previewLayer = [CALayer layer];
218 [previewView setWantsLayer:YES];
219 [previewView setLayer:previewLayer];
220 [previewLayer setBackgroundColor:[NSColor blackColor].CGColor];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400221 [previewLayer setContentsGravity:kCAGravityResizeAspectFill];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500222 [previewLayer setFrame:previewView.frame];
223
224 [controlsPanel setWantsLayer:YES];
225 [controlsPanel setLayer:[CALayer layer]];
226 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
227 [controlsPanel.layer setFrame:controlsPanel.frame];
228
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400229 previewHolder = [[RendererConnectionsHolder alloc] init];
230 videoHolder = [[RendererConnectionsHolder alloc] init];
231
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400232 [loadingIndicator setColor:[NSColor whiteColor]];
233 [loadingIndicator setNumberOfLines:100];
234 [loadingIndicator setWidthOfLine:2];
235 [loadingIndicator setLengthOfLine:2];
236 [loadingIndicator setInnerMargin:30];
237
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400238 [self.videoView setCallDelegate:self];
Alexandre Lision58cab672015-06-09 15:25:40 -0400239
Alexandre Lisionc5148052015-03-04 15:10:35 -0500240 [self connect];
241}
242
243- (void) connect
244{
245 QObject::connect(CallModel::instance()->selectionModel(),
246 &QItemSelectionModel::currentChanged,
247 [=](const QModelIndex &current, const QModelIndex &previous) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500248 if(!current.isValid()) {
249 [self animateOut];
250 return;
251 }
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400252
253 auto call = CallModel::instance()->getCall(current);
254 if (call->state() == Call::State::HOLD) {
255 call << Call::Action::HOLD;
256 }
257
Alexandre Lision58cab672015-06-09 15:25:40 -0400258 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500259 [self updateCall];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400260 [self updateAllActions];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500261 [self animateOut];
262 });
263
264 QObject::connect(CallModel::instance(),
265 &QAbstractItemModel::dataChanged,
266 [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500267 [self updateCall];
268 });
269
270 QObject::connect(CallModel::instance()->userActionModel(),
271 &QAbstractItemModel::dataChanged,
272 [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500273 const int first(topLeft.row()),last(bottomRight.row());
274 for(int i = first; i <= last;i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400275 [self updateActionAtIndex:i];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500276 }
277 });
278
279 QObject::connect(CallModel::instance(),
280 &CallModel::callStateChanged,
281 [self](Call* c, Call::State state) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500282 [self updateCall];
283 });
284}
285
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400286- (void) monitorIncomingTextMessages:(Media::Text*) media
287{
288 /* connect to incoming chat messages to open the chat view */
289 QObject::disconnect(self.messageConnection);
290 self.messageConnection = QObject::connect(media,
291 &Media::Text::messageReceived,
292 [self] (const QMap<QString,QString>& m) {
293 if([[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]])
294 [self uncollapseRightView];
295 });
296}
297
Alexandre Lisionc5148052015-03-04 15:10:35 -0500298-(void) connectVideoSignals
299{
300 QModelIndex idx = CallModel::instance()->selectionModel()->currentIndex();
301 Call* call = CallModel::instance()->getCall(idx);
Alexandre Lision58cab672015-06-09 15:25:40 -0400302 self.videoStarted = QObject::connect(call,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500303 &Call::videoStarted,
304 [=](Video::Renderer* renderer) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400305 NSLog(@"Video started!");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400306 [self connectVideoRenderer:renderer];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500307 });
308
309 if(call->videoRenderer())
310 {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500311 [self connectVideoRenderer:call->videoRenderer()];
312 }
313
314 [self connectPreviewRenderer];
315
316}
317
318-(void) connectPreviewRenderer
319{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400320 QObject::disconnect(previewHolder.frameUpdated);
321 QObject::disconnect(previewHolder.stopped);
322 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500323 previewHolder.started = QObject::connect(Video::PreviewManager::instance(),
324 &Video::PreviewManager::previewStarted,
325 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500326 QObject::disconnect(previewHolder.frameUpdated);
327 previewHolder.frameUpdated = QObject::connect(renderer,
328 &Video::Renderer::frameUpdated,
329 [=]() {
330 [self renderer:Video::PreviewManager::instance()->previewRenderer()
331 renderFrameForView:previewView];
332 });
333 });
334
335 previewHolder.stopped = QObject::connect(Video::PreviewManager::instance(),
336 &Video::PreviewManager::previewStopped,
337 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500338 QObject::disconnect(previewHolder.frameUpdated);
339 [previewView.layer setContents:nil];
340 });
341
342 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance()->previewRenderer(),
343 &Video::Renderer::frameUpdated,
344 [=]() {
345 [self renderer:Video::PreviewManager::instance()->previewRenderer()
346 renderFrameForView:previewView];
347 });
348}
349
350-(void) connectVideoRenderer: (Video::Renderer*)renderer
351{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400352 QObject::disconnect(videoHolder.frameUpdated);
353 QObject::disconnect(videoHolder.started);
354 QObject::disconnect(videoHolder.stopped);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500355 videoHolder.frameUpdated = QObject::connect(renderer,
356 &Video::Renderer::frameUpdated,
357 [=]() {
358 [self renderer:renderer renderFrameForView:videoView];
359 });
360
361 videoHolder.started = QObject::connect(renderer,
362 &Video::Renderer::started,
363 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500364 QObject::disconnect(videoHolder.frameUpdated);
365 videoHolder.frameUpdated = QObject::connect(renderer,
366 &Video::Renderer::frameUpdated,
367 [=]() {
368 [self renderer:renderer renderFrameForView:videoView];
369 });
370 });
371
372 videoHolder.stopped = QObject::connect(renderer,
373 &Video::Renderer::stopped,
374 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500375 QObject::disconnect(videoHolder.frameUpdated);
376 [videoView.layer setContents:nil];
377 });
378}
379
380-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
381{
Alexandre Lision911e0072015-07-13 16:19:08 -0400382 auto data = renderer->currentSmartFrame();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500383 QSize res = renderer->size();
384
Alexandre Lision911e0072015-07-13 16:19:08 -0400385 auto buf = reinterpret_cast<const unsigned char*>(data->data());
Alexandre Lisionc5148052015-03-04 15:10:35 -0500386
387 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
388 CGContextRef newContext = CGBitmapContextCreate((void *)buf,
389 res.width(),
390 res.height(),
391 8,
392 4*res.width(),
393 colorSpace,
394 kCGImageAlphaPremultipliedLast);
395
396
397 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
398
399 /*We release some components*/
400 CGContextRelease(newContext);
401 CGColorSpaceRelease(colorSpace);
402
403 [CATransaction begin];
404 view.layer.contents = (__bridge id)newImage;
405 [CATransaction commit];
406
407 CFRelease(newImage);
408}
409
410- (void) initFrame
411{
412 [self.view setFrame:self.view.superview.bounds];
413 [self.view setHidden:YES];
414 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400415 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500416}
417
418# pragma private IN/OUT animations
419
420-(void) animateIn
421{
422 NSLog(@"animateIn");
423 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
424 [self.view setHidden:NO];
425
426 [CATransaction begin];
427 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
428 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
429 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
430 [animation setDuration:0.2f];
431 [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
432 [CATransaction setCompletionBlock:^{
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400433
434 // when call comes in we want to show the controls/header
435 [self mouseIsMoving:YES];
436
Alexandre Lisionc5148052015-03-04 15:10:35 -0500437 [self connectVideoSignals];
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400438 /* check if text media is already present */
439 if (CallModel::instance()->selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) {
440 Media::Text *text = CallModel::instance()->selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::IN);
441 [self monitorIncomingTextMessages:text];
442 } else if (CallModel::instance()->selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) {
443 Media::Text *text = CallModel::instance()->selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::OUT);
444 [self monitorIncomingTextMessages:text];
445 } else {
446 /* monitor media for messaging text messaging */
447 self.mediaAddedConnection = QObject::connect(CallModel::instance()->selectedCall(),
448 &Call::mediaAdded,
449 [self] (Media::Media* media) {
450 if (media->type() == Media::Media::Type::TEXT) { [self monitorIncomingTextMessages:(Media::Text*)media];
451 QObject::disconnect(self.mediaAddedConnection);
452 }
453 });
454 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500455 }];
456 [self.view.layer addAnimation:animation forKey:animation.keyPath];
457
458 [CATransaction commit];
459}
460
461-(void) cleanUp
462{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400463 QObject::disconnect(videoHolder.frameUpdated);
464 QObject::disconnect(videoHolder.started);
465 QObject::disconnect(videoHolder.stopped);
466 QObject::disconnect(previewHolder.frameUpdated);
467 QObject::disconnect(previewHolder.stopped);
468 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500469 [videoView.layer setContents:nil];
470 [previewView.layer setContents:nil];
471}
472
473-(void) animateOut
474{
475 NSLog(@"animateOut");
476 if(self.view.frame.origin.x < 0) {
477 NSLog(@"Already hidden");
478 if (CallModel::instance()->selectionModel()->currentIndex().isValid()) {
479 [self animateIn];
480 }
481 return;
482 }
483
484 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
485 [CATransaction begin];
486 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
487 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
488 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
489 [animation setDuration:0.2f];
490 [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
491
492 [CATransaction setCompletionBlock:^{
493 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400494 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500495 [self cleanUp];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500496 if (CallModel::instance()->selectionModel()->currentIndex().isValid()) {
497 [self animateIn];
498 }
499 }];
500 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400501
502 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500503 [CATransaction commit];
504}
505
506/**
507 * Debug purpose
508 */
509-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
510{
511 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
512}
513
Alexandre Lision58cab672015-06-09 15:25:40 -0400514-(void)collapseRightView
515{
516 NSView *right = [[splitView subviews] objectAtIndex:1];
517 NSView *left = [[splitView subviews] objectAtIndex:0];
518 NSRect leftFrame = [left frame];
519 [right setHidden:YES];
520 [splitView display];
521}
Alexandre Lisionc5148052015-03-04 15:10:35 -0500522
Alexandre Lision58cab672015-06-09 15:25:40 -0400523-(void)uncollapseRightView
524{
525 NSView *left = [[splitView subviews] objectAtIndex:0];
526 NSView *right = [[splitView subviews] objectAtIndex:1];
527 [right setHidden:NO];
528
529 CGFloat dividerThickness = [splitView dividerThickness];
530
531 // get the different frames
532 NSRect leftFrame = [left frame];
533 NSRect rightFrame = [right frame];
534
535 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
536 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
537 [left setFrameSize:leftFrame.size];
538 [right setFrame:rightFrame];
539 [splitView display];
Alexandre Lision16d9c0a2015-08-10 12:05:15 -0400540
541 [self.chatVC takeFocus];
Alexandre Lision58cab672015-06-09 15:25:40 -0400542}
543
544
545#pragma mark - Button methods
546
Alexandre Lision2db8f472015-07-22 15:05:46 -0400547- (IBAction)addToContact:(NSButton*) sender {
548 auto contactmethod = CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex())->peerContactMethod();
549
550 if (self.addToContactPopover != nullptr) {
551 [self.addToContactPopover performClose:self];
552 self.addToContactPopover = NULL;
553 } else if (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder()) {
554 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
555 [editorVC setMethodToLink:contactmethod];
556 [editorVC setContactLinkedDelegate:self];
557 self.addToContactPopover = [[NSPopover alloc] init];
558 [self.addToContactPopover setContentSize:editorVC.view.frame.size];
559 [self.addToContactPopover setContentViewController:editorVC];
560 [self.addToContactPopover setAnimates:YES];
561 [self.addToContactPopover setBehavior:NSPopoverBehaviorTransient];
562 [self.addToContactPopover setDelegate:self];
563
564 [self.addToContactPopover showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMaxXEdge];
565 }
566}
567
Alexandre Lisionc5148052015-03-04 15:10:35 -0500568- (IBAction)hangUp:(id)sender {
569 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::REFUSE;
570}
571
572- (IBAction)accept:(id)sender {
573 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::ACCEPT;
574}
575
576- (IBAction)toggleRecording:(id)sender {
Alexandre Lision66643432015-06-04 11:59:36 -0400577 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::RECORD_AUDIO;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500578}
579
580- (IBAction)toggleHold:(id)sender {
581 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::HOLD;
582}
583
Alexandre Lision58cab672015-06-09 15:25:40 -0400584-(IBAction)toggleChat:(id)sender;
585{
586 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
587 if (rightViewCollapsed) {
588 [self uncollapseRightView];
589 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex())->addOutgoingMedia<Media::Text>();
590 } else {
591 [self collapseRightView];
592 }
593 [chatButton setState:rightViewCollapsed];
594}
595
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400596- (IBAction)muteAudio:(id)sender
597{
598 UserActionModel* uam = CallModel::instance()->userActionModel();
599 uam << UserActionModel::Action::MUTE_AUDIO;
600}
601
602- (IBAction)muteVideo:(id)sender
603{
604 UserActionModel* uam = CallModel::instance()->userActionModel();
605 uam << UserActionModel::Action::MUTE_VIDEO;
606}
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400607- (IBAction)displayQualityPopUp:(id)sender {
608 [self.qualityPopOver showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxXEdge];
609}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400610
Alexandre Lision2db8f472015-07-22 15:05:46 -0400611#pragma mark - NSPopOverDelegate
612
613- (void)popoverDidClose:(NSNotification *)notification
614{
615 if (self.addToContactPopover != nullptr) {
616 [self.addToContactPopover performClose:self];
617 self.addToContactPopover = NULL;
618 }
619}
620
621#pragma mark - ContactLinkedDelegate
622
623- (void)contactLinked
624{
625 if (self.addToContactPopover != nullptr) {
626 [self.addToContactPopover performClose:self];
627 self.addToContactPopover = NULL;
628 }
629}
630
Alexandre Lision58cab672015-06-09 15:25:40 -0400631#pragma mark - NSSplitViewDelegate
632
633/* 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.
634 */
635- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
636{
637 NSView* rightView = [[splitView subviews] objectAtIndex:1];
638 return ([subview isEqual:rightView]);
639}
640
641
642- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
643{
644 NSView* rightView = [[splitView subviews] objectAtIndex:1];
645 return ([subview isEqual:rightView]);
646}
647
648
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400649# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400650
651- (void) callShouldToggleFullScreen
652{
653 if(self.splitView.isInFullScreenMode)
654 [self.splitView exitFullScreenModeWithOptions:nil];
655 else {
656 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
657 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
658 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
659 NSFullScreenModeApplicationPresentationOptions, nil];
660
661 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
662 }
663}
664
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400665-(void) mouseIsMoving:(BOOL) move
666{
667 [[controlsPanel animator] setAlphaValue:move]; // fade out
668 [[headerContainer animator] setAlphaValue:move];
669}
670
Alexandre Lisionc5148052015-03-04 15:10:35 -0500671@end