diff options
author | Michal Kubecek <mkubecek@suse.cz> | 2015-04-13 09:21:39 +0200 |
---|---|---|
committer | Michal Kubecek <mkubecek@suse.cz> | 2015-04-13 09:21:39 +0200 |
commit | e2bc6f4153813cc570ae814c8ddb74628009b488 (patch) | |
tree | a40b171be1d859c2232ccc94f758010f9ae54d3c /src/gui/mphoneform.ui.h | |
download | twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.gz twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.lz twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.xz twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.zip |
initial checkin
Check in contents of upstream 1.4.2 tarball, exclude generated files.
Diffstat (limited to 'src/gui/mphoneform.ui.h')
-rw-r--r-- | src/gui/mphoneform.ui.h | 3126 |
1 files changed, 3126 insertions, 0 deletions
diff --git a/src/gui/mphoneform.ui.h b/src/gui/mphoneform.ui.h new file mode 100644 index 0000000..6d008e6 --- /dev/null +++ b/src/gui/mphoneform.ui.h @@ -0,0 +1,3126 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, pres:erving your code. Ceate an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ +/* + Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.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 2 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 +*/ + +#include "twinkle_config.h" +#include "twinklesystray.h" + +// Time (s) that the conversation timer of a line should stay visible after +// a call has ended +#define HIDE_LINE_TIMER_AFTER 5 + +void MphoneForm::init() +{ + // Forms + dtmfForm = 0; + inviteForm = 0; + redirectForm = 0; + transferForm = 0; + termCapForm = 0; + srvRedirectForm = 0; + userProfileForm = 0; + sysSettingsForm = 0; + logViewForm = 0; + historyForm = 0; + selectUserForm = 0; + selectProfileForm = 0; + getAddressForm = 0; + sysTray = 0; + + // Popup menu for a single buddy + QIconSet inviteIcon(QPixmap::fromMimeSource("invite.png")); + QIconSet messageIcon(QPixmap::fromMimeSource("message.png")); + QIconSet editIcon(QPixmap::fromMimeSource("edit16.png")); + QIconSet deleteIcon(QPixmap::fromMimeSource("editdelete.png")); + buddyPopupMenu = new QPopupMenu(this); + MEMMAN_NEW(buddyPopupMenu); + buddyPopupMenu->insertItem(inviteIcon, tr("&Call..."), this, SLOT(doCallBuddy())); + buddyPopupMenu->insertItem(messageIcon, tr("Instant &message..."), this, SLOT(doMessageBuddy())); + buddyPopupMenu->insertItem(editIcon, tr("&Edit..."), this, SLOT(doEditBuddy())); + buddyPopupMenu->insertItem(deleteIcon, tr("&Delete"), this, SLOT(doDeleteBuddy())); + + // Change availibility sub popup menu + changeAvailabilityPopupMenu = new QPopupMenu(this); + MEMMAN_NEW(changeAvailabilityPopupMenu); + QIconSet availOnlineIcon(QPixmap::fromMimeSource("presence_online.png")); + QIconSet availOfflineIcon(QPixmap::fromMimeSource("presence_offline.png")); + changeAvailabilityPopupMenu->insertItem(availOfflineIcon, tr("O&ffline"), this, + SLOT(doAvailabilityOffline())); + changeAvailabilityPopupMenu->insertItem(availOnlineIcon, tr("&Online"), this, + SLOT(doAvailabilityOnline())); + + // Popup menu for a buddy list (click on profile name) + QIconSet changeAvailabilityIcon(QPixmap::fromMimeSource("presence_online.png")); + QIconSet addIcon(QPixmap::fromMimeSource("buddy.png")); + buddyListPopupMenu = new QPopupMenu(this); + MEMMAN_NEW(buddyListPopupMenu); + buddyListPopupMenu->insertItem(changeAvailabilityIcon, tr("&Change availability"), + changeAvailabilityPopupMenu); + buddyListPopupMenu->insertItem(addIcon, tr("&Add buddy..."), this, SLOT(doAddBuddy())); + + // Tool tip for buddy list + buddyToolTip = new BuddyListViewTip(buddyListView); + MEMMAN_NEW(buddyToolTip); + + // Line timers + lineTimer1 = 0; + lineTimer2 = 0; + timer1TextLabel->hide(); + timer2TextLabel->hide(); + + // Timer to hide the conversation timer after a conversation has ended. + hideLineTimer1 = new QTimer(this); + MEMMAN_NEW(hideLineTimer1); + hideLineTimer2 = new QTimer(this); + MEMMAN_NEW(hideLineTimer2); + connect(hideLineTimer1, SIGNAL(timeout()), timer1TextLabel, SLOT(hide())); + connect(hideLineTimer2, SIGNAL(timeout()), timer2TextLabel, SLOT(hide())); + + // Attach the MWI flash slot to the MWI flash timer + connect(&tmrFlashMWI, SIGNAL(timeout()), this, SLOT(flashMWI())); + + // Set toolbar icons for disabled options. + setDisabledIcon(callInvite, "invite-disabled.png"); + setDisabledIcon(callAnswer, "answer-disabled.png"); + setDisabledIcon(callBye, "bye-disabled.png"); + setDisabledIcon(callReject, "reject-disabled.png"); + setDisabledIcon(callRedirect, "redirect-disabled.png"); + setDisabledIcon(callTransfer, "transfer-disabled.png"); + setDisabledIcon(callHold, "hold-disabled.png"); + setDisabledIcon(callConference, "conf-disabled.png"); + setDisabledIcon(callMute, "mute-disabled.png"); + setDisabledIcon(callDTMF, "dtmf-disabled.png"); + setDisabledIcon(callRedial, "redial-disabled.png"); + + // Set tool button icons for disabled options + setDisabledIcon(addressToolButton, "kontact_contacts-disabled.png"); + + // Some text labels on the main window are implemented as QLineEdit + // objects as these do not automatically resize when a text set with setText + // does not fit. The background of a QLineEdit is static however, it does not + // automatically take a background color passed by the -bg parameter. + // Set the background color of these QLineEdit objects here. + from1Label->setPaletteBackgroundColor(paletteBackgroundColor()); + to1Label->setPaletteBackgroundColor(paletteBackgroundColor()); + subject1Label->setPaletteBackgroundColor(paletteBackgroundColor()); + from2Label->setPaletteBackgroundColor(paletteBackgroundColor()); + to2Label->setPaletteBackgroundColor(paletteBackgroundColor()); + subject2Label->setPaletteBackgroundColor(paletteBackgroundColor()); + + // A QComboBox accepts a new line through copy/paste. + QRegExp rxNoNewLine("[^\\n\\r]*"); + callComboBox->setValidator(new QRegExpValidator(rxNoNewLine, this)); + + if (sys_config->get_gui_use_systray()) { + // Create system tray icon + sysTray = new t_twinkle_sys_tray(this, "twinkle_sys_tray"); + MEMMAN_NEW(sysTray); + sysTray->setPixmap( + QPixmap::fromMimeSource("sys_idle_dis.png")); + sysTray->setCaption(PRODUCT_NAME); + QToolTip::add(sysTray, PRODUCT_NAME); + + // Add items to the system tray menu +#ifdef HAVE_KDE + KPopupMenu *menu; +#else + QPopupMenu *menu; +#endif + menu = sysTray->contextMenu(); + + // Call menu + callInvite->addTo(menu); + callAnswer->addTo(menu); + callBye->addTo(menu); + callReject->addTo(menu); + callRedirect->addTo(menu); + callTransfer->addTo(menu); + callHold->addTo(menu); + callConference->addTo(menu); + callMute->addTo(menu); + callDTMF->addTo(menu); + callRedial->addTo(menu); + + menu->insertSeparator(); + + // Messaging + actionSendMsg->addTo(menu); + + menu->insertSeparator(); + + // Line activation + actgrActivateLine->addTo(menu); + + menu->insertSeparator(); + + // Service menu + serviceDnd->addTo(menu); + serviceRedirection->addTo(menu); + serviceAutoAnswer->addTo(menu); + servicesVoice_mailAction->addTo(menu); + + menu->insertSeparator(); + + // View menu + viewCall_HistoryAction->addTo(menu); + + menu->insertSeparator(); + + // Diamondcard menu + menu->insertItem("Diamondcard", Diamondcard); + + // Exit application when user selects Quit from the tray menu + connect(sysTray, SIGNAL(quitSelected()), + this, SLOT(fileExit())); + + sysTray->dock(); + sysTray->show(); + } +} + +void MphoneForm::destroy() +{ + if (dtmfForm) { + MEMMAN_DELETE(dtmfForm); + delete dtmfForm; + } + if (inviteForm) { + MEMMAN_DELETE(inviteForm); + delete inviteForm; + } + if (redirectForm) { + MEMMAN_DELETE(redirectForm); + delete redirectForm; + } + if (termCapForm) { + MEMMAN_DELETE(termCapForm); + delete termCapForm; + } + if (srvRedirectForm) { + MEMMAN_DELETE(srvRedirectForm); + delete srvRedirectForm; + } + if (userProfileForm) { + MEMMAN_DELETE(userProfileForm); + delete userProfileForm; + } + if (transferForm) { + MEMMAN_DELETE(transferForm); + delete transferForm; + } + if (sysSettingsForm) { + MEMMAN_DELETE(sysSettingsForm); + delete sysSettingsForm; + } + if (logViewForm) { + if (logViewForm->isShown()) logViewForm->close(); + MEMMAN_DELETE(logViewForm); + delete logViewForm; + } + if (historyForm) { + if (historyForm->isShown()) historyForm->close(); + MEMMAN_DELETE(historyForm); + delete historyForm; + } + if (selectUserForm) { + MEMMAN_DELETE(selectUserForm); + delete selectUserForm; + } + if (selectProfileForm) { + MEMMAN_DELETE(selectProfileForm); + delete selectProfileForm; + } + if (getAddressForm) { + MEMMAN_DELETE(getAddressForm); + delete getAddressForm; + } + if (sysTray) { + MEMMAN_DELETE(sysTray); + delete sysTray; + } + + if (lineTimer1) { + MEMMAN_DELETE(lineTimer1); + delete lineTimer1; + } + if (lineTimer2) { + MEMMAN_DELETE(lineTimer2); + delete lineTimer2; + } + MEMMAN_DELETE(hideLineTimer1); + delete hideLineTimer1; + MEMMAN_DELETE(hideLineTimer2); + delete hideLineTimer2; + MEMMAN_DELETE(buddyPopupMenu); + delete buddyPopupMenu; + MEMMAN_DELETE(changeAvailabilityPopupMenu); + delete changeAvailabilityPopupMenu; + MEMMAN_DELETE(buddyListPopupMenu); + delete buddyListPopupMenu; + MEMMAN_DELETE(buddyToolTip); + delete buddyToolTip; +} + +QString MphoneForm::lineSubstate2str( int line) { + QString reason; + + t_call_info call_info = phone->get_call_info(line); + + switch(phone->get_line_substate(line)) { + case LSSUB_IDLE: + return tr("idle"); + case LSSUB_SEIZED: + return tr("dialing"); + case LSSUB_OUTGOING_PROGRESS: + reason = call_info.last_provisional_reason.c_str(); + if (reason == "") { + return tr("attempting call, please wait"); + } + return reason; + case LSSUB_INCOMING_PROGRESS: + return QString("<font color=red>") + tr("incoming call") + "</font>"; + case LSSUB_ANSWERING: + return tr("establishing call, please wait"); + case LSSUB_ESTABLISHED: + if (phone->has_line_media(line)) { + return tr("established"); + } else { + return tr("established (waiting for media)"); + } + break; + case LSSUB_RELEASING: + return tr("releasing call, please wait"); + default: + return tr("unknown state"); + } +} + +void MphoneForm::closeEvent( QCloseEvent *e ) +{ + if (sysTray && sys_config->get_gui_hide_on_close()) { + hide(); + } else { + fileExit(); + } +} + +void MphoneForm::fileExit() +{ + hide(); + QApplication::exit(0); +} + +// Append a string to the display window +void MphoneForm::display( const QString &s ) +{ + displayContents.push_back(s); + if (displayContents.size() > 100) { + displayContents.pop_front(); + } + + displayTextEdit->setText(displayContents.join("\n")); + + // Set cursor position at the end of text + displayTextEdit->setCursorPosition(displayTextEdit->paragraphs() - 1, 0); +} + +// Print message header on display +void MphoneForm::displayHeader() +{ + display(""); + display(current_time2str("%a %H:%M:%S").c_str()); +} + +// Update the conversation timer +void MphoneForm::showLineTimer(int line) +{ + struct timeval t; + gettimeofday(&t, NULL); + + QLabel *timerLabel; + + if (line == 0) { + timerLabel = timer1TextLabel; + } else { + timerLabel = timer2TextLabel; + } + + // Calculate duration of call + t_call_record cr = phone->get_call_hist(line); + unsigned long duration = t.tv_sec - cr.time_answer; + + timerLabel->setText(timer2str(duration).c_str()); +} + +void MphoneForm::showLineTimer1() +{ + showLineTimer(0); +} + +void MphoneForm::showLineTimer2() +{ + showLineTimer(1); +} + +// Update visibility of the conversation timer for a line +// Initialize the timer for a new established call. +void MphoneForm::updateLineTimer(int line) +{ + QLabel *timerLabel; + QTimer **timer; + QTimer *hideLineTimer; + + if (line == 0) { + timerLabel = timer1TextLabel; + timer = &lineTimer1; + hideLineTimer = hideLineTimer1; + } else { + timerLabel = timer2TextLabel; + timer = &lineTimer2; + hideLineTimer = hideLineTimer2; + } + + t_line_substate line_substate = phone->get_line_substate(line); + + // Stop hide timer if necessary + switch(line_substate) { + case LSSUB_IDLE: + case LSSUB_RELEASING: + // Timer can be shown as long as line is idle or releasing. + break; + default: + // The timer showing the call duration should only stay + // for a few seconds as long as the line is idle or being + // released. + // If a new call arrives on the line, the hide timer should + // be stopped, otherwise the timer of the new call will + // automatically disappear. + if (hideLineTimer->isActive()) { + hideLineTimer->stop(); + if (*timer == NULL) timerLabel->hide(); + } + break; + } + + switch(line_substate) { + case LSSUB_ESTABLISHED: + // Initialize and show call duration timer + if (*timer == NULL) { + timerLabel->setText(timer2str(0).c_str()); + timerLabel->show(); + *timer = new QTimer(this); + MEMMAN_NEW(*timer); + + if (line == 0) { + connect(*timer, SIGNAL(timeout()), this, + SLOT(showLineTimer1())); + } else { + connect(*timer, SIGNAL(timeout()), this, + SLOT(showLineTimer2())); + } + + // Update timer every 1s + (*timer)->start(1000, false); + } + break; + default: + // Hide call duration timer + if (*timer != NULL) { + // Hide the timer after a few seconds + hideLineTimer->start(HIDE_LINE_TIMER_AFTER * 1000, true); + (*timer)->stop(); + MEMMAN_DELETE(*timer); + *timer = NULL; + } + break; + } +} + +void MphoneForm::updateLineEncryptionState(int line) +{ + QLabel *cryptLabel, *sasLabel; + if (line == 0) { + cryptLabel = crypt1Label; + sasLabel = line1SasLabel; + } else { + cryptLabel = crypt2Label; + sasLabel = line2SasLabel; + } + + t_audio_session *as = phone->get_line(line)->get_audio_session(); + if (as && phone->is_line_encrypted(line)) { + string zrtp_sas = as->get_zrtp_sas(); + bool zrtp_sas_confirmed = as->get_zrtp_sas_confirmed(); + string srtp_cipher_mode = as->get_srtp_cipher_mode(); + + QToolTip::remove(cryptLabel); + QString toolTip = tr("Voice is encrypted") + " ("; + toolTip.append(srtp_cipher_mode.c_str()).append(")"); + + if (!zrtp_sas.empty()) { + // Set tool tip on encryption icon + toolTip.append("\nSAS = "); + toolTip.append(zrtp_sas.c_str()); + + // Show SAS + sasLabel->setText(zrtp_sas.c_str()); + sasLabel->show(); + } else { + sasLabel->hide(); + } + + if (!zrtp_sas_confirmed) { + toolTip.append("\n").append(tr("Click to confirm SAS.")); + cryptLabel->setFrameStyle(QFrame::Panel | QFrame::Raised); + cryptLabel->setPixmap( + QPixmap::fromMimeSource("encrypted.png")); + } else { + toolTip.append("\n").append(tr("Click to clear SAS verification.")); + cryptLabel->setFrameStyle(QFrame::NoFrame); + cryptLabel->setPixmap( + QPixmap::fromMimeSource("encrypted_verified.png")); + } + + QToolTip::add(cryptLabel, toolTip); + cryptLabel->show(); + } else { + cryptLabel->hide(); + sasLabel->hide(); + } +} + +void MphoneForm::updateLineStatus(int line) +{ + QString state; + bool on_hold; // indicates if a line is put on-hold + bool in_conference; // indicates if a line is in a conference + bool is_muted; // indicates is a line is muted + t_refer_state refer_state; // indicates if a call transfer is in progress + bool is_transfer_consult; // indicates if the call is a consultation + bool to_be_transferred; // indicates if the line is to be transferred after consultation + t_call_info call_info; + unsigned short dummy; + + QLabel *statLabel, *holdLabel, *muteLabel, *confLabel, *referLabel, *statusTextLabel; + + if (line == 0) { + statLabel = line1StatLabel; + holdLabel = line1HoldLabel; + muteLabel = line1MuteLabel; + confLabel = line1ConfLabel; + referLabel = line1ReferLabel; + statusTextLabel = status1TextLabel; + } else { + statLabel = line2StatLabel; + holdLabel = line2HoldLabel; + muteLabel = line2MuteLabel; + confLabel = line2ConfLabel; + referLabel = line2ReferLabel; + statusTextLabel = status2TextLabel; + } + + state = lineSubstate2str(line); + on_hold = phone->is_line_on_hold(line); + if (on_hold) { + holdLabel->show(); + } else { + holdLabel->hide(); + } + in_conference = phone->part_of_3way(line); + if (in_conference) { + confLabel->show(); + } else { + confLabel->hide(); + } + is_muted = phone->is_line_muted(line); + if (is_muted) { + muteLabel->show(); + } else { + muteLabel->hide(); + } + refer_state = phone->get_line_refer_state(line); + is_transfer_consult = phone->is_line_transfer_consult(line, dummy); + to_be_transferred = phone->line_to_be_transferred(line, dummy); + if (refer_state != REFST_NULL || is_transfer_consult || to_be_transferred) { + QString toolTip; + QToolTip::remove(referLabel); + referLabel->show(); + if (is_transfer_consult) { + referLabel->setPixmap( + QPixmap::fromMimeSource("consult-xfer.png")); + toolTip = tr("Transfer consultation"); + } else { + referLabel->setPixmap( + QPixmap::fromMimeSource("cf.png")); + toolTip = tr("Transferring call"); + } + QToolTip::add(referLabel, toolTip); + } else { + referLabel->hide(); + } + + statusTextLabel->setText(state); + + t_line_substate line_substate; + line_substate = phone->get_line_substate(line); + switch (line_substate) { + case LSSUB_IDLE: + ((t_gui *)ui)->clearLineFields(line); + statLabel->hide(); + break; + case LSSUB_SEIZED: + case LSSUB_OUTGOING_PROGRESS: + statLabel->setPixmap(QPixmap::fromMimeSource("stat_outgoing.png")); + statLabel->show(); + break; + case LSSUB_INCOMING_PROGRESS: + statLabel->setPixmap(QPixmap::fromMimeSource("stat_ringing.png")); + statLabel->show(); + break; + case LSSUB_ANSWERING: + statLabel->setPixmap(QPixmap::fromMimeSource("gear.png")); + statLabel->show(); + break; + case LSSUB_ESTABLISHED: + if (phone->has_line_media(line)) { + statLabel->setPixmap(QPixmap::fromMimeSource( + "stat_established.png")); + } else { + statLabel->setPixmap(QPixmap::fromMimeSource( + "stat_established_nomedia.png")); + } + statLabel->show(); + break; + case LSSUB_RELEASING: + statLabel->setPixmap(QPixmap::fromMimeSource("gear.png")); + statLabel->show(); + break; + default: + statLabel->hide(); + break; + } + + updateLineEncryptionState(line); + updateLineTimer(line); +} + +// Update line state and enable/disable buttons depending on state +void MphoneForm::updateState() +{ + QString state; + int line, other_line; + bool on_hold; // indicates if a line is put on-hold + bool in_conference; // indicates if a line is in a conference + bool is_muted; // indicates is a line is muted + t_refer_state refer_state; // indicates if a call transfer is in progress + bool is_transfer_consult; // indicates if the call is a consultation + bool to_be_transferred; // indicates if the line is to be transferred after consultation + bool has_media; // indicates if a media stream is present + t_call_info call_info; + unsigned short dummy; + + // Update status of line 1 + updateLineStatus(0); + + // Update status of line 2 + updateLineStatus(1); + + // Disable/enable controls depending on the active line state + t_line_substate line_substate; + line = phone->get_active_line(); + line_substate = phone->get_line_substate(line); + on_hold = phone->is_line_on_hold(line); + in_conference = phone->part_of_3way(line); + is_muted = phone->is_line_muted(line); + refer_state = phone->get_line_refer_state(line); + is_transfer_consult = phone->is_line_transfer_consult(line, dummy); + to_be_transferred = phone->line_to_be_transferred(line, dummy); + has_media = phone->has_line_media(line); + other_line = (line == 0 ? 1 : 0); + call_info = phone->get_call_info(line); + t_user *user_config = phone->get_line_user(line); + + // The active line may change when one of the parties in a conference + // releases the call. If this happens, then update the state of the + // line radio buttons. + if (line == 0 && line2RadioButton->isOn()) + { + line1RadioButton->setChecked(true); + } else if (line == 1 && line1RadioButton->isOn()) + { + line2RadioButton->setChecked(true); + } + + // Same logic for the activate line menu items + if (line == 0 && actionLine2->isOn()) + { + actionLine1->setOn(true); + } else if (line == 1 && actionLine1->isOn()) + { + actionLine2->setOn(true); + } + + switch(line_substate) { + case LSSUB_IDLE: + enableCallOptions(true); + callAnswer->setEnabled(false); + callBye->setEnabled(false); + callReject->setEnabled(false); + callRedirect->setEnabled(false); + callTransfer->setEnabled(false); + callHold->setEnabled(false); + callConference->setEnabled(false); + callMute->setEnabled(false); + callDTMF->setEnabled(false); + callRedial->setEnabled(ui->can_redial()); + break; + case LSSUB_OUTGOING_PROGRESS: + enableCallOptions(false); + callAnswer->setEnabled(false); + callBye->setEnabled(true); + callReject->setEnabled(false); + callRedirect->setEnabled(false); + + if (is_transfer_consult && user_config->get_allow_transfer_consultation_inprog()) { + callTransfer->setEnabled(true); + } else { + callTransfer->setEnabled(false); + } + + callHold->setEnabled(false); + callConference->setEnabled(false); + callMute->setEnabled(false); + callDTMF->setEnabled(call_info.dtmf_supported); + callRedial->setEnabled(false); + break; + case LSSUB_INCOMING_PROGRESS: + enableCallOptions(false); + callAnswer->setEnabled(true); + callBye->setEnabled(false); + callReject->setEnabled(true); + callRedirect->setEnabled(true); + callTransfer->setEnabled(false); + callHold->setEnabled(false); + callConference->setEnabled(false); + callMute->setEnabled(false); + callDTMF->setEnabled(call_info.dtmf_supported); + callRedial->setEnabled(false); + break; + case LSSUB_ESTABLISHED: + enableCallOptions(false); + callInvite->setEnabled(false); + callAnswer->setEnabled(false); + callBye->setEnabled(true); + callReject->setEnabled(false); + callRedirect->setEnabled(false); + + if (in_conference) { + callTransfer->setEnabled(false); + callHold->setEnabled(false); + callConference->setEnabled(false); + callDTMF->setEnabled(false); + } else { + callTransfer->setEnabled(has_media && + call_info.refer_supported && + refer_state == REFST_NULL && + !to_be_transferred); + callHold->setEnabled(has_media); + callDTMF->setEnabled(call_info.dtmf_supported); + + if (phone->get_line_substate(other_line) == + LSSUB_ESTABLISHED) + { + // If one of the lines is transferring a call, then a + // conference cannot be setup. + if (refer_state != REFST_NULL || + phone->get_line_refer_state(other_line) != REFST_NULL) + { + callConference->setEnabled(false); + } else { + callConference->setEnabled(has_media); + } + } else { + callConference->setEnabled(false); + } + } + + callMute->setEnabled(true); + callRedial->setEnabled(false); + break; + case LSSUB_SEIZED: + case LSSUB_ANSWERING: + case LSSUB_RELEASING: + // During dialing, answering and call release no other actions are + // possible + enableCallOptions(false); + callAnswer->setEnabled(false); + callBye->setEnabled(false); + callReject->setEnabled(false); + callRedirect->setEnabled(false); + callTransfer->setEnabled(false); + callHold->setEnabled(false); + callConference->setEnabled(false); + callMute->setEnabled(false); + callDTMF->setEnabled(false); + callRedial->setEnabled(false); + break; + default: + enableCallOptions(true); + callAnswer->setEnabled(true); + callBye->setEnabled(true); + callReject->setEnabled(true); + callRedirect->setEnabled(true); + callTransfer->setEnabled(true); + callHold->setEnabled(true); + callConference->setEnabled(false); + callMute->setEnabled(true); + callDTMF->setEnabled(true); + callRedial->setEnabled(ui->can_redial()); + } + + // Set hold action in correct state + callHold->setOn(on_hold); + + // Set mute action in correct state + callMute->setOn(is_muted); + + // Set transfer action in correct state + callTransfer->setOn(is_transfer_consult); + + // Hide redirect form if it is still visible, but not applicable anymore + if (!callRedirect->isEnabled() && redirectForm && + redirectForm->isVisible()) + { + redirectForm->hide(); + } + + // Hide transfer form if it is still visible, but not applicable anymore + if (!callTransfer->isEnabled() && transferForm && + transferForm->isVisible()) + { + transferForm->hide(); + } + + // Hide DTMF form if it is still visible, but not applicable anymore + if (!callDTMF->isEnabled() && dtmfForm && + dtmfForm->isVisible()) + { + dtmfForm->hide(); + } + + // Set last called address in the redial tool tip + t_url last_url; + string last_display; + string last_subject; + t_user *last_user; + bool hide_user; + if (callRedial->isEnabled() && + ui->get_last_call_info(last_url, last_display, last_subject, &last_user, hide_user)) + { + QString s = "<b>"; + s += tr("Repeat last call"); + s += "</b><br>"; + s += "<table>"; + + s += "<tr><td>"; + s += tr("User:").append("</td><td>"); + s += last_user->get_profile_name().c_str(); + s.append("</td></tr><tr><td>").append(tr("Call:")).append("</td><td>"); + s += ui->format_sip_address(last_user, + last_display, last_url).c_str(); + s += "</td></tr>"; + + if (!last_subject.empty()) { + s.append("<tr><td>").append(tr("Subject:")).append("</td><td>"); + s += last_subject.c_str(); + s += "</td></tr>"; + } + + if (hide_user) { + s.append("<tr><td colspan=2>").append(tr("Hide identity")); + s += "</td></tr>"; + } + + s += "</table>"; + callRedial->setToolTip(s); + } else { + callRedial->setToolTip(tr("Repeat last call")); + } + callRedial->setStatusTip(tr("Repeat last call")); + + updateSysTrayStatus(); +} + +// Update registration status +void MphoneForm::updateRegStatus() +{ + size_t num_registered = 0; + size_t num_failed = 0; + QString toolTip = "<b>"; + toolTip.append(tr("Registration status:")); + toolTip.append("</b><br>"); + toolTip.append("<table>"); + + // Count number of succesful and failed registrations. + // Determine tool tip showing registration details for all users. + list<t_user *>user_list = phone->ref_users(); + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) { + toolTip.append("<tr><td>"); + toolTip.append((*i)->get_profile_name().c_str()); + toolTip.append("</td><td>"); + if (phone->get_is_registered(*i)) { + num_registered++; + toolTip.append(tr("Registered")); + } else if (phone->get_last_reg_failed(*i)) { + num_failed++; + toolTip.append(tr("Failed")); + } else { + toolTip.append(tr("Not registered").replace(' ', " ")); + } + toolTip.append("</td></tr>"); + } + toolTip.append("</table><br>"); + toolTip.append("<i>"); + toolTip.append(tr("Click to show registrations.").replace(' ', " ")); + toolTip.append("</i>"); + + // Set registration status + if (num_registered == user_list.size()) { + // All users are registered + statRegLabel->setPixmap(QPixmap::fromMimeSource("twinkle16.png")); + } else if (num_failed == user_list.size()) { + // All users failed to register + statRegLabel->setPixmap(QPixmap::fromMimeSource("reg_failed.png")); + } else if (num_registered > 0) { + // Some users are registered + statRegLabel->setPixmap(QPixmap::fromMimeSource( + "twinkle16.png")); + } else if (num_failed > 0) { + // Some users failed, none are registered + statRegLabel->setPixmap(QPixmap::fromMimeSource("reg_failed.png")); + } else { + // No users are registered, no users failed + statRegLabel->setPixmap(QPixmap::fromMimeSource("twinkle16-disabled.png")); + } + + // Set tool tip with detailed info. + QToolTip::remove(statRegLabel); + + if (num_registered > 0 || num_failed > 0) { + QToolTip::add(statRegLabel, toolTip); + } else { + QToolTip::add(statRegLabel, tr("No users are registered.")); + } + + updateSysTrayStatus(); +} + +// Create a status message based on the number of waiting messages. +// On return, msg_waiting will indicate if the MWI indicator should show +// waiting messages. +QString MphoneForm::getMWIStatus(const t_mwi &mwi, bool &msg_waiting) const +{ + QString status; + msg_waiting = false; + t_msg_summary summary = mwi.get_voice_msg_summary(); + + if (summary.newmsgs > 0 && summary.oldmsgs > 0) { + if (summary.oldmsgs == 1) { + status = tr("%1 new, 1 old message"). + arg(summary.newmsgs); + } else { + status = tr("%1 new, %2 old messages"). + arg(summary.newmsgs). + arg(summary.oldmsgs); + } + msg_waiting = true; + } else if (summary.newmsgs > 0 && summary.oldmsgs == 0) { + if (summary.newmsgs == 1) { + status = tr("1 new message"); + } else { + status = tr("%1 new messages"). + arg(summary.newmsgs); + } + msg_waiting = true; + } else if (summary.oldmsgs > 0) { + if (summary.oldmsgs == 1) { + status = tr("1 old message"); + } else { + status = tr("%1 old messages"). + arg(summary.oldmsgs); + } + } else { + if (mwi.get_msg_waiting()) { + status = tr("Messages waiting"); + msg_waiting = true; + } else { + status = tr("No messages"); + } + } + + return status.replace(' ', " "); +} + +// Flash the MWI icon +void MphoneForm::flashMWI() +{ + if (mwiFlashStatus) { + mwiFlashStatus = false; + statMWILabel->setPixmap(QPixmap::fromMimeSource( + "mwi_none16.png")); + } else { + mwiFlashStatus = true; + statMWILabel->setPixmap(QPixmap::fromMimeSource( + "mwi_new16.png")); + } +} + +// Update MWI +void MphoneForm::updateMwi() +{ + bool mwi_known = false; + bool mwi_new_msgs = false; + bool mwi_failure = false; + + // Determine tool tip + QString toolTip = tr("<b>Voice mail status:</b>").append("\n"); + toolTip.append("<br><table>"); + list<t_user *>user_list = phone->ref_users(); + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) { + toolTip.append("<tr><td>"); + toolTip.append((*i)->get_profile_name().c_str()); + t_mwi mwi = phone->get_mwi(*i); + toolTip.append("</td><td>"); + if (phone->is_mwi_subscribed(*i)) { + if (mwi.get_status() == t_mwi::MWI_KNOWN) { + bool new_msgs; + QString status = getMWIStatus(mwi, new_msgs); + toolTip.append(status); + mwi_known = true; + mwi_new_msgs |= new_msgs; + } else if (mwi.get_status() == t_mwi::MWI_FAILED) { + toolTip.append(tr("Failure")); + mwi_failure = true; + } else { + toolTip.append(tr("Unknown")); + } + } else { + if ((*i)->get_mwi_sollicited()) { + if (mwi.get_status() == t_mwi::MWI_FAILED) { + toolTip.append(tr("Failure")); + mwi_failure = true; + } else { + toolTip.append(tr("Unknown")); + } + } else { + // Unsollicited MWI + if (mwi.get_status() == t_mwi::MWI_KNOWN) { + bool new_msgs; + QString status = getMWIStatus(mwi, new_msgs); + toolTip.append(status); + mwi_known = true; + mwi_new_msgs |= new_msgs; + } else { + toolTip.append(tr("Unknown")); + } + } + } + toolTip.append("</td></tr>"); + } + + toolTip.append("</table><br>"); + toolTip.append("<i>"); + toolTip.append(tr("Click to access voice mail.").replace(' ', " ")); + toolTip.append("</i>"); + + // Set MWI icon + if (mwi_new_msgs) { + statMWILabel->setPixmap(QPixmap::fromMimeSource( + "mwi_new16.png")); + mwiFlashStatus = true; + + // Start the flash MWI timer to flash the indicator + tmrFlashMWI.start(1000); + } else if (mwi_failure) { + tmrFlashMWI.stop(); + statMWILabel->setPixmap(QPixmap::fromMimeSource( + "mwi_failure16.png")); + } else if (mwi_known) { + tmrFlashMWI.stop(); + statMWILabel->setPixmap(QPixmap::fromMimeSource( + "mwi_none16.png")); + } else { + tmrFlashMWI.stop(); + statMWILabel->setPixmap(QPixmap::fromMimeSource( + "mwi_none16_dis.png")); + } + + // Set tool tip + QToolTip::remove(statMWILabel); + QToolTip::add(statMWILabel, toolTip); + + updateSysTrayStatus(); +} + +// Update active services status +void MphoneForm::updateServicesStatus() +{ + size_t num_dnd = 0; + size_t num_cf = 0; + size_t num_auto_answer = 0; + QString tipDnd = "<b>"; + tipDnd += tr("Do not disturb active for:").replace(' ', " "); + tipDnd += "</b><br>\n<table>"; + QString tipCf = "<b>"; + tipCf += tr("Redirection active for:").replace(' ', " "); + tipCf += "</b><br>\n<table>"; + QString tipAa = "<b>"; + tipAa += tr("Auto answer active for:").replace(' ', " "); + tipAa += "</b><br>\n<table>"; + + // Calculate number of services active. + // Determine tool tips with detailed service status for all users. + list<t_user *>user_list = phone->ref_users(); + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) { + if (phone->ref_service(*i)->is_dnd_active()) { + num_dnd++; + tipDnd.append("<tr><td>"); + tipDnd.append((*i)->get_profile_name().c_str()); + tipDnd.append("</td></tr>"); + } + if (phone->ref_service(*i)->is_cf_active()) { + num_cf++; + tipCf.append("<tr><td>"); + tipCf.append((*i)->get_profile_name().c_str()); + tipCf.append("</td></tr>"); + } + if (phone->ref_service(*i)->is_auto_answer_active()) { + num_auto_answer++; + tipAa.append("<tr><td>"); + tipAa.append((*i)->get_profile_name().c_str()); + tipAa.append("</td></tr>"); + } + } + + QString footer = "<i>"; + footer += tr("Click to activate/deactivate").replace(' ', " "); + footer += "</i>"; + + tipDnd.append("</table><br>"); + tipDnd.append(footer); + tipCf.append("</table><br>"); + tipCf.append(footer); + tipAa.append("</table><br>"); + tipAa.append(footer); + + // Set service status + if (num_dnd == user_list.size()) { + // All users enabled dnd + statDndLabel->setPixmap(QPixmap::fromMimeSource("cancel.png")); + } else if (num_dnd > 0) { + // Some users enabled dnd + statDndLabel->setPixmap(QPixmap::fromMimeSource("cancel.png")); + } else { + // No users enabeld dnd + statDndLabel->setPixmap(QPixmap::fromMimeSource("cancel-disabled.png")); + } + + if (num_cf == user_list.size()) { + // All users enabled redirecton + statCfLabel->setPixmap(QPixmap::fromMimeSource("cf.png")); + } else if (num_cf > 0) { + // Some users enabled redirection + statCfLabel->setPixmap(QPixmap::fromMimeSource("cf.png")); + } else { + // No users enabled redirection + statCfLabel->setPixmap(QPixmap::fromMimeSource("cf-disabled.png")); + } + + if (num_auto_answer == user_list.size()) { + // All users enabled auto answer + statAaLabel->setPixmap(QPixmap::fromMimeSource("auto_answer.png")); + } else if (num_auto_answer > 0) { + // Some users enabled auto answer + statAaLabel->setPixmap(QPixmap::fromMimeSource( + "auto_answer.png")); + } else { + // No users enabeld auto answer + statAaLabel->setPixmap(QPixmap::fromMimeSource( + "auto_answer-disabled.png")); + } + + // Set tool tip with detailed info for multiple users. + QToolTip::remove(statDndLabel); + QToolTip::remove(statCfLabel); + QToolTip::remove(statAaLabel); + + QString clickToActivate("<i>"); + clickToActivate += tr("Click to activate").replace(' ', " "); + clickToActivate += "</i>"; + if (num_dnd > 0) { + QToolTip::add(statDndLabel, tipDnd); + } else { + QString status("<p>"); + status += tr("Do not disturb is not active.").replace(' ', " "); + status += "</p>"; + status += clickToActivate; + QToolTip::add(statDndLabel, status); + } + + if (num_cf > 0) { + QToolTip::add(statCfLabel, tipCf); + } else { + QString status("<p>"); + status += tr("Redirection is not active.").replace(' ', " "); + status += "</p>"; + status += clickToActivate; + QToolTip::add(statCfLabel, status); + } + + if (num_auto_answer > 0) { + QToolTip::add(statAaLabel, tipAa); + } else { + QString status("<p>"); + status += tr("Auto answer is not active.").replace(' ', " "); + status += "</p>"; + status += clickToActivate; + QToolTip::add(statAaLabel, status); + } + + updateSysTrayStatus(); +} + +void MphoneForm::updateMissedCallStatus(int num_missed_calls) +{ + QToolTip::remove(statMissedLabel); + + QString clickDetails("<i>"); + clickDetails += tr("Click to see call history for details.").replace(' ', " "); + clickDetails += "</i>"; + if (num_missed_calls == 0) { + statMissedLabel->setPixmap(QPixmap::fromMimeSource("missed-disabled.png")); + QString status("<p>"); + status += tr("You have no missed calls.").replace(' ', " "); + status += "</p>"; + status += clickDetails; + QToolTip::add(statMissedLabel, status); + } else { + statMissedLabel->setPixmap( + QPixmap::fromMimeSource("missed.png")); + + QString tip("<p>"); + if (num_missed_calls == 1) { + tip += tr("You missed 1 call.").replace(' ', " "); + } else { + tip += tr("You missed %1 calls.").arg(num_missed_calls). + replace(' ', " "); + } + tip += "</p>"; + tip += clickDetails; + QToolTip::add(statMissedLabel, tip); + } + + updateSysTrayStatus(); +} + +// Update system tray status +void MphoneForm::updateSysTrayStatus() +{ + QString icon_name; + bool cf_active = false; + bool dnd_active = false; + bool auto_answer_active = false; + bool multi_services = false; + int num_services; + bool msg_waiting = false; + + if (!sysTray) return; + + // Get status of active line + int line = phone->get_active_line(); + t_line_substate line_substate = phone->get_line_substate(line); + + list<t_user *> user_list = phone->ref_users(); + + switch(line_substate) { + case LSSUB_IDLE: + case LSSUB_SEIZED: + // Determine MWI and service status + user_list = phone->ref_users(); + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) { + t_mwi mwi = phone->get_mwi(*i); + if (mwi.get_status() == t_mwi::MWI_KNOWN && + mwi.get_msg_waiting() && + mwi.get_voice_msg_summary().newmsgs > 0) + { + msg_waiting = true; + } else if (phone->ref_service(*i)->multiple_services_active()) { + multi_services = true; + } else { + if (phone->ref_service(*i)->is_dnd_active()) { + dnd_active = true; + } + if (phone->ref_service(*i)->is_cf_active()) { + cf_active = true; + } + if (phone->ref_service(*i)->is_auto_answer_active()) { + auto_answer_active = true; + } + } + } + + // If there are messages waiting, then show MWI icon + if (msg_waiting) { + icon_name = "sys_mwi"; + break; + } + + // If there are missed calls, then show the missed call icon + if (call_history->get_num_missed_calls() > 0) { + icon_name = "sys_missed"; + break; + } + + // If a service is active, then show the service icon + num_services = (dnd_active ? 1 : 0) + (cf_active ? 1 : 0) + + (auto_answer_active ? 1 : 0); + + if (multi_services || num_services > 1) { + icon_name = "sys_services"; + } else if (dnd_active) { + icon_name = "sys_dnd"; + } else if (cf_active) { + icon_name = "sys_redir"; + } else if (auto_answer_active) { + icon_name = "sys_auto_ans"; + } else { + // No service is active, show the idle icon + if (icon_name.isEmpty()) icon_name = "sys_idle"; + } + + break; + case LSSUB_ESTABLISHED: + if (phone->is_line_on_hold(line)) { + icon_name = "sys_hold"; + } else if (phone->is_line_muted(line)) { + icon_name = "sys_mute"; + } else if (phone->is_line_encrypted(line)) { + t_audio_session *as = phone->get_line(line)->get_audio_session(); + if (as && as->get_zrtp_sas_confirmed()) { + icon_name = "sys_encrypted_verified"; + } else { + icon_name = "sys_encrypted"; + } + } else { + icon_name = "sys_busy_estab"; + } + break; + default: + // Line is in a busy transient state + icon_name = "sys_busy_trans"; + } + + // Based on the registration status use the active or disabled version + // of the icon. + bool registered = false; + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) { + if (phone->get_is_registered(*i)) { + registered = true; + break; + } + } + + if (registered) { + icon_name += ".png"; + } else { + icon_name += "_dis.png"; + } + + sysTray->setPixmap(QPixmap::fromMimeSource(icon_name)); +} + +// Update menu status based on the number of active users +void MphoneForm::updateMenuStatus() +{ + // Some menu options should be toggle actions when there is only + // 1 user active, but they should be normal actions when there are + // multiple users. + disconnect(serviceDnd, 0, 0, 0); + disconnect(serviceAutoAnswer, 0, 0, 0); + if (phone->ref_users().size() == 1) { + t_service *srv = phone->ref_service(phone->ref_users().front()); + + serviceDnd->setToggleAction(true); + serviceDnd->setOn(srv->is_dnd_active()); + connect(serviceDnd, SIGNAL(toggled(bool)), + this, SLOT(srvDnd(bool))); + + serviceAutoAnswer->setToggleAction(true); + serviceAutoAnswer->setOn(srv->is_auto_answer_active()); + connect(serviceAutoAnswer, SIGNAL(toggled(bool)), + this, SLOT(srvAutoAnswer(bool))); + } else { + serviceDnd->setOn(false); + serviceDnd->setToggleAction(false); + connect(serviceDnd, SIGNAL(activated()), + this, SLOT(srvDnd())); + + serviceAutoAnswer->setOn(false); + serviceAutoAnswer->setToggleAction(false); + connect(serviceAutoAnswer, SIGNAL(activated()), + this, SLOT(srvAutoAnswer())); + } + + updateDiamondcardMenu(); +} + +void MphoneForm::updateDiamondcardMenu() +{ + // If one Diamondcard user is active, then create actions in the Diamondcard + // main menu for recharging, call history, etc. These actions will show the + // Diamondcard web page. + // If multiple Diamondcard users are active then create a submenu of each + // Diamondcard action. In each submenu create an item for each user. + // When a user item is clicked, the web page for the action and that user is + // shown. + list<t_user *> diamondcard_users = diamondcard_get_users(phone); + + // Menu item identifiers + static int rechargeId = -1; + static int balanceHistoryId = -1; + static int callHistoryId = -1; + static int adminCenterId = -1; + + // Sub menu's + static QPopupMenu *rechargeMenu = NULL; + static QPopupMenu *balanceHistoryMenu = NULL; + static QPopupMenu *callHistoryMenu = NULL; + static QPopupMenu *adminCenterMenu = NULL; + + // Clear old menu + removeDiamondcardAction(rechargeId); + removeDiamondcardAction(balanceHistoryId); + removeDiamondcardAction(callHistoryId); + removeDiamondcardAction(adminCenterId); + removeDiamondcardMenu(rechargeMenu); + removeDiamondcardMenu(balanceHistoryMenu); + removeDiamondcardMenu(callHistoryMenu); + removeDiamondcardMenu(adminCenterMenu); + + if (diamondcard_users.size() <= 1) + { + rechargeId = Diamondcard->insertItem(tr("Recharge..."), this, SLOT(DiamondcardRecharge(int))); + Diamondcard->setItemParameter(rechargeId, 0); + balanceHistoryId = Diamondcard->insertItem(tr("Balance history..."), this, SLOT(DiamondcardBalanceHistory(int))); + Diamondcard->setItemParameter(balanceHistoryId, 0); + callHistoryId = Diamondcard->insertItem(tr("Call history..."), this, SLOT(DiamondcardCallHistory(int))); + Diamondcard->setItemParameter(callHistoryId, 0); + adminCenterId = Diamondcard->insertItem(tr("Admin center..."), this, SLOT(DiamondcardAdminCenter(int))); + Diamondcard->setItemParameter(adminCenterId, 0); + + // Disable actions as there is no active Diamondcard users. + if (diamondcard_users.empty()) { + Diamondcard->setItemEnabled(rechargeId, false); + Diamondcard->setItemEnabled(balanceHistoryId, false); + Diamondcard->setItemEnabled(callHistoryId, false); + Diamondcard->setItemEnabled(adminCenterId, false); + } + } + else + { + rechargeMenu = new QPopupMenu(this); + balanceHistoryMenu = new QPopupMenu(this); + callHistoryMenu = new QPopupMenu(this); + adminCenterMenu = new QPopupMenu(this); + // No MEMMAN registration as the popup menu may be automatically + // deleted by Qt on application close down. This would show up as + // a memory leak in MEMMAN. + + // Insert a menu item for each Diamondcard user. + int idx = 0; + for (list<t_user *>::const_iterator it = diamondcard_users.begin(); + it != diamondcard_users.end(); ++it) + { + int menuId; + t_user *user = *it; + + // Set the index in the user list as parameter to the menu item. + // When the menu item gets clicked, then the receiver of the signal + // received this parameter and can use it as an index in the user list + // to find the user. + + menuId = rechargeMenu->insertItem(user->get_profile_name().c_str(), this, + SLOT(DiamondcardRecharge(int))); + rechargeMenu->setItemParameter(menuId, idx); + menuId = balanceHistoryMenu->insertItem(user->get_profile_name().c_str(), this, + SLOT(DiamondcardBalanceHistory(int))); + balanceHistoryMenu->setItemParameter(menuId, idx); + menuId = callHistoryMenu->insertItem(user->get_profile_name().c_str(), this, + SLOT(DiamondcardCallHistory(int))); + callHistoryMenu->setItemParameter(menuId, idx); + menuId = adminCenterMenu->insertItem(user->get_profile_name().c_str(), this, + SLOT(DiamondcardAdminCenter(int))); + adminCenterMenu->setItemParameter(menuId, idx); + + ++idx; + } + + // Add the Diamondcard popup menus to the main Diamondcard menu. + Diamondcard->insertItem(tr("Recharge"), rechargeMenu); + Diamondcard->insertItem(tr("Balance history"), balanceHistoryMenu); + Diamondcard->insertItem(tr("Call history"), callHistoryMenu); + Diamondcard->insertItem(tr("Admin center"), adminCenterMenu); + } +} + +void MphoneForm::removeDiamondcardAction(int &id) +{ + if (id != -1) { + Diamondcard->removeItem(id); + id = -1; + } +} + +void MphoneForm::removeDiamondcardMenu(QPopupMenu* &menu) +{ + if (menu) { + delete menu; + menu = NULL; + } +} + +void MphoneForm::phoneRegister() +{ + t_gui *gui = (t_gui *)ui; + list<t_user *> user_list = phone->ref_users(); + + if (user_list.size() > 1) { + if (selectUserForm) { + MEMMAN_DELETE(selectUserForm); + delete (selectUserForm); + } + + selectUserForm = new SelectUserForm(this, "register", true); + MEMMAN_NEW(selectUserForm); + + connect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, + SLOT(do_phoneRegister(list<t_user *>))); + selectUserForm->show(SELECT_REGISTER); + } else { + gui->action_register(user_list); + } +} + +void MphoneForm::do_phoneRegister(list<t_user *> user_list) +{ + ((t_gui *)ui)->action_register(user_list); +} + +void MphoneForm::phoneDeregister() +{ + t_gui *gui = (t_gui *)ui; + list<t_user *> user_list = phone->ref_users(); + + if (user_list.size() > 1) { + if (selectUserForm) { + MEMMAN_DELETE(selectUserForm); + delete (selectUserForm); + } + + selectUserForm = new SelectUserForm(this, "deregister", true); + MEMMAN_NEW(selectUserForm); + + connect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, + SLOT(do_phoneDeregister(list<t_user *>))); + selectUserForm->show(SELECT_DEREGISTER); + } else { + gui->action_deregister(user_list, false); + } +} + +void MphoneForm::do_phoneDeregister(list<t_user *> user_list) +{ + ((t_gui *)ui)->action_deregister(user_list, false); +} + +void MphoneForm::phoneDeregisterAll() +{ + t_gui *gui = (t_gui *)ui; + list<t_user *> user_list = phone->ref_users(); + + if (user_list.size() > 1) { + if (selectUserForm) { + MEMMAN_DELETE(selectUserForm); + delete (selectUserForm); + } + + selectUserForm = new SelectUserForm(this, "deregister all", true); + MEMMAN_NEW(selectUserForm); + + connect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, + SLOT(do_phoneDeregisterAll(list<t_user *>))); + selectUserForm->show(SELECT_DEREGISTER_ALL); + } else { + gui->action_deregister(user_list, true); + } +} + +void MphoneForm::do_phoneDeregisterAll(list<t_user *> user_list) +{ + ((t_gui *)ui)->action_deregister(user_list, true); +} + +void MphoneForm::phoneShowRegistrations() +{ + list<t_user *> user_list = phone->ref_users(); + ((t_gui *)ui)->action_show_registrations(user_list); +} + + +// Show the semi-modal invite window +void MphoneForm::phoneInvite(t_user * user_config, + const QString &dest, const QString &subject, bool anonymous) +{ + // Seize the line, so no incoming call can take the line + if (!((t_gui *)ui)->action_seize()) return; + + if (inviteForm) { + inviteForm->clear(); + } else { + inviteForm = new InviteForm(this, "invite", true); + MEMMAN_NEW(inviteForm); + + // Initialize the destination history list + for (int i = callComboBox->count() - 1; i >= 0; i--) { + inviteForm->addToInviteComboBox(callComboBox->text(i)); + } + + connect(inviteForm, + SIGNAL(destination(t_user *, const QString &, const t_url &, + const QString &, bool)), + this, + SLOT(do_phoneInvite(t_user *, const QString &, + const t_url &, const QString &, bool))); + + connect(inviteForm, SIGNAL(raw_destination(const QString &)), + this, SLOT(addToCallComboBox(const QString &))); + } + + inviteForm->show(user_config, dest, subject, anonymous); + updateState(); +} + +void MphoneForm::phoneInvite(const QString &dest, const QString &subject, bool anonymous) +{ + t_user *user = phone->ref_user_profile(userComboBox->currentText().ascii()); + if (!user) { + log_file->write_report("Cannot find user profile.", + "MphoneForm::phoneInvite", + LOG_NORMAL, LOG_CRITICAL); + return; + } + phoneInvite(user, dest, subject, anonymous); +} + +void MphoneForm::phoneInvite() +{ + t_user *user = phone->ref_user_profile(userComboBox->currentText().ascii()); + if (!user) { + log_file->write_report("Cannot find user profile.", + "MphoneForm::phoneInvite", + LOG_NORMAL, LOG_CRITICAL); + return; + } + phoneInvite(user, "", "", false); +} + +// Execute the invite action. This slot is connected to the destination +// signal of the invite window. +void MphoneForm::do_phoneInvite(t_user *user_config, const QString &display, + const t_url &destination, const QString &subject, + bool anonymous) +{ + ((t_gui *)ui)->action_invite(user_config, destination, display.ascii(), subject.ascii(), + anonymous); + updateState(); +} + +// Redial last call +void MphoneForm::phoneRedial(void) +{ + t_url url; + string display, subject; + t_user *user_config; + bool hide_user; + + if (!ui->get_last_call_info(url, display, subject, &user_config, hide_user)) return; + ((t_gui *)ui)->action_invite(user_config, url, display, subject, hide_user); + updateState(); +} + + +void MphoneForm::phoneAnswer() +{ + ((t_gui *)ui)->action_answer(); + updateState(); +} + +// A call can be answered from the systray popup. The user may have +// switched lines, the systray popup answer button should answer the +// correct line. +void MphoneForm::phoneAnswerFromSystrayPopup() +{ +#ifdef HAVE_KDE + unsigned short line = ((t_gui *)ui)->get_line_sys_tray_popup(); + unsigned short active_line = phone->get_active_line(); + + if (line != active_line) { + ((t_gui *)ui)->action_activate_line(line); + } + + ((t_gui *)ui)->action_answer(); + updateState(); +#endif +} + +void MphoneForm::phoneBye() +{ + ((t_gui *)ui)->action_bye(); + updateState(); +} + + +void MphoneForm::phoneReject() +{ + ((t_gui *)ui)->action_reject(); + updateState(); +} + +// A call can be rejected from the systray popup. The user may have +// switched lines, the systray popup reject button should answer the +// correct line. +void MphoneForm::phoneRejectFromSystrayPopup() +{ +#ifdef HAVE_KDE + unsigned short line = ((t_gui *)ui)->get_line_sys_tray_popup(); + ((t_gui *)ui)->action_reject(line); + updateState(); +#endif +} + + +// Show the semi-modal redirect form +void MphoneForm::phoneRedirect(const list<string> &contacts) +{ + int active_line = phone->get_active_line(); + t_user *user_config = phone->get_line_user(active_line); + + if (redirectForm) { + MEMMAN_DELETE(redirectForm); + delete (redirectForm); + } + + redirectForm = new RedirectForm(this, "redirect", true); + MEMMAN_NEW(redirectForm); + connect(redirectForm, SIGNAL(destinations(const list<t_display_url> &)), + this, SLOT(do_phoneRedirect(const list<t_display_url> &))); + + redirectForm->show(user_config, contacts); +} + +void MphoneForm::phoneRedirect() +{ + const list<string> l; + phoneRedirect(l); +} + +// Execute the redirect action. +void MphoneForm::do_phoneRedirect(const list<t_display_url> &destinations) +{ + ((t_gui *)ui)->action_redirect(destinations); + updateState(); +} + +// Show the semi-modal call transfer window +void MphoneForm::phoneTransfer(const string &dest, t_transfer_type transfer_type) +{ + int active_line = phone->get_active_line(); + t_user *user_config = phone->get_line_user(active_line); + + // Hold the call if setting in user profile indicates call hold + if (user_config->get_referrer_hold()) { + phoneHold(true); + } + + if (transferForm) { + MEMMAN_DELETE(transferForm); + delete transferForm; + } + + transferForm = new TransferForm(this, "transfer", true); + MEMMAN_NEW(transferForm); + connect(transferForm, SIGNAL(destination(const t_display_url &, t_transfer_type)), + this, SLOT(do_phoneTransfer(const t_display_url &, t_transfer_type))); + + if (dest.empty() && transfer_type == TRANSFER_BASIC) { + // Let form pick a default transfer type based on the current + // call status. + transferForm->show(user_config); + } else { + // Set passed destination and transfer type in form + transferForm->show(user_config, dest, transfer_type); + } + updateState(); +} + +void MphoneForm::phoneTransfer() +{ + unsigned short active_line = phone->get_active_line(); + unsigned short dummy; + + if (phone->is_line_transfer_consult(active_line, dummy)) { + do_phoneTransferLine(); + } else { + phoneTransfer("", TRANSFER_BASIC); + } +} + +// Execute the transfer action. This slot is connected to the destination +// signal of the transfer window. +void MphoneForm::do_phoneTransfer(const t_display_url &destination, + t_transfer_type transfer_type) +{ + unsigned short active_line; + unsigned short other_line; + + switch (transfer_type) { + case TRANSFER_BASIC: + ((t_gui *)ui)->action_refer(destination.url, destination.display); + break; + case TRANSFER_CONSULT: + ((t_gui *)ui)->action_setup_consultation_call( + destination.url, destination.display); + break; + case TRANSFER_OTHER_LINE: + active_line = phone->get_active_line(); + other_line = (active_line == 0 ? 1 : 0); + + if (phone->get_line_substate(other_line) == LSSUB_ESTABLISHED) { + ((t_gui *)ui)->action_refer(active_line, other_line); + } else { + // The other line was released while the user was entering + // the refer-target. + t_user *user_config = phone->get_line_user(active_line); + if (user_config->get_referrer_hold()) { + phoneHold(false); + } + } + break; + default: + assert(false); + } + + updateState(); +} + +// Transfer the remote party on the held line to the remote party on the +// active line. +void MphoneForm::do_phoneTransferLine() +{ + unsigned short active_line = phone->get_active_line(); + unsigned short line_to_be_transferred; + + if (!phone->is_line_transfer_consult(active_line, line_to_be_transferred)) { + // Somehow the line is not a consultation call. + updateState(); + return; + } + + ((t_gui *)ui)->action_refer(line_to_be_transferred, active_line); + updateState(); +} + +void MphoneForm::phoneHold(bool on) +{ + if (on) { + ((t_gui *)ui)->action_hold(); + } else { + ((t_gui *)ui)->action_retrieve(); + } + + updateState(); +} + +void MphoneForm::phoneConference() +{ + ((t_gui *)ui)->action_conference(); + updateState(); +} + +void MphoneForm::phoneMute(bool on) +{ + ((t_gui *)ui)->action_mute(on); + updateState(); +} + +void MphoneForm::phoneTermCap(const QString &dest) +{ + // In-dialog OPTIONS request + int line = phone->get_active_line(); + if (phone->get_line_substate(line) == LSSUB_ESTABLISHED) { + ((t_gui *)ui)->action_options(); + return; + } + + // Out-of-dialog OPTIONS request + if (termCapForm) { + MEMMAN_DELETE(termCapForm); + delete (termCapForm); + } + + termCapForm = new TermCapForm(this, "termcap", true); + MEMMAN_NEW(termCapForm); + connect(termCapForm, SIGNAL(destination(t_user *, const t_url &)), + this, SLOT(do_phoneTermCap(t_user *, const t_url &))); + + t_user *user = phone->ref_user_profile(userComboBox->currentText().ascii()); + if (!user) { + log_file->write_report("Cannot find user profile.", + "MphoneForm::phoneTermcap", + LOG_NORMAL, LOG_CRITICAL); + return; + } + termCapForm->show(user, dest); +} + +void MphoneForm::phoneTermCap() +{ + phoneTermCap(""); +} + +void MphoneForm::do_phoneTermCap(t_user *user_config, const t_url &destination) +{ + ((t_gui *)ui)->action_options(user_config, destination); +} + +void MphoneForm::phoneDTMF() +{ + if (!dtmfForm) { + dtmfForm = new DtmfForm(this); + MEMMAN_NEW(dtmfForm); + connect(dtmfForm, SIGNAL(digits(const QString &)), + this, SLOT(sendDTMF(const QString &))); + } + + dtmfForm->show(); +} + +void MphoneForm::sendDTMF(const QString &digits) +{ + ((t_gui *)ui)->action_dtmf(digits.ascii()); +} + +void MphoneForm::startMessageSession(void) +{ + t_user *user = phone->ref_user_profile(userComboBox->currentText().ascii()); + if (!user) { + log_file->write_report("Cannot find user profile.", + "MphoneForm::startMessageSession", + LOG_NORMAL, LOG_CRITICAL); + return; + } + + im::t_msg_session *session = new im::t_msg_session(user); + MEMMAN_NEW(session); + ((t_gui *)ui)->addMessageSession(session); + MessageFormView *messageFormView = new MessageFormView(NULL, session); + MEMMAN_NEW(messageFormView); + messageFormView->show(); +} + +void MphoneForm::startMessageSession(t_buddy *buddy) +{ + t_user *user_config = buddy->get_user_profile(); + t_url dest_url(ui->expand_destination(user_config, buddy->get_sip_address())); + if (!dest_url.is_valid()) return; + string display = buddy->get_name(); + + // Find an existing session + im::t_msg_session *session = ((t_gui *)ui)->getMessageSession(user_config, dest_url, display); + if (!session) { + // There is no session yet, create one. + session = new im::t_msg_session(user_config, t_display_url(dest_url, display)); + MEMMAN_NEW(session); + ((t_gui *)ui)->addMessageSession(session); + MessageFormView *view = new MessageFormView(NULL, session); + MEMMAN_NEW(view); + view->show(); + } +} + +void MphoneForm::phoneConfirmZrtpSas(int line) +{ + ((t_gui *)ui)->action_confirm_zrtp_sas(line); + updateState(); +} + +void MphoneForm::phoneConfirmZrtpSas() +{ + ((t_gui *)ui)->action_confirm_zrtp_sas(); + updateState(); +} + +void MphoneForm::phoneResetZrtpSasConfirmation(int line) +{ + ((t_gui *)ui)->action_reset_zrtp_sas_confirmation(line); + updateState(); +} + +void MphoneForm::phoneResetZrtpSasConfirmation() +{ + ((t_gui *)ui)->action_reset_zrtp_sas_confirmation(); + updateState(); +} + +void MphoneForm::phoneEnableZrtp(bool on) +{ + if (on) { + ((t_gui *)ui)->action_enable_zrtp(); + } else { + ((t_gui *)ui)->action_zrtp_request_go_clear(); + } + + updateState(); +} + +void MphoneForm::phoneZrtpGoClearOk(unsigned short line) +{ + ((t_gui *)ui)->action_zrtp_go_clear_ok(line); + updateState(); +} + +// Radio button for line 1 changed state +void MphoneForm::line1rbChangedState( bool on ) +{ + // If the radio button is switched off, then return, the toggle + // on the other line will handle the action + if (!on) return; + + ((t_gui *)ui)->action_activate_line(0); +} + +void MphoneForm::line2rbChangedState( bool on ) +{ + // If the radio button is switched off, then return, the toggle + // on the other line will handle the action + if (!on) return; + + ((t_gui *)ui)->action_activate_line(1); +} + +void MphoneForm::actionLine1Toggled( bool on) +{ + if (!on) return; + ((t_gui *)ui)->action_activate_line(0); +} + +void MphoneForm::actionLine2Toggled( bool on) +{ + if (!on) return; + ((t_gui *)ui)->action_activate_line(1); +} + +// Enable/disable dnd when there is 1 user active +void MphoneForm::srvDnd( bool on ) +{ + ((t_gui *)ui)->srv_dnd(phone->ref_users(), on); + updateServicesStatus(); +} + +// Enable/disable dnd when there are multiple users active +void MphoneForm::srvDnd() +{ + if (selectUserForm) { + MEMMAN_DELETE(selectUserForm); + delete (selectUserForm); + } + + selectUserForm = new SelectUserForm(this, "dnd", true); + MEMMAN_NEW(selectUserForm); + + connect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, + SLOT(do_srvDnd_enable(list<t_user *>))); + connect(selectUserForm, SIGNAL(not_selected(list<t_user *>)), this, + SLOT(do_srvDnd_disable(list<t_user *>))); + + selectUserForm->show(SELECT_DND); +} + +void MphoneForm::do_srvDnd_enable(list<t_user *> user_list) { + ((t_gui *)ui)->srv_dnd(user_list, true); + updateServicesStatus(); +} + +void MphoneForm::do_srvDnd_disable(list<t_user *> user_list) { + ((t_gui *)ui)->srv_dnd(user_list, false); + updateServicesStatus(); +} + +// Enable/disable auto answer when there is 1 user active +void MphoneForm::srvAutoAnswer( bool on ) +{ + ((t_gui *)ui)->srv_auto_answer(phone->ref_users(), on); + updateServicesStatus(); +} + +// Enable/disable auto answer when there are multiple users active +void MphoneForm::srvAutoAnswer() +{ + if (selectUserForm) { + MEMMAN_DELETE(selectUserForm); + delete (selectUserForm); + } + + selectUserForm = new SelectUserForm(this, "auto answer", true); + MEMMAN_NEW(selectUserForm); + + connect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, + SLOT(do_srvAutoAnswer_enable(list<t_user *>))); + connect(selectUserForm, SIGNAL(not_selected(list<t_user *>)), this, + SLOT(do_srvAutoAnswer_disable(list<t_user *>))); + + selectUserForm->show(SELECT_AUTO_ANSWER); +} + +void MphoneForm::do_srvAutoAnswer_enable(list<t_user *> user_list) { + ((t_gui *)ui)->srv_auto_answer(user_list, true); + updateServicesStatus(); +} + +void MphoneForm::do_srvAutoAnswer_disable(list<t_user *> user_list) { + ((t_gui *)ui)->srv_auto_answer(user_list, false); + updateServicesStatus(); +} + +void MphoneForm::srvRedirect() +{ + if (!srvRedirectForm) { + srvRedirectForm = new SrvRedirectForm(this, "call redirection", true); + MEMMAN_NEW(srvRedirectForm); + connect(srvRedirectForm, + SIGNAL(destinations(t_user *, + const list<t_display_url> &, + const list<t_display_url> &, + const list<t_display_url> &)), + this, + SLOT(do_srvRedirect(t_user *, + const list<t_display_url> &, + const list<t_display_url> &, + const list<t_display_url> &))); + } + + srvRedirectForm->show(); +} + +void MphoneForm::do_srvRedirect(t_user *user_config, + const list<t_display_url> &always, + const list<t_display_url> &busy, + const list<t_display_url> &noanswer) +{ + // Redirection always + if (always.empty()) { + ((t_gui *)ui)->srv_disable_cf(user_config, CF_ALWAYS); + } else { + ((t_gui *)ui)->srv_enable_cf(user_config, CF_ALWAYS, always); + } + + // Redirection busy + if (busy.empty()) { + ((t_gui *)ui)->srv_disable_cf(user_config, CF_BUSY); + } else { + ((t_gui *)ui)->srv_enable_cf(user_config, CF_BUSY, busy); + } + + // Redirection no answer + if (noanswer.empty()) { + ((t_gui *)ui)->srv_disable_cf(user_config, CF_NOANSWER); + } else { + ((t_gui *)ui)->srv_enable_cf(user_config, CF_NOANSWER, noanswer); + } + + updateServicesStatus(); +} + + +void MphoneForm::about() +{ + QString s = sys_config->about(true).c_str(); + + QMessageBox mbAbout(PRODUCT_NAME, s.replace(' ', " "), + QMessageBox::Information, + QMessageBox::Ok | QMessageBox::Default, + QMessageBox::NoButton, QMessageBox::NoButton); + mbAbout.setIconPixmap(QPixmap::fromMimeSource("twinkle48.png")); + mbAbout.exec(); +} + +void MphoneForm::aboutQt() +{ + QMessageBox::aboutQt(this, PRODUCT_NAME); +} + +void MphoneForm::manual() +{ + ((t_gui *)ui)->open_url_in_browser("http://www.twinklephone.com"); +} + +void MphoneForm::editUserProfile() +{ + if (!userProfileForm) { + userProfileForm = new UserProfileForm(this, "user profile", true); + MEMMAN_NEW(userProfileForm); + + connect(userProfileForm, + SIGNAL(authCredentialsChanged(t_user *, const string&)), + this, + SLOT(updateAuthCache(t_user *, const string&))); + + connect(userProfileForm, + SIGNAL(stunServerChanged(t_user *)), + this, + SLOT(updateStunSettings(t_user *))); + + // MWI settings change triggers an unsubscribe + connect(userProfileForm, + SIGNAL(mwiChangeUnsubscribe(t_user *)), + this, + SLOT(unsubscribeMWI(t_user *))); + + // MWI settings change triggers a subscribe + connect(userProfileForm, + SIGNAL(mwiChangeSubscribe(t_user *)), + this, + SLOT(subscribeMWI(t_user *))); + } + + userProfileForm->show(phone->ref_users(), + userComboBox->currentText()); +} + +void MphoneForm::editSysSettings() +{ + if (!sysSettingsForm) { + sysSettingsForm = new SysSettingsForm(this, "system settings", true); + MEMMAN_NEW(sysSettingsForm); + connect(sysSettingsForm, SIGNAL(sipUdpPortChanged()), + this, SLOT(updateSipUdpPort())); + connect(sysSettingsForm, SIGNAL(rtpPortChanged()), + this, SLOT(updateRtpPorts())); + } + + sysSettingsForm->show(); +} + +void MphoneForm::selectProfile() +{ + if (!selectProfileForm) { + selectProfileForm = new SelectProfileForm(this, "select profile", true); + MEMMAN_NEW(selectProfileForm); + connect(selectProfileForm, SIGNAL(selection(const list<string> &)), + this, SLOT(newUsers(const list<string> &))); + connect(selectProfileForm, SIGNAL(profileRenamed()), + this, SLOT(updateUserComboBox())); + connect(selectProfileForm, SIGNAL(profileRenamed()), + this, SLOT(populateBuddyList())); + } + + selectProfileForm->showForm(this); +} + +// A new set of users has been selected. +// Remove users from the current user set that are not in the selection. +// Add users from the selection that are not in the current set of users. +void MphoneForm::newUsers(const list<string> &profiles) +{ + string error_msg; + + // NOTE: First users must be removed. It could be that a + // user profile of an active was renamed. In this case, the user + // with the old profile name is first removed and then added again. + + list<t_user *> user_list = phone->ref_users(); + + // Remove current users that are not selected anymore. + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) { + if (std::find(profiles.begin(), profiles.end(), + (*i)->get_filename().c_str()) == profiles.end()) + { + // User is not selected anymore. + // Unsubscribe MWI + if (phone->is_mwi_subscribed(*i)) { + phone->pub_unsubscribe_mwi(*i); + } + + // Unpublish presence of user + phone->pub_unpublish_presence(*i); + + // Unsubscribe presence + phone->pub_unsubscribe_presence(*i); + + // Deregister user + if (phone->get_is_registered(*i)) { + phone->pub_registration(*i, REG_DEREGISTER); + } + + log_file->write_header("MphoneForm::newUsers"); + log_file->write_raw("Stop user profile: "); + log_file->write_raw((*i)->get_profile_name()); + log_file->write_endl(); + log_file->write_footer(); + phone->remove_phone_user(*(*i)); + } + } + + // Determine which users to add + list<string> add_profile_list; + for (list<string>::const_iterator i = profiles.begin(); i != profiles.end(); i++) { + QString profile = (*i).c_str(); + // Strip off the .cfg extension + profile.truncate(profile.length() - 4); + + if (!phone->ref_user_profile(profile.ascii())) { + add_profile_list.push_back(*i); + } + } + + // Add new phone users + QProgressDialog progress(tr("Starting user profiles..."), "Abort", add_profile_list.size(), this, + "starting user profiles", true); + progress.setCaption(PRODUCT_NAME); + progress.setMinimumDuration(200); + int progressStep = 0; + for (list<string>::iterator i = add_profile_list.begin(); i != add_profile_list.end(); i++) { + progress.setProgress(progressStep); + qApp->processEvents(); + + if (progress.wasCancelled()) { + log_file->write_report("User aborted startup of new users.", + "MphoneForm::newUsers"); + break; + } + + t_user user_config; + + // Read user configuration + if (user_config.read_config(*i, error_msg)) { + t_user *dup_user; + + log_file->write_header("MphoneForm::newUsers"); + log_file->write_raw("Run user profile: "); + log_file->write_raw(user_config.get_profile_name()); + log_file->write_endl(); + log_file->write_footer(); + + if (phone->add_phone_user(user_config, &dup_user)) + { + // NAT discovery + if (!phone->stun_discover_nat(&user_config, error_msg)) + { + // Warn user that the STUN settings will not work. + ((t_gui *)ui)->cb_show_msg(this, error_msg, + MSG_WARNING); + } + + // Register at startup + if (user_config.get_register_at_startup()) { + phone->pub_registration(&user_config, + REG_REGISTER, + DUR_REGISTRATION(&user_config)); + } else { + // No registration needed, initialize extensions now. + phone->init_extensions(&user_config); + } + + // Extension initialization will be done after + // registration succeeded. + } else { + error_msg = tr("The following profiles are both for user %1").arg(user_config.get_name().c_str()).ascii(); + error_msg += '@'; + error_msg += user_config.get_domain(); + error_msg += ":\n\n"; + error_msg += user_config.get_profile_name(); + error_msg += "\n"; + error_msg += dup_user->get_profile_name(); + error_msg += "\n\n"; + error_msg += tr("You can only run multiple profiles for different users."); + + log_file->write_report(error_msg, + "MphoneForm::newUsers", + LOG_NORMAL, LOG_WARNING); + ui->cb_display_msg(error_msg, MSG_WARNING); + } + } else { + log_file->write_report(error_msg, + "MphoneForm::newUsers", + LOG_NORMAL, LOG_CRITICAL); + ui->cb_display_msg(error_msg, MSG_CRITICAL); + } + + progressStep++; + } + progress.setProgress(add_profile_list.size()); + + populateBuddyList(); + updateUserComboBox(); + updateRegStatus(); + updateMwi(); + updateServicesStatus(); + updateSysTrayStatus(); + updateMenuStatus(); + updateState(); + + call_history->clear_num_missed_calls(); +} + +void MphoneForm::updateUserComboBox() +{ + QString current_user; + + if (userComboBox->count() == 0) { + // The last used profile + current_user = sys_config->get_last_used_profile().c_str(); + } else { + // Keep the current active profile + current_user = userComboBox->currentText(); + } + + ((t_gui *)ui)->fill_user_combo(userComboBox); + + // If previous selected user is still active, make it the current user + for (int i = 0; i < userComboBox->count(); i++) { + if (userComboBox->text(i) == current_user) { + userComboBox->setCurrentItem(i); + } + } +} + +void MphoneForm::updateSipUdpPort() +{ + ((t_gui *)ui)->cb_show_msg(sysSettingsForm, + tr("You have changed the SIP UDP port. This setting will only become "\ + "active when you restart Twinkle.").ascii(), + MSG_INFO); +} + +void MphoneForm::updateRtpPorts() +{ + phone->init_rtp_ports(); +} + +void MphoneForm::updateStunSettings(t_user *user_config) +{ + string s; + if (!phone->stun_discover_nat(user_config, s)) { + // Warn user that the STUN settings will not work. + ((t_gui *)ui)->cb_show_msg(this, s, MSG_WARNING); + } + + if (!user_config->get_use_stun()) { + // Disable STUN + phone->disable_stun(user_config); + } + + // Synchronize the sending of NAT keep alives with the user profile settings. + phone->sync_nat_keepalive(user_config); +} + +void MphoneForm::updateAuthCache(t_user *user_config, const string &realm) +{ + phone->remove_cached_credentials(user_config, realm); +} + +void MphoneForm::unsubscribeMWI(t_user *user_config) +{ + phone->pub_unsubscribe_mwi(user_config); +} + +void MphoneForm::subscribeMWI(t_user *user_config) +{ + phone->pub_subscribe_mwi(user_config); +} + +void MphoneForm::viewLog() +{ + if (!logViewForm) { + logViewForm = new LogViewForm(NULL); + MEMMAN_NEW(logViewForm); + } + + logViewForm->show(); +} + +void MphoneForm::updateLog(bool log_zapped) +{ + if (logViewForm) logViewForm->update(log_zapped); +} + +void MphoneForm::viewHistory() +{ + if (!historyForm) { + historyForm = new HistoryForm(NULL); + MEMMAN_NEW(historyForm); + } + + connect(historyForm, + SIGNAL(call(t_user *, const QString &, const QString &, bool)), this, + SLOT(phoneInvite(t_user *, const QString &, const QString &, bool))); + + historyForm->show(); +} + +void MphoneForm::updateCallHistory() +{ + if (historyForm) historyForm->update(); +} + +t_twinkle_sys_tray *MphoneForm::getSysTray() +{ + return sysTray; +} + +// Execute call directly from the main window (press call button) +void MphoneForm::quickCall() +{ + string display, dest_str; + + t_user *from_user = phone->ref_user_profile( + userComboBox->currentText().ascii()); + if (!from_user) { + log_file->write_report("Cannot find user profile.", + "MphoneForm::quickCall", + LOG_NORMAL, LOG_CRITICAL); + return; + } + + ui->expand_destination(from_user, + callComboBox->currentText().stripWhiteSpace().ascii(), + display, dest_str); + t_url dest(dest_str); + + if (dest.is_valid()) { + QString destination = callComboBox->currentText(); + addToCallComboBox(destination); + if (inviteForm) inviteForm->addToInviteComboBox(destination); + callComboBox->setFocus(); + do_phoneInvite(from_user, display.c_str(), dest, "", false); + } +} + +// Add a destination to the list of callComboBox +void MphoneForm::addToCallComboBox(const QString &destination) +{ + // Remove duplicate entries + for (int i = callComboBox->count() - 1; i >= 0; i--) { + if (callComboBox->text(i) == destination) { + callComboBox->removeItem(i); + } + } + + // Add entry + callComboBox->insertItem(destination, 0); + callComboBox->setCurrentItem(0); + + // Remove last entry is list exceeds maximum size + if (callComboBox->count() > SIZE_REDIAL_LIST) { + callComboBox->removeItem(callComboBox->count() - 1); + } + + // Clearing the edit line must be done here as this function is + // also called when a call is made through the inviteForm. + // The insertItem puts the text also in the edit field. So it must + // be cleared here. + callComboBox->clearEdit(); +} + +void MphoneForm::showAddressBook() +{ + if (!getAddressForm) { + getAddressForm = new GetAddressForm( + this, "select address", true); + MEMMAN_NEW(getAddressForm); + } + + connect(getAddressForm, + SIGNAL(address(const QString &)), + this, SLOT(selectedAddress(const QString &))); + + getAddressForm->show(); +} + +void MphoneForm::selectedAddress(const QString &address) +{ + callComboBox->setEditText(address); +} + +// Enable/disable the various call widgets +void MphoneForm::enableCallOptions(bool enable) +{ + // Enable/disable widgets + callInvite->setEnabled(enable); + callPushButton->setEnabled(enable); + callComboBox->setEnabled(enable); + addressToolButton->setEnabled(enable); + + // Set focus on callComboBox + if (enable) { + callComboBox->setFocus(); + } +} + +void MphoneForm::keyPressEvent(QKeyEvent *e) +{ + if (callPushButton->isEnabled()) { + // Quick dial + switch (e->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + quickCall(); + break; + default: + e->ignore(); + } + } else if (callDTMF->isEnabled()) { + // DTMF keys + switch (e->key()) { + case Qt::Key_1: + sendDTMF("1"); + break; + case Qt::Key_2: + case Qt::Key_A: + case Qt::Key_B: + case Qt::Key_C: + sendDTMF("2"); + break; + case Qt::Key_3: + case Qt::Key_D: + case Qt::Key_E: + case Qt::Key_F: + sendDTMF("3"); + break; + case Qt::Key_4: + case Qt::Key_G: + case Qt::Key_H: + case Qt::Key_I: + sendDTMF("4"); + break; + case Qt::Key_5: + case Qt::Key_J: + case Qt::Key_K: + case Qt::Key_L: + sendDTMF("5"); + break; + case Qt::Key_6: + case Qt::Key_M: + case Qt::Key_N: + case Qt::Key_O: + sendDTMF("6"); + break; + case Qt::Key_7: + case Qt::Key_P: + case Qt::Key_Q: + case Qt::Key_R: + case Qt::Key_S: + sendDTMF("7"); + break; + case Qt::Key_8: + case Qt::Key_T: + case Qt::Key_U: + case Qt::Key_V: + sendDTMF("8"); + break; + case Qt::Key_9: + case Qt::Key_W: + case Qt::Key_X: + case Qt::Key_Y: + case Qt::Key_Z: + sendDTMF("9"); + break; + case Qt::Key_0: + case Qt::Key_Space: + sendDTMF("0"); + break; + case Qt::Key_Asterisk: + sendDTMF("*"); + break; + case Qt::Key_NumberSign: + sendDTMF("#"); + break; + default: + e->ignore(); + } + } else { + e->ignore(); + } +} + +// QLabels do not have mouse click events. I want the status labels +// to be clickable however. Explicitly check here if a status label has +// been clicked. +void MphoneForm::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton && e->type() == QEvent::MouseButtonRelease) { + processLeftMouseButtonRelease(e); + } else if (e->button() == Qt::RightButton && e->type() == QEvent::MouseButtonRelease) { + processRightMouseButtonRelease(e); + } else { + e->ignore(); + } +} + +void MphoneForm::processLeftMouseButtonRelease(QMouseEvent *e) +{ + if (statAaLabel->hasMouse()) { + if (phone->ref_users().size() == 1) { + bool enable = !serviceAutoAnswer->isOn(); + srvAutoAnswer(enable); + serviceAutoAnswer->setOn(enable); + } else { + srvAutoAnswer(); + } + } else if (statDndLabel->hasMouse()) { + if (phone->ref_users().size() == 1) { + bool enable = !serviceDnd->isOn(); + srvDnd(enable); + serviceDnd->setOn(enable); + } else { + srvDnd(); + } + } else if (statCfLabel->hasMouse()) { + srvRedirect(); + } else if (statMWILabel->hasMouse()) { + popupMenuVoiceMail(e->globalPos()); + } else if (statMissedLabel->hasMouse()) { + // Open the history form, when the user clicks on the + // missed calls indication. + viewHistory(); + } else if (statRegLabel->hasMouse()) { + // Fetch registration status + phoneShowRegistrations(); + } else if (crypt1Label->hasMouse()) { + processCryptLabelClick(0); + } else if (crypt2Label->hasMouse()) { + processCryptLabelClick(1); + } else { + e->ignore(); + } +} + +void MphoneForm::processRightMouseButtonRelease(QMouseEvent *e) +{ + e->ignore(); +} + +void MphoneForm::processCryptLabelClick(int line) +{ + t_audio_session *as = phone->get_line(line)->get_audio_session(); + if (!as) return; + + if (as->get_zrtp_sas_confirmed()) { + phoneResetZrtpSasConfirmation(line); + } else { + phoneConfirmZrtpSas(line); + } +} + +// Show popup menu to access voice mail +void MphoneForm::popupMenuVoiceMail(const QPoint &pos) +{ + QPopupMenu menu(this); + QIconSet vmIcon(QPixmap::fromMimeSource("mwi_none16.png")); + vmIcon.setPixmap(QPixmap::fromMimeSource("mwi_none16_dis.png"), + QIconSet::Automatic, QIconSet::Disabled); + + list<t_user *>user_list = phone->ref_users(); + map<int, t_user *> vm; + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); ++i) { + QString address = (*i)->get_mwi_vm_address().c_str(); + QString entry = (*i)->get_profile_name().c_str(); + entry += " - "; + if (address.isEmpty()) { + entry += tr("not provisioned"); + } else { + entry += address; + } + + int id = menu.insertItem(vmIcon, entry); + if (address.isEmpty()) { + menu.setItemEnabled(id, false); + } + + vm.insert(make_pair(id, *i)); + } + + int selected; + + // If multiple profiles are active, then show the popup menu. + // If one profile is active, then call voice mail immediately. + if (user_list.size() > 1) { + selected = menu.exec(pos); + if (selected == -1) return; + } else { + if (vm.begin()->second->get_mwi_vm_address().empty()) { + ui->cb_show_msg( + tr("You must provision your voice mail address in your " + "user profile, before you can access it.").ascii(), + MSG_INFO); + return; + } + selected = vm.begin()->first; + } + + // Call can only be made if line is idle + int line = phone->get_active_line(); + if (phone->get_line_state(line) == LS_BUSY) { + ui->cb_show_msg(tr("The line is busy. Cannot access voice mail.").ascii(), + MSG_WARNING); + return; + } + + t_user *selectedUser = vm[selected]; + string display, dest_str; + ui->expand_destination(selectedUser, + selectedUser->get_mwi_vm_address(), + display, dest_str); + t_url dest(dest_str); + + if (dest.is_valid()) { + QString destination = selectedUser->get_mwi_vm_address().c_str(); + addToCallComboBox(destination); + if (inviteForm) inviteForm->addToInviteComboBox(destination); + callComboBox->setFocus(); + do_phoneInvite(selectedUser, display.c_str(), dest, "", false); + } else { + QString msg(tr("The voice mail address %1 is an invalid address. " + "Please provision a valid address in your user profile.")); + ui->cb_show_msg(msg.arg(selectedUser->get_mwi_vm_address().c_str()).ascii(), + MSG_CRITICAL); + } +} + +void MphoneForm::popupMenuVoiceMail(void) +{ + popupMenuVoiceMail(QCursor::pos()); +} + +void MphoneForm::showDisplay(bool on) +{ + if (on) { + displayGroupBox->show(); + } else { + int hDisplay = displayGroupBox->height(); + displayGroupBox->hide(); + + if (hDisplay < minimumHeight()) { + setMinimumHeight(minimumHeight() - hDisplay); + } + resize(width(), minimumHeight()); + } + + viewDisplay = on; + viewDisplayAction->setOn(on); +} + +void MphoneForm::showBuddyList(bool on) +{ + if (on) { + buddyListView->show(); + } else { + buddyListView->hide(); + } + + viewBuddyList = on; + viewBuddyListAction->setOn(on); +} + +void MphoneForm::showCompactLineStatus(bool on) +{ + if (on) { + int hLabels = fromhead1Label->height() + + tohead1Label->height() + + subjecthead1Label->height() + + fromhead2Label->height() + + tohead2Label->height() + + subjecthead2Label->height(); + + fromhead1Label->hide(); + tohead1Label->hide(); + subjecthead1Label->hide(); + from1Label->hide(); + to1Label->hide(); + subject1Label->hide(); + photo1Label->hide(); + fromhead2Label->hide(); + tohead2Label->hide(); + subjecthead2Label->hide(); + from2Label->hide(); + to2Label->hide(); + subject2Label->hide(); + photo2Label->hide(); + + if (hLabels < minimumHeight()) { + setMinimumHeight(minimumHeight() - hLabels); + } + resize(width(), minimumHeight()); + } else { + fromhead1Label->show(); + tohead1Label->show(); + subjecthead1Label->show(); + from1Label->show(); + to1Label->show(); + subject1Label->show(); + fromhead2Label->show(); + tohead2Label->show(); + subjecthead2Label->show(); + from2Label->show(); + to2Label->show(); + subject2Label->show(); + } + + viewCompactLineStatus = on; + //viewCompactLineStatusAction->setOn(on); +} + +bool MphoneForm::getViewDisplay() +{ + return viewDisplay; +} + +bool MphoneForm::getViewBuddyList() +{ + return viewBuddyList; +} + +bool MphoneForm::getViewCompactLineStatus() +{ + return viewCompactLineStatus; +} + +void MphoneForm::populateBuddyList() +{ + buddyListView->clear(); + + list<t_user *> user_list = phone->ref_users(); + for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); ++i) { + t_presence_epa *epa = phone->ref_presence_epa(*i); + if (!epa) continue; + + BLViewUserItem *profileItem = new BLViewUserItem(buddyListView, epa); + t_buddy_list *buddy_list = phone->ref_buddy_list(*i); + + list<t_buddy> *buddies = buddy_list->get_records(); + for (list<t_buddy>::iterator bit = buddies->begin(); bit != buddies->end(); ++bit) { + QString name = bit->get_name().c_str(); + new BuddyListViewItem(profileItem, &(*bit)); + } + + profileItem->setOpen(true); + } +} + +void MphoneForm::showBuddyListPopupMenu(QListViewItem *item, const QPoint &pos) +{ + if (!item) return; + + BuddyListViewItem *buddyItem = dynamic_cast<BuddyListViewItem *>(item); + if (buddyItem) { + buddyPopupMenu->popup(pos); + } else { + buddyListPopupMenu->popup(pos); + } +} + +void MphoneForm::doCallBuddy() +{ + QListViewItem *qitem = buddyListView->currentItem(); + BuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem); + if (!item) return; + + t_buddy *buddy = item->get_buddy(); + t_user *user_config = buddy->get_user_profile(); + + phoneInvite(user_config, buddy->get_sip_address().c_str(), "", false); +} + +void MphoneForm::doMessageBuddy(QListViewItem *qitem) +{ + BuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem); + if (!item) return; + + t_buddy *buddy = item->get_buddy(); + + startMessageSession(buddy); +} + +void MphoneForm::doMessageBuddy() +{ + QListViewItem *item = buddyListView->currentItem(); + doMessageBuddy(item); +} + +void MphoneForm::doEditBuddy() +{ + QListViewItem *qitem = buddyListView->currentItem(); + BuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem); + if (!item) return; + + t_buddy *buddy = item->get_buddy(); + + BuddyForm *form = new BuddyForm(this, "new_buddy", true, Qt::WDestructiveClose); + // Do not call MEMMAN as this form will be deleted automatically. + form->showEdit(*buddy); +} + +void MphoneForm::doDeleteBuddy() +{ + QListViewItem *qitem = buddyListView->currentItem(); + BuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem); + if (!item) return; + + t_buddy *buddy = item->get_buddy(); + t_buddy_list *buddy_list = buddy->get_buddy_list(); + + // Delete the list item before deleting the buddy as + // deleting the item will detach the item from the buddy. + delete item; + + if (buddy->is_presence_terminated()) { + buddy_list->del_buddy(*buddy); + } else { + buddy->unsubscribe_presence(true); + } + + string err_msg; + if (!buddy_list->save(err_msg)) { + QString msg = tr("Failed to save buddy list: %1").arg(err_msg.c_str()); + ((t_gui *)ui)->cb_show_msg(this, msg.ascii(), MSG_CRITICAL); + } +} + +void MphoneForm::doAddBuddy() +{ + QListViewItem *qitem = buddyListView->currentItem(); + BLViewUserItem *item = dynamic_cast<BLViewUserItem *>(qitem); + if (!item) return; + + t_phone_user *pu = item->get_presence_epa()->get_phone_user(); + if (!pu) return; + t_buddy_list *buddy_list = pu->get_buddy_list(); + if (!buddy_list) return; + + BuddyForm *form = new BuddyForm(this, "new_buddy", true, Qt::WDestructiveClose); + // Do not call MEMMAN as this form will be deleted automatically. + form->showNew(*buddy_list, item); +} + +void MphoneForm::doAvailabilityOffline() +{ + QListViewItem *qitem = buddyListView->currentItem(); + BLViewUserItem *item = dynamic_cast<BLViewUserItem *>(qitem); + if (!item) return; + + t_phone_user *pu = item->get_presence_epa()->get_phone_user(); + if (!pu) return; + + pu->publish_presence(t_presence_state::ST_BASIC_CLOSED); +} + +void MphoneForm::doAvailabilityOnline() +{ + QListViewItem *qitem = buddyListView->currentItem(); + BLViewUserItem *item = dynamic_cast<BLViewUserItem *>(qitem); + if (!item) return; + + t_phone_user *pu = item->get_presence_epa()->get_phone_user(); + if (!pu) return; + + pu->publish_presence(t_presence_state::ST_BASIC_OPEN); +} + +void MphoneForm::DiamondcardSignUp() +{ + DiamondcardProfileForm *f = new DiamondcardProfileForm(this, "select profile", true, + Qt::WDestructiveClose); + + connect(f, SIGNAL(newDiamondcardProfile(const QString&)), + this, SLOT(newDiamondcardUser(const QString &))); + + f->show(NULL); +} + +void MphoneForm::newDiamondcardUser(const QString &filename) +{ + list<string> profileFilenames; + list<t_user *> users = phone->ref_users(); + + for (list<t_user *>::const_iterator it = users.begin(); it != users.end(); ++it) { + t_user *user = *it; + profileFilenames.push_back(user->get_filename()); + } + + profileFilenames.push_back(filename.ascii()); + newUsers(profileFilenames); +} + +void MphoneForm::DiamondcardAction(t_dc_action action, int userIdx) +{ + list<t_user *> diamondcard_users = diamondcard_get_users(phone); + vector<t_user *> v(diamondcard_users.begin(), diamondcard_users.end()); + + if (userIdx < 0 || (unsigned int)userIdx >= v.size()) return; + + t_user *user = v[userIdx]; + QString url(diamondcard_url(action, user->get_name(), user->get_auth_pass()).c_str()); + ((t_gui *)ui)->open_url_in_browser(url); +} + +void MphoneForm::DiamondcardRecharge(int userIdx) +{ + DiamondcardAction(DC_ACT_RECHARGE, userIdx); +} + +void MphoneForm::DiamondcardBalanceHistory(int userIdx) +{ + DiamondcardAction(DC_ACT_BALANCE_HISTORY, userIdx); +} + +void MphoneForm::DiamondcardCallHistory(int userIdx) +{ + DiamondcardAction(DC_ACT_CALL_HISTORY, userIdx); +} + +void MphoneForm::DiamondcardAdminCenter(int userIdx) +{ + DiamondcardAction(DC_ACT_ADMIN_CENTER, userIdx); +} |