blob: 0fad742676a930af5b83bc1d86ff9a4375f15fab [file] [log] [blame]
Edric Milaret627500d2015-03-27 16:41:40 -04001/***************************************************************************
Edric Milaret4bba46d2015-04-29 16:33:38 -04002 * Copyright (C) 2015 by Savoir-Faire Linux *
Edric Milaret627500d2015-03-27 16:41:40 -04003 * Author: Edric Ladent Milaret <edric.ladent-milaret@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, see <http://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
19#include "callwidget.h"
20#include "ui_callwidget.h"
21
Edric Milaret43f3c1e2015-07-16 17:52:47 -040022#include <QClipboard>
23
Edric Milaret53f57b62015-05-11 11:02:17 -040024#include <memory>
Edric Milaret627500d2015-03-27 16:41:40 -040025
Edric Milaret43f3c1e2015-07-16 17:52:47 -040026//ERROR is defined in windows.h
27#include "utils.h"
28#undef ERROR
29
Edric Milaret627500d2015-03-27 16:41:40 -040030#include "audio/settings.h"
31#include "personmodel.h"
Edric Milaret43f3c1e2015-07-16 17:52:47 -040032#include "person.h"
Edric Milaret627500d2015-03-27 16:41:40 -040033#include "fallbackpersoncollection.h"
Edric Milaret67007d12015-05-07 09:40:09 -040034#include "categorizedcontactmodel.h"
Edric Milaret6a642e22015-05-27 15:06:30 -040035#include "localhistorycollection.h"
Edric Milaret1b197eb2015-06-01 15:09:56 -040036#include "media/text.h"
37#include "media/recording.h"
38#include "media/textrecording.h"
Edric Milaret627500d2015-03-27 16:41:40 -040039
Edric Milaret559bda52015-04-29 17:02:31 -040040#include "wizarddialog.h"
Edric Milaret83b248c2015-06-02 11:42:23 -040041#include "windowscontactbackend.h"
Edric Milaret43f3c1e2015-07-16 17:52:47 -040042#include "contactdialog.h"
43#include "contactpicker.h"
Edric Milaret2afd2bf2015-07-21 17:12:25 -040044#include "contactmethodpicker.h"
Edric Milaret627500d2015-03-27 16:41:40 -040045
46CallWidget::CallWidget(QWidget *parent) :
47 NavWidget(Main ,parent),
Edric Milaret7d40a4a2015-05-13 13:01:15 -040048 ui(new Ui::CallWidget),
49 menu_(new QMenu())
Edric Milaret627500d2015-03-27 16:41:40 -040050{
51 ui->setupUi(this);
52
Edric Milaret627500d2015-03-27 16:41:40 -040053 ui->callInvite->setVisible(false);
Edric Milareta9f937f2015-06-01 17:10:34 -040054 ui->videoWidget->setVisible(false);
Edric Milaret627500d2015-03-27 16:41:40 -040055
Edric Milaret6456baa2015-05-21 12:18:07 -040056 setActualCall(nullptr);
Edric Milaret627500d2015-03-27 16:41:40 -040057 videoRenderer_ = nullptr;
58
Edric Milaret3aca8e32015-06-12 10:01:40 -040059 connect(ui->videoWidget, SIGNAL(setChatVisibility(bool)),
60 ui->instantMessagingWidget, SLOT(setVisible(bool)));
61
Edric Milaretd8528fa2015-07-07 14:13:51 -040062 ui->spinnerWidget->hide();
Edric Milaretc7ab5502015-06-15 11:18:02 -040063 spinner_ = new QMovie(":/images/spinner.gif");
64 if (spinner_->isValid()) {
65 ui->spinnerLabel->setMovie(spinner_);
66 }
67
Edric Milaret627500d2015-03-27 16:41:40 -040068 try {
69 callModel_ = CallModel::instance();
70
71 connect(callModel_, SIGNAL(incomingCall(Call*)),
72 this, SLOT(callIncoming(Call*)));
73 connect(callModel_, SIGNAL(callAdded(Call*,Call*)),
74 this, SLOT(addedCall(Call*, Call*)));
75 connect(callModel_, SIGNAL(callStateChanged(Call*, Call::State)),
76 this, SLOT(callStateChanged(Call*, Call::State)));
77
Edric Milarete5afb072015-06-02 16:45:44 -040078 connect(AccountModel::instance(),
79 SIGNAL(accountStateChanged(Account*,Account::RegistrationState)),
80 this,
81 SLOT(checkRegistrationState(Account*,Account::RegistrationState)));
82
Edric Milaret67007d12015-05-07 09:40:09 -040083 connect(AccountModel::instance()
84 , SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))
85 , this
86 , SLOT(findRingAccount(QModelIndex, QModelIndex, QVector<int>)));
87
Edric Milaret627500d2015-03-27 16:41:40 -040088 ui->callList->setModel(callModel_);
Edric Milaret029b95a2015-06-09 09:51:44 -040089 ui->callList->setSelectionModel(callModel_->selectionModel());
Edric Milaret627500d2015-03-27 16:41:40 -040090
Edric Milaret43f3c1e2015-07-16 17:52:47 -040091 auto personCollection = PersonModel::instance()->
92 addCollection<WindowsContactBackend>(LoadOptions::FORCE_ENABLED);
93
94 CategorizedContactModel::instance()->setSortAlphabetical(false);
95 CategorizedContactModel::instance()->setUnreachableHidden(true);
96 ui->contactView->setModel(CategorizedContactModel::instance());
97 contactDelegate_ = new ContactDelegate();
98 ui->contactView->setItemDelegate(contactDelegate_);
99
Edric Milaret627500d2015-03-27 16:41:40 -0400100 CategorizedHistoryModel::instance()->
Edric Milaret6a642e22015-05-27 15:06:30 -0400101 addCollection<LocalHistoryCollection>(LoadOptions::FORCE_ENABLED);
Edric Milaret627500d2015-03-27 16:41:40 -0400102
Edric Milaret12353822015-05-14 14:41:09 -0400103 ui->historyList->setModel(CategorizedHistoryModel::SortedProxy::instance()->model());
104 CategorizedHistoryModel::SortedProxy::instance()->model()->sort(0, Qt::DescendingOrder);
Edric Milaret53f57b62015-05-11 11:02:17 -0400105 ui->historyList->setHeaderHidden(true);
Edric Milaret01f23842015-06-22 14:46:01 -0400106 historyDelegate_ = new HistoryDelegate();
107 ui->historyList->setItemDelegate(historyDelegate_);
Edric Milaret77dd6982015-07-07 16:45:41 -0400108
109 connect(CategorizedHistoryModel::SortedProxy::instance()->model(), &QSortFilterProxyModel::layoutChanged, [=]() {
110 auto idx = CategorizedHistoryModel::SortedProxy::instance()->model()->index(0,0);
111 if (idx.isValid())
112 ui->historyList->setExpanded(idx, true);
113 });
114
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400115 ui->historyList->setContextMenuPolicy(Qt::CustomContextMenu);
116 connect(ui->historyList, &QListView::customContextMenuRequested, [=](const QPoint& pos){
117 if (ui->historyList->currentIndex().parent().isValid()) {
118 QPoint globalPos = ui->historyList->mapToGlobal(pos);
119 QMenu menu;
120
121 ContactMethod* contactMethod = ui->historyList->currentIndex()
122 .data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod*>();
123
124 auto copyAction = new QAction("Copy number", this);
125 menu.addAction(copyAction);
126 connect(copyAction, &QAction::triggered, [=]() {
127 QApplication::clipboard()->setText(contactMethod->uri());
128 });
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400129 if (not contactMethod->contact()) {
130 auto addNew = new QAction("Add to new contact", this);
131 menu.addAction(addNew);
132 connect(addNew, &QAction::triggered, [=]() {
133 ContactDialog dialog(contactMethod->uri());
134 auto ret = dialog.exec();
135 if (!ret || dialog.getName().isEmpty())
136 return;
137 auto *newPerson = new Person();
138 newPerson->setFormattedName(dialog.getName());
139 Person::ContactMethods cM;
140 cM.append(contactMethod);
141 newPerson->setContactMethods(cM);
142 newPerson->setUid(Utils::GenGUID().toLocal8Bit());
143 PersonModel::instance()->addNewPerson(newPerson, personCollection);
144 });
145 auto addExisting = new QAction("Add to existing contact", this);
146 menu.addAction(addExisting);
147 connect(addExisting, &QAction::triggered, [=]() {
148 /* Force LRC to update contact model as adding a number
149 to a contact without one didn't render him reachable */
150 CategorizedContactModel::instance()->setUnreachableHidden(false);
151
152 ContactPicker contactPicker;
153 contactPicker.move(globalPos.x(), globalPos.y() - (contactPicker.height()/2));
154 auto ret = contactPicker.exec();
155 if (!ret)
156 return;
157 auto p = contactPicker.getPersonSelected();
158 Person::ContactMethods cM (p->phoneNumbers());
159 cM.append(contactMethod);
160 p->setContactMethods(cM);
161 p->save();
162 CategorizedContactModel::instance()->setUnreachableHidden(true);
163 });
164 }
165 menu.exec(globalPos);
166 }
167 });
Edric Milaret53f57b62015-05-11 11:02:17 -0400168
Edric Milaret12353822015-05-14 14:41:09 -0400169 ui->sortComboBox->setModel(CategorizedHistoryModel::SortedProxy::instance()->categoryModel());
Edric Milaret7d40a4a2015-05-13 13:01:15 -0400170
Edric Milaret627500d2015-03-27 16:41:40 -0400171 findRingAccount();
172
173 } catch (...) {
174 qDebug() << "INIT ERROR";
175 }
176}
177
178CallWidget::~CallWidget()
179{
180 delete ui;
Edric Milaretc7ab5502015-06-15 11:18:02 -0400181 delete spinner_;
182 delete menu_;
Edric Milaret01f23842015-06-22 14:46:01 -0400183 delete historyDelegate_;
184 delete contactDelegate_;
Edric Milaret627500d2015-03-27 16:41:40 -0400185}
186
187void
Edric Milaret67007d12015-05-07 09:40:09 -0400188CallWidget::findRingAccount(QModelIndex idx1, QModelIndex idx2, QVector<int> vec)
189{
190 Q_UNUSED(idx1)
191 Q_UNUSED(idx2)
192 Q_UNUSED(vec)
193
194 auto a_count = AccountModel::instance()->rowCount();
195 auto found = false;
196 for (int i = 0; i < a_count; ++i) {
197 auto idx = AccountModel::instance()->index(i, 0);
198 auto protocol = idx.data(static_cast<int>(Account::Role::Proto));
199 if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING) {
200 auto username = idx.data(static_cast<int>(Account::Role::Username));
201 ui->ringIdLabel->setText(
Edric Milaret68779c42015-07-09 16:53:03 -0400202 "Your Ring ID: " + username.toString());
Edric Milaret67007d12015-05-07 09:40:09 -0400203 found = true;
204 return;
205 }
206 }
207 if (not found){
208 ui->ringIdLabel->setText("NO RING ACCOUNT FOUND");
209 }
210}
211
212void
Edric Milarete5afb072015-06-02 16:45:44 -0400213CallWidget::checkRegistrationState(Account *account,
214 Account::RegistrationState state)
215{
216 Q_UNUSED(account);
217
218 QPixmap pm(20,20);
219 pm.fill();
220 QPainter p(&pm);
221 QPen pen(Qt::black, 2);
222 p.setPen(pen);
223 p.setRenderHint(QPainter::Antialiasing, true);
224
225 switch(state) {
226 case Account::RegistrationState::ERROR:
227 p.setBrush(Qt::red);
228 break;
229 case Account::RegistrationState::TRYING:
230 p.setBrush(Qt::yellow);
231 break;
232 case Account::RegistrationState::READY:
Edric Milaret023e8f12015-07-07 14:52:40 -0400233 {
Edric Milarete5afb072015-06-02 16:45:44 -0400234 p.setBrush(Qt::green);
235 auto a_count = AccountModel::instance()->rowCount();
236 for (int i = 0; i < a_count; ++i) {
237 auto idx = AccountModel::instance()->index(i, 0);
238 auto stateAcc = idx.data(
239 static_cast<int>(Account::Role::RegistrationState));
240 if (stateAcc.value<Account::RegistrationState>()
241 != Account::RegistrationState::READY) {
242 checkRegistrationState(nullptr,
243 stateAcc.value<Account::RegistrationState>());
244 return;
245 }
246 }
Edric Milaret023e8f12015-07-07 14:52:40 -0400247 }
248 break;
249 default:
Edric Milarete5afb072015-06-02 16:45:44 -0400250 break;
251 }
252 p.drawEllipse(3, 3, 16, 16);
253 ui->accountStateHintLabel->setPixmap(pm);
254 ui->accountStateHintLabel->show();
255}
256
257void
Edric Milaret67007d12015-05-07 09:40:09 -0400258CallWidget::findRingAccount()
259{
Edric Milaret559bda52015-04-29 17:02:31 -0400260
Edric Milaret627500d2015-03-27 16:41:40 -0400261 auto a_count = AccountModel::instance()->rowCount();
262 auto found = false;
263 for (int i = 0; i < a_count; ++i) {
264 auto idx = AccountModel::instance()->index(i, 0);
265 auto protocol = idx.data(static_cast<int>(Account::Role::Proto));
266 if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING) {
Edric Milaret68779c42015-07-09 16:53:03 -0400267 auto account = AccountModel::instance()->getAccountByModelIndex(idx);
268 if (account->displayName().isEmpty())
269 account->displayName() = account->alias();
270 auto username = account->username();
Edric Milaret627500d2015-03-27 16:41:40 -0400271 ui->ringIdLabel->setText(
Edric Milaret68779c42015-07-09 16:53:03 -0400272 "Your Ring ID: " + username);
Edric Milaret627500d2015-03-27 16:41:40 -0400273 found = true;
274 return;
275 }
276 }
277 if (!found) {
Edric Milaret559bda52015-04-29 17:02:31 -0400278 WizardDialog *wizardDialog = new WizardDialog();
279 wizardDialog->exec();
280 delete wizardDialog;
Edric Milaret627500d2015-03-27 16:41:40 -0400281 }
282}
283
284void
285CallWidget::callIncoming(Call *call)
286{
Edric Milaret435cdfc2015-06-01 16:15:50 -0400287 if (!call->account()->isAutoAnswer()) {
Edric Milaretd9ebe232015-06-15 10:44:49 -0400288 ui->callLabel->setText("Call from " + call->formattedName());
Edric Milaret627500d2015-03-27 16:41:40 -0400289 ui->callInvite->setVisible(true);
Edric Milaret435cdfc2015-06-01 16:15:50 -0400290 ui->callInvite->raise();
291 }
Edric Milaret6456baa2015-05-21 12:18:07 -0400292 setActualCall(call);
Edric Milaret627500d2015-03-27 16:41:40 -0400293}
294
295void
296CallWidget::on_acceptButton_clicked()
297{
298 if (actualCall_ != nullptr)
Edric Milaret53f57b62015-05-11 11:02:17 -0400299 actualCall_->performAction(Call::Action::ACCEPT);
Edric Milaret627500d2015-03-27 16:41:40 -0400300 ui->callInvite->setVisible(false);
301}
302
303void
304CallWidget::on_refuseButton_clicked()
305{
306 if (actualCall_ == nullptr)
307 return;
308 actualCall_->performAction(Call::Action::REFUSE);
Edric Milaret6456baa2015-05-21 12:18:07 -0400309 setActualCall(nullptr);
Edric Milaret627500d2015-03-27 16:41:40 -0400310 ui->callInvite->setVisible(false);
311}
312
313void
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400314CallWidget::addedCall(Call* call, Call* parent)
315{
Edric Milaret627500d2015-03-27 16:41:40 -0400316 Q_UNUSED(parent);
317 if (call->direction() == Call::Direction::OUTGOING) {
Edric Milaretc7ab5502015-06-15 11:18:02 -0400318 displaySpinner(true);
Edric Milaret6456baa2015-05-21 12:18:07 -0400319 setActualCall(call);
Edric Milaret627500d2015-03-27 16:41:40 -0400320 }
321}
322
323void
Edric Milaret67007d12015-05-07 09:40:09 -0400324CallWidget::callStateChanged(Call* call, Call::State previousState)
325{
Edric Milaret627500d2015-03-27 16:41:40 -0400326 Q_UNUSED(previousState)
Edric Milaretb5b49072015-05-08 11:56:07 -0400327 if (call == nullptr)
328 return;
Edric Milaret627500d2015-03-27 16:41:40 -0400329 ui->callList->setCurrentIndex(callModel_->getIndex(actualCall_));
Edric Milaretc7ab5502015-06-15 11:18:02 -0400330 if (call->state() == Call::State::OVER
331 || call->state() == Call::State::ERROR
332 || call->state() == Call::State::FAILURE
333 || call->state() == Call::State::ABORTED) {
Edric Milaret6456baa2015-05-21 12:18:07 -0400334 setActualCall(nullptr);
Edric Milaret627500d2015-03-27 16:41:40 -0400335 ui->videoWidget->hide();
Edric Milaretc7ab5502015-06-15 11:18:02 -0400336 displaySpinner(false);
Edric Milaret4f630842015-06-11 16:10:29 -0400337 auto onHoldCall = callModel_->getActiveCalls().first();
Edric Milaretd8528fa2015-07-07 14:13:51 -0400338 if (onHoldCall != nullptr && onHoldCall->state() == Call::State::HOLD) {
Edric Milaret4f630842015-06-11 16:10:29 -0400339 setActualCall(onHoldCall);
340 onHoldCall->performAction(Call::Action::HOLD);
341 }
Edric Milaret53f57b62015-05-11 11:02:17 -0400342 } else if (call->state() == Call::State::CURRENT) {
Edric Milaretc7ab5502015-06-15 11:18:02 -0400343 displaySpinner(false);
Edric Milaret627500d2015-03-27 16:41:40 -0400344 ui->videoWidget->show();
Edric Milaret627500d2015-03-27 16:41:40 -0400345 }
Edric Milaretf25d3bd2015-06-04 10:23:24 -0400346 ui->callStateLabel->setText("Call State : " + call->toHumanStateName());
Edric Milaret627500d2015-03-27 16:41:40 -0400347}
348
349void
350CallWidget::on_callList_activated(const QModelIndex &index)
351{
352 Call *callSelected = callModel_->getCall(index);
353 if (actualCall_ != nullptr) {
354 if (callSelected == actualCall_)
355 return;
Edric Milaret72c406d2015-04-28 13:23:54 -0400356 ui->videoWidget->hide();
Edric Milaret627500d2015-03-27 16:41:40 -0400357 actualCall_->performAction(Call::Action::HOLD);
358 }
Edric Milaret6456baa2015-05-21 12:18:07 -0400359 setActualCall(callSelected);
Edric Milaret627500d2015-03-27 16:41:40 -0400360 actualCall_->performAction(Call::Action::HOLD);
Edric Milaret72c406d2015-04-28 13:23:54 -0400361 ui->videoWidget->show();
Edric Milaret627500d2015-03-27 16:41:40 -0400362}
363
364void
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400365CallWidget::atExit()
366{
Edric Milaret627500d2015-03-27 16:41:40 -0400367
368}
Edric Milaret67007d12015-05-07 09:40:09 -0400369
370void
371CallWidget::on_contactView_doubleClicked(const QModelIndex &index)
372{
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400373 if (not index.isValid())
374 return;
375
Edric Milaret2afd2bf2015-07-21 17:12:25 -0400376 ContactMethod* uri = nullptr;
Edric Milaret67007d12015-05-07 09:40:09 -0400377
Edric Milaret7d40a4a2015-05-13 13:01:15 -0400378 auto var = index.child(0,0).data(
379 static_cast<int>(Person::Role::Object));
380 if (var.isValid()) {
381 Person* person = var.value<Person*>();
Edric Milaret2afd2bf2015-07-21 17:12:25 -0400382 if (person->phoneNumbers().size() == 1) {
383 uri = person->phoneNumbers().at(0);
384 } else if (person->phoneNumbers().size() > 1) {
385 ContactMethodPicker dlg(person->phoneNumbers());
386 auto pos = QCursor::pos();
387 dlg.move(pos.x(), pos.y());
388 if (dlg.exec())
389 uri = dlg.getSelected();
390 }
391 if (uri) {
392 auto outCall = CallModel::instance()->dialingCall(person->formattedName());
393 outCall->setDialNumber(uri);
394 outCall->performAction(Call::Action::ACCEPT);
Edric Milaret67007d12015-05-07 09:40:09 -0400395 }
396 }
Edric Milaret67007d12015-05-07 09:40:09 -0400397}
Edric Milaret53f57b62015-05-11 11:02:17 -0400398
Edric Milaret6456baa2015-05-21 12:18:07 -0400399void
400CallWidget::on_historyList_doubleClicked(const QModelIndex &index)
Edric Milaret53f57b62015-05-11 11:02:17 -0400401{
Edric Milaret12353822015-05-14 14:41:09 -0400402 if (not index.isValid())
403 return;
404
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400405 auto number = index.data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod*>();
406 if (number) {
407 auto outCall = CallModel::instance()->dialingCall();
Edric Milaret53f57b62015-05-11 11:02:17 -0400408 outCall->setDialNumber(number);
409 outCall->performAction(Call::Action::ACCEPT);
410 }
411}
Edric Milaret6456baa2015-05-21 12:18:07 -0400412
413void
414CallWidget::setActualCall(Call* value)
415{
416 actualCall_ = value;
Edric Milaretcdc978b2015-06-04 11:25:12 -0400417 ui->instantMessagingWidget->setMediaText(actualCall_);
Edric Milaret12353822015-05-14 14:41:09 -0400418}
Edric Milaretcdc978b2015-06-04 11:25:12 -0400419
Edric Milaret12353822015-05-14 14:41:09 -0400420void
421CallWidget::on_sortComboBox_currentIndexChanged(int index)
422{
423 auto idx = CategorizedHistoryModel::SortedProxy::instance()->
424 categoryModel()->index(index, 0);
425 CategorizedHistoryModel::SortedProxy::instance()->categorySelectionModel()->
426 setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
427 ui->historyList->setModel(CategorizedHistoryModel::SortedProxy::instance()->model());
Edric Milaret6456baa2015-05-21 12:18:07 -0400428}
Edric Milaretc7ab5502015-06-15 11:18:02 -0400429
430void
431CallWidget::displaySpinner(bool display)
432{
Edric Milaretd8528fa2015-07-07 14:13:51 -0400433 display ? ui->spinnerWidget->show() : ui->spinnerWidget->hide();
Edric Milaretc7ab5502015-06-15 11:18:02 -0400434 if (ui->spinnerLabel->movie())
435 display ? ui->spinnerLabel->movie()->start() : ui->spinnerLabel->movie()->stop();
436}
Edric Milaretd8528fa2015-07-07 14:13:51 -0400437
438void CallWidget::on_cancelButton_clicked()
439{
440 if (actualCall_)
441 actualCall_->performAction(Call::Action::REFUSE);
442}