blob: 79f34b2a5437aed7b70f70bfeb1f4800f0459014 [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"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040048
Alexandre Lisionc5148052015-03-04 15:10:35 -050049@interface RendererConnectionsHolder : NSObject
50
51@property QMetaObject::Connection frameUpdated;
52@property QMetaObject::Connection started;
53@property QMetaObject::Connection stopped;
54
55@end
56
57@implementation RendererConnectionsHolder
58
59@end
60
Olivier Soldano994971f2017-12-05 16:30:12 -050061@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate> {
62 std::string convUid_;
63 std::string callUid_;
64 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050065 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050066}
Alexandre Lisionc5148052015-03-04 15:10:35 -050067
Alexandre Lision210fe212016-01-27 11:15:13 -050068// Main container
69@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
70
Alexandre Lision89edc6a2015-11-09 11:30:47 -050071// Header info
72@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Alexandre Lision883719f2015-10-22 17:37:45 -040073@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
74@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050075@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
Alexandre Lision210fe212016-01-27 11:15:13 -050076@property (unsafe_unretained) IBOutlet NSImageView* personPhoto;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050077
78// Call Controls
79@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
Alexandre Lision62005312016-01-28 15:55:16 -050080
Alexandre Lision4baba4c2016-02-11 13:00:57 -050081@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
82@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
83@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
84@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
85@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
86@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
87@property (unsafe_unretained) IBOutlet IconButton* addContactButton;
88@property (unsafe_unretained) IBOutlet IconButton* transferButton;
89@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
90@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040091
Alexandre Lision62005312016-01-28 15:55:16 -050092@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
93@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
94
Alexandre Lisiond18fa272015-06-15 11:18:03 -040095
Alexandre Lision89edc6a2015-11-09 11:30:47 -050096// Join call panel
97@property (unsafe_unretained) IBOutlet NSView* joinPanel;
98@property (unsafe_unretained) IBOutlet NSButton* mergeCallsButton;
99
Alexandre Lision2db8f472015-07-22 15:05:46 -0400100@property (strong) NSPopover* addToContactPopover;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500101@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision883719f2015-10-22 17:37:45 -0400102@property (strong) IBOutlet ChatVC* chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400103
Alexandre Lision210fe212016-01-27 11:15:13 -0500104// Ringing call panel
105@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
106@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
107@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;
108
109// Outgoing call panel
110@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
111@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500112
113// Video
Alexandre Lision58cab672015-06-09 15:25:40 -0400114@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lision58cab672015-06-09 15:25:40 -0400115@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500116
117@property RendererConnectionsHolder* previewHolder;
118@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400119@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500120@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400121@property QMetaObject::Connection messageConnection;
122@property QMetaObject::Connection mediaAddedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500123
124@end
125
126@implementation CurrentCallVC
Olivier Soldano994971f2017-12-05 16:30:12 -0500127@synthesize personLabel, personPhoto, stateLabel, holdOnOffButton, hangUpButton,
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500128 recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
Olivier Soldano994971f2017-12-05 16:30:12 -0500129 muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView,
130 incomingDisplayName, incomingPersonPhoto, previewView, splitView, loadingIndicator, ringingPanel, joinPanel,
131 outgoingPanel;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500132
133@synthesize previewHolder;
134@synthesize videoHolder;
135
Olivier Soldano994971f2017-12-05 16:30:12 -0500136-(void) setCurrentCall:(const std::string&)callUid
137 conversation:(const std::string&)convUid
138 account:(const lrc::api::account::Info*)account;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500139{
Olivier Soldano994971f2017-12-05 16:30:12 -0500140 if(account == nil)
141 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400142
Olivier Soldano994971f2017-12-05 16:30:12 -0500143 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500144
Olivier Soldano994971f2017-12-05 16:30:12 -0500145 if (not callModel->hasCall(callUid)){
146 callUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400147 return;
148 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500149 callUid_ = callUid;
150 convUid_ = convUid;
151 accountInfo_ = account;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500152 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500153}
154
155- (void)awakeFromNib
156{
157 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400158 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500159
Alexandre Lisionc5148052015-03-04 15:10:35 -0500160 [previewView setWantsLayer:YES];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500161 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
162 [previewView.layer setContentsGravity:kCAGravityResizeAspectFill];
163 [previewView.layer setFrame:previewView.frame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500164
165 [controlsPanel setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500166 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
167 [controlsPanel.layer setFrame:controlsPanel.frame];
168
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400169 previewHolder = [[RendererConnectionsHolder alloc] init];
170 videoHolder = [[RendererConnectionsHolder alloc] init];
171
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400172 [loadingIndicator setColor:[NSColor whiteColor]];
173 [loadingIndicator setNumberOfLines:100];
174 [loadingIndicator setWidthOfLine:2];
175 [loadingIndicator setLengthOfLine:2];
176 [loadingIndicator setInnerMargin:30];
177
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400178 [self.videoView setCallDelegate:self];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500179}
180
Anthony Léonard66a050c2018-01-03 16:32:34 -0500181-(void) updateDurationLabel
182{
183 if (accountInfo_ != nil) {
184 auto* callModel = accountInfo_->callModel.get();
185 if (callModel->hasCall(callUid_)) {
186 auto& callStatus = callModel->getCall(callUid_).status;
187 if (callStatus != lrc::api::call::Status::ENDED &&
188 callStatus != lrc::api::call::Status::TERMINATING &&
189 callStatus != lrc::api::call::Status::INVALID) {
190 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
191 return;
192 }
193 }
194 }
195
196 // If call is not running anymore or accountInfo_ is not set for any reason
197 // we stop the refresh loop
198 [refreshDurationTimer invalidate];
199 refreshDurationTimer = nil;
200 [timeSpentLabel setHidden:YES];
201}
202
Olivier Soldano994971f2017-12-05 16:30:12 -0500203-(void) updateCall:(BOOL) firstRun
Alexandre Lisionc5148052015-03-04 15:10:35 -0500204{
Olivier Soldano994971f2017-12-05 16:30:12 -0500205 if (accountInfo_ == nil)
206 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400207
Olivier Soldano994971f2017-12-05 16:30:12 -0500208 auto* callModel = accountInfo_->callModel.get();
209 if (not callModel->hasCall(callUid_)) {
210 return;
211 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500212
Olivier Soldano994971f2017-12-05 16:30:12 -0500213 auto currentCall = callModel->getCall(callUid_);
214 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
215 [personLabel setStringValue:@(currentCall.peer.c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500216 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
217 [timeSpentLabel setHidden:NO];
218 if (refreshDurationTimer == nil)
219 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
220 target:self
221 selector:@selector(updateDurationLabel)
222 userInfo:nil
223 repeats:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500224 [stateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400225
Olivier Soldano994971f2017-12-05 16:30:12 -0500226 if (firstRun) {
227 //QVariant photo = GlobalInstances::pixmapManipulator().callPhoto(current, QSize(100,100));
228 //[personPhoto setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
229 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500230
Olivier Soldano994971f2017-12-05 16:30:12 -0500231 // Default values for this views
232 [loadingIndicator setHidden:YES];
233 [videoView setShouldAcceptInteractions:NO];
234 [ringingPanel setHidden:YES];
235 [outgoingPanel setHidden:YES];
236 [controlsPanel setHidden:NO];
237 [headerContainer setHidden:NO];
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500238 [chatButton setHidden:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500239
Olivier Soldano994971f2017-12-05 16:30:12 -0500240 using Status = lrc::api::call::Status;
241 switch (currentCall.status) {
242 case Status::SEARCHING:
243 case Status::OUTGOING_REQUESTED:
244 case Status::CONNECTING:
245 [loadingIndicator setHidden:NO];
246 break;
247 case Status::INCOMING_RINGING:
248 [controlsPanel setHidden:YES];
249 [outgoingPanel setHidden:YES];
250 [self setupIncoming:callUid_];
251 break;
252 case Status::OUTGOING_RINGING:
253 [controlsPanel setHidden:YES];
254 [outgoingPanel setHidden:NO];
255 break;
256// case Status::CONFERENCE:
257// [self setupConference:currentCall];
258// break;
259 case Status::PAUSED:
260 case Status::PEER_PAUSED:
261 case Status::INACTIVE:
262 case Status::IN_PROGRESS:
263 case Status::CONNECTED:
264 case Status::AUTO_ANSWERING:
265 break;
266 case Status::ENDED:
267 case Status::TERMINATING:
268 case Status::INVALID:
269 [controlsPanel setHidden:YES];
270 [outgoingPanel setHidden:NO];
271 if(self.splitView.isInFullScreenMode)
272 [self.splitView exitFullScreenModeWithOptions:nil];
273 [self animateOut];
274 break;
275 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500276
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500277}
278
Olivier Soldano994971f2017-12-05 16:30:12 -0500279-(void) setupIncoming:(const std::string&) callId
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500280{
Olivier Soldano994971f2017-12-05 16:30:12 -0500281 if (accountInfo_ == nil)
282 return;
283
284 auto* callModel = accountInfo_->callModel.get();
285 if (not callModel->hasCall(callUid_)) {
286 return;
287 }
288
289 auto call = callModel->getCall(callUid_);
290 auto* convModel = accountInfo_->conversationModel.get();
291
292 [ringingPanel setHidden:NO];
293 [controlsPanel setHidden:YES];
294 [headerContainer setHidden:YES];
295 auto it = std::find_if(convModel->allFilteredConversations().begin(), convModel->allFilteredConversations().end(),
296 [self] (const lrc::api::conversation::Info& conv) {
297 return convUid_ == conv.uid;
298 });
299 if (it != convModel->allFilteredConversations().end()) {
300 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
301 QVariant photo = imgManip.conversationPhoto(*it, *accountInfo_);
302 [incomingPersonPhoto setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
303 [incomingDisplayName setStringValue:@(call.peer.c_str())];
304 }
305}
306
307
308-(void) setupConference:(Call*) c
309{
310 [videoView setShouldAcceptInteractions:YES];
311 [self.chatButton setHidden:NO];
312 [joinPanel setHidden:YES];
313 [self.addParticipantButton setHidden:NO];
314 [self.transferButton setHidden:YES];
315}
316
317-(void)collapseRightView
318{
319 NSView *right = [[splitView subviews] objectAtIndex:1];
320 NSView *left = [[splitView subviews] objectAtIndex:0];
321 NSRect leftFrame = [left frame];
322 [right setHidden:YES];
323 [splitView display];
324}
325
326- (void) changeCallSelection:(std::string&)callUid
327{
328 if (accountInfo_ == nil)
329 return;
330
331 auto* callModel = accountInfo_->callModel.get();
332 if (not callModel->hasCall(callUid)) {
333 return;
334 }
335
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500336 QObject::disconnect(self.selectedCallChanged);
Olivier Soldano994971f2017-12-05 16:30:12 -0500337 self.selectedCallChanged = QObject::connect(callModel,
338 &lrc::api::NewCallModel::callStatusChanged,
339 [self](const std::string callId) {
Alexandre Lision210fe212016-01-27 11:15:13 -0500340 [self updateCall:NO];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500341 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500342}
343
344-(void) connectVideoSignals
345{
Olivier Soldano994971f2017-12-05 16:30:12 -0500346 if (accountInfo_ == nil)
347 return;
348
349 auto* callModel = accountInfo_->callModel.get();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500350 QObject::disconnect(self.videoStarted);
Olivier Soldano994971f2017-12-05 16:30:12 -0500351 self.videoStarted = QObject::connect(callModel,
352 &lrc::api::NewCallModel::remotePreviewStarted,
353 [self](const std::string& callId, Video::Renderer* renderer) {
354 NSLog(@"Video started!");
355 [self connectVideoRenderer:renderer];
356 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500357
Olivier Soldano994971f2017-12-05 16:30:12 -0500358 if (callModel->hasCall(callUid_)) {
359 if (auto renderer = callModel->getRenderer(callUid_)) {
360 QObject::disconnect(self.videoStarted);
361 [self connectVideoRenderer: renderer];
362 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500363 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500364 [self connectPreviewRenderer];
365
366}
367
368-(void) connectPreviewRenderer
369{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400370 QObject::disconnect(previewHolder.frameUpdated);
371 QObject::disconnect(previewHolder.stopped);
372 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400373 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500374 &Video::PreviewManager::previewStarted,
375 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500376 QObject::disconnect(previewHolder.frameUpdated);
377 previewHolder.frameUpdated = QObject::connect(renderer,
378 &Video::Renderer::frameUpdated,
379 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400380 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400381 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500382 });
383 });
384
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400385 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500386 &Video::PreviewManager::previewStopped,
387 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500388 QObject::disconnect(previewHolder.frameUpdated);
389 [previewView.layer setContents:nil];
390 });
391
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400392 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500393 &Video::Renderer::frameUpdated,
394 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400395 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400396 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500397 });
398}
399
400-(void) connectVideoRenderer: (Video::Renderer*)renderer
401{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400402 QObject::disconnect(videoHolder.frameUpdated);
403 QObject::disconnect(videoHolder.started);
404 QObject::disconnect(videoHolder.stopped);
Olivier Soldano994971f2017-12-05 16:30:12 -0500405
406 if(renderer == nil)
407 return;
408
Alexandre Lisionc5148052015-03-04 15:10:35 -0500409 videoHolder.frameUpdated = QObject::connect(renderer,
410 &Video::Renderer::frameUpdated,
411 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400412 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500413 });
414
415 videoHolder.started = QObject::connect(renderer,
416 &Video::Renderer::started,
417 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500418 QObject::disconnect(videoHolder.frameUpdated);
419 videoHolder.frameUpdated = QObject::connect(renderer,
420 &Video::Renderer::frameUpdated,
421 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400422 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500423 });
424 });
425
426 videoHolder.stopped = QObject::connect(renderer,
427 &Video::Renderer::stopped,
428 [=]() {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500429 QObject::disconnect(videoHolder.frameUpdated);
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400430 [(CallLayer*)videoView.layer setVideoRunning:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500431 });
432}
433
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400434-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(NSView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500435{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500436 QSize res = renderer->size();
437
Alexandre Lision6731e132015-10-14 14:29:06 -0400438 auto frame_ptr = renderer->currentFrame();
439 auto frame_data = frame_ptr.ptr;
440 if (!frame_data)
441 return;
442
Alexandre Lisionc5148052015-03-04 15:10:35 -0500443 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400444 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500445 res.width(),
446 res.height(),
447 8,
448 4*res.width(),
449 colorSpace,
450 kCGImageAlphaPremultipliedLast);
451
452
453 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
454
455 /*We release some components*/
456 CGContextRelease(newContext);
457 CGColorSpaceRelease(colorSpace);
458
459 [CATransaction begin];
460 view.layer.contents = (__bridge id)newImage;
461 [CATransaction commit];
462
463 CFRelease(newImage);
464}
465
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400466-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallView*) view
467{
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400468 auto frame_ptr = renderer->currentFrame();
469 if (!frame_ptr.ptr)
470 return;
471
472 CallLayer* callLayer = (CallLayer*) view.layer;
473
Anthony Léonarda9d7c8e2018-01-03 14:41:17 -0500474 [callLayer setCurrentFrame:std::move(frame_ptr)];
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400475 [callLayer setVideoRunning:YES];
476}
477
Alexandre Lisionc5148052015-03-04 15:10:35 -0500478- (void) initFrame
479{
480 [self.view setFrame:self.view.superview.bounds];
481 [self.view setHidden:YES];
482 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400483 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500484}
485
486# pragma private IN/OUT animations
487
Olivier Soldano994971f2017-12-05 16:30:12 -0500488-(void)uncollapseRightView
489{
490 NSView *left = [[splitView subviews] objectAtIndex:0];
491 NSView *right = [[splitView subviews] objectAtIndex:1];
492 [right setHidden:NO];
493
494 CGFloat dividerThickness = [splitView dividerThickness];
495
496 // get the different frames
497 NSRect leftFrame = [left frame];
498 NSRect rightFrame = [right frame];
499
500 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
501 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
502 [left setFrameSize:leftFrame.size];
503 [right setFrame:rightFrame];
504 [splitView display];
505
506 [self.chatVC takeFocus];
507}
508
Alexandre Lisionc5148052015-03-04 15:10:35 -0500509-(void) animateIn
510{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500511 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
512 [self.view setHidden:NO];
513
514 [CATransaction begin];
515 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
516 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
517 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
518 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500519 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500520 [CATransaction setCompletionBlock:^{
Olivier Soldano994971f2017-12-05 16:30:12 -0500521 if (accountInfo_ == nil)
522 return;
523
524 auto* callModel = accountInfo_->callModel.get();
525 auto* convModel = accountInfo_->conversationModel.get();
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400526
527 // when call comes in we want to show the controls/header
528 [self mouseIsMoving:YES];
529
Alexandre Lisionc5148052015-03-04 15:10:35 -0500530 [self connectVideoSignals];
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400531 /* check if text media is already present */
Olivier Soldano994971f2017-12-05 16:30:12 -0500532 if(not callModel->hasCall(callUid_))
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400533 return;
Alexandre Lision21666f32015-09-22 17:04:36 -0400534
Alexandre Lision22d615b2016-01-25 12:54:34 -0500535 [loadingIndicator setAnimates:YES];
Alexandre Lision210fe212016-01-27 11:15:13 -0500536 [self updateCall:YES];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500537
Olivier Soldano994971f2017-12-05 16:30:12 -0500538 /* monitor media for messaging text messaging */
539 QObject::disconnect(self.messageConnection);
540 self.messageConnection = QObject::connect(convModel,
541 &lrc::api::ConversationModel::interactionStatusUpdated,
542 [self] (std::string convUid,
543 uint64_t msgId,
544 lrc::api::interaction::Info msg) {
545 if (msg.type == lrc::api::interaction::Type::TEXT) {
546 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
547 return;
548 }
549 [self uncollapseRightView];
550 }
551 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500552 }];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500553
Olivier Soldano994971f2017-12-05 16:30:12 -0500554 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500555 [CATransaction commit];
556}
557
558-(void) cleanUp
559{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400560 QObject::disconnect(videoHolder.frameUpdated);
561 QObject::disconnect(videoHolder.started);
562 QObject::disconnect(videoHolder.stopped);
563 QObject::disconnect(previewHolder.frameUpdated);
564 QObject::disconnect(previewHolder.stopped);
565 QObject::disconnect(previewHolder.started);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500566 [previewView.layer setContents:nil];
Alexandre Lision883719f2015-10-22 17:37:45 -0400567
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500568 [_brokerPopoverVC performClose:self];
Alexandre Lision883719f2015-10-22 17:37:45 -0400569 [self.addToContactPopover performClose:self];
Alexandre Lision0e66aea2015-11-02 16:36:30 -0500570
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500571 [self.chatButton setHidden:YES];
572 [self.addParticipantButton setHidden:YES];
573 [self.transferButton setHidden:YES];
574
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500575 [self.chatButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500576 [self.mergeCallsButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400577 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500578
579 [personLabel setStringValue:@""];
580 [timeSpentLabel setStringValue:@""];
581 [stateLabel setStringValue:@""];
582 [self.addContactButton setHidden:YES];
Alexandre Lision62005312016-01-28 15:55:16 -0500583
584 [advancedButton setPressed:NO];
585 [advancedPanel setHidden:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500586}
587
588-(void) animateOut
589{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500590 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500591 return;
592 }
593
594 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
595 [CATransaction begin];
596 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
597 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
598 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
599 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500600 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500601
602 [CATransaction setCompletionBlock:^{
603 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400604 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500605 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500606// if (currentCall_) {
607// [self animateIn];
608// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500609 }];
610 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400611
612 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500613 [CATransaction commit];
614}
615
616/**
617 * Debug purpose
618 */
619-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
620{
621 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
622}
623
Alexandre Lision58cab672015-06-09 15:25:40 -0400624#pragma mark - Button methods
625
Alexandre Lisionc5148052015-03-04 15:10:35 -0500626- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500627 if (accountInfo_ == nil)
628 return;
629
630 auto* callModel = accountInfo_->callModel.get();
631
632 callModel->hangUp(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500633}
634
635- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500636 if (accountInfo_ == nil)
637 return;
638
639 auto* callModel = accountInfo_->callModel.get();
640
641 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500642}
643
644- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500645 if (accountInfo_ == nil)
646 return;
647
648 auto* callModel = accountInfo_->callModel.get();
649
650 callModel->toggleAudioRecord(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500651}
652
653- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500654 if (accountInfo_ == nil)
655 return;
656
657 auto* callModel = accountInfo_->callModel.get();
658
659 callModel->togglePause(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500660}
661
Alexandre Lision62005312016-01-28 15:55:16 -0500662- (IBAction)toggleAdvancedControls:(id)sender {
663 [advancedButton setPressed:!advancedButton.isPressed];
664 [advancedPanel setHidden:![advancedButton isPressed]];
665}
666
667- (IBAction)showDialpad:(id)sender {
668 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
669 [appDelegate showDialpad];
670}
671
Alexandre Lision58cab672015-06-09 15:25:40 -0400672-(IBAction)toggleChat:(id)sender;
673{
674 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
675 if (rightViewCollapsed) {
676 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400677 } else {
678 [self collapseRightView];
679 }
Alexandre Lision62005312016-01-28 15:55:16 -0500680 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400681}
682
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400683- (IBAction)muteAudio:(id)sender
684{
Olivier Soldano994971f2017-12-05 16:30:12 -0500685 if (accountInfo_ == nil)
686 return;
687
688 auto* callModel = accountInfo_->callModel.get();
689
690 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400691}
692
693- (IBAction)muteVideo:(id)sender
694{
Olivier Soldano994971f2017-12-05 16:30:12 -0500695 if (accountInfo_ == nil)
696 return;
697
698 auto* callModel = accountInfo_->callModel.get();
699
700 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400701}
Alexandre Lision883719f2015-10-22 17:37:45 -0400702
703- (IBAction)toggleTransferView:(id)sender {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500704 if (_brokerPopoverVC != nullptr) {
705 [_brokerPopoverVC performClose:self];
706 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500707 [self.transferButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400708 } else {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500709 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
710 _brokerPopoverVC = [[NSPopover alloc] init];
711 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
712 [_brokerPopoverVC setContentViewController:brokerVC];
713 [_brokerPopoverVC setAnimates:YES];
714 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
715 [_brokerPopoverVC setDelegate:self];
716 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
Alexandre Lision883719f2015-10-22 17:37:45 -0400717 [videoView setCallDelegate:nil];
718 }
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400719}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400720
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500721- (IBAction)toggleAddParticipantView:(id)sender {
722 if (_brokerPopoverVC != nullptr) {
723 [_brokerPopoverVC performClose:self];
724 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500725 [self.addParticipantButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500726 } else {
727 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::CONFERENCE];
728 _brokerPopoverVC = [[NSPopover alloc] init];
729 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
730 [_brokerPopoverVC setContentViewController:brokerVC];
731 [_brokerPopoverVC setAnimates:YES];
732 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
733 [_brokerPopoverVC setDelegate:self];
734 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
735 [videoView setCallDelegate:nil];
736 }
737}
738
739/**
740 * Merge current call with its parent call
741 */
742- (IBAction)mergeCalls:(id)sender
743{
Olivier Soldano994971f2017-12-05 16:30:12 -0500744// auto current = CallModel::instance().selectedCall();
745// current->joinToParent();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500746}
747
Alexandre Lision2db8f472015-07-22 15:05:46 -0400748#pragma mark - NSPopOverDelegate
749
Alexandre Lision266fca02015-09-28 14:47:05 -0400750- (void)popoverWillClose:(NSNotification *)notification
751{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500752 if (_brokerPopoverVC != nullptr) {
753 [_brokerPopoverVC performClose:self];
754 _brokerPopoverVC = NULL;
Alexandre Lision883719f2015-10-22 17:37:45 -0400755 }
Alexandre Lision266fca02015-09-28 14:47:05 -0400756
Alexandre Lision2db8f472015-07-22 15:05:46 -0400757 if (self.addToContactPopover != nullptr) {
758 [self.addToContactPopover performClose:self];
759 self.addToContactPopover = NULL;
760 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400761
Alexandre Lision62005312016-01-28 15:55:16 -0500762 [self.addContactButton setPressed:NO];
763 [self.transferButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500764 [self.addParticipantButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400765}
766
767- (void)popoverDidClose:(NSNotification *)notification
768{
769 [videoView setCallDelegate:self];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400770}
771
772#pragma mark - ContactLinkedDelegate
773
774- (void)contactLinked
775{
776 if (self.addToContactPopover != nullptr) {
777 [self.addToContactPopover performClose:self];
778 self.addToContactPopover = NULL;
779 }
780}
781
Alexandre Lision58cab672015-06-09 15:25:40 -0400782#pragma mark - NSSplitViewDelegate
783
784/* 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.
785 */
786- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
787{
788 NSView* rightView = [[splitView subviews] objectAtIndex:1];
789 return ([subview isEqual:rightView]);
790}
791
792
793- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
794{
795 NSView* rightView = [[splitView subviews] objectAtIndex:1];
796 return ([subview isEqual:rightView]);
797}
798
799
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400800# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400801
802- (void) callShouldToggleFullScreen
803{
804 if(self.splitView.isInFullScreenMode)
805 [self.splitView exitFullScreenModeWithOptions:nil];
806 else {
807 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
808 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
809 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
810 NSFullScreenModeApplicationPresentationOptions, nil];
811
812 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
813 }
814}
815
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400816-(void) mouseIsMoving:(BOOL) move
817{
818 [[controlsPanel animator] setAlphaValue:move]; // fade out
819 [[headerContainer animator] setAlphaValue:move];
820}
821
Alexandre Lisionc5148052015-03-04 15:10:35 -0500822@end