blob: 0993f93d77c04668886cd8407143e5d4eae5fe58 [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"
Alexandre Lision210fe212016-01-27 11:15:13 -050042#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040043#import "PersonLinkerVC.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040044#import "ChatVC.h"
Alexandre Lision883719f2015-10-22 17:37:45 -040045#import "BrokerVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050046#import "views/IconButton.h"
Anthony Léonard14e7bf32017-06-08 08:13:16 -040047#import "views/CallLayer.h"
Anthony Léonard6f819752018-01-05 09:53:40 -050048#import "utils.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040049
Alexandre Lisionc5148052015-03-04 15:10:35 -050050@interface RendererConnectionsHolder : NSObject
51
52@property QMetaObject::Connection frameUpdated;
53@property QMetaObject::Connection started;
54@property QMetaObject::Connection stopped;
55
56@end
57
58@implementation RendererConnectionsHolder
59
60@end
61
Olivier Soldano994971f2017-12-05 16:30:12 -050062@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate> {
63 std::string convUid_;
64 std::string callUid_;
65 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050066 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050067}
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 (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
83@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
84@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
85@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
86@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
87@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
88@property (unsafe_unretained) IBOutlet IconButton* addContactButton;
89@property (unsafe_unretained) IBOutlet IconButton* transferButton;
90@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
91@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040092
Alexandre Lision62005312016-01-28 15:55:16 -050093@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
94@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
95
Alexandre Lisiond18fa272015-06-15 11:18:03 -040096
Alexandre Lision89edc6a2015-11-09 11:30:47 -050097// Join call panel
98@property (unsafe_unretained) IBOutlet NSView* joinPanel;
99@property (unsafe_unretained) IBOutlet NSButton* mergeCallsButton;
100
Alexandre Lision2db8f472015-07-22 15:05:46 -0400101@property (strong) NSPopover* addToContactPopover;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500102@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision883719f2015-10-22 17:37:45 -0400103@property (strong) IBOutlet ChatVC* chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400104
Alexandre Lision210fe212016-01-27 11:15:13 -0500105// Ringing call panel
106@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
107@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
108@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;
109
110// Outgoing call panel
111@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
112@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500113
114// Video
Alexandre Lision58cab672015-06-09 15:25:40 -0400115@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lision58cab672015-06-09 15:25:40 -0400116@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500117
118@property RendererConnectionsHolder* previewHolder;
119@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400120@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500121@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400122@property QMetaObject::Connection messageConnection;
123@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500124
125@end
126
127@implementation CurrentCallVC
Olivier Soldano994971f2017-12-05 16:30:12 -0500128@synthesize personLabel, personPhoto, stateLabel, holdOnOffButton, hangUpButton,
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500129 recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
Olivier Soldano994971f2017-12-05 16:30:12 -0500130 muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView,
131 incomingDisplayName, incomingPersonPhoto, previewView, splitView, loadingIndicator, ringingPanel, joinPanel,
132 outgoingPanel;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500133
134@synthesize previewHolder;
135@synthesize videoHolder;
136
Olivier Soldano994971f2017-12-05 16:30:12 -0500137-(void) setCurrentCall:(const std::string&)callUid
138 conversation:(const std::string&)convUid
139 account:(const lrc::api::account::Info*)account;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500140{
Olivier Soldano994971f2017-12-05 16:30:12 -0500141 if(account == nil)
142 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400143
Olivier Soldano994971f2017-12-05 16:30:12 -0500144 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500145
Olivier Soldano994971f2017-12-05 16:30:12 -0500146 if (not callModel->hasCall(callUid)){
147 callUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400148 return;
149 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500150 callUid_ = callUid;
151 convUid_ = convUid;
152 accountInfo_ = account;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500153 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500154}
155
156- (void)awakeFromNib
157{
158 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400159 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500160
Alexandre Lisionc5148052015-03-04 15:10:35 -0500161 [previewView setWantsLayer:YES];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500162 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
163 [previewView.layer setContentsGravity:kCAGravityResizeAspectFill];
164 [previewView.layer setFrame:previewView.frame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500165
166 [controlsPanel setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500167 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
168 [controlsPanel.layer setFrame:controlsPanel.frame];
169
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400170 previewHolder = [[RendererConnectionsHolder alloc] init];
171 videoHolder = [[RendererConnectionsHolder alloc] init];
172
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400173 [loadingIndicator setColor:[NSColor whiteColor]];
174 [loadingIndicator setNumberOfLines:100];
175 [loadingIndicator setWidthOfLine:2];
176 [loadingIndicator setLengthOfLine:2];
177 [loadingIndicator setInnerMargin:30];
178
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400179 [self.videoView setCallDelegate:self];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500180}
181
Anthony Léonard66a050c2018-01-03 16:32:34 -0500182-(void) updateDurationLabel
183{
184 if (accountInfo_ != nil) {
185 auto* callModel = accountInfo_->callModel.get();
186 if (callModel->hasCall(callUid_)) {
187 auto& callStatus = callModel->getCall(callUid_).status;
188 if (callStatus != lrc::api::call::Status::ENDED &&
189 callStatus != lrc::api::call::Status::TERMINATING &&
190 callStatus != lrc::api::call::Status::INVALID) {
191 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
192 return;
193 }
194 }
195 }
196
197 // If call is not running anymore or accountInfo_ is not set for any reason
198 // we stop the refresh loop
199 [refreshDurationTimer invalidate];
200 refreshDurationTimer = nil;
201 [timeSpentLabel setHidden:YES];
202}
203
Olivier Soldano994971f2017-12-05 16:30:12 -0500204-(void) updateCall:(BOOL) firstRun
Alexandre Lisionc5148052015-03-04 15:10:35 -0500205{
Olivier Soldano994971f2017-12-05 16:30:12 -0500206 if (accountInfo_ == nil)
207 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400208
Olivier Soldano994971f2017-12-05 16:30:12 -0500209 auto* callModel = accountInfo_->callModel.get();
210 if (not callModel->hasCall(callUid_)) {
211 return;
212 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500213
Olivier Soldano994971f2017-12-05 16:30:12 -0500214 auto currentCall = callModel->getCall(callUid_);
215 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
Anthony Léonard8ee26782018-01-05 10:23:04 -0500216 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
217 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end())
218 [personLabel setStringValue:bestNameForConversation(*convIt, *accountInfo_->conversationModel)];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500219 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
220 [timeSpentLabel setHidden:NO];
221 if (refreshDurationTimer == nil)
222 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
223 target:self
224 selector:@selector(updateDurationLabel)
225 userInfo:nil
226 repeats:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500227 [stateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400228
Olivier Soldano994971f2017-12-05 16:30:12 -0500229 if (firstRun) {
230 //QVariant photo = GlobalInstances::pixmapManipulator().callPhoto(current, QSize(100,100));
231 //[personPhoto setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
232 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500233
Olivier Soldano994971f2017-12-05 16:30:12 -0500234 // Default values for this views
235 [loadingIndicator setHidden:YES];
236 [videoView setShouldAcceptInteractions:NO];
237 [ringingPanel setHidden:YES];
238 [outgoingPanel setHidden:YES];
239 [controlsPanel setHidden:NO];
240 [headerContainer setHidden:NO];
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500241 [chatButton setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500242
Olivier Soldano994971f2017-12-05 16:30:12 -0500243 using Status = lrc::api::call::Status;
244 switch (currentCall.status) {
245 case Status::SEARCHING:
246 case Status::OUTGOING_REQUESTED:
247 case Status::CONNECTING:
248 [loadingIndicator setHidden:NO];
249 break;
250 case Status::INCOMING_RINGING:
251 [controlsPanel setHidden:YES];
252 [outgoingPanel setHidden:YES];
253 [self setupIncoming:callUid_];
254 break;
255 case Status::OUTGOING_RINGING:
256 [controlsPanel setHidden:YES];
257 [outgoingPanel setHidden:NO];
258 break;
259// case Status::CONFERENCE:
260// [self setupConference:currentCall];
261// break;
262 case Status::PAUSED:
263 case Status::PEER_PAUSED:
264 case Status::INACTIVE:
265 case Status::IN_PROGRESS:
266 case Status::CONNECTED:
267 case Status::AUTO_ANSWERING:
268 break;
269 case Status::ENDED:
270 case Status::TERMINATING:
271 case Status::INVALID:
272 [controlsPanel setHidden:YES];
273 [outgoingPanel setHidden:NO];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400274 [self hideWithAnimation:false];
Olivier Soldano994971f2017-12-05 16:30:12 -0500275 break;
276 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500277
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500278}
279
Olivier Soldano994971f2017-12-05 16:30:12 -0500280-(void) setupIncoming:(const std::string&) callId
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500281{
Olivier Soldano994971f2017-12-05 16:30:12 -0500282 if (accountInfo_ == nil)
283 return;
284
285 auto* callModel = accountInfo_->callModel.get();
286 if (not callModel->hasCall(callUid_)) {
287 return;
288 }
289
290 auto call = callModel->getCall(callUid_);
291 auto* convModel = accountInfo_->conversationModel.get();
292
293 [ringingPanel setHidden:NO];
294 [controlsPanel setHidden:YES];
295 [headerContainer setHidden:YES];
Anthony Léonard6f819752018-01-05 09:53:40 -0500296 auto it = getConversationFromUid(convUid_, *convModel);
Olivier Soldano994971f2017-12-05 16:30:12 -0500297 if (it != convModel->allFilteredConversations().end()) {
298 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
299 QVariant photo = imgManip.conversationPhoto(*it, *accountInfo_);
300 [incomingPersonPhoto setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
Anthony Léonard8ee26782018-01-05 10:23:04 -0500301 [incomingDisplayName setStringValue:bestNameForConversation(*it, *convModel)];
Olivier Soldano994971f2017-12-05 16:30:12 -0500302 }
303}
304
305
306-(void) setupConference:(Call*) c
307{
308 [videoView setShouldAcceptInteractions:YES];
309 [self.chatButton setHidden:NO];
310 [joinPanel setHidden:YES];
311 [self.addParticipantButton setHidden:NO];
312 [self.transferButton setHidden:YES];
313}
314
315-(void)collapseRightView
316{
317 NSView *right = [[splitView subviews] objectAtIndex:1];
318 NSView *left = [[splitView subviews] objectAtIndex:0];
319 NSRect leftFrame = [left frame];
320 [right setHidden:YES];
321 [splitView display];
322}
323
324- (void) changeCallSelection:(std::string&)callUid
325{
326 if (accountInfo_ == nil)
327 return;
328
329 auto* callModel = accountInfo_->callModel.get();
330 if (not callModel->hasCall(callUid)) {
331 return;
332 }
333
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500334 QObject::disconnect(self.selectedCallChanged);
Olivier Soldano994971f2017-12-05 16:30:12 -0500335 self.selectedCallChanged = QObject::connect(callModel,
336 &lrc::api::NewCallModel::callStatusChanged,
337 [self](const std::string callId) {
Alexandre Lision210fe212016-01-27 11:15:13 -0500338 [self updateCall:NO];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500339 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500340}
341
342-(void) connectVideoSignals
343{
Olivier Soldano994971f2017-12-05 16:30:12 -0500344 if (accountInfo_ == nil)
345 return;
346
347 auto* callModel = accountInfo_->callModel.get();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500348 QObject::disconnect(self.videoStarted);
Olivier Soldano994971f2017-12-05 16:30:12 -0500349 self.videoStarted = QObject::connect(callModel,
350 &lrc::api::NewCallModel::remotePreviewStarted,
351 [self](const std::string& callId, Video::Renderer* renderer) {
352 NSLog(@"Video started!");
353 [self connectVideoRenderer:renderer];
354 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500355
Olivier Soldano994971f2017-12-05 16:30:12 -0500356 if (callModel->hasCall(callUid_)) {
357 if (auto renderer = callModel->getRenderer(callUid_)) {
358 QObject::disconnect(self.videoStarted);
359 [self connectVideoRenderer: renderer];
360 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500361 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500362 [self connectPreviewRenderer];
363
364}
365
366-(void) connectPreviewRenderer
367{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400368 QObject::disconnect(previewHolder.frameUpdated);
369 QObject::disconnect(previewHolder.stopped);
370 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400371 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500372 &Video::PreviewManager::previewStarted,
373 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500374 QObject::disconnect(previewHolder.frameUpdated);
375 previewHolder.frameUpdated = QObject::connect(renderer,
376 &Video::Renderer::frameUpdated,
377 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400378 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400379 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500380 });
381 });
382
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400383 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500384 &Video::PreviewManager::previewStopped,
385 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500386 QObject::disconnect(previewHolder.frameUpdated);
387 [previewView.layer setContents:nil];
388 });
389
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400390 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500391 &Video::Renderer::frameUpdated,
392 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400393 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400394 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500395 });
396}
397
398-(void) connectVideoRenderer: (Video::Renderer*)renderer
399{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400400 QObject::disconnect(videoHolder.frameUpdated);
401 QObject::disconnect(videoHolder.started);
402 QObject::disconnect(videoHolder.stopped);
Olivier Soldano994971f2017-12-05 16:30:12 -0500403
404 if(renderer == nil)
405 return;
406
Alexandre Lisionc5148052015-03-04 15:10:35 -0500407 videoHolder.frameUpdated = QObject::connect(renderer,
408 &Video::Renderer::frameUpdated,
409 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400410 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500411 });
412
413 videoHolder.started = QObject::connect(renderer,
414 &Video::Renderer::started,
415 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500416 QObject::disconnect(videoHolder.frameUpdated);
417 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
424 videoHolder.stopped = QObject::connect(renderer,
425 &Video::Renderer::stopped,
426 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500427 QObject::disconnect(videoHolder.frameUpdated);
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400428 [(CallLayer*)videoView.layer setVideoRunning:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500429 });
430}
431
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400432-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(NSView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500433{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500434 QSize res = renderer->size();
435
Alexandre Lision6731e132015-10-14 14:29:06 -0400436 auto frame_ptr = renderer->currentFrame();
437 auto frame_data = frame_ptr.ptr;
438 if (!frame_data)
439 return;
440
Alexandre Lisionc5148052015-03-04 15:10:35 -0500441 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400442 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500443 res.width(),
444 res.height(),
445 8,
446 4*res.width(),
447 colorSpace,
448 kCGImageAlphaPremultipliedLast);
449
450
451 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
452
453 /*We release some components*/
454 CGContextRelease(newContext);
455 CGColorSpaceRelease(colorSpace);
456
457 [CATransaction begin];
458 view.layer.contents = (__bridge id)newImage;
459 [CATransaction commit];
460
461 CFRelease(newImage);
462}
463
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400464-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallView*) view
465{
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400466 auto frame_ptr = renderer->currentFrame();
467 if (!frame_ptr.ptr)
468 return;
469
470 CallLayer* callLayer = (CallLayer*) view.layer;
471
Anthony Léonarda9d7c8e2018-01-03 14:41:17 -0500472 [callLayer setCurrentFrame:std::move(frame_ptr)];
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400473 [callLayer setVideoRunning:YES];
474}
475
Alexandre Lisionc5148052015-03-04 15:10:35 -0500476- (void) initFrame
477{
478 [self.view setFrame:self.view.superview.bounds];
479 [self.view setHidden:YES];
480 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400481 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500482}
483
484# pragma private IN/OUT animations
485
Olivier Soldano994971f2017-12-05 16:30:12 -0500486-(void)uncollapseRightView
487{
488 NSView *left = [[splitView subviews] objectAtIndex:0];
489 NSView *right = [[splitView subviews] objectAtIndex:1];
490 [right setHidden:NO];
491
492 CGFloat dividerThickness = [splitView dividerThickness];
493
494 // get the different frames
495 NSRect leftFrame = [left frame];
496 NSRect rightFrame = [right frame];
497
498 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
499 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
500 [left setFrameSize:leftFrame.size];
501 [right setFrame:rightFrame];
502 [splitView display];
503
504 [self.chatVC takeFocus];
505}
506
Alexandre Lisionc5148052015-03-04 15:10:35 -0500507-(void) cleanUp
508{
Anthony Léonard5c287642018-02-21 15:54:19 -0500509 if(self.splitView.isInFullScreenMode)
510 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400511 QObject::disconnect(videoHolder.frameUpdated);
512 QObject::disconnect(videoHolder.started);
513 QObject::disconnect(videoHolder.stopped);
514 QObject::disconnect(previewHolder.frameUpdated);
515 QObject::disconnect(previewHolder.stopped);
516 QObject::disconnect(previewHolder.started);
Anthony Léonardc0013bb2018-02-20 16:23:02 -0500517 QObject::disconnect(self.messageConnection);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500518 [previewView.layer setContents:nil];
Alexandre Lision883719f2015-10-22 17:37:45 -0400519
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500520 [_brokerPopoverVC performClose:self];
Alexandre Lision883719f2015-10-22 17:37:45 -0400521 [self.addToContactPopover performClose:self];
Alexandre Lision0e66aea2015-11-02 16:36:30 -0500522
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500523 [self.chatButton setHidden:YES];
524 [self.addParticipantButton setHidden:YES];
525 [self.transferButton setHidden:YES];
526
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500527 [self.chatButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500528 [self.mergeCallsButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400529 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500530
531 [personLabel setStringValue:@""];
532 [timeSpentLabel setStringValue:@""];
533 [stateLabel setStringValue:@""];
534 [self.addContactButton setHidden:YES];
Alexandre Lision62005312016-01-28 15:55:16 -0500535
536 [advancedButton setPressed:NO];
537 [advancedPanel setHidden:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500538}
539
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400540-(void) setupCallView
541{
542 if (accountInfo_ == nil)
543 return;
544
545 auto* callModel = accountInfo_->callModel.get();
546 auto* convModel = accountInfo_->conversationModel.get();
547
548 // when call comes in we want to show the controls/header
549 [self mouseIsMoving:YES];
550
551 [self connectVideoSignals];
552 /* check if text media is already present */
553 if(not callModel->hasCall(callUid_))
554 return;
555
556 [loadingIndicator setAnimates:YES];
557 [self updateCall:YES];
558
559 /* monitor media for messaging text messaging */
560 QObject::disconnect(self.messageConnection);
561 self.messageConnection = QObject::connect(convModel,
562 &lrc::api::ConversationModel::interactionStatusUpdated,
563 [self] (std::string convUid,
564 uint64_t msgId,
565 lrc::api::interaction::Info msg) {
566 if (msg.type == lrc::api::interaction::Type::TEXT) {
567 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
568 return;
569 }
570 [self uncollapseRightView];
571 }
572 });
573}
574
575-(void) showWithAnimation:(BOOL)animate
576{
577 if (!animate) {
578 [self.view setHidden:NO];
579 [self setupCallView];
580 return;
581 }
582
583 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
584 [self.view setHidden:NO];
585
586 [CATransaction begin];
587 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
588 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
589 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
590 [animation setDuration:0.2f];
591 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
592 [CATransaction setCompletionBlock:^{
593 [self setupCallView];
594 }];
595
596 [self.view.layer addAnimation:animation forKey:animation.keyPath];
597 [CATransaction commit];
598}
599
600-(void) hideWithAnimation:(BOOL)animate
Alexandre Lisionc5148052015-03-04 15:10:35 -0500601{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500602 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500603 return;
604 }
605
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400606 if (!animate) {
607 [self.view setHidden:YES];
608 return;
609 }
610
Alexandre Lisionc5148052015-03-04 15:10:35 -0500611 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
612 [CATransaction begin];
613 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
614 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
615 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
616 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500617 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500618
619 [CATransaction setCompletionBlock:^{
620 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400621 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500622 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500623// if (currentCall_) {
624// [self animateIn];
625// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500626 }];
627 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400628
629 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500630 [CATransaction commit];
631}
632
633/**
634 * Debug purpose
635 */
636-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
637{
638 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
639}
640
Alexandre Lision58cab672015-06-09 15:25:40 -0400641#pragma mark - Button methods
642
Alexandre Lisionc5148052015-03-04 15:10:35 -0500643- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500644 if (accountInfo_ == nil)
645 return;
646
647 auto* callModel = accountInfo_->callModel.get();
648
649 callModel->hangUp(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500650}
651
652- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500653 if (accountInfo_ == nil)
654 return;
655
Anthony Léonardf584cc52018-01-19 12:00:19 -0500656 // If we accept a conversation with a non trusted contact, we first accept it
657 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
658 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
659 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
660 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
661 accountInfo_->conversationModel->makePermanent(convUid_);
662 }
663
Olivier Soldano994971f2017-12-05 16:30:12 -0500664 auto* callModel = accountInfo_->callModel.get();
665
666 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500667}
668
669- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500670 if (accountInfo_ == nil)
671 return;
672
673 auto* callModel = accountInfo_->callModel.get();
674
675 callModel->toggleAudioRecord(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500676}
677
678- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500679 if (accountInfo_ == nil)
680 return;
681
682 auto* callModel = accountInfo_->callModel.get();
683
684 callModel->togglePause(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500685}
686
Alexandre Lision62005312016-01-28 15:55:16 -0500687- (IBAction)toggleAdvancedControls:(id)sender {
688 [advancedButton setPressed:!advancedButton.isPressed];
689 [advancedPanel setHidden:![advancedButton isPressed]];
690}
691
692- (IBAction)showDialpad:(id)sender {
693 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
694 [appDelegate showDialpad];
695}
696
Alexandre Lision58cab672015-06-09 15:25:40 -0400697-(IBAction)toggleChat:(id)sender;
698{
699 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
700 if (rightViewCollapsed) {
701 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400702 } else {
703 [self collapseRightView];
704 }
Alexandre Lision62005312016-01-28 15:55:16 -0500705 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400706}
707
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400708- (IBAction)muteAudio:(id)sender
709{
Olivier Soldano994971f2017-12-05 16:30:12 -0500710 if (accountInfo_ == nil)
711 return;
712
713 auto* callModel = accountInfo_->callModel.get();
714
715 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400716}
717
718- (IBAction)muteVideo:(id)sender
719{
Olivier Soldano994971f2017-12-05 16:30:12 -0500720 if (accountInfo_ == nil)
721 return;
722
723 auto* callModel = accountInfo_->callModel.get();
724
725 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400726}
Alexandre Lision883719f2015-10-22 17:37:45 -0400727
728- (IBAction)toggleTransferView:(id)sender {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500729 if (_brokerPopoverVC != nullptr) {
730 [_brokerPopoverVC performClose:self];
731 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500732 [self.transferButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400733 } else {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500734 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
735 _brokerPopoverVC = [[NSPopover alloc] init];
736 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
737 [_brokerPopoverVC setContentViewController:brokerVC];
738 [_brokerPopoverVC setAnimates:YES];
739 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
740 [_brokerPopoverVC setDelegate:self];
741 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
Alexandre Lision883719f2015-10-22 17:37:45 -0400742 [videoView setCallDelegate:nil];
743 }
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400744}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400745
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500746- (IBAction)toggleAddParticipantView:(id)sender {
747 if (_brokerPopoverVC != nullptr) {
748 [_brokerPopoverVC performClose:self];
749 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500750 [self.addParticipantButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500751 } else {
752 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::CONFERENCE];
753 _brokerPopoverVC = [[NSPopover alloc] init];
754 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
755 [_brokerPopoverVC setContentViewController:brokerVC];
756 [_brokerPopoverVC setAnimates:YES];
757 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
758 [_brokerPopoverVC setDelegate:self];
759 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
760 [videoView setCallDelegate:nil];
761 }
762}
763
764/**
765 * Merge current call with its parent call
766 */
767- (IBAction)mergeCalls:(id)sender
768{
Olivier Soldano994971f2017-12-05 16:30:12 -0500769// auto current = CallModel::instance().selectedCall();
770// current->joinToParent();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500771}
772
Alexandre Lision2db8f472015-07-22 15:05:46 -0400773#pragma mark - NSPopOverDelegate
774
Alexandre Lision266fca02015-09-28 14:47:05 -0400775- (void)popoverWillClose:(NSNotification *)notification
776{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500777 if (_brokerPopoverVC != nullptr) {
778 [_brokerPopoverVC performClose:self];
779 _brokerPopoverVC = NULL;
Alexandre Lision883719f2015-10-22 17:37:45 -0400780 }
Alexandre Lision266fca02015-09-28 14:47:05 -0400781
Alexandre Lision2db8f472015-07-22 15:05:46 -0400782 if (self.addToContactPopover != nullptr) {
783 [self.addToContactPopover performClose:self];
784 self.addToContactPopover = NULL;
785 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400786
Alexandre Lision62005312016-01-28 15:55:16 -0500787 [self.addContactButton setPressed:NO];
788 [self.transferButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500789 [self.addParticipantButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400790}
791
792- (void)popoverDidClose:(NSNotification *)notification
793{
794 [videoView setCallDelegate:self];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400795}
796
797#pragma mark - ContactLinkedDelegate
798
799- (void)contactLinked
800{
801 if (self.addToContactPopover != nullptr) {
802 [self.addToContactPopover performClose:self];
803 self.addToContactPopover = NULL;
804 }
805}
806
Alexandre Lision58cab672015-06-09 15:25:40 -0400807#pragma mark - NSSplitViewDelegate
808
809/* 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.
810 */
811- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
812{
813 NSView* rightView = [[splitView subviews] objectAtIndex:1];
814 return ([subview isEqual:rightView]);
815}
816
817
818- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
819{
820 NSView* rightView = [[splitView subviews] objectAtIndex:1];
821 return ([subview isEqual:rightView]);
822}
823
824
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400825# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400826
827- (void) callShouldToggleFullScreen
828{
829 if(self.splitView.isInFullScreenMode)
830 [self.splitView exitFullScreenModeWithOptions:nil];
831 else {
832 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
833 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
834 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
835 NSFullScreenModeApplicationPresentationOptions, nil];
836
837 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
838 }
839}
840
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400841-(void) mouseIsMoving:(BOOL) move
842{
843 [[controlsPanel animator] setAlphaValue:move]; // fade out
844 [[headerContainer animator] setAlphaValue:move];
845}
846
Alexandre Lisionc5148052015-03-04 15:10:35 -0500847@end