blob: ebfc4020df23be26f7c49b545f1c550ff68a3c0d [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 <video/previewmanager.h>
33#import <video/renderer.h>
Olivier Soldano994971f2017-12-05 16:30:12 -050034#import <api/newcallmodel.h>
35#import <api/call.h>
36#import <api/conversationmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050037#import <globalinstances.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050038
Alexandre Lision62005312016-01-28 15:55:16 -050039#import "AppDelegate.h"
Alexandre Lisionf47a2562015-06-15 15:48:29 -040040#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040041#import "views/CallView.h"
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040042#import "views/NSColor+RingTheme.h"
Alexandre Lision210fe212016-01-27 11:15:13 -050043#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040044#import "PersonLinkerVC.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040045#import "ChatVC.h"
Alexandre Lision883719f2015-10-22 17:37:45 -040046#import "BrokerVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050047#import "views/IconButton.h"
Anthony Léonard14e7bf32017-06-08 08:13:16 -040048#import "views/CallLayer.h"
Anthony Léonard6f819752018-01-05 09:53:40 -050049#import "utils.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
Olivier Soldano994971f2017-12-05 16:30:12 -050063@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate> {
64 std::string convUid_;
65 std::string callUid_;
66 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050067 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050068}
Alexandre Lisionc5148052015-03-04 15:10:35 -050069
Alexandre Lision210fe212016-01-27 11:15:13 -050070// Main container
71@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040072@property (unsafe_unretained) IBOutlet NSImageView* backgroundImage;
73@property (unsafe_unretained) IBOutlet NSBox* bluerBackgroundEffect;
Alexandre Lision210fe212016-01-27 11:15:13 -050074
Alexandre Lision89edc6a2015-11-09 11:30:47 -050075// Header info
76@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Alexandre Lision883719f2015-10-22 17:37:45 -040077@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
78@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050079@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
Alexandre Lision210fe212016-01-27 11:15:13 -050080@property (unsafe_unretained) IBOutlet NSImageView* personPhoto;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050081
Andreas Traczyk77a50d52018-05-08 17:47:31 -040082@property (unsafe_unretained) IBOutlet NSImageView* outgoingPhoto;
83@property (unsafe_unretained) IBOutlet NSTextField* outgoingPersonLabel;
84@property (unsafe_unretained) IBOutlet NSTextField* outgoingStateLabel;
85@property (unsafe_unretained) IBOutlet NSTextField* outgoingId;
86
Alexandre Lision89edc6a2015-11-09 11:30:47 -050087// Call Controls
88@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
Alexandre Lision62005312016-01-28 15:55:16 -050089
Alexandre Lision4baba4c2016-02-11 13:00:57 -050090@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
91@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
92@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
93@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
94@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
95@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
Alexandre Lision4baba4c2016-02-11 13:00:57 -050096@property (unsafe_unretained) IBOutlet IconButton* transferButton;
97@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
98@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Andreas Traczyk77a50d52018-05-08 17:47:31 -040099@property (unsafe_unretained) IBOutlet IconButton* qualityButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400100
Alexandre Lision62005312016-01-28 15:55:16 -0500101@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
102@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400103@property (assign) IBOutlet NSLayoutConstraint* callRecordButtonMarginLeft;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500104
Alexandre Lision2db8f472015-07-22 15:05:46 -0400105@property (strong) NSPopover* addToContactPopover;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500106@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision883719f2015-10-22 17:37:45 -0400107@property (strong) IBOutlet ChatVC* chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400108
Alexandre Lision210fe212016-01-27 11:15:13 -0500109// Ringing call panel
110@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
111@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
112@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;
113
114// Outgoing call panel
115@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
116@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500117
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400118// audio only view
119@property (unsafe_unretained) IBOutlet NSView* audioCallView;
120@property (unsafe_unretained) IBOutlet NSImageView* audioCallPhoto;
121@property (unsafe_unretained) IBOutlet NSTextField* audioCallTime;
122@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonLabel;
123@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonId;
124
Alexandre Lisionc5148052015-03-04 15:10:35 -0500125// Video
Alexandre Lision58cab672015-06-09 15:25:40 -0400126@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lision58cab672015-06-09 15:25:40 -0400127@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500128
129@property RendererConnectionsHolder* previewHolder;
130@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400131@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500132@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400133@property QMetaObject::Connection messageConnection;
134@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500135
136@end
137
138@implementation CurrentCallVC
Olivier Soldano994971f2017-12-05 16:30:12 -0500139@synthesize personLabel, personPhoto, stateLabel, holdOnOffButton, hangUpButton,
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500140 recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
Olivier Soldano994971f2017-12-05 16:30:12 -0500141 muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView,
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400142 incomingDisplayName, incomingPersonPhoto, previewView, splitView, loadingIndicator, ringingPanel,
143 outgoingPanel, outgoingPersonLabel, outgoingStateLabel, outgoingPhoto, outgoingId,
144 callRecordButtonMarginLeft, audioCallView, audioCallPhoto, audioCallTime, audioCallPersonLabel, audioCallPersonId, backgroundImage, bluerBackgroundEffect;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500145
146@synthesize previewHolder;
147@synthesize videoHolder;
148
Olivier Soldano994971f2017-12-05 16:30:12 -0500149-(void) setCurrentCall:(const std::string&)callUid
150 conversation:(const std::string&)convUid
151 account:(const lrc::api::account::Info*)account;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500152{
Olivier Soldano994971f2017-12-05 16:30:12 -0500153 if(account == nil)
154 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400155
Olivier Soldano994971f2017-12-05 16:30:12 -0500156 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500157
Olivier Soldano994971f2017-12-05 16:30:12 -0500158 if (not callModel->hasCall(callUid)){
159 callUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400160 return;
161 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500162 callUid_ = callUid;
163 convUid_ = convUid;
164 accountInfo_ = account;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500165 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400166 auto currentCall = callModel->getCall(callUid_);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400167 [muteVideoButton setHidden: currentCall.isAudioOnly ? YES: NO];
168 callRecordButtonMarginLeft.constant = currentCall.isAudioOnly ? -40.0f: 10.0f;
169 [previewView setHidden: YES];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400170 videoView.callId = callUid;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500171}
172
173- (void)awakeFromNib
174{
175 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400176 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500177
Alexandre Lisionc5148052015-03-04 15:10:35 -0500178 [previewView setWantsLayer:YES];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500179 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
180 [previewView.layer setContentsGravity:kCAGravityResizeAspectFill];
181 [previewView.layer setFrame:previewView.frame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500182
183 [controlsPanel setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500184 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
185 [controlsPanel.layer setFrame:controlsPanel.frame];
186
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400187 previewHolder = [[RendererConnectionsHolder alloc] init];
188 videoHolder = [[RendererConnectionsHolder alloc] init];
189
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400190 [loadingIndicator setColor:[NSColor whiteColor]];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400191 [loadingIndicator setNumberOfLines:200];
192 [loadingIndicator setWidthOfLine:4];
193 [loadingIndicator setLengthOfLine:4];
194 [loadingIndicator setInnerMargin:59];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400195
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400196 [self.videoView setCallDelegate:self];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400197 CGColor* color = [[[NSColor blackColor] colorWithAlphaComponent:0.2] CGColor];
198 [headerContainer.layer setBackgroundColor:color];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400199 [bluerBackgroundEffect setWantsLayer:YES];
200 bluerBackgroundEffect.alphaValue = 0.6;
201 [audioCallView setWantsLayer:YES];
202 [audioCallView.layer setBackgroundColor: [[NSColor clearColor] CGColor]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500203}
204
Anthony Léonard66a050c2018-01-03 16:32:34 -0500205-(void) updateDurationLabel
206{
207 if (accountInfo_ != nil) {
208 auto* callModel = accountInfo_->callModel.get();
209 if (callModel->hasCall(callUid_)) {
210 auto& callStatus = callModel->getCall(callUid_).status;
211 if (callStatus != lrc::api::call::Status::ENDED &&
212 callStatus != lrc::api::call::Status::TERMINATING &&
213 callStatus != lrc::api::call::Status::INVALID) {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400214 if(callModel->getCall(callUid_).isAudioOnly) {
215 [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
216 } else {
217 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
218 }
Anthony Léonard66a050c2018-01-03 16:32:34 -0500219 return;
220 }
221 }
222 }
223
224 // If call is not running anymore or accountInfo_ is not set for any reason
225 // we stop the refresh loop
226 [refreshDurationTimer invalidate];
227 refreshDurationTimer = nil;
228 [timeSpentLabel setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400229 [audioCallView setHidden:YES];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500230}
231
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400232-(void) updateCall
Alexandre Lisionc5148052015-03-04 15:10:35 -0500233{
Olivier Soldano994971f2017-12-05 16:30:12 -0500234 if (accountInfo_ == nil)
235 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400236
Olivier Soldano994971f2017-12-05 16:30:12 -0500237 auto* callModel = accountInfo_->callModel.get();
238 if (not callModel->hasCall(callUid_)) {
239 return;
240 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500241
Olivier Soldano994971f2017-12-05 16:30:12 -0500242 auto currentCall = callModel->getCall(callUid_);
243 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
Anthony Léonard8ee26782018-01-05 10:23:04 -0500244 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400245 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
246 NSString* bestName = bestNameForConversation(*convIt, *accountInfo_->conversationModel);
247 [personLabel setStringValue:bestName];
248 [outgoingPersonLabel setStringValue:bestName];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400249 [audioCallPersonLabel setStringValue:bestName];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400250 NSString* ringID = bestIDForConversation(*convIt, *accountInfo_->conversationModel);
251 if([bestName isEqualToString:ringID]) {
252 ringID = @"";
253 }
254 [outgoingId setStringValue:ringID];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400255 [audioCallPersonId setStringValue:ringID];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400256 }
257
Anthony Léonard66a050c2018-01-03 16:32:34 -0500258 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400259 [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500260 if (refreshDurationTimer == nil)
261 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
262 target:self
263 selector:@selector(updateDurationLabel)
264 userInfo:nil
265 repeats:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500266 [stateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400267 [outgoingStateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500268
Olivier Soldano994971f2017-12-05 16:30:12 -0500269 // Default values for this views
270 [loadingIndicator setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500271 [ringingPanel setHidden:YES];
272 [outgoingPanel setHidden:YES];
273 [controlsPanel setHidden:NO];
274 [headerContainer setHidden:NO];
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500275 [chatButton setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400276 [self setBackground];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500277
Olivier Soldano994971f2017-12-05 16:30:12 -0500278 using Status = lrc::api::call::Status;
279 switch (currentCall.status) {
280 case Status::SEARCHING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500281 case Status::CONNECTING:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400282 [headerContainer setHidden:YES];
283 [outgoingPanel setHidden:NO];
284 [outgoingPhoto setHidden:NO];
285 [self setupContactInfo:outgoingPhoto];
Olivier Soldano994971f2017-12-05 16:30:12 -0500286 [loadingIndicator setHidden:NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400287 [controlsPanel setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500288 break;
289 case Status::INCOMING_RINGING:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400290 [outgoingPhoto setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500291 [controlsPanel setHidden:YES];
292 [outgoingPanel setHidden:YES];
293 [self setupIncoming:callUid_];
294 break;
295 case Status::OUTGOING_RINGING:
296 [controlsPanel setHidden:YES];
297 [outgoingPanel setHidden:NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400298 [loadingIndicator setHidden:NO];
299 [headerContainer setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500300 break;
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400301 /*case Status::CONFERENCE:
302 [self setupConference:currentCall];
303 break;*/
Olivier Soldano994971f2017-12-05 16:30:12 -0500304 case Status::PAUSED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500305 case Status::INACTIVE:
306 case Status::IN_PROGRESS:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400307 // change constraints (uncollapse avatar)
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400308 if(currentCall.isAudioOnly) {
309 [self setUpAudioOnlyView];
310 } else {
311 [self setUpVideoCallView];
312 }
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400313 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500314 case Status::CONNECTED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500315 break;
316 case Status::ENDED:
317 case Status::TERMINATING:
318 case Status::INVALID:
319 [controlsPanel setHidden:YES];
320 [outgoingPanel setHidden:NO];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400321 [self hideWithAnimation:false];
Olivier Soldano994971f2017-12-05 16:30:12 -0500322 break;
323 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500324
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500325}
326
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400327-(void) setUpVideoCallView {
328 [self setupContactInfo:personPhoto];
329 [timeSpentLabel setHidden:NO];
330 [outgoingPhoto setHidden:YES];
331 [headerContainer setHidden:NO];
332 [previewView setHidden: NO];
333 [bluerBackgroundEffect setHidden:YES];
334 [bluerBackgroundEffect.layer setBackgroundFilters: nil];
335 [backgroundImage setHidden:YES];
336}
337
338-(void) setUpAudioOnlyView {
339 [audioCallView setHidden:NO];
340 [headerContainer setHidden:YES];
341 [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
342}
343
344-(void) setBackground {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400345 auto* convModel = accountInfo_->conversationModel.get();
346 auto it = getConversationFromUid(convUid_, *convModel);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400347 NSImage *image= [self getContactImageOfSize:120.0 withDefaultAvatar:NO];
348 if(image) {
349 CIFilter* bluerFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
350 [bluerFilter setDefaults];
351 [bluerFilter setValue:[NSNumber numberWithFloat: 30] forKey:@"inputRadius"];
352 [bluerBackgroundEffect.layer setBackgroundFilters:@[bluerFilter]];
353 [backgroundImage setImage: image];
354 [backgroundImage setHidden:NO];
355 } else {
356 // [bluerBackgroundEffect setHidden:YES];
357 [bluerBackgroundEffect.layer setBackgroundFilters:nil];
358 [bluerBackgroundEffect setFillColor:[NSColor ringDarkGrey]];
359 [backgroundImage setHidden:YES];
360 [backgroundImage setImage:nil];
361 // [videoView.layer setBackgroundColor:[[NSColor ringDarkGrey] CGColor]];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400362 }
363}
364
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400365-(NSImage *) getContactImageOfSize: (double) size withDefaultAvatar:(BOOL) shouldDrawDefault {
366 auto* convModel = accountInfo_->conversationModel.get();
367 auto convIt = getConversationFromUid(convUid_, *convModel);
368 if (convIt == convModel->allFilteredConversations().end()) {
369 return nil;
370 }
371 if(shouldDrawDefault) {
372 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
373 QVariant photo = imgManip.conversationPhoto(*convIt, *accountInfo_, QSize(size, size), NO);
374 return QtMac::toNSImage(qvariant_cast<QPixmap>(photo));
375 }
376 auto contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
377 NSData *imageData = [[NSData alloc] initWithBase64EncodedString:@(contact.profileInfo.avatar.c_str()) options:NSDataBase64DecodingIgnoreUnknownCharacters];
378 return [[NSImage alloc] initWithData:imageData];
379}
380
381
382-(void) setupContactInfo:(NSImageView*)imageView
383{
384 [imageView setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
385}
386
Olivier Soldano994971f2017-12-05 16:30:12 -0500387-(void) setupIncoming:(const std::string&) callId
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500388{
Olivier Soldano994971f2017-12-05 16:30:12 -0500389 if (accountInfo_ == nil)
390 return;
391
392 auto* callModel = accountInfo_->callModel.get();
393 if (not callModel->hasCall(callUid_)) {
394 return;
395 }
396
397 auto call = callModel->getCall(callUid_);
398 auto* convModel = accountInfo_->conversationModel.get();
399
400 [ringingPanel setHidden:NO];
401 [controlsPanel setHidden:YES];
402 [headerContainer setHidden:YES];
Anthony Léonard6f819752018-01-05 09:53:40 -0500403 auto it = getConversationFromUid(convUid_, *convModel);
Olivier Soldano994971f2017-12-05 16:30:12 -0500404 if (it != convModel->allFilteredConversations().end()) {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400405 [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Anthony Léonard8ee26782018-01-05 10:23:04 -0500406 [incomingDisplayName setStringValue:bestNameForConversation(*it, *convModel)];
Olivier Soldano994971f2017-12-05 16:30:12 -0500407 }
408}
409
410
411-(void) setupConference:(Call*) c
412{
413 [videoView setShouldAcceptInteractions:YES];
414 [self.chatButton setHidden:NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500415 [self.addParticipantButton setHidden:NO];
416 [self.transferButton setHidden:YES];
417}
418
419-(void)collapseRightView
420{
421 NSView *right = [[splitView subviews] objectAtIndex:1];
422 NSView *left = [[splitView subviews] objectAtIndex:0];
423 NSRect leftFrame = [left frame];
424 [right setHidden:YES];
425 [splitView display];
426}
427
428- (void) changeCallSelection:(std::string&)callUid
429{
430 if (accountInfo_ == nil)
431 return;
432
433 auto* callModel = accountInfo_->callModel.get();
434 if (not callModel->hasCall(callUid)) {
435 return;
436 }
437
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500438 QObject::disconnect(self.selectedCallChanged);
Olivier Soldano994971f2017-12-05 16:30:12 -0500439 self.selectedCallChanged = QObject::connect(callModel,
440 &lrc::api::NewCallModel::callStatusChanged,
441 [self](const std::string callId) {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400442 [self updateCall];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500443 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500444}
445
446-(void) connectVideoSignals
447{
Olivier Soldano994971f2017-12-05 16:30:12 -0500448 if (accountInfo_ == nil)
449 return;
450
451 auto* callModel = accountInfo_->callModel.get();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500452 QObject::disconnect(self.videoStarted);
Olivier Soldano994971f2017-12-05 16:30:12 -0500453 self.videoStarted = QObject::connect(callModel,
454 &lrc::api::NewCallModel::remotePreviewStarted,
455 [self](const std::string& callId, Video::Renderer* renderer) {
456 NSLog(@"Video started!");
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400457 [videoView setLayer:[[CallLayer alloc] init]];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400458 [videoView setShouldAcceptInteractions:YES];
459 [self mouseIsMoving: NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500460 [self connectVideoRenderer:renderer];
461 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500462
Olivier Soldano994971f2017-12-05 16:30:12 -0500463 if (callModel->hasCall(callUid_)) {
464 if (auto renderer = callModel->getRenderer(callUid_)) {
465 QObject::disconnect(self.videoStarted);
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400466 [videoView setLayer:[[CallLayer alloc] init]];
Olivier Soldano994971f2017-12-05 16:30:12 -0500467 [self connectVideoRenderer: renderer];
468 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500469 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500470 [self connectPreviewRenderer];
471
472}
473
474-(void) connectPreviewRenderer
475{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400476 QObject::disconnect(previewHolder.frameUpdated);
477 QObject::disconnect(previewHolder.stopped);
478 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400479 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500480 &Video::PreviewManager::previewStarted,
481 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500482 QObject::disconnect(previewHolder.frameUpdated);
483 previewHolder.frameUpdated = QObject::connect(renderer,
484 &Video::Renderer::frameUpdated,
485 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400486 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400487 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500488 });
489 });
490
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400491 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500492 &Video::PreviewManager::previewStopped,
493 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500494 QObject::disconnect(previewHolder.frameUpdated);
495 [previewView.layer setContents:nil];
496 });
497
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400498 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500499 &Video::Renderer::frameUpdated,
500 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400501 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400502 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500503 });
504}
505
506-(void) connectVideoRenderer: (Video::Renderer*)renderer
507{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400508 QObject::disconnect(videoHolder.frameUpdated);
509 QObject::disconnect(videoHolder.started);
510 QObject::disconnect(videoHolder.stopped);
Olivier Soldano994971f2017-12-05 16:30:12 -0500511
512 if(renderer == nil)
513 return;
514
Alexandre Lisionc5148052015-03-04 15:10:35 -0500515 videoHolder.frameUpdated = QObject::connect(renderer,
516 &Video::Renderer::frameUpdated,
517 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400518 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500519 });
520
521 videoHolder.started = QObject::connect(renderer,
522 &Video::Renderer::started,
523 [=]() {
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400524 [self mouseIsMoving: NO];
525 [videoView setShouldAcceptInteractions:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500526 QObject::disconnect(videoHolder.frameUpdated);
527 videoHolder.frameUpdated = QObject::connect(renderer,
528 &Video::Renderer::frameUpdated,
529 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400530 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500531 });
532 });
533
534 videoHolder.stopped = QObject::connect(renderer,
535 &Video::Renderer::stopped,
536 [=]() {
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400537 [videoView setLayer:[CALayer layer]];
538 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400539 [self mouseIsMoving: YES];
540 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500541 QObject::disconnect(videoHolder.frameUpdated);
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400542 [(CallLayer*)videoView.layer setVideoRunning:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500543 });
544}
545
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400546-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(NSView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500547{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500548 QSize res = renderer->size();
549
Alexandre Lision6731e132015-10-14 14:29:06 -0400550 auto frame_ptr = renderer->currentFrame();
551 auto frame_data = frame_ptr.ptr;
552 if (!frame_data)
553 return;
554
Alexandre Lisionc5148052015-03-04 15:10:35 -0500555 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400556 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500557 res.width(),
558 res.height(),
559 8,
560 4*res.width(),
561 colorSpace,
562 kCGImageAlphaPremultipliedLast);
563
564
565 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
566
567 /*We release some components*/
568 CGContextRelease(newContext);
569 CGColorSpaceRelease(colorSpace);
570
571 [CATransaction begin];
572 view.layer.contents = (__bridge id)newImage;
573 [CATransaction commit];
574
575 CFRelease(newImage);
576}
577
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400578-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallView*) view
579{
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400580 auto frame_ptr = renderer->currentFrame();
581 if (!frame_ptr.ptr)
582 return;
583
584 CallLayer* callLayer = (CallLayer*) view.layer;
585
Anthony Léonarda9d7c8e2018-01-03 14:41:17 -0500586 [callLayer setCurrentFrame:std::move(frame_ptr)];
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400587 [callLayer setVideoRunning:YES];
588}
589
Alexandre Lisionc5148052015-03-04 15:10:35 -0500590- (void) initFrame
591{
592 [self.view setFrame:self.view.superview.bounds];
593 [self.view setHidden:YES];
594 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400595 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500596}
597
598# pragma private IN/OUT animations
599
Olivier Soldano994971f2017-12-05 16:30:12 -0500600-(void)uncollapseRightView
601{
602 NSView *left = [[splitView subviews] objectAtIndex:0];
603 NSView *right = [[splitView subviews] objectAtIndex:1];
604 [right setHidden:NO];
605
606 CGFloat dividerThickness = [splitView dividerThickness];
607
608 // get the different frames
609 NSRect leftFrame = [left frame];
610 NSRect rightFrame = [right frame];
611
612 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
613 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
614 [left setFrameSize:leftFrame.size];
615 [right setFrame:rightFrame];
616 [splitView display];
617
618 [self.chatVC takeFocus];
619}
620
Alexandre Lisionc5148052015-03-04 15:10:35 -0500621-(void) cleanUp
622{
Anthony Léonard5c287642018-02-21 15:54:19 -0500623 if(self.splitView.isInFullScreenMode)
624 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400625 QObject::disconnect(videoHolder.frameUpdated);
626 QObject::disconnect(videoHolder.started);
627 QObject::disconnect(videoHolder.stopped);
628 QObject::disconnect(previewHolder.frameUpdated);
629 QObject::disconnect(previewHolder.stopped);
630 QObject::disconnect(previewHolder.started);
Anthony Léonardc0013bb2018-02-20 16:23:02 -0500631 QObject::disconnect(self.messageConnection);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500632 [previewView.layer setContents:nil];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400633 [previewView setHidden: YES];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400634 [videoView setLayer:[CALayer layer]];
635 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Alexandre Lision883719f2015-10-22 17:37:45 -0400636
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500637 [self.chatButton setHidden:YES];
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500638 [self.chatButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400639 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500640
641 [personLabel setStringValue:@""];
642 [timeSpentLabel setStringValue:@""];
643 [stateLabel setStringValue:@""];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400644 //audio view
645 [audioCallTime setStringValue:@""];
646 [audioCallPersonId setStringValue:@""];
647 [audioCallPersonLabel setStringValue:@""];
648 [audioCallView setHidden:YES];
649 [audioCallPhoto setImage:nil];
650 //background view
651 [bluerBackgroundEffect setHidden:NO];
652 [backgroundImage setHidden:NO];
653 [backgroundImage setImage:nil];
654 [bluerBackgroundEffect setBackgroundFilters:nil];
655 //outgoing view
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400656 [outgoingPersonLabel setStringValue:@""];
657 [outgoingStateLabel setStringValue:@""];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500658}
659
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400660-(void) setupCallView
661{
662 if (accountInfo_ == nil)
663 return;
664
665 auto* callModel = accountInfo_->callModel.get();
666 auto* convModel = accountInfo_->conversationModel.get();
667
668 // when call comes in we want to show the controls/header
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400669 [self mouseIsMoving: YES];
670 [videoView setShouldAcceptInteractions: NO];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400671
672 [self connectVideoSignals];
673 /* check if text media is already present */
674 if(not callModel->hasCall(callUid_))
675 return;
676
677 [loadingIndicator setAnimates:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400678 [self updateCall];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400679
680 /* monitor media for messaging text messaging */
681 QObject::disconnect(self.messageConnection);
682 self.messageConnection = QObject::connect(convModel,
683 &lrc::api::ConversationModel::interactionStatusUpdated,
684 [self] (std::string convUid,
685 uint64_t msgId,
686 lrc::api::interaction::Info msg) {
687 if (msg.type == lrc::api::interaction::Type::TEXT) {
688 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
689 return;
690 }
691 [self uncollapseRightView];
692 }
693 });
694}
695
696-(void) showWithAnimation:(BOOL)animate
697{
698 if (!animate) {
699 [self.view setHidden:NO];
700 [self setupCallView];
701 return;
702 }
703
704 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
705 [self.view setHidden:NO];
706
707 [CATransaction begin];
708 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
709 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
710 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
711 [animation setDuration:0.2f];
712 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
713 [CATransaction setCompletionBlock:^{
714 [self setupCallView];
715 }];
716
717 [self.view.layer addAnimation:animation forKey:animation.keyPath];
718 [CATransaction commit];
719}
720
721-(void) hideWithAnimation:(BOOL)animate
Alexandre Lisionc5148052015-03-04 15:10:35 -0500722{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500723 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500724 return;
725 }
726
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400727 if (!animate) {
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400728 [self cleanUp];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400729 [self.view setHidden:YES];
730 return;
731 }
732
Alexandre Lisionc5148052015-03-04 15:10:35 -0500733 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
734 [CATransaction begin];
735 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
736 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
737 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
738 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500739 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500740
741 [CATransaction setCompletionBlock:^{
742 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400743 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500744 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500745// if (currentCall_) {
746// [self animateIn];
747// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500748 }];
749 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400750
751 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500752 [CATransaction commit];
753}
754
755/**
756 * Debug purpose
757 */
758-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
759{
760 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
761}
762
Alexandre Lision58cab672015-06-09 15:25:40 -0400763#pragma mark - Button methods
764
Alexandre Lisionc5148052015-03-04 15:10:35 -0500765- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500766 if (accountInfo_ == nil)
767 return;
768
769 auto* callModel = accountInfo_->callModel.get();
770
771 callModel->hangUp(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500772}
773
774- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500775 if (accountInfo_ == nil)
776 return;
777
Anthony Léonardf584cc52018-01-19 12:00:19 -0500778 // If we accept a conversation with a non trusted contact, we first accept it
779 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
780 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
781 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
782 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
783 accountInfo_->conversationModel->makePermanent(convUid_);
784 }
785
Olivier Soldano994971f2017-12-05 16:30:12 -0500786 auto* callModel = accountInfo_->callModel.get();
787
788 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500789}
790
791- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500792 if (accountInfo_ == nil)
793 return;
794
795 auto* callModel = accountInfo_->callModel.get();
796
797 callModel->toggleAudioRecord(callUid_);
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400798 [recordOnOffButton setPressed:!recordOnOffButton.isPressed];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500799}
800
801- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500802 if (accountInfo_ == nil)
803 return;
804
805 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400806 auto currentCall = callModel->getCall(callUid_);
807 if (currentCall.status != lrc::api::call::Status::PAUSED) {
808 holdOnOffButton.image = [NSImage imageNamed:@"ic_action_holdoff.png"];
809 } else {
810 holdOnOffButton.image = [NSImage imageNamed:@"ic_action_hold.png"];
811 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500812
813 callModel->togglePause(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500814}
815
Alexandre Lision62005312016-01-28 15:55:16 -0500816- (IBAction)toggleAdvancedControls:(id)sender {
817 [advancedButton setPressed:!advancedButton.isPressed];
818 [advancedPanel setHidden:![advancedButton isPressed]];
819}
820
821- (IBAction)showDialpad:(id)sender {
822 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
823 [appDelegate showDialpad];
824}
825
Alexandre Lision58cab672015-06-09 15:25:40 -0400826-(IBAction)toggleChat:(id)sender;
827{
828 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
829 if (rightViewCollapsed) {
830 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400831 } else {
832 [self collapseRightView];
833 }
Alexandre Lision62005312016-01-28 15:55:16 -0500834 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400835}
836
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400837- (IBAction)muteAudio:(id)sender
838{
Olivier Soldano994971f2017-12-05 16:30:12 -0500839 if (accountInfo_ == nil)
840 return;
841
842 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400843 auto currentCall = callModel->getCall(callUid_);
844 if (currentCall.audioMuted) {
845 muteAudioButton.image = [NSImage imageNamed:@"ic_action_audio.png"];
846 } else {
847 muteAudioButton.image = [NSImage imageNamed:@"ic_action_mute_audio.png"];
848 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500849 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400850}
851
852- (IBAction)muteVideo:(id)sender
853{
Olivier Soldano994971f2017-12-05 16:30:12 -0500854 if (accountInfo_ == nil)
855 return;
Olivier Soldano994971f2017-12-05 16:30:12 -0500856 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400857 auto currentCall = callModel->getCall(callUid_);
858 if(!currentCall.isAudioOnly) {
859 if (currentCall.videoMuted) {
860 muteVideoButton.image = [NSImage imageNamed:@"ic_action_video.png"];
861 } else {
862 muteVideoButton.image = [NSImage imageNamed:@"ic_action_mute_video.png"];
863 }
864 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500865 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400866}
Alexandre Lision883719f2015-10-22 17:37:45 -0400867
868- (IBAction)toggleTransferView:(id)sender {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500869 if (_brokerPopoverVC != nullptr) {
870 [_brokerPopoverVC performClose:self];
871 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500872 [self.transferButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400873 } else {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500874 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
875 _brokerPopoverVC = [[NSPopover alloc] init];
876 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
877 [_brokerPopoverVC setContentViewController:brokerVC];
878 [_brokerPopoverVC setAnimates:YES];
879 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
880 [_brokerPopoverVC setDelegate:self];
881 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
Alexandre Lision883719f2015-10-22 17:37:45 -0400882 [videoView setCallDelegate:nil];
883 }
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400884}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400885
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500886- (IBAction)toggleAddParticipantView:(id)sender {
887 if (_brokerPopoverVC != nullptr) {
888 [_brokerPopoverVC performClose:self];
889 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500890 [self.addParticipantButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500891 } else {
892 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::CONFERENCE];
893 _brokerPopoverVC = [[NSPopover alloc] init];
894 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
895 [_brokerPopoverVC setContentViewController:brokerVC];
896 [_brokerPopoverVC setAnimates:YES];
897 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
898 [_brokerPopoverVC setDelegate:self];
899 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
900 [videoView setCallDelegate:nil];
901 }
902}
903
Alexandre Lision2db8f472015-07-22 15:05:46 -0400904#pragma mark - NSPopOverDelegate
905
Alexandre Lision266fca02015-09-28 14:47:05 -0400906- (void)popoverWillClose:(NSNotification *)notification
907{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500908 if (_brokerPopoverVC != nullptr) {
909 [_brokerPopoverVC performClose:self];
910 _brokerPopoverVC = NULL;
Alexandre Lision883719f2015-10-22 17:37:45 -0400911 }
Alexandre Lision266fca02015-09-28 14:47:05 -0400912
Alexandre Lision2db8f472015-07-22 15:05:46 -0400913 if (self.addToContactPopover != nullptr) {
914 [self.addToContactPopover performClose:self];
915 self.addToContactPopover = NULL;
916 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400917
Alexandre Lision62005312016-01-28 15:55:16 -0500918 [self.transferButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500919 [self.addParticipantButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400920}
921
922- (void)popoverDidClose:(NSNotification *)notification
923{
924 [videoView setCallDelegate:self];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400925}
926
927#pragma mark - ContactLinkedDelegate
928
929- (void)contactLinked
930{
931 if (self.addToContactPopover != nullptr) {
932 [self.addToContactPopover performClose:self];
933 self.addToContactPopover = NULL;
934 }
935}
936
Alexandre Lision58cab672015-06-09 15:25:40 -0400937#pragma mark - NSSplitViewDelegate
938
939/* 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.
940 */
941- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
942{
943 NSView* rightView = [[splitView subviews] objectAtIndex:1];
944 return ([subview isEqual:rightView]);
945}
946
947
948- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
949{
950 NSView* rightView = [[splitView subviews] objectAtIndex:1];
951 return ([subview isEqual:rightView]);
952}
953
954
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400955# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400956
957- (void) callShouldToggleFullScreen
958{
959 if(self.splitView.isInFullScreenMode)
960 [self.splitView exitFullScreenModeWithOptions:nil];
961 else {
962 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
963 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
964 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
965 NSFullScreenModeApplicationPresentationOptions, nil];
966
967 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
968 }
969}
970
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400971-(void) mouseIsMoving:(BOOL) move
972{
973 [[controlsPanel animator] setAlphaValue:move]; // fade out
974 [[headerContainer animator] setAlphaValue:move];
975}
976
Alexandre Lisionc5148052015-03-04 15:10:35 -0500977@end