#include "CallView.h" ///Retrieve current and older calls from the daemon, fill history and the calls TreeView and enable drag n' drop CallView::CallView(ModelType type, QWidget* parent) : QTreeWidget(parent), CallModel<CallTreeItem*,QTreeWidgetItem*>(type) { if (type == ActiveCall) initCall(); else if (type == History) initHistory(); //Widget part setAcceptDrops(true); setDragEnabled(true); CallTreeItemDelegate *delegate = new CallTreeItemDelegate(); setItemDelegate(delegate); setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding)); //User Interface events connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*,int))); connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(itemClicked(QTreeWidgetItem*,int))); //D-Bus event // CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); // connect(&callManager, SIGNAL(callStateChanged(const QString &, const QString &)), // this, SLOT(callStateChangedSignal(const QString &, const QString &))); // connect(&callManager, SIGNAL(incomingCall(const QString &, const QString &, const QString &)), // this, SLOT(incomingCallSignal(const QString &, const QString &))); // connect(&callManager, SIGNAL(conferenceCreated(const QString &)), // this, SLOT(conferenceCreatedSignal(const QString &))); // connect(&callManager, SIGNAL(conferenceChanged(const QString &, const QString &)), // this, SLOT(conferenceChangedSignal(const QString &, const QString &))); // connect(&callManager, SIGNAL(conferenceRemoved(const QString &)), // this, SLOT(conferenceRemovedSignal(const QString &))); // connect(&callManager, SIGNAL(incomingMessage(const QString &, const QString &)), // this, SLOT(incomingMessageSignal(const QString &, const QString &))); // connect(&callManager, SIGNAL(voiceMailNotify(const QString &, int)), // this, SLOT(voiceMailNotifySignal(const QString &, int))); } /***************************************************************************** * * * Drag and drop related code * * * ****************************************************************************/ ///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) QByteArray encodedCallId = data->data( MIME_CALLID ); QByteArray encodedPhoneNumber = data->data( MIME_PHONENUMBER ); qDebug() << "In export"<< QString(encodedCallId); qDebug() << "In export2"<< QString(encodedPhoneNumber); if (!QString(encodedCallId).isEmpty()) { clearArtefact(getIndex(encodedCallId)); if (!parent) { qDebug() << "Call dropped on empty space"; if (getIndex(encodedCallId)->parent()) { qDebug() << "Detaching participant"; detachParticipant(getCall(encodedCallId)); } else qDebug() << "The call is not in a conversation (doing nothing)"; return true; } if (getCall(parent)->getCallId() == QString(encodedCallId)) { qDebug() << "Call dropped on itself (doing nothing)"; return true; } if ((parent->childCount()) && (getIndex(encodedCallId)->childCount())) { qDebug() << "Merging two conferences"; mergeConferences(getCall(parent),getCall(encodedCallId)); return true; } else if ((parent->parent()) || (parent->childCount())) { qDebug() << "Call dropped on a conference"; if ((getIndex(encodedCallId)->childCount()) && (!parent->childCount())) { qDebug() << "Conference dropped on a call (doing nothing)"; return true; } QTreeWidgetItem* call1 = getIndex(encodedCallId); QTreeWidgetItem* call2 = (parent->parent())?parent->parent():parent; if (call1->parent()) { qDebug() << "Call 1 is part of a conference"; if (call1->parent() == call2) { qDebug() << "Call dropped on it's own conference (doing nothing)"; return true; } else if (getIndex(call1)->childCount()) { qDebug() << "Merging two conferences"; mergeConferences(getCall(call1),getCall(call2)); } else if (call1->parent()) { qDebug() << "Moving call from a conference to an other"; detachParticipant(getCall(encodedCallId)); } } qDebug() << "Adding participant"; addParticipant(getCall(call1),getCall(call2)); return true; } else if ((getIndex(encodedCallId)->childCount()) && (!parent->childCount())) { qDebug() << "Call dropped on it's own conference (doing nothing)"; return true; } qDebug() << "Call dropped on another call"; createConferenceFromCall(getCall(encodedCallId),getCall(parent)); return true; } return false; } ///Encode data to be tranported during the drag n' drop operation QMimeData* CallView::mimeData( const QList<QTreeWidgetItem *> items) const { qDebug() << "A call is being dragged"; if (items.size() < 1) { return NULL; } QMimeData *mimeData = new QMimeData(); //Call ID for internal call merging and spliting if (getCall(items[0])->isConference()) { qDebug() << "Conf id:" << getCall(items[0])->getConfId().toAscii(); mimeData->setData(MIME_CALLID, getCall(items[0])->getConfId().toAscii()); } else { qDebug() << "Call id:" << getCall(items[0])->getConfId().toAscii(); mimeData->setData(MIME_CALLID, getCall(items[0])->getCallId().toAscii()); } //Plain text for other applications mimeData->setData(MIME_PLAIN_TEXT, QString(getCall(items[0])->getPeerName()+"\n"+getCall(items[0])->getPeerPhoneNumber()).toAscii()); //TODO Comment this line if you don't want to see ugly artefact, but the caller details will not be visible while dragged items[0]->setText(0, getCall(items[0])->getPeerName() + "\n" + getCall(items[0])->getPeerPhoneNumber()); 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) { //InternalCallModelStruct<CallTreeItem>* aNewStruct = privateCallList_call[CallModel<CallTreeItem>::addCall(call, parent)]; QTreeWidgetItem* callItem = new QTreeWidgetItem(); //aNewStruct->treeItem = callItem; updateIndex(call,callItem); //privateCallList_item[callItem] = aNewStruct; //aNewStruct->call = ; //privateCallList_widget[aNewStruct->call] = aNewStruct; insertItem(callItem,parent); setCurrentItem(callItem); connect(call, SIGNAL(isOver(Call*)), this, SLOT(destroyCall(Call*))); return call; } /***************************************************************************** * * * View related code * * * ****************************************************************************/ ///Set the TreeView header text void CallView::setTitle(QString title) { headerItem()->setText(0,title); } ///Select an item in the TreeView bool CallView::selectItem(Call* item) { if (getIndex(item)) { setCurrentItem(getIndex(item)); return true; } else return false; } ///Return the current item Call* CallView::getCurrentItem() { if (currentItem() && getCall(QTreeWidget::currentItem())) return getCall(QTreeWidget::currentItem()); else return false; } ///Remove a TreeView item and delete it bool CallView::removeItem(Call* item) { if (indexOfTopLevelItem(getIndex(item)) != -1) {//TODO To remove once safe removeItemWidget(getIndex(item),0); return true; } else return false; } ///Return the TreeView, this QWidget* CallView::getWidget() { return this; } ///Convenience wrapper around extractItem(QTreeWidgetItem*) QTreeWidgetItem* CallView::extractItem(QString callId) { QTreeWidgetItem* currentItem = getIndex(callId); 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)) { qDebug() << "The conversation does not exist"; 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) { return insertItem(item,(parent)?getIndex(parent):0); } ///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) { qDebug() << "This is not a valid call"; return 0; } if (!parent) insertTopLevelItem(0,item); else parent->addChild(item); //privateCallList_widget.remove(privateCallList_item[item]->call); //TODO needed? CallTreeItem* callItem = new CallTreeItem(); updateWidget(getCall(item), callItem); callItem->setCall(getCall(item)); //privateCallList_widget[privateCallList_item[item]->call] = privateCallList_item[item]; setItemWidget(item,0,callItem); expandAll(); return callItem; } ///Remove a call from the interface void CallView::destroyCall(Call* toDestroy) { if (getIndex(toDestroy) == currentItem()) setCurrentItem(0); if (!getIndex(toDestroy)) qDebug() << "Call not found"; else if (indexOfTopLevelItem(getIndex(toDestroy)) != -1) takeTopLevelItem(indexOfTopLevelItem(getIndex(toDestroy))); else if (getIndex(toDestroy)->parent()) //May crash here getIndex(toDestroy)->parent()->removeChild(getIndex(toDestroy)); else qDebug() << "Call not found"; } /// @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) { item->setText(0,""); } /***************************************************************************** * * * Event related code * * * ****************************************************************************/ void CallView::itemDoubleClicked(QTreeWidgetItem* item, int column) { Q_UNUSED(column) qDebug() << "Item doubleclicked"; switch(getCall(item)->getState()) { case CALL_STATE_HOLD: getCall(item)->actionPerformed(CALL_ACTION_HOLD); break; case CALL_STATE_DIALING: getCall(item)->actionPerformed(CALL_ACTION_ACCEPT); break; default: qDebug() << "Double clicked an item with no action on double click."; } } void CallView::itemClicked(QTreeWidgetItem* item, int column) { Q_UNUSED(column) emit itemChanged(getCall(item)); qDebug() << "Item clicked"; } /***************************************************************************** * * * Conference related code * * * ****************************************************************************/ ///Add a new conference, get the call list and update the interface as needed Call* CallView::addConference(const QString & confID) { qDebug() << "Conference created"; Call* newConf = CallModel<CallTreeItem*,QTreeWidgetItem*>::addConference(confID); //InternalCallModelStruct<CallTreeItem>* aNewStruct = privateCallList_callId[confID]; // if (!aNewStruct) { // qDebug() << "Conference failed"; // return 0; // } QTreeWidgetItem* confItem = new QTreeWidgetItem(); //aNewStruct->treeItem = confItem; updateIndex(newConf,confItem); //privateCallList_item[confItem] = aNewStruct; //aNewStruct->call = ; insertItem(confItem,(QTreeWidgetItem*)0); //privateCallList_widget[aNewStruct->call] = aNewStruct; setCurrentItem(confItem); CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); QStringList callList = callManager.getParticipantList(confID); foreach (QString callId, callList) { qDebug() << "Adding " << callId << "to the conversation"; insertItem(extractItem(getIndex(callId)),confItem); } 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 bool CallView::conferenceChanged(const QString &confId, const QString &state) { qDebug() << "Conference changed"; if (!CallModel<CallTreeItem*,QTreeWidgetItem*>::conferenceChanged(confId, state)) return false; CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); QStringList callList = callManager.getParticipantList(confId); QList<QTreeWidgetItem*> buffer; foreach (QString callId, callList) { if (getCall(callId)) { QTreeWidgetItem* item3 = extractItem(getIndex(callId)); insertItem(item3, getIndex(confId)); buffer << getIndex(callId); } else qDebug() << "Call " << callId << " does not exist"; } for (int j =0; j < getIndex(confId)->childCount();j++) { if (buffer.indexOf(getIndex(confId)->child(j)) == -1) insertItem(extractItem(getIndex(confId)->child(j))); } Q_ASSERT_X(getIndex(confId)->childCount() == 0,"changind conference","A conference can't have no participants"); return true; } ///Remove a conference from the model and the TreeView void CallView::conferenceRemoved(const QString &confId) { while (getIndex(confId)->childCount()) { insertItem(extractItem(getIndex(confId)->child(0))); } takeTopLevelItem(indexOfTopLevelItem(getIndex(confId))); CallModel<CallTreeItem*,QTreeWidgetItem*>::conferenceRemoved(confId); qDebug() << "Conference removed"; } ///Clear the list of old calls //TODO Clear them from the daemon void CallView::clearHistory() { historyCalls.clear(); } // void CallView::callStateChangedSignal(const QString& callId, const QString& state) // { // qDebug() << "Signal : Call State Changed for call " << callId << " . New state : " << state; // Call* call = findCallByCallId(callId); // if(!call) { // if(state == CALL_STATE_CHANGE_RINGING) { // call = addRingingCall(callId); // } // else { // qDebug() << "Call doesn't exist in this client. Might have been initialized by another client instance before this one started."; // return; // } // } // else { // call->stateChanged(state); // } // } // // void CallView::incomingCallSignal(const QString& accountId, const QString& callId) // { // Q_UNUSED(accountId) // qDebug() << "Signal : Incoming Call ! ID = " << callId; // addIncomingCall(callId); // } // void CallView::conferenceCreatedSignal(const QString& confId) { addConference(confId); } void CallView::conferenceChangedSignal(const QString& confId, const QString& state) { conferenceChanged(confId, state); } void CallView::conferenceRemovedSignal(const QString& confId) { conferenceRemoved(confId); } // // void CallView::incomingMessageSignal(const QString& accountId, const QString& message) // { // Q_UNUSED(accountId) // Q_UNUSED(message) // //TODO // } // // void CallView::voiceMailNotifySignal(const QString& accountId, int count) // { // Q_UNUSED(accountId) // Q_UNUSED(count) // //TODO // }