blob: b6dc35e1d7aa1d96c35bc38cb6b13b721a075fed [file] [log] [blame]
Alexandre Lisionc5148052015-03-04 15:10:35 -05001/*
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04002 * Copyright (C) 2015 Savoir-faire Linux Inc.
Alexandre Lisionc5148052015-03-04 15:10:35 -05003 * 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 Lisionc5148052015-03-04 15:10:35 -050018 */
19#import "CurrentCallVC.h"
20
21#import <QuartzCore/QuartzCore.h>
22
23#import <call.h>
24#import <callmodel.h>
25#import <useractionmodel.h>
26#import <contactmethod.h>
27#import <qabstractitemmodel.h>
28#import <QItemSelectionModel>
29#import <QItemSelection>
Alexandre Lisionc5148052015-03-04 15:10:35 -050030#import <video/previewmanager.h>
31#import <video/renderer.h>
Alexandre Lision58cab672015-06-09 15:25:40 -040032#import <media/text.h>
Alexandre Lision2db8f472015-07-22 15:05:46 -040033#import <person.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050034
Alexandre Lisionf47a2562015-06-15 15:48:29 -040035#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040036#import "views/CallView.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040037#import "PersonLinkerVC.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040038#import "ChatVC.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040039
Alexandre Lisionc5148052015-03-04 15:10:35 -050040@interface RendererConnectionsHolder : NSObject
41
42@property QMetaObject::Connection frameUpdated;
43@property QMetaObject::Connection started;
44@property QMetaObject::Connection stopped;
45
46@end
47
48@implementation RendererConnectionsHolder
49
50@end
51
Alexandre Lision2db8f472015-07-22 15:05:46 -040052@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate>
Alexandre Lisionc5148052015-03-04 15:10:35 -050053
Alexandre Lision58cab672015-06-09 15:25:40 -040054@property (unsafe_unretained) IBOutlet NSTextField *personLabel;
55@property (unsafe_unretained) IBOutlet NSTextField *stateLabel;
56@property (unsafe_unretained) IBOutlet NSButton *holdOnOffButton;
57@property (unsafe_unretained) IBOutlet NSButton *hangUpButton;
58@property (unsafe_unretained) IBOutlet NSButton *recordOnOffButton;
59@property (unsafe_unretained) IBOutlet NSButton *pickUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040060@property (unsafe_unretained) IBOutlet NSButton *muteAudioButton;
61@property (unsafe_unretained) IBOutlet NSButton *muteVideoButton;
Alexandre Lision2db8f472015-07-22 15:05:46 -040062@property (unsafe_unretained) IBOutlet NSButton *addContactButton;
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -040063@property (unsafe_unretained) IBOutlet NSView *headerContainer;
Alexandre Lision266fca02015-09-28 14:47:05 -040064@property (unsafe_unretained) IBOutlet NSButton *qualityButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040065
Alexandre Lisionf47a2562015-06-15 15:48:29 -040066@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040067
Alexandre Lision58cab672015-06-09 15:25:40 -040068@property (unsafe_unretained) IBOutlet NSTextField *timeSpentLabel;
69@property (unsafe_unretained) IBOutlet NSView *controlsPanel;
70@property (unsafe_unretained) IBOutlet NSSplitView *splitView;
71@property (unsafe_unretained) IBOutlet NSButton *chatButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -050072
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -040073@property (strong) IBOutlet NSPopover *qualityPopOver;
Alexandre Lision2db8f472015-07-22 15:05:46 -040074@property (strong) NSPopover* addToContactPopover;
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040075@property (strong) IBOutlet ChatVC *chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -040076
Alexandre Lisionc5148052015-03-04 15:10:35 -050077@property QHash<int, NSButton*> actionHash;
78
79// Video
Alexandre Lision58cab672015-06-09 15:25:40 -040080@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lisionc5148052015-03-04 15:10:35 -050081@property CALayer* videoLayer;
Alexandre Lision58cab672015-06-09 15:25:40 -040082@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -050083@property CALayer* previewLayer;
84
85@property RendererConnectionsHolder* previewHolder;
86@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -040087@property QMetaObject::Connection videoStarted;
Alexandre Lisionb65c0272015-07-22 15:51:29 -040088@property QMetaObject::Connection messageConnection;
89@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -050090
91@end
92
93@implementation CurrentCallVC
Alexandre Lisiond18fa272015-06-15 11:18:03 -040094@synthesize personLabel, actionHash, stateLabel, holdOnOffButton, hangUpButton,
95 recordOnOffButton, pickUpButton, chatButton, timeSpentLabel,
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -040096 muteVideoButton, muteAudioButton, controlsPanel, headerContainer, videoView, videoLayer, previewLayer, previewView, splitView, loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -050097
98@synthesize previewHolder;
99@synthesize videoHolder;
100
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400101- (void) updateAllActions
Alexandre Lisionc5148052015-03-04 15:10:35 -0500102{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400103 for(int i = 0 ; i <= CallModel::instance().userActionModel()->rowCount() ; i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400104 [self updateActionAtIndex:i];
105 }
106}
107
108- (void) updateActionAtIndex:(int) row
109{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400110 const QModelIndex& idx = CallModel::instance().userActionModel()->index(row,0);
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400111 UserActionModel::Action action = qvariant_cast<UserActionModel::Action>(idx.data(UserActionModel::Role::ACTION));
112 NSButton* a = actionHash[(int) action];
Alexandre Lision266fca02015-09-28 14:47:05 -0400113 if (a) {
114 [a setHidden:!(idx.flags() & Qt::ItemIsEnabled)];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400115 [a setState:(idx.data(Qt::CheckStateRole) == Qt::Checked) ? NSOnState : NSOffState];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500116 }
117}
118
119-(void) updateCall
120{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400121 QModelIndex callIdx = CallModel::instance().selectionModel()->currentIndex();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400122 if (!callIdx.isValid()) {
123 return;
124 }
Alexandre Lision58cab672015-06-09 15:25:40 -0400125 [personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()];
126 [timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500127
Alexandre Lision2db8f472015-07-22 15:05:46 -0400128 auto contactmethod = qvariant_cast<Call*>(callIdx.data(static_cast<int>(Call::Role::Object)))->peerContactMethod();
129 BOOL shouldShow = (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder());
130 [self.addContactButton setHidden:!shouldShow];
131
Alexandre Lision58cab672015-06-09 15:25:40 -0400132 Call::State state = callIdx.data((int)Call::Role::State).value<Call::State>();
Alexandre Lision266fca02015-09-28 14:47:05 -0400133
134 // Default values for this views
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400135 [loadingIndicator setHidden:YES];
Alexandre Lision266fca02015-09-28 14:47:05 -0400136 [self.qualityButton setHidden:YES];
137 [self.chatButton setHidden:YES];
138 [videoView setShouldAcceptInteractions:NO];
139
140
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400141 [stateLabel setStringValue:callIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500142 switch (state) {
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400143 case Call::State::DIALING:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400144 [loadingIndicator setHidden:NO];
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400145 break;
146 case Call::State::NEW:
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400147 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500148 case Call::State::INITIALIZATION:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400149 [loadingIndicator setHidden:NO];
150 break;
151 case Call::State::CONNECTED:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400152 [loadingIndicator setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500153 break;
154 case Call::State::RINGING:
Alexandre Lisionc5148052015-03-04 15:10:35 -0500155 break;
156 case Call::State::CURRENT:
Alexandre Lision74dd47f2015-04-14 13:47:42 -0400157 [videoView setShouldAcceptInteractions:YES];
Alexandre Lision266fca02015-09-28 14:47:05 -0400158 [self.chatButton setHidden:NO];
159 [self.qualityButton setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500160 break;
161 case Call::State::HOLD:
Alexandre Lisionc5148052015-03-04 15:10:35 -0500162 break;
163 case Call::State::BUSY:
Alexandre Lisionc5148052015-03-04 15:10:35 -0500164 break;
165 case Call::State::OVER:
Alexandre Lision266fca02015-09-28 14:47:05 -0400166 case Call::State::FAILURE:
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400167 if(self.splitView.isInFullScreenMode)
168 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500169 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500170 }
171
172}
173
174- (void)awakeFromNib
175{
176 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400177 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500178 [self.view setLayer:[CALayer layer]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500179
180 [controlsPanel setWantsLayer:YES];
181 [controlsPanel setLayer:[CALayer layer]];
182 [controlsPanel.layer setZPosition:2.0];
183 [controlsPanel.layer setBackgroundColor:[NSColor whiteColor].CGColor];
184
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400185 actionHash[ (int)UserActionModel::Action::ACCEPT] = pickUpButton;
186 actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton;
187 actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton;
188 actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400189 actionHash[ (int)UserActionModel::Action::MUTE_AUDIO] = muteAudioButton;
190 actionHash[ (int)UserActionModel::Action::MUTE_VIDEO] = muteVideoButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500191
Alexandre Lisionc5148052015-03-04 15:10:35 -0500192 videoLayer = [CALayer layer];
193 [videoView setWantsLayer:YES];
194 [videoView setLayer:videoLayer];
195 [videoView.layer setBackgroundColor:[NSColor blackColor].CGColor];
196 [videoView.layer setFrame:videoView.frame];
197 [videoView.layer setContentsGravity:kCAGravityResizeAspect];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500198
199 previewLayer = [CALayer layer];
200 [previewView setWantsLayer:YES];
201 [previewView setLayer:previewLayer];
202 [previewLayer setBackgroundColor:[NSColor blackColor].CGColor];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400203 [previewLayer setContentsGravity:kCAGravityResizeAspectFill];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500204 [previewLayer setFrame:previewView.frame];
205
206 [controlsPanel setWantsLayer:YES];
207 [controlsPanel setLayer:[CALayer layer]];
208 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
209 [controlsPanel.layer setFrame:controlsPanel.frame];
210
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400211 previewHolder = [[RendererConnectionsHolder alloc] init];
212 videoHolder = [[RendererConnectionsHolder alloc] init];
213
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400214 [loadingIndicator setColor:[NSColor whiteColor]];
215 [loadingIndicator setNumberOfLines:100];
216 [loadingIndicator setWidthOfLine:2];
217 [loadingIndicator setLengthOfLine:2];
218 [loadingIndicator setInnerMargin:30];
219
Alexandre Lision266fca02015-09-28 14:47:05 -0400220 [self.qualityPopOver setDelegate:self];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400221 [self.videoView setCallDelegate:self];
Alexandre Lision58cab672015-06-09 15:25:40 -0400222
Alexandre Lisionc5148052015-03-04 15:10:35 -0500223 [self connect];
224}
225
226- (void) connect
227{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400228 QObject::connect(CallModel::instance().selectionModel(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500229 &QItemSelectionModel::currentChanged,
230 [=](const QModelIndex &current, const QModelIndex &previous) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500231 if(!current.isValid()) {
232 [self animateOut];
233 return;
234 }
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400235
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400236 auto call = CallModel::instance().getCall(current);
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400237 if (call->state() == Call::State::HOLD) {
238 call << Call::Action::HOLD;
239 }
240
Alexandre Lision58cab672015-06-09 15:25:40 -0400241 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500242 [self updateCall];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400243 [self updateAllActions];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500244 [self animateOut];
245 });
246
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400247 QObject::connect(CallModel::instance().userActionModel(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500248 &QAbstractItemModel::dataChanged,
249 [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500250 const int first(topLeft.row()),last(bottomRight.row());
251 for(int i = first; i <= last;i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400252 [self updateActionAtIndex:i];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500253 }
254 });
255
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400256 QObject::connect(&CallModel::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500257 &CallModel::callStateChanged,
258 [self](Call* c, Call::State state) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500259 [self updateCall];
260 });
261}
262
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400263- (void) monitorIncomingTextMessages:(Media::Text*) media
264{
265 /* connect to incoming chat messages to open the chat view */
266 QObject::disconnect(self.messageConnection);
267 self.messageConnection = QObject::connect(media,
268 &Media::Text::messageReceived,
269 [self] (const QMap<QString,QString>& m) {
270 if([[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]])
271 [self uncollapseRightView];
272 });
273}
274
Alexandre Lisionc5148052015-03-04 15:10:35 -0500275-(void) connectVideoSignals
276{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400277 QModelIndex idx = CallModel::instance().selectionModel()->currentIndex();
278 Call* call = CallModel::instance().getCall(idx);
Alexandre Lision58cab672015-06-09 15:25:40 -0400279 self.videoStarted = QObject::connect(call,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500280 &Call::videoStarted,
281 [=](Video::Renderer* renderer) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400282 NSLog(@"Video started!");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400283 [self connectVideoRenderer:renderer];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500284 });
285
286 if(call->videoRenderer())
287 {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500288 [self connectVideoRenderer:call->videoRenderer()];
289 }
290
291 [self connectPreviewRenderer];
292
293}
294
295-(void) connectPreviewRenderer
296{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400297 QObject::disconnect(previewHolder.frameUpdated);
298 QObject::disconnect(previewHolder.stopped);
299 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400300 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500301 &Video::PreviewManager::previewStarted,
302 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500303 QObject::disconnect(previewHolder.frameUpdated);
304 previewHolder.frameUpdated = QObject::connect(renderer,
305 &Video::Renderer::frameUpdated,
306 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400307 [self renderer:Video::PreviewManager::instance().previewRenderer()
Alexandre Lisionc5148052015-03-04 15:10:35 -0500308 renderFrameForView:previewView];
309 });
310 });
311
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400312 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500313 &Video::PreviewManager::previewStopped,
314 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500315 QObject::disconnect(previewHolder.frameUpdated);
316 [previewView.layer setContents:nil];
317 });
318
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400319 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500320 &Video::Renderer::frameUpdated,
321 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400322 [self renderer:Video::PreviewManager::instance().previewRenderer()
Alexandre Lisionc5148052015-03-04 15:10:35 -0500323 renderFrameForView:previewView];
324 });
325}
326
327-(void) connectVideoRenderer: (Video::Renderer*)renderer
328{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400329 QObject::disconnect(videoHolder.frameUpdated);
330 QObject::disconnect(videoHolder.started);
331 QObject::disconnect(videoHolder.stopped);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500332 videoHolder.frameUpdated = QObject::connect(renderer,
333 &Video::Renderer::frameUpdated,
334 [=]() {
335 [self renderer:renderer renderFrameForView:videoView];
336 });
337
338 videoHolder.started = QObject::connect(renderer,
339 &Video::Renderer::started,
340 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500341 QObject::disconnect(videoHolder.frameUpdated);
342 videoHolder.frameUpdated = QObject::connect(renderer,
343 &Video::Renderer::frameUpdated,
344 [=]() {
345 [self renderer:renderer renderFrameForView:videoView];
346 });
347 });
348
349 videoHolder.stopped = QObject::connect(renderer,
350 &Video::Renderer::stopped,
351 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500352 QObject::disconnect(videoHolder.frameUpdated);
353 [videoView.layer setContents:nil];
354 });
355}
356
357-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
358{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500359 QSize res = renderer->size();
360
Alexandre Lision6731e132015-10-14 14:29:06 -0400361 auto frame_ptr = renderer->currentFrame();
362 auto frame_data = frame_ptr.ptr;
363 if (!frame_data)
364 return;
365
Alexandre Lisionc5148052015-03-04 15:10:35 -0500366
367 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400368 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500369 res.width(),
370 res.height(),
371 8,
372 4*res.width(),
373 colorSpace,
374 kCGImageAlphaPremultipliedLast);
375
376
377 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
378
379 /*We release some components*/
380 CGContextRelease(newContext);
381 CGColorSpaceRelease(colorSpace);
382
383 [CATransaction begin];
384 view.layer.contents = (__bridge id)newImage;
385 [CATransaction commit];
386
387 CFRelease(newImage);
388}
389
390- (void) initFrame
391{
392 [self.view setFrame:self.view.superview.bounds];
393 [self.view setHidden:YES];
394 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400395 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500396}
397
398# pragma private IN/OUT animations
399
400-(void) animateIn
401{
402 NSLog(@"animateIn");
403 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
404 [self.view setHidden:NO];
405
406 [CATransaction begin];
407 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
408 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
409 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
410 [animation setDuration:0.2f];
411 [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
412 [CATransaction setCompletionBlock:^{
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400413
414 // when call comes in we want to show the controls/header
415 [self mouseIsMoving:YES];
416
Alexandre Lisionc5148052015-03-04 15:10:35 -0500417 [self connectVideoSignals];
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400418 /* check if text media is already present */
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400419 if(!CallModel::instance().selectedCall())
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400420 return;
Alexandre Lision21666f32015-09-22 17:04:36 -0400421
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400422 QObject::connect(CallModel::instance().selectedCall(),
Alexandre Lision21666f32015-09-22 17:04:36 -0400423 &Call::changed,
424 [=]() {
425 [self updateCall];
426 });
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400427 if (CallModel::instance().selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) {
428 Media::Text *text = CallModel::instance().selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::IN);
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400429 [self monitorIncomingTextMessages:text];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400430 } else if (CallModel::instance().selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) {
431 Media::Text *text = CallModel::instance().selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::OUT);
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400432 [self monitorIncomingTextMessages:text];
433 } else {
434 /* monitor media for messaging text messaging */
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400435 self.mediaAddedConnection = QObject::connect(CallModel::instance().selectedCall(),
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400436 &Call::mediaAdded,
437 [self] (Media::Media* media) {
438 if (media->type() == Media::Media::Type::TEXT) { [self monitorIncomingTextMessages:(Media::Text*)media];
439 QObject::disconnect(self.mediaAddedConnection);
440 }
441 });
442 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500443 }];
444 [self.view.layer addAnimation:animation forKey:animation.keyPath];
445
446 [CATransaction commit];
447}
448
449-(void) cleanUp
450{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400451 QObject::disconnect(videoHolder.frameUpdated);
452 QObject::disconnect(videoHolder.started);
453 QObject::disconnect(videoHolder.stopped);
454 QObject::disconnect(previewHolder.frameUpdated);
455 QObject::disconnect(previewHolder.stopped);
456 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500457 [videoView.layer setContents:nil];
458 [previewView.layer setContents:nil];
459}
460
461-(void) animateOut
462{
463 NSLog(@"animateOut");
464 if(self.view.frame.origin.x < 0) {
465 NSLog(@"Already hidden");
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400466 if (CallModel::instance().selectionModel()->currentIndex().isValid()) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500467 [self animateIn];
468 }
469 return;
470 }
471
472 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
473 [CATransaction begin];
474 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
475 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
476 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
477 [animation setDuration:0.2f];
478 [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
479
480 [CATransaction setCompletionBlock:^{
481 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400482 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500483 [self cleanUp];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400484 if (CallModel::instance().selectionModel()->currentIndex().isValid()) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500485 [self animateIn];
486 }
487 }];
488 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400489
490 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500491 [CATransaction commit];
492}
493
494/**
495 * Debug purpose
496 */
497-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
498{
499 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
500}
501
Alexandre Lision58cab672015-06-09 15:25:40 -0400502-(void)collapseRightView
503{
504 NSView *right = [[splitView subviews] objectAtIndex:1];
505 NSView *left = [[splitView subviews] objectAtIndex:0];
506 NSRect leftFrame = [left frame];
507 [right setHidden:YES];
508 [splitView display];
509}
Alexandre Lisionc5148052015-03-04 15:10:35 -0500510
Alexandre Lision58cab672015-06-09 15:25:40 -0400511-(void)uncollapseRightView
512{
513 NSView *left = [[splitView subviews] objectAtIndex:0];
514 NSView *right = [[splitView subviews] objectAtIndex:1];
515 [right setHidden:NO];
516
517 CGFloat dividerThickness = [splitView dividerThickness];
518
519 // get the different frames
520 NSRect leftFrame = [left frame];
521 NSRect rightFrame = [right frame];
522
523 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
524 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
525 [left setFrameSize:leftFrame.size];
526 [right setFrame:rightFrame];
527 [splitView display];
Alexandre Lision16d9c0a2015-08-10 12:05:15 -0400528
529 [self.chatVC takeFocus];
Alexandre Lision58cab672015-06-09 15:25:40 -0400530}
531
532
533#pragma mark - Button methods
534
Alexandre Lision2db8f472015-07-22 15:05:46 -0400535- (IBAction)addToContact:(NSButton*) sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400536 auto contactmethod = CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex())->peerContactMethod();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400537
538 if (self.addToContactPopover != nullptr) {
539 [self.addToContactPopover performClose:self];
540 self.addToContactPopover = NULL;
Alexandre Lision266fca02015-09-28 14:47:05 -0400541 [self.addContactButton setState:NSOffState];
542
Alexandre Lision2db8f472015-07-22 15:05:46 -0400543 } else if (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder()) {
544 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
545 [editorVC setMethodToLink:contactmethod];
546 [editorVC setContactLinkedDelegate:self];
547 self.addToContactPopover = [[NSPopover alloc] init];
548 [self.addToContactPopover setContentSize:editorVC.view.frame.size];
549 [self.addToContactPopover setContentViewController:editorVC];
550 [self.addToContactPopover setAnimates:YES];
551 [self.addToContactPopover setBehavior:NSPopoverBehaviorTransient];
552 [self.addToContactPopover setDelegate:self];
553
554 [self.addToContactPopover showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMaxXEdge];
555 }
556}
557
Alexandre Lisionc5148052015-03-04 15:10:35 -0500558- (IBAction)hangUp:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400559 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::REFUSE;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500560}
561
562- (IBAction)accept:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400563 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::ACCEPT;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500564}
565
566- (IBAction)toggleRecording:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400567 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::RECORD_AUDIO;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500568}
569
570- (IBAction)toggleHold:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400571 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::HOLD;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500572}
573
Alexandre Lision58cab672015-06-09 15:25:40 -0400574-(IBAction)toggleChat:(id)sender;
575{
576 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
577 if (rightViewCollapsed) {
578 [self uncollapseRightView];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400579 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex())->addOutgoingMedia<Media::Text>();
Alexandre Lision58cab672015-06-09 15:25:40 -0400580 } else {
581 [self collapseRightView];
582 }
583 [chatButton setState:rightViewCollapsed];
584}
585
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400586- (IBAction)muteAudio:(id)sender
587{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400588 UserActionModel* uam = CallModel::instance().userActionModel();
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400589 uam << UserActionModel::Action::MUTE_AUDIO;
590}
591
592- (IBAction)muteVideo:(id)sender
593{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400594 UserActionModel* uam = CallModel::instance().userActionModel();
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400595 uam << UserActionModel::Action::MUTE_VIDEO;
596}
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400597- (IBAction)displayQualityPopUp:(id)sender {
Alexandre Lision266fca02015-09-28 14:47:05 -0400598
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400599 [self.qualityPopOver showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxXEdge];
600}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400601
Alexandre Lision2db8f472015-07-22 15:05:46 -0400602#pragma mark - NSPopOverDelegate
603
Alexandre Lision266fca02015-09-28 14:47:05 -0400604- (void)popoverWillClose:(NSNotification *)notification
605{
606 [self.qualityButton setState:NSOffState];
607 [self.addContactButton setState:NSOffState];
608}
609
Alexandre Lision2db8f472015-07-22 15:05:46 -0400610- (void)popoverDidClose:(NSNotification *)notification
611{
612 if (self.addToContactPopover != nullptr) {
613 [self.addToContactPopover performClose:self];
614 self.addToContactPopover = NULL;
615 }
616}
617
618#pragma mark - ContactLinkedDelegate
619
620- (void)contactLinked
621{
622 if (self.addToContactPopover != nullptr) {
623 [self.addToContactPopover performClose:self];
624 self.addToContactPopover = NULL;
625 }
626}
627
Alexandre Lision58cab672015-06-09 15:25:40 -0400628#pragma mark - NSSplitViewDelegate
629
630/* 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.
631 */
632- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
633{
634 NSView* rightView = [[splitView subviews] objectAtIndex:1];
635 return ([subview isEqual:rightView]);
636}
637
638
639- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
640{
641 NSView* rightView = [[splitView subviews] objectAtIndex:1];
642 return ([subview isEqual:rightView]);
643}
644
645
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400646# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400647
648- (void) callShouldToggleFullScreen
649{
650 if(self.splitView.isInFullScreenMode)
651 [self.splitView exitFullScreenModeWithOptions:nil];
652 else {
653 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
654 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
655 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
656 NSFullScreenModeApplicationPresentationOptions, nil];
657
658 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
659 }
660}
661
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400662-(void) mouseIsMoving:(BOOL) move
663{
664 [[controlsPanel animator] setAlphaValue:move]; // fade out
665 [[headerContainer animator] setAlphaValue:move];
666}
667
Alexandre Lisionc5148052015-03-04 15:10:35 -0500668@end