blob: ba42a20cdedd08ede20cf220ca4849effd1262a7 [file] [log] [blame]
Alexandre Lisionc5148052015-03-04 15:10:35 -05001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 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
Alexandre Lision210fe212016-01-27 11:15:13 -050023///Qt
24#import <QMimeData>
25#import <QtMacExtras/qmacfunctions.h>
26#import <QtCore/qabstractitemmodel.h>
27#import <QItemSelectionModel>
28#import <QItemSelection>
29#import <QPixmap>
30
31///LRC
Alexandre Lisionc5148052015-03-04 15:10:35 -050032#import <call.h>
33#import <callmodel.h>
Alexandre Lision89edc6a2015-11-09 11:30:47 -050034#import <recentmodel.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050035#import <useractionmodel.h>
36#import <contactmethod.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050037#import <video/previewmanager.h>
38#import <video/renderer.h>
Alexandre Lision58cab672015-06-09 15:25:40 -040039#import <media/text.h>
Alexandre Lision2db8f472015-07-22 15:05:46 -040040#import <person.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050041#import <globalinstances.h>
Anthony Léonard60970852017-07-21 11:33:41 -040042#import <personmodel.h>
43#import <peerprofilecollection.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050044
Alexandre Lision62005312016-01-28 15:55:16 -050045#import "AppDelegate.h"
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 Lision210fe212016-01-27 11:15:13 -050048#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040049#import "PersonLinkerVC.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040050#import "ChatVC.h"
Alexandre Lision883719f2015-10-22 17:37:45 -040051#import "BrokerVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050052#import "views/IconButton.h"
Anthony Léonard14e7bf32017-06-08 08:13:16 -040053#import "views/CallLayer.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040054
Alexandre Lisionc5148052015-03-04 15:10:35 -050055@interface RendererConnectionsHolder : NSObject
56
57@property QMetaObject::Connection frameUpdated;
58@property QMetaObject::Connection started;
59@property QMetaObject::Connection stopped;
60
61@end
62
63@implementation RendererConnectionsHolder
64
65@end
66
Alexandre Lision2db8f472015-07-22 15:05:46 -040067@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate>
Alexandre Lisionc5148052015-03-04 15:10:35 -050068
Alexandre Lision210fe212016-01-27 11:15:13 -050069// Main container
70@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
71
Alexandre Lision89edc6a2015-11-09 11:30:47 -050072// Header info
73@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Alexandre Lision883719f2015-10-22 17:37:45 -040074@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
75@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050076@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
Alexandre Lision210fe212016-01-27 11:15:13 -050077@property (unsafe_unretained) IBOutlet NSImageView* personPhoto;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050078
79// Call Controls
80@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
Alexandre Lision62005312016-01-28 15:55:16 -050081
Alexandre Lision4baba4c2016-02-11 13:00:57 -050082@property QHash<int, IconButton*> actionHash;
83@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
84@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
85@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
86@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
87@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
88@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
89@property (unsafe_unretained) IBOutlet IconButton* addContactButton;
90@property (unsafe_unretained) IBOutlet IconButton* transferButton;
91@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
92@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040093
Alexandre Lision62005312016-01-28 15:55:16 -050094@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
95@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
96
Alexandre Lisiond18fa272015-06-15 11:18:03 -040097
Alexandre Lision89edc6a2015-11-09 11:30:47 -050098// Join call panel
99@property (unsafe_unretained) IBOutlet NSView* joinPanel;
100@property (unsafe_unretained) IBOutlet NSButton* mergeCallsButton;
101
Alexandre Lision2db8f472015-07-22 15:05:46 -0400102@property (strong) NSPopover* addToContactPopover;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500103@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision883719f2015-10-22 17:37:45 -0400104@property (strong) IBOutlet ChatVC* chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400105
Alexandre Lision210fe212016-01-27 11:15:13 -0500106// Ringing call panel
107@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
108@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
109@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;
110
111// Outgoing call panel
112@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
113@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500114
115// Video
Alexandre Lision58cab672015-06-09 15:25:40 -0400116@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lision58cab672015-06-09 15:25:40 -0400117@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500118
119@property RendererConnectionsHolder* previewHolder;
120@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400121@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500122@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400123@property QMetaObject::Connection messageConnection;
124@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500125
126@end
127
128@implementation CurrentCallVC
Alexandre Lision210fe212016-01-27 11:15:13 -0500129@synthesize personLabel, personPhoto, actionHash, stateLabel, holdOnOffButton, hangUpButton,
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500130 recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
Alexandre Lision62005312016-01-28 15:55:16 -0500131 muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView, incomingDisplayName, incomingPersonPhoto,
Alexandre Lision210fe212016-01-27 11:15:13 -0500132 previewView, splitView, loadingIndicator, ringingPanel, joinPanel, outgoingPanel;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500133
134@synthesize previewHolder;
135@synthesize videoHolder;
136
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400137- (void) updateAllActions
Alexandre Lisionc5148052015-03-04 15:10:35 -0500138{
Alexandre Lisionf7e11142015-12-15 10:58:11 -0500139 for (int i = 0 ; i < CallModel::instance().userActionModel()->rowCount() ; i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400140 [self updateActionAtIndex:i];
141 }
142}
143
144- (void) updateActionAtIndex:(int) row
145{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400146 const QModelIndex& idx = CallModel::instance().userActionModel()->index(row,0);
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400147 UserActionModel::Action action = qvariant_cast<UserActionModel::Action>(idx.data(UserActionModel::Role::ACTION));
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500148 if (auto a = actionHash[(int) action]) {
Alexandre Lision266fca02015-09-28 14:47:05 -0400149 [a setHidden:!(idx.flags() & Qt::ItemIsEnabled)];
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500150 [a setPressed:(idx.data(Qt::CheckStateRole) == Qt::Checked) ? YES : NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500151 }
152}
153
Alexandre Lision210fe212016-01-27 11:15:13 -0500154-(void) updateCall:(BOOL) firstRun
Alexandre Lisionc5148052015-03-04 15:10:35 -0500155{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400156 QModelIndex callIdx = CallModel::instance().selectionModel()->currentIndex();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400157 if (!callIdx.isValid()) {
158 return;
159 }
Alexandre Lision210fe212016-01-27 11:15:13 -0500160 auto current = CallModel::instance().selectedCall();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500161
Alexandre Lision58cab672015-06-09 15:25:40 -0400162 [personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()];
163 [timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()];
Alexandre Lision57227772016-01-15 17:19:36 -0500164 [stateLabel setStringValue:callIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500165
Alexandre Lision210fe212016-01-27 11:15:13 -0500166 if (firstRun) {
Alexandre Lision261f1b92016-04-04 12:35:34 -0400167 QVariant photo = GlobalInstances::pixmapManipulator().callPhoto(current, QSize(100,100));
Alexandre Lision210fe212016-01-27 11:15:13 -0500168 [personPhoto setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
169 }
170
Alexandre Lision2db8f472015-07-22 15:05:46 -0400171 auto contactmethod = qvariant_cast<Call*>(callIdx.data(static_cast<int>(Call::Role::Object)))->peerContactMethod();
172 BOOL shouldShow = (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder());
173 [self.addContactButton setHidden:!shouldShow];
174
Alexandre Lision266fca02015-09-28 14:47:05 -0400175 // Default values for this views
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400176 [loadingIndicator setHidden:YES];
Alexandre Lision266fca02015-09-28 14:47:05 -0400177 [videoView setShouldAcceptInteractions:NO];
Alexandre Lision210fe212016-01-27 11:15:13 -0500178 [ringingPanel setHidden:YES];
179 [outgoingPanel setHidden:YES];
180 [controlsPanel setHidden:NO];
181 [headerContainer setHidden:NO];
Alexandre Lision266fca02015-09-28 14:47:05 -0400182
Alexandre Lision210fe212016-01-27 11:15:13 -0500183 auto state = callIdx.data((int)Call::Role::State).value<Call::State>();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500184 switch (state) {
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400185 case Call::State::NEW:
Alexandre Lisione6dbf092015-04-11 17:19:35 -0400186 break;
Alexandre Lision22d615b2016-01-25 12:54:34 -0500187 case Call::State::DIALING:
Alexandre Lisionc5148052015-03-04 15:10:35 -0500188 case Call::State::INITIALIZATION:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400189 case Call::State::CONNECTED:
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400190 [loadingIndicator setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500191 case Call::State::RINGING:
Alexandre Lision210fe212016-01-27 11:15:13 -0500192 [controlsPanel setHidden:YES];
193 [outgoingPanel setHidden:NO];
194 break;
195 case Call::State::INCOMING:
196 [self setupIncoming:current];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500197 break;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500198 case Call::State::CONFERENCE:
Alexandre Lision210fe212016-01-27 11:15:13 -0500199 [self setupConference:current];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500200 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500201 case Call::State::CURRENT:
Alexandre Lision210fe212016-01-27 11:15:13 -0500202 [self setupCurrent:current];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500203 break;
204 case Call::State::HOLD:
Alexandre Lisionc5148052015-03-04 15:10:35 -0500205 break;
206 case Call::State::BUSY:
Alexandre Lisionc5148052015-03-04 15:10:35 -0500207 break;
208 case Call::State::OVER:
Alexandre Lision266fca02015-09-28 14:47:05 -0400209 case Call::State::FAILURE:
Alexandre Lision210fe212016-01-27 11:15:13 -0500210 [controlsPanel setHidden:YES];
211 [outgoingPanel setHidden:NO];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400212 if(self.splitView.isInFullScreenMode)
213 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500214 break;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500215 }
216
217}
218
Alexandre Lision210fe212016-01-27 11:15:13 -0500219-(void) setupIncoming:(Call*) c
220{
221 [ringingPanel setHidden:NO];
222 [controlsPanel setHidden:YES];
223 [headerContainer setHidden:YES];
224 QVariant photo = GlobalInstances::pixmapManipulator().callPhoto(c, QSize(100,100));
225 [incomingPersonPhoto setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
226 [incomingDisplayName setStringValue:c->formattedName().toNSString()];
227}
228
229-(void) setupCurrent:(Call*) c
230{
231 [joinPanel setHidden:!c->hasParentCall()];
232 [controlsPanel setHidden:c->hasParentCall()];
233 [videoView setShouldAcceptInteractions:YES];
234 [self.chatButton setHidden:NO];
235 [self.addParticipantButton setHidden:NO];
236 [self.transferButton setHidden:NO];
237}
238
239-(void) setupConference:(Call*) c
240{
241 [videoView setShouldAcceptInteractions:YES];
242 [self.chatButton setHidden:NO];
243 [joinPanel setHidden:YES];
244 [self.addParticipantButton setHidden:NO];
245 [self.transferButton setHidden:YES];
246}
247
248
Alexandre Lisionc5148052015-03-04 15:10:35 -0500249- (void)awakeFromNib
250{
251 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400252 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500253
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400254 actionHash[ (int)UserActionModel::Action::ACCEPT] = pickUpButton;
255 actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton;
256 actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton;
257 actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400258 actionHash[ (int)UserActionModel::Action::MUTE_AUDIO] = muteAudioButton;
259 actionHash[ (int)UserActionModel::Action::MUTE_VIDEO] = muteVideoButton;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500260
Alexandre Lisionc5148052015-03-04 15:10:35 -0500261 [previewView setWantsLayer:YES];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500262 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
263 [previewView.layer setContentsGravity:kCAGravityResizeAspectFill];
264 [previewView.layer setFrame:previewView.frame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500265
266 [controlsPanel setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500267 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
268 [controlsPanel.layer setFrame:controlsPanel.frame];
269
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400270 previewHolder = [[RendererConnectionsHolder alloc] init];
271 videoHolder = [[RendererConnectionsHolder alloc] init];
272
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400273 [loadingIndicator setColor:[NSColor whiteColor]];
274 [loadingIndicator setNumberOfLines:100];
275 [loadingIndicator setWidthOfLine:2];
276 [loadingIndicator setLengthOfLine:2];
277 [loadingIndicator setInnerMargin:30];
278
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400279 [self.videoView setCallDelegate:self];
Alexandre Lision58cab672015-06-09 15:25:40 -0400280
Alexandre Lisionc5148052015-03-04 15:10:35 -0500281 [self connect];
282}
283
284- (void) connect
285{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500286 QObject::connect(RecentModel::instance().selectionModel(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500287 &QItemSelectionModel::currentChanged,
288 [=](const QModelIndex &current, const QModelIndex &previous) {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500289 auto call = RecentModel::instance().getActiveCall(current);
290 if(!current.isValid() || !call) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500291 return;
292 }
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400293
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500294 [self changeCallSelection:call];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500295
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400296 if (call->state() == Call::State::HOLD) {
297 call << Call::Action::HOLD;
298 }
299
Alexandre Lision58cab672015-06-09 15:25:40 -0400300 [self collapseRightView];
Alexandre Lision210fe212016-01-27 11:15:13 -0500301 [self updateCall:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400302 [self updateAllActions];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500303 });
304
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400305 QObject::connect(CallModel::instance().userActionModel(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500306 &QAbstractItemModel::dataChanged,
307 [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500308 const int first(topLeft.row()),last(bottomRight.row());
309 for(int i = first; i <= last;i++) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400310 [self updateActionAtIndex:i];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500311 }
312 });
313
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400314 QObject::connect(&CallModel::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500315 &CallModel::callStateChanged,
316 [self](Call* c, Call::State state) {
Alexandre Lision57227772016-01-15 17:19:36 -0500317 auto current = CallModel::instance().selectionModel()->currentIndex();
318 if (!current.isValid())
319 [self animateOut];
320 else if (CallModel::instance().getIndex(c) == current) {
321 if (c->state() == Call::State::OVER) {
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500322 RecentModel::instance().selectionModel()->clearCurrentIndex();
Alexandre Lision57227772016-01-15 17:19:36 -0500323 } else {
Alexandre Lision210fe212016-01-27 11:15:13 -0500324 [self updateCall:NO];
Alexandre Lision57227772016-01-15 17:19:36 -0500325 }
326 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500327 });
328
329 QObject::connect(&CallModel::instance(),
330 &CallModel::incomingCall,
331 [self](Call* c) {
332 [self changeCallSelection:c];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500333 });
334}
335
336- (void) changeCallSelection:(Call* )c
337{
338 QObject::disconnect(self.selectedCallChanged);
339 CallModel::instance().selectCall(c);
340 self.selectedCallChanged = QObject::connect(CallModel::instance().selectedCall(),
341 &Call::changed,
342 [=]() {
Alexandre Lision210fe212016-01-27 11:15:13 -0500343 [self updateCall:NO];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500344 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500345}
346
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400347- (void) monitorIncomingTextMessages:(Media::Text*) media
348{
349 /* connect to incoming chat messages to open the chat view */
350 QObject::disconnect(self.messageConnection);
351 self.messageConnection = QObject::connect(media,
352 &Media::Text::messageReceived,
353 [self] (const QMap<QString,QString>& m) {
354 if([[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]])
355 [self uncollapseRightView];
356 });
357}
358
Alexandre Lisionc5148052015-03-04 15:10:35 -0500359-(void) connectVideoSignals
360{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400361 QModelIndex idx = CallModel::instance().selectionModel()->currentIndex();
362 Call* call = CallModel::instance().getCall(idx);
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500363 QObject::disconnect(self.videoStarted);
Alexandre Lision58cab672015-06-09 15:25:40 -0400364 self.videoStarted = QObject::connect(call,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500365 &Call::videoStarted,
366 [=](Video::Renderer* renderer) {
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400367 NSLog(@"Video started!");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400368 [self connectVideoRenderer:renderer];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500369 });
370
371 if(call->videoRenderer())
372 {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500373 [self connectVideoRenderer:call->videoRenderer()];
374 }
375
376 [self connectPreviewRenderer];
377
378}
379
380-(void) connectPreviewRenderer
381{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400382 QObject::disconnect(previewHolder.frameUpdated);
383 QObject::disconnect(previewHolder.stopped);
384 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400385 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500386 &Video::PreviewManager::previewStarted,
387 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500388 QObject::disconnect(previewHolder.frameUpdated);
389 previewHolder.frameUpdated = QObject::connect(renderer,
390 &Video::Renderer::frameUpdated,
391 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400392 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400393 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500394 });
395 });
396
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400397 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500398 &Video::PreviewManager::previewStopped,
399 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500400 QObject::disconnect(previewHolder.frameUpdated);
401 [previewView.layer setContents:nil];
402 });
403
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400404 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500405 &Video::Renderer::frameUpdated,
406 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400407 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400408 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500409 });
410}
411
412-(void) connectVideoRenderer: (Video::Renderer*)renderer
413{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400414 QObject::disconnect(videoHolder.frameUpdated);
415 QObject::disconnect(videoHolder.started);
416 QObject::disconnect(videoHolder.stopped);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500417 videoHolder.frameUpdated = QObject::connect(renderer,
418 &Video::Renderer::frameUpdated,
419 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400420 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500421 });
422
423 videoHolder.started = QObject::connect(renderer,
424 &Video::Renderer::started,
425 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500426 QObject::disconnect(videoHolder.frameUpdated);
427 videoHolder.frameUpdated = QObject::connect(renderer,
428 &Video::Renderer::frameUpdated,
429 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400430 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500431 });
432 });
433
434 videoHolder.stopped = QObject::connect(renderer,
435 &Video::Renderer::stopped,
436 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500437 QObject::disconnect(videoHolder.frameUpdated);
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400438 [(CallLayer*)videoView.layer setVideoRunning:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500439 });
440}
441
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400442-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(NSView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500443{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500444 QSize res = renderer->size();
445
Alexandre Lision6731e132015-10-14 14:29:06 -0400446 auto frame_ptr = renderer->currentFrame();
447 auto frame_data = frame_ptr.ptr;
448 if (!frame_data)
449 return;
450
Alexandre Lisionc5148052015-03-04 15:10:35 -0500451 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400452 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500453 res.width(),
454 res.height(),
455 8,
456 4*res.width(),
457 colorSpace,
458 kCGImageAlphaPremultipliedLast);
459
460
461 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
462
463 /*We release some components*/
464 CGContextRelease(newContext);
465 CGColorSpaceRelease(colorSpace);
466
467 [CATransaction begin];
468 view.layer.contents = (__bridge id)newImage;
469 [CATransaction commit];
470
471 CFRelease(newImage);
472}
473
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400474-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallView*) view
475{
476 QSize res = renderer->size();
477
478 auto frame_ptr = renderer->currentFrame();
479 if (!frame_ptr.ptr)
480 return;
481
482 CallLayer* callLayer = (CallLayer*) view.layer;
483
484 [callLayer setCurrentFrame:std::move(frame_ptr) ofSize:res];
485 [callLayer setVideoRunning:YES];
486}
487
Alexandre Lisionc5148052015-03-04 15:10:35 -0500488- (void) initFrame
489{
490 [self.view setFrame:self.view.superview.bounds];
491 [self.view setHidden:YES];
492 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400493 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500494}
495
496# pragma private IN/OUT animations
497
498-(void) animateIn
499{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500500 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
501 [self.view setHidden:NO];
502
503 [CATransaction begin];
504 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
505 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
506 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
507 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500508 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500509 [CATransaction setCompletionBlock:^{
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400510
511 // when call comes in we want to show the controls/header
512 [self mouseIsMoving:YES];
513
Alexandre Lisionc5148052015-03-04 15:10:35 -0500514 [self connectVideoSignals];
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400515 /* check if text media is already present */
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400516 if(!CallModel::instance().selectedCall())
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400517 return;
Alexandre Lision21666f32015-09-22 17:04:36 -0400518
Alexandre Lision22d615b2016-01-25 12:54:34 -0500519 [loadingIndicator setAnimates:YES];
Alexandre Lision210fe212016-01-27 11:15:13 -0500520 [self updateCall:YES];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500521
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400522 if (CallModel::instance().selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) {
523 Media::Text *text = CallModel::instance().selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::IN);
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400524 [self monitorIncomingTextMessages:text];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400525 } else if (CallModel::instance().selectedCall()->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) {
526 Media::Text *text = CallModel::instance().selectedCall()->firstMedia<Media::Text>(Media::Media::Direction::OUT);
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400527 [self monitorIncomingTextMessages:text];
528 } else {
529 /* monitor media for messaging text messaging */
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400530 self.mediaAddedConnection = QObject::connect(CallModel::instance().selectedCall(),
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400531 &Call::mediaAdded,
532 [self] (Media::Media* media) {
533 if (media->type() == Media::Media::Type::TEXT) { [self monitorIncomingTextMessages:(Media::Text*)media];
534 QObject::disconnect(self.mediaAddedConnection);
535 }
536 });
537 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500538 }];
539 [self.view.layer addAnimation:animation forKey:animation.keyPath];
540
541 [CATransaction commit];
542}
543
544-(void) cleanUp
545{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400546 QObject::disconnect(videoHolder.frameUpdated);
547 QObject::disconnect(videoHolder.started);
548 QObject::disconnect(videoHolder.stopped);
549 QObject::disconnect(previewHolder.frameUpdated);
550 QObject::disconnect(previewHolder.stopped);
551 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500552 [previewView.layer setContents:nil];
Alexandre Lision883719f2015-10-22 17:37:45 -0400553
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500554 [_brokerPopoverVC performClose:self];
Alexandre Lision883719f2015-10-22 17:37:45 -0400555 [self.addToContactPopover performClose:self];
Alexandre Lision0e66aea2015-11-02 16:36:30 -0500556
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500557 [self.chatButton setHidden:YES];
558 [self.addParticipantButton setHidden:YES];
559 [self.transferButton setHidden:YES];
560
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500561 [self.chatButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500562 [self.mergeCallsButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400563 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500564
565 [personLabel setStringValue:@""];
566 [timeSpentLabel setStringValue:@""];
567 [stateLabel setStringValue:@""];
568 [self.addContactButton setHidden:YES];
Alexandre Lision62005312016-01-28 15:55:16 -0500569
570 [advancedButton setPressed:NO];
571 [advancedPanel setHidden:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500572}
573
574-(void) animateOut
575{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500576 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500577 return;
578 }
579
580 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
581 [CATransaction begin];
582 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
583 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
584 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
585 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500586 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500587
588 [CATransaction setCompletionBlock:^{
589 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400590 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500591 [self cleanUp];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500592 if (RecentModel::instance().getActiveCall(RecentModel::instance().selectionModel()->currentIndex())) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500593 [self animateIn];
594 }
595 }];
596 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400597
598 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500599 [CATransaction commit];
600}
601
602/**
603 * Debug purpose
604 */
605-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
606{
607 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
608}
609
Alexandre Lision58cab672015-06-09 15:25:40 -0400610-(void)collapseRightView
611{
612 NSView *right = [[splitView subviews] objectAtIndex:1];
613 NSView *left = [[splitView subviews] objectAtIndex:0];
614 NSRect leftFrame = [left frame];
615 [right setHidden:YES];
616 [splitView display];
617}
Alexandre Lisionc5148052015-03-04 15:10:35 -0500618
Alexandre Lision58cab672015-06-09 15:25:40 -0400619-(void)uncollapseRightView
620{
621 NSView *left = [[splitView subviews] objectAtIndex:0];
622 NSView *right = [[splitView subviews] objectAtIndex:1];
623 [right setHidden:NO];
624
625 CGFloat dividerThickness = [splitView dividerThickness];
626
627 // get the different frames
628 NSRect leftFrame = [left frame];
629 NSRect rightFrame = [right frame];
630
631 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
632 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
633 [left setFrameSize:leftFrame.size];
634 [right setFrame:rightFrame];
635 [splitView display];
Alexandre Lision16d9c0a2015-08-10 12:05:15 -0400636
637 [self.chatVC takeFocus];
Alexandre Lision58cab672015-06-09 15:25:40 -0400638}
639
640
641#pragma mark - Button methods
642
Alexandre Lision2db8f472015-07-22 15:05:46 -0400643- (IBAction)addToContact:(NSButton*) sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400644 auto contactmethod = CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex())->peerContactMethod();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400645
Anthony Léonard60970852017-07-21 11:33:41 -0400646 // TODO: Uncomment to reuse contact name editing popover
647// if (self.addToContactPopover != nullptr) {
648// [self.addToContactPopover performClose:self];
649// self.addToContactPopover = NULL;
650// } else if (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder()) {
651// auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
652// [editorVC setMethodToLink:contactmethod];
653// [editorVC setContactLinkedDelegate:self];
654// self.addToContactPopover = [[NSPopover alloc] init];
655// [self.addToContactPopover setContentSize:editorVC.view.frame.size];
656// [self.addToContactPopover setContentViewController:editorVC];
657// [self.addToContactPopover setAnimates:YES];
658// [self.addToContactPopover setBehavior:NSPopoverBehaviorTransient];
659// [self.addToContactPopover setDelegate:self];
660//
661// [self.addToContactPopover showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMaxXEdge];
662// }
Alexandre Lision2db8f472015-07-22 15:05:46 -0400663
Anthony Léonard60970852017-07-21 11:33:41 -0400664 auto* newPerson = new Person();
665 newPerson->setFormattedName(contactmethod->bestName());
666
667 Person::ContactMethods numbers;
668 numbers << contactmethod;
669 newPerson->setContactMethods(numbers);
670 contactmethod->setPerson(newPerson);
671
672 auto personCollections = PersonModel::instance().collections();
673 CollectionInterface *peerProfileCollection = nil;
674 foreach(auto collection, personCollections) {
675 if(dynamic_cast<PeerProfileCollection*>(collection))
676 peerProfileCollection = collection;
677 }
678 if(peerProfileCollection) {
679 PersonModel::instance().addNewPerson(newPerson, peerProfileCollection);
Alexandre Lision2db8f472015-07-22 15:05:46 -0400680 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400681
682 [videoView setCallDelegate:nil];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400683}
684
Alexandre Lisionc5148052015-03-04 15:10:35 -0500685- (IBAction)hangUp:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400686 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::REFUSE;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500687}
688
689- (IBAction)accept:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400690 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::ACCEPT;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500691}
692
693- (IBAction)toggleRecording:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400694 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::RECORD_AUDIO;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500695}
696
697- (IBAction)toggleHold:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400698 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::HOLD;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500699}
700
Alexandre Lision62005312016-01-28 15:55:16 -0500701- (IBAction)toggleAdvancedControls:(id)sender {
702 [advancedButton setPressed:!advancedButton.isPressed];
703 [advancedPanel setHidden:![advancedButton isPressed]];
704}
705
706- (IBAction)showDialpad:(id)sender {
707 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
708 [appDelegate showDialpad];
709}
710
Alexandre Lision58cab672015-06-09 15:25:40 -0400711-(IBAction)toggleChat:(id)sender;
712{
713 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
714 if (rightViewCollapsed) {
715 [self uncollapseRightView];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400716 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex())->addOutgoingMedia<Media::Text>();
Alexandre Lision58cab672015-06-09 15:25:40 -0400717 } else {
718 [self collapseRightView];
719 }
Alexandre Lision62005312016-01-28 15:55:16 -0500720 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400721}
722
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400723- (IBAction)muteAudio:(id)sender
724{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400725 UserActionModel* uam = CallModel::instance().userActionModel();
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400726 uam << UserActionModel::Action::MUTE_AUDIO;
727}
728
729- (IBAction)muteVideo:(id)sender
730{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400731 UserActionModel* uam = CallModel::instance().userActionModel();
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400732 uam << UserActionModel::Action::MUTE_VIDEO;
733}
Alexandre Lision883719f2015-10-22 17:37:45 -0400734
735- (IBAction)toggleTransferView:(id)sender {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500736 if (_brokerPopoverVC != nullptr) {
737 [_brokerPopoverVC performClose:self];
738 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500739 [self.transferButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400740 } else {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500741 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
742 _brokerPopoverVC = [[NSPopover alloc] init];
743 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
744 [_brokerPopoverVC setContentViewController:brokerVC];
745 [_brokerPopoverVC setAnimates:YES];
746 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
747 [_brokerPopoverVC setDelegate:self];
748 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
Alexandre Lision883719f2015-10-22 17:37:45 -0400749 [videoView setCallDelegate:nil];
750 }
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400751}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400752
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500753- (IBAction)toggleAddParticipantView:(id)sender {
754 if (_brokerPopoverVC != nullptr) {
755 [_brokerPopoverVC performClose:self];
756 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500757 [self.addParticipantButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500758 } else {
759 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::CONFERENCE];
760 _brokerPopoverVC = [[NSPopover alloc] init];
761 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
762 [_brokerPopoverVC setContentViewController:brokerVC];
763 [_brokerPopoverVC setAnimates:YES];
764 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
765 [_brokerPopoverVC setDelegate:self];
766 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
767 [videoView setCallDelegate:nil];
768 }
769}
770
771/**
772 * Merge current call with its parent call
773 */
774- (IBAction)mergeCalls:(id)sender
775{
776 auto current = CallModel::instance().selectedCall();
777 current->joinToParent();
778}
779
Alexandre Lision2db8f472015-07-22 15:05:46 -0400780#pragma mark - NSPopOverDelegate
781
Alexandre Lision266fca02015-09-28 14:47:05 -0400782- (void)popoverWillClose:(NSNotification *)notification
783{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500784 if (_brokerPopoverVC != nullptr) {
785 [_brokerPopoverVC performClose:self];
786 _brokerPopoverVC = NULL;
Alexandre Lision883719f2015-10-22 17:37:45 -0400787 }
Alexandre Lision266fca02015-09-28 14:47:05 -0400788
Alexandre Lision2db8f472015-07-22 15:05:46 -0400789 if (self.addToContactPopover != nullptr) {
790 [self.addToContactPopover performClose:self];
791 self.addToContactPopover = NULL;
792 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400793
Alexandre Lision62005312016-01-28 15:55:16 -0500794 [self.addContactButton setPressed:NO];
795 [self.transferButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500796 [self.addParticipantButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400797}
798
799- (void)popoverDidClose:(NSNotification *)notification
800{
801 [videoView setCallDelegate:self];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400802}
803
804#pragma mark - ContactLinkedDelegate
805
806- (void)contactLinked
807{
808 if (self.addToContactPopover != nullptr) {
809 [self.addToContactPopover performClose:self];
810 self.addToContactPopover = NULL;
811 }
812}
813
Alexandre Lision58cab672015-06-09 15:25:40 -0400814#pragma mark - NSSplitViewDelegate
815
816/* 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.
817 */
818- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
819{
820 NSView* rightView = [[splitView subviews] objectAtIndex:1];
821 return ([subview isEqual:rightView]);
822}
823
824
825- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
826{
827 NSView* rightView = [[splitView subviews] objectAtIndex:1];
828 return ([subview isEqual:rightView]);
829}
830
831
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400832# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400833
834- (void) callShouldToggleFullScreen
835{
836 if(self.splitView.isInFullScreenMode)
837 [self.splitView exitFullScreenModeWithOptions:nil];
838 else {
839 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
840 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
841 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
842 NSFullScreenModeApplicationPresentationOptions, nil];
843
844 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
845 }
846}
847
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400848-(void) mouseIsMoving:(BOOL) move
849{
850 [[controlsPanel animator] setAlphaValue:move]; // fade out
851 [[headerContainer animator] setAlphaValue:move];
852}
853
Alexandre Lisionc5148052015-03-04 15:10:35 -0500854@end