callwidget.cpp 15 KB
Newer Older
Edric Milaret's avatar
Edric Milaret committed
1
/***************************************************************************
Edric Milaret's avatar
Edric Milaret committed
2
 * Copyright (C) 2015 by Savoir-faire Linux                                *
Edric Milaret's avatar
Edric Milaret committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
 *                                                                         *
 * This program is free software; you can redistribute it and/or modify    *
 * it under the terms of the GNU General Public License as published by    *
 * the Free Software Foundation; either version 3 of the License, or       *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 * GNU General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
 **************************************************************************/

#include "callwidget.h"
#include "ui_callwidget.h"

22 23
#include <QClipboard>

24
#include <memory>
Edric Milaret's avatar
Edric Milaret committed
25

26 27 28 29
//ERROR is defined in windows.h
#include "utils.h"
#undef ERROR

Edric Milaret's avatar
Edric Milaret committed
30 31
#include "audio/settings.h"
#include "personmodel.h"
32
#include "person.h"
Edric Milaret's avatar
Edric Milaret committed
33
#include "fallbackpersoncollection.h"
34
#include "categorizedcontactmodel.h"
Edric Milaret's avatar
Edric Milaret committed
35
#include "localhistorycollection.h"
36 37 38
#include "media/text.h"
#include "media/recording.h"
#include "media/textrecording.h"
39 40
#include "recentmodel.h"

41
#include "wizarddialog.h"
Edric Milaret's avatar
Edric Milaret committed
42
#include "windowscontactbackend.h"
43
#include "contactpicker.h"
44
#include "contactmethodpicker.h"
45
#include "globalsystemtray.h"
46 47 48
#include "historydelegate.h"
#include "contactdelegate.h"
#include "smartlistdelegate.h"
Edric Milaret's avatar
Edric Milaret committed
49

50
CallWidget::CallWidget(QWidget* parent) :
51
    NavWidget(END ,parent),
Edric Milaret's avatar
Edric Milaret committed
52 53
    ui(new Ui::CallWidget),
    menu_(new QMenu())
Edric Milaret's avatar
Edric Milaret committed
54
{
55 56
    setMouseTracking(true);

Edric Milaret's avatar
Edric Milaret committed
57 58
    ui->setupUi(this);

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    // TODO : add this in style sheet forms
    QPalette palette;
    palette.setColor(QPalette::WindowText, QColor(255,255,255));
    ui->callerIdLabel->setPalette(palette);

    QPalette palette2;
    palette2.setColor(QPalette::WindowText, QColor(141,141,141));

    ui->wantToTalkLabel->setPalette(palette2);
    ui->outboundCallLabel->setPalette(palette2);
    ui->cancelCallLabel->setPalette(palette2);
    ui->acceptLabel->setPalette(palette2);
    ui->refuseLabel->setPalette(palette2);

    QFont font = ui->callerIdLabel->font();
    font.setPointSize(20);

    ui->callerIdLabel->setFont(font);
    // end of TODO : add this in style sheet forms

Edric Milaret's avatar
Edric Milaret committed
79 80
    ui->callInvite->setVisible(false);

81
    setActualCall(nullptr);
Edric Milaret's avatar
Edric Milaret committed
82 83
    videoRenderer_ = nullptr;

84 85 86
    connect(ui->videoWidget, SIGNAL(setChatVisibility(bool)),
            ui->instantMessagingWidget, SLOT(setVisible(bool)));

87 88 89 90
    QPixmap logo(":/images/logo-ring-standard-coul.png");
    ui->ringLogo->setPixmap(logo.scaledToHeight(100, Qt::SmoothTransformation));
    ui->ringLogo->setAlignment(Qt::AlignHCenter);

Edric Milaret's avatar
Edric Milaret committed
91
    try {
Edric Milaret's avatar
Edric Milaret committed
92
        callModel_ = &CallModel::instance();
Edric Milaret's avatar
Edric Milaret committed
93 94 95 96 97 98 99 100

        connect(callModel_, SIGNAL(incomingCall(Call*)),
                this, SLOT(callIncoming(Call*)));
        connect(callModel_, SIGNAL(callAdded(Call*,Call*)),
                this, SLOT(addedCall(Call*, Call*)));
        connect(callModel_, SIGNAL(callStateChanged(Call*, Call::State)),
                this, SLOT(callStateChanged(Call*, Call::State)));

Edric Milaret's avatar
Edric Milaret committed
101
        connect(&AccountModel::instance()
102 103 104 105
                , SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))
                , this
                , SLOT(findRingAccount(QModelIndex, QModelIndex, QVector<int>)));

Edric Milaret's avatar
Edric Milaret committed
106
        ui->smartList->setModel(RecentModel::instance().peopleProxy());
107 108 109 110
        connect(ui->smartList->selectionModel(),
                SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                this,
                SLOT(smartListSelectionChanged(QItemSelection,QItemSelection)));
111 112 113

        connect(ui->smartList, &QTreeView::entered, this, &CallWidget::on_entered);

114
        smartListDelegate_ = new SmartListDelegate();
115
        ui->smartList->setSmartListItemDelegate(smartListDelegate_);
Edric Milaret's avatar
Edric Milaret committed
116

Edric Milaret's avatar
Edric Milaret committed
117
        PersonModel::instance().
118
                addCollection<WindowsContactBackend>(LoadOptions::FORCE_ENABLED);
Edric Milaret's avatar
Edric Milaret committed
119

Edric Milaret's avatar
Edric Milaret committed
120 121 122
        CategorizedContactModel::instance().setSortAlphabetical(false);
        CategorizedContactModel::instance().setUnreachableHidden(true);
        ui->contactView->setModel(&CategorizedContactModel::instance());
123 124
        contactDelegate_ = new ContactDelegate();
        ui->contactView->setItemDelegate(contactDelegate_);
125

Edric Milaret's avatar
Edric Milaret committed
126
        CategorizedHistoryModel::instance().
127
                addCollection<LocalHistoryCollection>(LoadOptions::FORCE_ENABLED);
128

Edric Milaret's avatar
Edric Milaret committed
129 130
        ui->historyList->setModel(CategorizedHistoryModel::SortedProxy::instance().model());
        CategorizedHistoryModel::SortedProxy::instance().model()->sort(0, Qt::DescendingOrder);
131
        ui->historyList->setHeaderHidden(true);
Edric Milaret's avatar
Edric Milaret committed
132 133
        historyDelegate_ = new HistoryDelegate();
        ui->historyList->setItemDelegate(historyDelegate_);
134

Edric Milaret's avatar
Edric Milaret committed
135 136
        connect(CategorizedHistoryModel::SortedProxy::instance().model(), &QSortFilterProxyModel::layoutChanged, [=]() {
            auto idx = CategorizedHistoryModel::SortedProxy::instance().model()->index(0,0);
137 138 139 140
            if (idx.isValid())
                ui->historyList->setExpanded(idx, true);
        });

141 142 143 144 145 146 147 148 149
        ui->historyList->setContextMenuPolicy(Qt::CustomContextMenu);
        connect(ui->historyList, &QListView::customContextMenuRequested, [=](const QPoint& pos){
            if (ui->historyList->currentIndex().parent().isValid()) {
                QPoint globalPos = ui->historyList->mapToGlobal(pos);
                QMenu menu;

                ContactMethod* contactMethod = ui->historyList->currentIndex()
                        .data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod*>();

150
                auto copyAction = new QAction(tr("Copy number"), this);
151 152 153 154
                menu.addAction(copyAction);
                connect(copyAction, &QAction::triggered, [=]() {
                    QApplication::clipboard()->setText(contactMethod->uri());
                });
155
                if (not contactMethod->contact() || contactMethod->contact()->isPlaceHolder()) {
156
                    auto addExisting = new QAction(tr("Add to contact"), this);
157 158
                    menu.addAction(addExisting);
                    connect(addExisting, &QAction::triggered, [=]() {
159
                        ContactPicker contactPicker(contactMethod);
160
                        contactPicker.move(globalPos.x(), globalPos.y() - (contactPicker.height()/2));
161
                        contactPicker.exec();
162 163 164 165 166
                    });
                }
                menu.exec(globalPos);
            }
        });
Edric Milaret's avatar
Edric Milaret committed
167

Edric Milaret's avatar
Edric Milaret committed
168 169
        findRingAccount();

Edric Milaret's avatar
Edric Milaret committed
170 171
    } catch (const std::exception& e) {
        qDebug() << "INIT ERROR" << e.what();
Edric Milaret's avatar
Edric Milaret committed
172 173 174 175 176 177
    }
}

CallWidget::~CallWidget()
{
    delete ui;
178
    delete menu_;
Edric Milaret's avatar
Edric Milaret committed
179
    delete contactDelegate_;
Edric Milaret's avatar
Edric Milaret committed
180 181 182
}

void
183 184 185 186 187 188
CallWidget::findRingAccount(QModelIndex idx1, QModelIndex idx2, QVector<int> vec)
{
    Q_UNUSED(idx1)
    Q_UNUSED(idx2)
    Q_UNUSED(vec)

Edric Milaret's avatar
Edric Milaret committed
189
    auto a_count = AccountModel::instance().rowCount();
190 191
    auto found = false;
    for (int i = 0; i < a_count; ++i) {
Edric Milaret's avatar
Edric Milaret committed
192
        auto idx = AccountModel::instance().index(i, 0);
193 194 195
        auto protocol = idx.data(static_cast<int>(Account::Role::Proto));
        if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING) {
            auto username = idx.data(static_cast<int>(Account::Role::Username));
196
            ui->ringIdLabel->setText(username.toString());
197 198 199 200
            found = true;
            return;
        }
    }
201
    if (not found) {
202
        ui->ringIdLabel->setText(tr("NO RING ACCOUNT FOUND"));
203 204 205 206 207 208
    }
}

void
CallWidget::findRingAccount()
{
209

Edric Milaret's avatar
Edric Milaret committed
210
    auto a_count = AccountModel::instance().rowCount();
Edric Milaret's avatar
Edric Milaret committed
211 212
    auto found = false;
    for (int i = 0; i < a_count; ++i) {
Edric Milaret's avatar
Edric Milaret committed
213
        auto idx = AccountModel::instance().index(i, 0);
Edric Milaret's avatar
Edric Milaret committed
214 215
        auto protocol = idx.data(static_cast<int>(Account::Role::Proto));
        if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING) {
Edric Milaret's avatar
Edric Milaret committed
216
            auto account = AccountModel::instance().getAccountByModelIndex(idx);
217 218 219
            if (account->displayName().isEmpty())
                account->displayName() = account->alias();
            auto username = account->username();
220
            ui->ringIdLabel->setText(username);
Edric Milaret's avatar
Edric Milaret committed
221 222 223 224 225
            found = true;
            return;
        }
    }
    if (!found) {
226
        ui->ringIdLabel->setText(tr("NO RING ACCOUNT FOUND"));
227 228 229
        WizardDialog *wizardDialog = new WizardDialog();
        wizardDialog->exec();
        delete wizardDialog;
Edric Milaret's avatar
Edric Milaret committed
230 231 232 233
    }
}

void
234
CallWidget::callIncoming(Call* call)
Edric Milaret's avatar
Edric Milaret committed
235
{
236 237
    ui->outboundCall->hide();

238 239 240 241 242
    if (!QApplication::activeWindow()) {
        GlobalSystemTray::instance().showMessage("Ring", "Call incoming from " + call->formattedName());
        QApplication::alert(this, 5000);
    }

Edric Milaret's avatar
Edric Milaret committed
243
    if (!call->account()->isAutoAnswer()) {
244
        ui->callerIdLabel->setText(QString(tr("%1", "%1 is the name of the caller"))
245
                               .arg(call->formattedName()));
246
        ui->stackedWidget->setCurrentWidget(ui->callInvitePage);
Edric Milaret's avatar
Edric Milaret committed
247
        ui->callInvite->setVisible(true);
Edric Milaret's avatar
Edric Milaret committed
248 249
        ui->callInvite->raise();
    }
250
    setActualCall(call);
Edric Milaret's avatar
Edric Milaret committed
251 252 253 254 255 256
}

void
CallWidget::on_acceptButton_clicked()
{
    if (actualCall_ != nullptr)
257
        actualCall_->performAction(Call::Action::ACCEPT);
Edric Milaret's avatar
Edric Milaret committed
258
    ui->callInvite->setVisible(false);
259
    ui->stackedWidget->setCurrentWidget(ui->videoPage);
Edric Milaret's avatar
Edric Milaret committed
260 261 262 263 264 265 266 267
}

void
CallWidget::on_refuseButton_clicked()
{
    if (actualCall_ == nullptr)
        return;
    actualCall_->performAction(Call::Action::REFUSE);
268
    setActualCall(nullptr);
Edric Milaret's avatar
Edric Milaret committed
269
    ui->callInvite->setVisible(false);
270
    ui->stackedWidget->setCurrentWidget(ui->welcomePage);
Edric Milaret's avatar
Edric Milaret committed
271 272 273
}

void
274 275
CallWidget::addedCall(Call* call, Call* parent)
{
Edric Milaret's avatar
Edric Milaret committed
276 277
    Q_UNUSED(parent);
    if (call->direction() == Call::Direction::OUTGOING) {
278
        setActualCall(call);
279
        ui->stackedWidget->setCurrentWidget(ui->callInvitePage);
Edric Milaret's avatar
Edric Milaret committed
280 281 282 283
    }
}

void
284 285
CallWidget::callStateChanged(Call* call, Call::State previousState)
{
Edric Milaret's avatar
Edric Milaret committed
286
    Q_UNUSED(previousState)
287 288
    if (call == nullptr)
        return;
289

290 291 292 293
    if (call->state() == Call::State::OVER
            || call->state() == Call::State::ERROR
            || call->state() == Call::State::FAILURE
            || call->state() == Call::State::ABORTED) {
294
        setActualCall(nullptr);
295
        ui->instantMessagingWidget->setMediaText(nullptr);
296 297 298 299 300 301 302
        ui->stackedWidget->setCurrentWidget(ui->welcomePage);
//TODO : Link this so that recentModel get selected correctly
//        auto onHoldCall = callModel_->getActiveCalls().first();
//        if (onHoldCall != nullptr && onHoldCall->state() == Call::State::HOLD) {
//            setActualCall(onHoldCall);
//            onHoldCall->performAction(Call::Action::HOLD);
//        }
303
    } else if (call->state() == Call::State::CURRENT) {
304
        ui->instantMessagingWidget->setMediaText(actualCall_);
305
        ui->stackedWidget->setCurrentWidget(ui->videoPage);
Edric Milaret's avatar
Edric Milaret committed
306 307 308 309
    }
}

void
310 311
CallWidget::atExit()
{
Edric Milaret's avatar
Edric Milaret committed
312
}
313 314

void
315
CallWidget::on_contactView_doubleClicked(const QModelIndex& index)
316
{
317 318 319
    if (not index.isValid())
        return;

320
    ContactMethod* uri = nullptr;
321

Edric Milaret's avatar
Edric Milaret committed
322 323 324 325
    auto var = index.child(0,0).data(
                static_cast<int>(Person::Role::Object));
    if (var.isValid()) {
        Person* person = var.value<Person*>();
326 327 328 329 330 331 332 333 334 335
        if (person->phoneNumbers().size() == 1) {
            uri = person->phoneNumbers().at(0);
        } else if (person->phoneNumbers().size() > 1) {
            ContactMethodPicker dlg(person->phoneNumbers());
            auto pos = QCursor::pos();
            dlg.move(pos.x(), pos.y());
            if (dlg.exec())
                uri = dlg.getSelected();
        }
        if (uri) {
Edric Milaret's avatar
Edric Milaret committed
336
            auto outCall = CallModel::instance().dialingCall(uri);
337
            outCall->performAction(Call::Action::ACCEPT);
338 339
            ui->mainTabMenu->setCurrentIndex(0);
            ui->smartList->setFocus();
340 341 342
        }
    }
}
343

344
void
345
CallWidget::on_historyList_doubleClicked(const QModelIndex& index)
346
{
Edric Milaret's avatar
Edric Milaret committed
347 348 349
    if (not index.isValid())
        return;

350 351
    auto number = index.data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod*>();
    if (number) {
Edric Milaret's avatar
Edric Milaret committed
352
        auto outCall = CallModel::instance().dialingCall(number);
353
        outCall->performAction(Call::Action::ACCEPT);
354 355
        ui->mainTabMenu->setCurrentIndex(0);
        ui->smartList->setFocus();
356 357
    }
}
358 359 360 361 362

void
CallWidget::setActualCall(Call* value)
{
    actualCall_ = value;
Edric Milaret's avatar
Edric Milaret committed
363 364
    CallModel::instance().selectCall(value);
    ui->videoWidget->pushRenderer(value);
365
}
366

367 368
void
CallWidget::on_cancelButton_clicked()
369 370 371 372
{
    if (actualCall_)
        actualCall_->performAction(Call::Action::REFUSE);
}
373 374

void
375
CallWidget::on_smartList_doubleClicked(const QModelIndex& index)
376
{
Edric Milaret's avatar
Edric Milaret committed
377 378
    auto realIndex = RecentModel::instance().peopleProxy()->mapToSource(index);
    if (RecentModel::instance().hasActiveCall(realIndex))
379 380 381 382 383 384 385 386 387 388
        return;

    ContactMethod* m = nullptr;
    if (auto cm = realIndex.data((int)Call::Role::ContactMethod).value<ContactMethod*>()) {
        m = cm;
    } else {
        if (auto person =  realIndex.data((int)Person::Role::Object).value<Person*>()) {
            m = person->phoneNumbers().first();
        }
    }
Edric Milaret's avatar
Edric Milaret committed
389 390
    if (m && !RecentModel::instance().index(0, 0, realIndex).isValid()) {
        Call* c = CallModel::instance().dialingCall(m);
391 392 393 394 395
        c->performAction(Call::Action::ACCEPT);
    }
}

void
396
CallWidget::smartListSelectionChanged(const QItemSelection& newSel, const QItemSelection& oldSel) {
397 398 399 400

    Q_UNUSED(oldSel)

    auto newIdx = newSel.indexes().first();
Edric Milaret's avatar
Edric Milaret committed
401
    if (not newIdx.isValid())
402 403
        return;

Edric Milaret's avatar
Edric Milaret committed
404
    auto newIdxCall = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(newIdx));
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419

    if (newIdxCall == actualCall_)
        return;
    if (newIdxCall) {
        setActualCall(newIdxCall);
        ui->stackedWidget->setCurrentWidget(ui->videoPage);
    } else {
        setActualCall(nullptr);
        ui->stackedWidget->setCurrentWidget(ui->welcomePage);
    }
}

void
CallWidget::placeCall()
{
420
    if (ui->ringContactLineEdit->text().isEmpty())
421
        return;
422
    Call* c = CallModel::instance().dialingCall(PhoneDirectoryModel::instance().getNumber(ui->ringContactLineEdit->text()));
423
    c->performAction(Call::Action::ACCEPT);
424
    ui->ringContactLineEdit->clear();
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
}

void
CallWidget::on_settingsButton_clicked()
{
    emit NavigationRequested(ScreenEnum::ConfScreen);
}

void
CallWidget::on_contactButton_clicked(bool checked)
{
    ui->mainTabMenu->setCurrentIndex(checked ? 1 : 0);
}

void
CallWidget::on_historicButton_clicked(bool checked)
{
    ui->mainTabMenu->setCurrentIndex(checked ? 2 : 0);
}
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

void
CallWidget::on_ringContactLineEdit_returnPressed()
{
    placeCall();
}

void
CallWidget::on_btnCall_clicked()
{
    placeCall();
}

void
CallWidget::on_btnvideo_clicked()
{
    if (not highLightedIndex_.isValid())
        return;

    on_smartList_doubleClicked(highLightedIndex_);
}