CallView.cpp 29.8 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright (C) 2009-2012 by Savoir-Faire Linux                         *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   Author : Emmanuel Lepage Valle <emmanuel.lepage@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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 **************************************************************************/

//Parent
22
#include "CallView.h"
23
24
25
26
27

//Qt
#include <QtGui/QInputDialog>
#include <QtGui/QTreeWidget>
#include <QtGui/QTreeWidgetItem>
28
29
30
#include <QtGui/QPushButton>
#include <QtGui/QSpacerItem>
#include <QtGui/QGridLayout>
31
#include <QtGui/QLabel>
32

33
34
//KDE
#include <KDebug>
35
#include <KLineEdit>
36
#include <KStandardDirs>
37
38

//SFLPhone library
39
#include "lib/Contact.h"
Emmanuel Lepage's avatar
Emmanuel Lepage committed
40
41
#include "lib/sflphone_const.h"
#include "lib/callmanager_interface_singleton.h"
42

43
44
//SFLPhone
#include "widgets/CallTreeItem.h"
45
#include "SFLPhone.h"
Emmanuel Lepage's avatar
Emmanuel Lepage committed
46
#include "SFLPhoneView.h"
47
#include "AkonadiBackend.h"
48

49
50

///Retrieve current and older calls from the daemon, fill history and the calls TreeView and enable drag n' drop
51
CallView::CallView(QWidget* parent) : QTreeWidget(parent),m_pActiveOverlay(0),m_pCallPendingTransfer(0)
52
53
54
55
{
   //Widget part
   setAcceptDrops(true);
   setDragEnabled(true);
56
   setAnimated(true);
57
58
59
   CallTreeItemDelegate *delegate = new CallTreeItemDelegate();
   setItemDelegate(delegate); 
   setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
60
61
62
63
   
   m_pTransferOverlay = new CallViewOverlay(this);
   m_pTransferOverlay->setVisible(false);
   m_pTransferOverlay->resize(size());
64
65
   QLabel* lblImg = new QLabel("<img width=100 height=100  src='"+KStandardDirs::locate("data","sflphone-client-kde/transferarraw.png")+"' />");
   m_pTransferOverlay->setCornerWidget(lblImg);
66
67
68
69
70
71
72
73
74
75

   m_pTransferB  = new QPushButton(m_pTransferOverlay);
   m_pTransferLE = new KLineEdit(m_pTransferOverlay);
   m_pTransferB->setText("Transfer");
   m_pTransferB->setMaximumSize(70,9000);
   QGridLayout* gl = new QGridLayout(m_pTransferOverlay);
   gl->addItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Minimum),0,0,1,3);
   gl->addWidget(m_pTransferLE,1,1,1,2);
   gl->addWidget(m_pTransferB,1,4,1,2);
   gl->addItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Minimum),2,0,1,3);
76

Emmanuel Lepage's avatar
Emmanuel Lepage committed
77
   //User Interface even
78
79
80
81
82
83
84
85
   //              SENDER                                   SIGNAL                              RECEIVER                     SLOT                        /
   /**/connect(this              , SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)              ) , this, SLOT( itemDoubleClicked(QTreeWidgetItem*,int)) );
   /**/connect(this              , SIGNAL(itemClicked(QTreeWidgetItem*,int)                    ) , this, SLOT( itemClicked(QTreeWidgetItem*,int))       );
   /**/connect(this              , SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)) , this, SLOT( itemClicked(QTreeWidgetItem*))           );
   /**/connect(SFLPhone::model() , SIGNAL(conferenceCreated(Call*)                             ) , this, SLOT( addConference(Call*))                    );
   /**/connect(SFLPhone::model() , SIGNAL(conferenceChanged(Call*)                             ) , this, SLOT( conferenceChanged(Call*))                );
   /**/connect(SFLPhone::model() , SIGNAL(aboutToRemoveConference(Call*)                       ) , this, SLOT( conferenceRemoved(Call*))                );
   /**/connect(SFLPhone::model() , SIGNAL(callAdded(Call*,Call*)                               ) , this, SLOT( addCall(Call*,Call*))                    );
86
87
   /**/connect(m_pTransferB      , SIGNAL(clicked()                                            ) , this, SLOT( transfer())                              );
   /**/connect(m_pTransferLE     , SIGNAL(returnPressed()                                      ) , this, SLOT( transfer())                              );
88
   /*                                                                                                                                                   */
89

90
91
92
93
94
95
96
97
98
}


/*****************************************************************************
 *                                                                           *
 *                        Drag and drop related code                         *
 *                                                                           *
 ****************************************************************************/

99
100
101
102
103
104
105
106
107
108
///Called when someone try to drop something on the tree
void CallView::dragEnterEvent ( QDragEnterEvent *e )
{
   kDebug() << "Potential drag event enter";
   e->accept();
}

///When a drag position change
void CallView::dragMoveEvent  ( QDragMoveEvent  *e )
{
109
   e->accept();
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
}

///When a drag event is leaving the widget
void CallView::dragLeaveEvent ( QDragLeaveEvent *e )
{
   kDebug() << "Potential drag event leave";
   e->accept();
}

///Proxy to handle transfer mime data
void CallView::transferDropEvent(Call* call,QMimeData* data)
{
   QByteArray encodedCallId = data->data( MIME_CALLID );
   SFLPhone::model()->attendedTransfer(SFLPhone::model()->getCall(encodedCallId),call);
}

///Proxy to handle conversation mime data
void CallView::conversationDropEvent(Call* call,QMimeData* data)
{
   kDebug() << "Calling real drag and drop function";
   dropMimeData(SFLPhone::model()->getIndex(call), 0, data, (Qt::DropAction)0);
}

Emmanuel Lepage's avatar
Emmanuel Lepage committed
133
///A call is dropped on another call
134
bool CallView::callToCall(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
135
136
137
{
   Q_UNUSED(index)
   Q_UNUSED(action)
138
139
   QByteArray encodedCallId      = data->data( MIME_CALLID      );
   if (!QString(encodedCallId).isEmpty()) {
140
141
      if (SFLPhone::model()->getIndex(encodedCallId))
        clearArtefact(SFLPhone::model()->getIndex(encodedCallId));
142

143
      if (!parent) {
144
         kDebug() << "Call dropped on empty space";
145
         if (SFLPhone::model()->getIndex(encodedCallId)->parent()) {
146
            kDebug() << "Detaching participant";
147
            SFLPhone::model()->detachParticipant(SFLPhone::model()->getCall(encodedCallId));
148
149
         }
         else
150
            kDebug() << "The call is not in a conversation (doing nothing)";
151
152
         return true;
      }
153

154
      if (SFLPhone::model()->getCall(parent)->getCallId() == QString(encodedCallId)) {
155
         kDebug() << "Call dropped on itself (doing nothing)";
156
157
         return true;
      }
158

159
      if ((parent->childCount()) && (SFLPhone::model()->getIndex(encodedCallId)->childCount())) {
160
         kDebug() << "Merging two conferences";
161
         SFLPhone::model()->mergeConferences(SFLPhone::model()->getCall(parent),SFLPhone::model()->getCall(encodedCallId));
162
163
164
         return true;
      }
      else if ((parent->parent()) || (parent->childCount())) {
165
         kDebug() << "Call dropped on a conference";
166

167
         if ((SFLPhone::model()->getIndex(encodedCallId)->childCount()) && (!parent->childCount())) {
168
            kDebug() << "Conference dropped on a call (doing nothing)";
169
170
            return true;
         }
171

172
         QTreeWidgetItem* call1 = SFLPhone::model()->getIndex(encodedCallId);
173
         QTreeWidgetItem* call2 = (parent->parent())?parent->parent():parent;
174

175
         if (call1->parent()) {
176
            kDebug() << "Call 1 is part of a conference";
177
            if (call1->parent() == call2) {
178
               kDebug() << "Call dropped on it's own conference (doing nothing)";
179
180
               return true;
            }
181
            else if (SFLPhone::model()->getIndex(call1)->childCount()) {
182
               kDebug() << "Merging two conferences";
183
               SFLPhone::model()->mergeConferences(SFLPhone::model()->getCall(call1),SFLPhone::model()->getCall(call2));
184
185
            }
            else if (call1->parent()) {
186
               kDebug() << "Moving call from a conference to an other";
187
               SFLPhone::model()->detachParticipant(SFLPhone::model()->getCall(encodedCallId));
188
189
            }
         }
190
         kDebug() << "Adding participant";
191
         int state = SFLPhone::model()->getCall(call1)->getState();
192
         if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) {
193
            SFLPhone::model()->getCall(call1)->actionPerformed(CALL_ACTION_ACCEPT);
194
         }
195
         state = SFLPhone::model()->getCall(call2)->getState();
196
         if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) {
197
            SFLPhone::model()->getCall(call2)->actionPerformed(CALL_ACTION_ACCEPT);
198
         }
199
         SFLPhone::model()->addParticipant(SFLPhone::model()->getCall(call1),SFLPhone::model()->getCall(call2));
200
201
         return true;
      }
202
      else if ((SFLPhone::model()->getIndex(encodedCallId)->childCount()) && (!parent->childCount())) {
203
         kDebug() << "Call dropped on it's own conference (doing nothing)";
204
205
206
         return true;
      }

207
      kDebug() << "Call dropped on another call";
208
      SFLPhone::model()->createConferenceFromCall(SFLPhone::model()->getCall(encodedCallId),SFLPhone::model()->getCall(parent));
209
210
      return true;
   }
211
212
213
   return false;
}

Emmanuel Lepage's avatar
Emmanuel Lepage committed
214
///A string containing a call number is dropped on a call
215
216
217
218
219
220
221
222
223
224
225
226
bool CallView::phoneNumberToCall(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
   Q_UNUSED(index)
   Q_UNUSED(action)
   QByteArray encodedPhoneNumber = data->data( MIME_PHONENUMBER );
   if (!QString(encodedPhoneNumber).isEmpty()) {
      Contact* contact = AkonadiBackend::getInstance()->getContactByPhone(encodedPhoneNumber);
      QString name;
      if (contact)
         name = contact->getFormattedName();
      else
         name = "Unknow";
227
      Call* call2 = SFLPhone::model()->addDialingCall(name, SFLPhone::model()->getCurrentAccountId());
228
229
230
      call2->appendText(QString(encodedPhoneNumber));
      if (!parent) {
         //Dropped on free space
231
         kDebug() << "Adding new dialing call";
232
233
234
235
      }
      else if (parent->childCount() || parent->parent()) {
         //Dropped on a conversation
         QTreeWidgetItem* call = (parent->parent())?parent->parent():parent;
236
         SFLPhone::model()->addParticipant(SFLPhone::model()->getCall(call),call2);
237
238
239
240
      }
      else {
         //Dropped on call
         call2->actionPerformed(CALL_ACTION_ACCEPT);
241
         int state = SFLPhone::model()->getCall(parent)->getState();
242
         if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) {
243
            SFLPhone::model()->getCall(parent)->actionPerformed(CALL_ACTION_ACCEPT);
244
         }
245
         SFLPhone::model()->createConferenceFromCall(call2,SFLPhone::model()->getCall(parent));
246
247
248
249
250
      }
   }
   return false;
}

Emmanuel Lepage's avatar
Emmanuel Lepage committed
251
///A contact ID is dropped on a call
252
253
bool CallView::contactToCall(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
254
   kDebug() << "contactToCall";
255
256
   Q_UNUSED( index  )
   Q_UNUSED( action )
257
258
259
260
261
262
   QByteArray encodedContact = data->data( MIME_CONTACT );
   if (!QString(encodedContact).isEmpty()) {
      Contact* contact = AkonadiBackend::getInstance()->getContactByUid(encodedContact);
      if (contact) {
         Call* call2;
         if (contact->getPhoneNumbers().count() == 1) {
263
            call2 = SFLPhone::model()->addDialingCall(contact->getFormattedName(), SFLPhone::model()->getCurrentAccountId());
264
265
266
267
268
269
270
271
272
273
274
275
            call2->appendText(contact->getPhoneNumbers()[0]->getNumber());
         }
         else if (contact->getPhoneNumbers().count() > 1) {
            bool ok = false;
            QHash<QString,QString> map;
            QStringList list;
            foreach (Contact::PhoneNumber* number, contact->getPhoneNumbers()) {
               map[number->getType()+" ("+number->getNumber()+")"] = number->getNumber();
               list << number->getType()+" ("+number->getNumber()+")";
            }
            QString result = QInputDialog::getItem (this, QString("Select phone number"), QString("This contact have many phone number, please select the one you wish to call"), list, 0, false, &ok);
            if (ok) {
276
               call2 = SFLPhone::model()->addDialingCall(contact->getFormattedName(), SFLPhone::model()->getCurrentAccountId());
277
278
279
               call2->appendText(map[result]);
            }
            else {
280
               kDebug() << "Operation cancelled";
281
282
283
284
               return false;
            }
         }
         else {
285
            kDebug() << "This contact have no valid phone number";
286
287
288
289
            return false;
         }
         if (!parent) {
            //Dropped on free space
290
            kDebug() << "Adding new dialing call";
291
292
293
294
         }
         else if (parent->childCount() || parent->parent()) {
            //Dropped on a conversation
            QTreeWidgetItem* call = (parent->parent())?parent->parent():parent;
295
            SFLPhone::model()->addParticipant(SFLPhone::model()->getCall(call),call2);
296
297
298
299
         }
         else {
            //Dropped on call
            call2->actionPerformed(CALL_ACTION_ACCEPT);
300
            int state = SFLPhone::model()->getCall(parent)->getState();
301
            if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) {
302
               SFLPhone::model()->getCall(parent)->actionPerformed(CALL_ACTION_ACCEPT);
303
            }
304
            SFLPhone::model()->createConferenceFromCall(call2,SFLPhone::model()->getCall(parent));
305
306
307
308
309
310
311
312
313
314
315
         }
      }
   }
   return false;
}

///Action performed when an item is dropped on the TreeView
bool CallView::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) 
{
   Q_UNUSED(index)
   Q_UNUSED(action)
316
   
317
318
319
320
321
   QByteArray encodedCallId      = data->data( MIME_CALLID      );
   QByteArray encodedPhoneNumber = data->data( MIME_PHONENUMBER );
   QByteArray encodedContact     = data->data( MIME_CONTACT     );

   if (!QString(encodedCallId).isEmpty()) {
322
      kDebug() << "CallId dropped"<< QString(encodedCallId);
323
324
325
      callToCall(parent, index, data, action);
   }
   else if (!QString(encodedPhoneNumber).isEmpty()) {
326
      kDebug() << "PhoneNumber dropped"<< QString(encodedPhoneNumber);
327
328
329
      phoneNumberToCall(parent, index, data, action);
   }
   else if (!QString(encodedContact).isEmpty()) {
330
      kDebug() << "Contact dropped"<< QString(encodedContact);
331
332
      contactToCall(parent, index, data, action);
   }
333
334
335
336
337
338
   return false;
}

///Encode data to be tranported during the drag n' drop operation
QMimeData* CallView::mimeData( const QList<QTreeWidgetItem *> items) const
{   
339
   kDebug() << "A call is being dragged";
340
341
342
343
344
345
346
   if (items.size() < 1) {
      return NULL;
   }
   
   QMimeData *mimeData = new QMimeData();
   
   //Call ID for internal call merging and spliting
347
348
   if (SFLPhone::model()->getCall(items[0])->isConference()) {
      mimeData->setData(MIME_CALLID, SFLPhone::model()->getCall(items[0])->getConfId().toAscii());
349
350
   }
   else {
351
      mimeData->setData(MIME_CALLID, SFLPhone::model()->getCall(items[0])->getCallId().toAscii());
352
353
354
   }
   
   //Plain text for other applications
355
   mimeData->setData(MIME_PLAIN_TEXT, QString(SFLPhone::model()->getCall(items[0])->getPeerName()+"\n"+SFLPhone::model()->getCall(items[0])->getPeerPhoneNumber()).toAscii());
356
357
   
   //TODO Comment this line if you don't want to see ugly artefact, but the caller details will not be visible while dragged
358
   items[0]->setText(0, SFLPhone::model()->getCall(items[0])->getPeerName() + "\n" + SFLPhone::model()->getCall(items[0])->getPeerPhoneNumber());
359
360
361
362
363
364
365
366
367
368
369
370
371
372
   return mimeData;
}


/*****************************************************************************
 *                                                                           *
 *                            Call related code                              *
 *                                                                           *
 ****************************************************************************/

///Add a call in the model structure, the call must exist before being added to the model
Call* CallView::addCall(Call* call, Call* parent) 
{
   QTreeWidgetItem* callItem = new QTreeWidgetItem();
373
   SFLPhone::model()->updateIndex(call,callItem);
374
375
376
377
378
379
380
381
   insertItem(callItem,parent);
   
   setCurrentItem(callItem);
   
   connect(call, SIGNAL(isOver(Call*)), this, SLOT(destroyCall(Call*)));
   return call;
}

382
383
384
385
386
387
388
389
390
391
392
///Transfer a call
void CallView::transfer()
{
   if (m_pCallPendingTransfer) {
      SFLPhone::model()->transfer(m_pCallPendingTransfer,m_pTransferLE->text());
      m_pCallPendingTransfer = 0;
      m_pTransferLE->clear();
      m_pTransferOverlay->setVisible(false);
   }
}

393
394
395
396
397
398
/*****************************************************************************
 *                                                                           *
 *                            View related code                              *
 *                                                                           *
 ****************************************************************************/

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
///Show the transfer overlay
void CallView::showTransferOverlay(Call* call)
{
   if (!m_pTransferOverlay) {
      kDebug() << "Creating overlay";
   }
   m_pTransferOverlay->setVisible(true);
   m_pCallPendingTransfer = call;
   m_pActiveOverlay = m_pTransferOverlay;
   m_pTransferLE->setFocus();
   connect(call,SIGNAL(changed()),this,SLOT(hideOverlay()));
}

///Is there an active overlay
bool CallView::haveOverlay()
{
   return (m_pActiveOverlay && m_pActiveOverlay->isVisible());
}

///Remove the active overlay
void CallView::hideOverlay()
{
   if (m_pActiveOverlay)
      m_pActiveOverlay->setVisible(false);
   m_pActiveOverlay = 0;
}

///Be sure the size of the overlay stay the same
void CallView::resizeEvent (QResizeEvent *e)
{
   if (m_pTransferOverlay)
      m_pTransferOverlay->resize(size());
   QTreeWidget::resizeEvent(e);
}

434
///Set the TreeView header text
435
void CallView::setTitle(const QString& title) 
436
437
438
439
440
441
442
{
   headerItem()->setText(0,title);
}

///Select an item in the TreeView
bool CallView::selectItem(Call* item) 
{
443
444
   if (SFLPhone::model()->getIndex(item)) {
      setCurrentItem(SFLPhone::model()->getIndex(item));
445
446
447
448
449
450
451
452
453
      return true;
   }
   else
      return false;
}

///Return the current item
Call* CallView::getCurrentItem() 
{
454
455
   if (currentItem() && SFLPhone::model()->getCall(QTreeWidget::currentItem()))
      return SFLPhone::model()->getCall(QTreeWidget::currentItem());
456
457
458
459
460
461
462
   else
      return false;
}

///Remove a TreeView item and delete it
bool CallView::removeItem(Call* item) 
{
463
464
465
466
467
468
   if (indexOfTopLevelItem(SFLPhone::model()->getIndex(item)) != -1) {
      QTreeWidgetItem* parent = itemAt(indexOfTopLevelItem(SFLPhone::model()->getIndex(item)),0);
      removeItemWidget(SFLPhone::model()->getIndex(item),0);
      if (parent->childCount() == 0) //TODO this have to be done in the daemon, not here, but oops still happen too often to ignore
         removeItemWidget(parent,0);
      return true;
469
470
471
472
473
474
475
476
477
478
479
480
   }
   else
      return false;
}

///Return the TreeView, this
QWidget* CallView::getWidget() 
{
   return this;
}

///Convenience wrapper around extractItem(QTreeWidgetItem*)
481
QTreeWidgetItem* CallView::extractItem(const QString& callId) 
482
{
483
   QTreeWidgetItem* currentItem = SFLPhone::model()->getIndex(callId);
484
485
486
487
488
489
490
491
492
493
   return extractItem(currentItem);
}

///Extract an item from the TreeView and return it, the item is -not- deleted
QTreeWidgetItem* CallView::extractItem(QTreeWidgetItem* item) 
{
   QTreeWidgetItem* parentItem = item->parent();
   
   if (parentItem) {
      if ((indexOfTopLevelItem(parentItem) == -1 ) || (parentItem->indexOfChild(item) == -1)) {
494
         kDebug() << "The conversation does not exist";
495
496
497
498
499
500
501
502
503
504
505
506
507
508
         return 0;
      }
      
      QTreeWidgetItem* toReturn = parentItem->takeChild(parentItem->indexOfChild(item));

      return toReturn;
   }
   else
      return takeTopLevelItem(indexOfTopLevelItem(item));
}

///Convenience wrapper around insertItem(QTreeWidgetItem*, QTreeWidgetItem*)
CallTreeItem* CallView::insertItem(QTreeWidgetItem* item, Call* parent) 
{
509
   return insertItem(item,(parent)?SFLPhone::model()->getIndex(parent):0);
510
511
512
513
514
515
}

///Insert a TreeView item in the TreeView as child of parent or as a top level item, also restore the item Widget
CallTreeItem* CallView::insertItem(QTreeWidgetItem* item, QTreeWidgetItem* parent) 
{
   if (!item) {
516
      kDebug() << "This is not a valid call";
517
518
519
520
521
522
523
524
525
      return 0;
   }
   
   if (!parent)
      insertTopLevelItem(0,item);
   else
      parent->addChild(item);
   
   CallTreeItem* callItem = new CallTreeItem();
526
527
528
529
530
   connect(callItem, SIGNAL(showChilds(CallTreeItem*)), this, SLOT(showDropOptions(CallTreeItem*)));
   connect(callItem, SIGNAL(askTransfer(Call*)), this, SLOT(showTransferOverlay(Call*)));
   connect(callItem, SIGNAL(transferDropEvent(Call*,QMimeData*)), this, SLOT(transferDropEvent(Call*,QMimeData*)));
   connect(callItem, SIGNAL(conversationDropEvent(Call*,QMimeData*)), this, SLOT(conversationDropEvent(Call*,QMimeData*)));
   
531
532
   SFLPhone::model()->updateWidget(SFLPhone::model()->getCall(item), callItem);
   callItem->setCall(SFLPhone::model()->getCall(item));
533
534
535
536
537
538
539
540
541
542
   
   setItemWidget(item,0,callItem);
   
   expandAll();
   return callItem;
}

///Remove a call from the interface
void CallView::destroyCall(Call* toDestroy) 
{
543
   if (SFLPhone::model()->getIndex(toDestroy) == currentItem())
544
545
      setCurrentItem(0);
   
546
   if (!SFLPhone::model()->getIndex(toDestroy))
547
       kDebug() << "Call not found";
548
549
   else if (indexOfTopLevelItem(SFLPhone::model()->getIndex(toDestroy)) != -1)
      takeTopLevelItem(indexOfTopLevelItem(SFLPhone::model()->getIndex(toDestroy)));
550
551
   else if (SFLPhone::model()->getIndex(toDestroy)->parent()) {
      QTreeWidgetItem* parent = SFLPhone::model()->getIndex(toDestroy)->parent();
552
      SFLPhone::model()->getIndex(toDestroy)->parent()->removeChild(SFLPhone::model()->getIndex(toDestroy));
553
554
555
556
557
558
559
      if (parent->childCount() == 0) /*This should never happen, but it does*/
         takeTopLevelItem(indexOfTopLevelItem(parent));
      else if (parent->childCount() == 1) {
         addTopLevelItem(extractItem(parent->child(0)));
         takeTopLevelItem(indexOfTopLevelItem(parent));
      } //TODO make sure it just never happen and remove this logic code
   }
560
   else
561
      kDebug() << "Call not found";
562
563
564
565
566
}

/// @todo Remove the text partially covering the TreeView item widget when it is being dragged, a beter implementation is needed
void CallView::clearArtefact(QTreeWidgetItem* item) 
{
567
568
   if (item)
      item->setText(0,"");
569
570
571
572
573
574
575
576
577
578
579
}


/*****************************************************************************
 *                                                                           *
 *                           Event related code                              *
 *                                                                           *
 ****************************************************************************/

void CallView::itemDoubleClicked(QTreeWidgetItem* item, int column) {
   Q_UNUSED(column)
580
   kDebug() << "Item doubleclicked" << SFLPhone::model()->getCall(item)->getState();
581
582
583
584
   switch(SFLPhone::model()->getCall(item)->getState()) {
      case CALL_STATE_INCOMING:
         SFLPhone::model()->getCall(item)->actionPerformed(CALL_ACTION_ACCEPT);
         break;
585
      case CALL_STATE_HOLD:
586
         SFLPhone::model()->getCall(item)->actionPerformed(CALL_ACTION_HOLD);
587
588
         break;
      case CALL_STATE_DIALING:
589
         SFLPhone::model()->getCall(item)->actionPerformed(CALL_ACTION_ACCEPT);
590
591
         break;
      default:
592
         kDebug() << "Double clicked an item with no action on double click.";
593
594
595
596
597
    }
}

void CallView::itemClicked(QTreeWidgetItem* item, int column) {
   Q_UNUSED(column)
598
   emit itemChanged(SFLPhone::model()->getCall(item));
599
   kDebug() << "Item clicked";
600
601
602
603
604
605
606
607
608
609
}


/*****************************************************************************
 *                                                                           *
 *                         Conference related code                           *
 *                                                                           *
 ****************************************************************************/

///Add a new conference, get the call list and update the interface as needed
610
Call* CallView::addConference(Call* conf) 
611
{
612
   kDebug() << "Conference created";
613
   Call* newConf =  conf;//SFLPhone::model()->addConference(confID);//TODO ELV?
614
615
   
   QTreeWidgetItem* confItem = new QTreeWidgetItem();
616
   SFLPhone::model()->updateIndex(conf,confItem);
Emmanuel Lepage's avatar
Emmanuel Lepage committed
617

618
   insertItem(confItem,(QTreeWidgetItem*)0);
Emmanuel Lepage's avatar
Emmanuel Lepage committed
619

620
621
622
623
   
   setCurrentItem(confItem);

   CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance();
624
   QStringList callList = callManager.getParticipantList(conf->getConfId());
625
626
   
   foreach (QString callId, callList) {
627
      kDebug() << "Adding " << callId << "to the conversation";
628
      insertItem(extractItem(SFLPhone::model()->getIndex(callId)),confItem);
629
630
631
632
633
634
635
   }
   
   Q_ASSERT_X(confItem->childCount() == 0, "add conference","Conference created, but without any participants");
   return newConf;
}

///Executed when the daemon signal a modification in an existing conference. Update the call list and update the TreeView
636
bool CallView::conferenceChanged(Call* conf) 
637
{
638
   kDebug() << "Conference changed";
639
640
   //if (!SFLPhone::model()->conferenceChanged(confId, state))
   //  return false;
641
642

   CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance();
643
   QStringList callList = callManager.getParticipantList(conf->getConfId());
644
645
646

   QList<QTreeWidgetItem*> buffer;
   foreach (QString callId, callList) {
647
648
649
650
      if (SFLPhone::model()->getCall(callId)) {
         QTreeWidgetItem* item3 = extractItem(SFLPhone::model()->getIndex(callId));
         insertItem(item3, SFLPhone::model()->getIndex(conf));
         buffer << SFLPhone::model()->getIndex(callId);
651
652
      }
      else
653
         kDebug() << "Call " << callId << " does not exist";
654
655
   }

656
657
658
659
660
   if (SFLPhone::model()->getIndex(conf)) /*Can happen is the daemon crashed*/
      for (int j =0; j < SFLPhone::model()->getIndex(conf)->childCount();j++) {
         if (buffer.indexOf(SFLPhone::model()->getIndex(conf)->child(j)) == -1)
            insertItem(extractItem(SFLPhone::model()->getIndex(conf)->child(j)));
      }
661
   
662
   Q_ASSERT_X(SFLPhone::model()->getIndex(conf)->childCount() == 0,"changind conference","A conference can't have no participants");
663
664
665
666
667
   
   return true;
}

///Remove a conference from the model and the TreeView
668
void CallView::conferenceRemoved(Call* conf) 
669
{
670
   kDebug() << "Attempting to remove conference";
671
672
673
674
   QTreeWidgetItem* idx = SFLPhone::model()->getIndex(conf);
   if (idx) {
   while (idx->childCount()) {
      insertItem(extractItem(SFLPhone::model()->getIndex(conf)->child(0)));
675
   }
676
677
   takeTopLevelItem(indexOfTopLevelItem(SFLPhone::model()->getIndex(conf)));
   //SFLPhone::model()->conferenceRemoved(confId);
678
   kDebug() << "Conference removed";
679
680
   }
   else {
681
      kDebug() << "Conference not found";
682
   }
683
684
685
686
687
}

///Clear the list of old calls //TODO Clear them from the daemon
void CallView::clearHistory()
{
688
   //SFLPhone::model()->getHistory().clear();
689
690
}

691
692
693
694
///Redirect keypresses to parent
void CallView::keyPressEvent(QKeyEvent* event) {
   SFLPhone::app()->view()->keyPressEvent(event);
}
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759


/*****************************************************************************
 *                                                                           *
 *                                 Overlay                                   *
 *                                                                           *
 ****************************************************************************/

///Constructor
CallViewOverlay::CallViewOverlay(QWidget* parent) : QWidget(parent),m_pIcon(0),m_pTimer(0),m_enabled(true),m_black("black")
{
   m_black.setAlpha(75);
}

///Add a widget (usually an icon) in the corner
void CallViewOverlay::setCornerWidget(QWidget* wdg) {
   wdg->setParent      ( this                       );
   wdg->setMinimumSize ( 100         , 100          );
   wdg->resize         ( 100         , 100          );
   wdg->move           ( width()-100 , height()-100 );
   m_pIcon = wdg;
}

///Overload the setVisible method to trigger the timer
void CallViewOverlay::setVisible(bool enabled) {
   if (m_enabled != enabled) {
      if (m_pTimer) {
         m_pTimer->stop();
         disconnect(m_pTimer);
      }
      m_pTimer = new QTimer(this); //TODO LEAK?
      connect(m_pTimer, SIGNAL(timeout()), this, SLOT(changeVisibility()));
      m_step = 0;
      m_black.setAlpha(0);
      repaint();
      m_pTimer->start(10);
   }
   m_enabled = enabled;
   QWidget::setVisible(enabled);
}

///How to paint the overlay
void CallViewOverlay::paintEvent(QPaintEvent* event) {
   Q_UNUSED(event)
   QPainter customPainter(this);
   customPainter.fillRect(rect(),m_black);
}

///Be sure the event is always the right size
void CallViewOverlay::resizeEvent(QResizeEvent *e) {
   Q_UNUSED(e)
   if (m_pIcon) {
      m_pIcon->setMinimumSize(100,100);
      m_pIcon->move(width()-100,height()-100);
   }
}

///Step by step animation to fade in/out
void CallViewOverlay::changeVisibility() {
   m_step++;
   m_black.setAlpha(0.1*m_step*m_step);
   repaint();
   if (m_step >= 35)
      m_pTimer->stop();
}