/****************************************************************************
** 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, see <https://www.gnu.org/licenses/>.
*/
#include "mphoneform.h"
#include "twinkle_config.h"
#include <QWhatsThis>
#include <QLabel>
#include <QPixmap>
#include <QMouseEvent>
#include <QCloseEvent>
#include <QKeyEvent>
#include <QtDebug>
#include <QEvent>
#include <QFrame>
#include "../audits/memman.h"
#include "user.h"
#include <QTextEdit>
#include <QCheckBox>
#include <QApplication>
#include <QDesktopWidget>
#include "gui.h"
#include <QPixmap>
#include <QIcon>
#include <QMessageBox>
#include "audits/memman.h"
#include "line.h"
#include "stun/stun_transaction.h"
#include "log.h"
#include <QProgressDialog>
#include "util.h"
#include <QTimer>
#include <QCursor>
#include <QRegExp>
#include <QValidator>
#include <QSettings>
#include <QRegExpValidator>
#include "buddyform.h"
#include "diamondcardprofileform.h"
#include "osd.h"
#include "incoming_call_popup.h"

extern QSettings*      g_gui_state;

// Time (s) that the conversation timer of a line should stay visible after
// a call has ended
#define HIDE_LINE_TIMER_AFTER	5

MphoneForm::MphoneForm(QWidget* parent)
    : QMainWindow(parent)
{
	setupUi(this);
	init();
}

MphoneForm::~MphoneForm()
{
	destroy();
}

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
	QIcon inviteIcon(QPixmap(":/icons/images/invite.png"));
	QIcon messageIcon(QPixmap(":/icons/images/message.png"));
	QIcon editIcon(QPixmap(":/icons/images/edit16.png"));
	QIcon deleteIcon(QPixmap(":/icons/images/editdelete.png"));
    buddyPopupMenu = new QMenu(this);
	MEMMAN_NEW(buddyPopupMenu);
    buddyPopupMenu->addAction(inviteIcon, tr("&Call..."), this, SLOT(doCallBuddy()));
    buddyPopupMenu->addAction(messageIcon, tr("Instant &message..."), this, SLOT(doMessageBuddy()));
    buddyPopupMenu->addAction(editIcon, tr("&Edit..."), this, SLOT(doEditBuddy()));
    buddyPopupMenu->addAction(deleteIcon, tr("&Delete"), this, SLOT(doDeleteBuddy()));
	
	// Popup menu for a buddy list (click on profile name)
	QIcon changeAvailabilityIcon(QPixmap(":/icons/images/presence_online.png"));
	QIcon addIcon(QPixmap(":/icons/images/buddy.png"));
    buddyListPopupMenu = new QMenu(this);
	MEMMAN_NEW(buddyListPopupMenu);
    changeAvailabilityPopupMenu = buddyListPopupMenu->addMenu(changeAvailabilityIcon, tr("&Change availability"));

    // Change availibility sub popup menu
    QIcon availOnlineIcon(QPixmap(":/icons/images/presence_online.png"));
    QIcon availOfflineIcon(QPixmap(":/icons/images/presence_offline.png"));
    changeAvailabilityPopupMenu->addAction(availOfflineIcon, tr("O&ffline"), this,
                        SLOT(doAvailabilityOffline()));
    changeAvailabilityPopupMenu->addAction(availOnlineIcon, tr("&Online"), this,
                        SLOT(doAvailabilityOnline()));

    buddyListPopupMenu->addAction(addIcon, tr("&Add buddy..."), this, SLOT(doAddBuddy()));
	
	// ToDo: Tool tip for buddy list
	
	// 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());

	osdWindow = new OSD(this);
	connect(osdWindow, SIGNAL(hangupClicked()), this, SLOT(phoneBye()));
	connect(osdWindow, SIGNAL(muteClicked()), this, SLOT(osdMuteClicked()));

	incomingCallPopup = new IncomingCallPopup(this);
	connect(incomingCallPopup, SIGNAL(answerClicked()), this, SLOT(phoneAnswer()));
	connect(incomingCallPopup, SIGNAL(rejectClicked()), this, SLOT(phoneReject()));
	
	// 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 QSystemTrayIcon(this);

		sysTray->setIcon(
				QPixmap(":/icons/images/sys_idle_dis.png"));
		sysTray->setToolTip(PRODUCT_NAME);
		
		// Add items to the system tray menu
		QMenu *menu;

		menu = new QMenu(this);
		sysTray->setContextMenu(menu);

		connect(sysTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
				this, SLOT(sysTrayIconClicked(QSystemTrayIcon::ActivationReason)));
		
		// Call menu
        menu->addAction(callInvite);
        menu->addAction(callAnswer);
        menu->addAction(callBye);
        menu->addAction(callReject);
        menu->addAction(callRedirect);
        menu->addAction(callTransfer);
        menu->addAction(callHold);
        menu->addAction(callConference);
        menu->addAction(callMute);
        menu->addAction(callDTMF);
        menu->addAction(callRedial);
		
        menu->addSeparator();
		
		// Messaging
        menu->addAction(actionSendMsg);
		
        menu->addSeparator();
		
		// Line activation
        menu->addActions(actgrActivateLine->actions());
		
        menu->addSeparator();
		
		// Service menu
        menu->addAction(serviceDnd);
        menu->addAction(serviceRedirection);
        menu->addAction(serviceAutoAnswer);
        menu->addAction(servicesVoice_mailAction);
		
        menu->addSeparator();
		
		// View menu
        menu->addAction(viewCall_HistoryAction);
		
        menu->addSeparator();
		
#ifdef WITH_DIAMONDCARD
		// Diamondcard menu
		menu->insertItem("Diamondcard", Diamondcard);
        menu->addSeparator();
#endif
		
        menu->addAction(fileExitAction);
		
		sysTray->show();
	}

#ifndef WITH_DIAMONDCARD
	Diamondcard->menuAction()->setVisible(false);
#endif

	restoreState(g_gui_state->value("mainwindow/state").toByteArray());
	restoreGeometry(g_gui_state->value("mainwindow/geometry").toByteArray());
	splitter2->restoreState(g_gui_state->value("mainwindow/mainsplitter").toByteArray());
}

void MphoneForm::destroy()
{
	g_gui_state->setValue("mainwindow/state", saveState());
	g_gui_state->setValue("mainwindow/geometry", saveGeometry());
	g_gui_state->setValue("mainwindow/mainsplitter", splitter2->saveState());

	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->isVisible()) logViewForm->close();
		MEMMAN_DELETE(logViewForm);
		delete logViewForm;
	}
	if (historyForm) {
        if (historyForm->isVisible()) 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(buddyListPopupMenu);
	delete buddyListPopupMenu;
}

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();
	qApp->quit();
}

// 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
    QTextCursor cursor;
    cursor = displayTextEdit->textCursor();
    cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
    displayTextEdit->setTextCursor(cursor);
}

// 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());
	updateOSD();
}

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)->setSingleShot(false);
            (*timer)->start(1000);
		}
		break;
	default:
		// Hide call duration timer
		if (*timer != NULL) {
			// Hide the timer after a few seconds
            hideLineTimer->setSingleShot(true);
            hideLineTimer->start(HIDE_LINE_TIMER_AFTER * 1000);
			(*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();
		
        cryptLabel->setToolTip(QString());
		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(":/icons/images/encrypted.png"));
		} else {
			toolTip.append("\n").append(tr("Click to clear SAS verification."));
            cryptLabel->setFrameStyle(QFrame::NoFrame);
			cryptLabel->setPixmap(
				QPixmap(":/icons/images/encrypted_verified.png"));
		}

        cryptLabel->setToolTip(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;
        referLabel->setToolTip(QString());
		referLabel->show();
		if (is_transfer_consult) {
			referLabel->setPixmap(
				QPixmap(":/icons/images/consult-xfer.png"));
			toolTip = tr("Transfer consultation");
		} else {
			referLabel->setPixmap(
				QPixmap(":/icons/images/cf.png"));
			toolTip = tr("Transferring call");
		}
        referLabel->setToolTip(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(":/icons/images/stat_outgoing.png"));
		statLabel->show();
		break;
	case LSSUB_INCOMING_PROGRESS:
		statLabel->setPixmap(QPixmap(":/icons/images/stat_ringing.png"));
		statLabel->show();

		break;
	case LSSUB_ANSWERING:
		statLabel->setPixmap(QPixmap(":/icons/images/gear.png"));
		statLabel->show();
		break;
	case LSSUB_ESTABLISHED:
		if (phone->has_line_media(line)) {
			statLabel->setPixmap(QPixmap(
					":/icons/images/stat_established.png"));
		} else {
			statLabel->setPixmap(QPixmap(
					":/icons/images/stat_established_nomedia.png"));
		}
		statLabel->show();
		break;
	case LSSUB_RELEASING:
		statLabel->setPixmap(QPixmap(":/icons/images/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->isChecked())
	{
		line1RadioButton->setChecked(true);
    } else if (line == 1 && line1RadioButton->isChecked())
	{
		line2RadioButton->setChecked(true);
	}
	
	// Same logic for the activate line menu items
    if (line == 0 && actionLine2->isChecked())
	{
        actionLine1->setChecked(true);
    } else if (line == 1 && actionLine1->isChecked())
	{
        actionLine2->setChecked(true);
	}

	updateOSD();
	
	bool showIncomingCallPopup = false;

	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);

			std::string name;
			t_call_record cr = phone->get_call_hist(line);
			// t_user *user_config = phone->get_line_user(line);

			// name = ui->format_sip_address(user_config, cr.from_display, cr.from_uri);
			name = cr.from_display;
			if (name.empty())
				name = cr.from_uri.encode_no_params_hdrs(false);

			incomingCallPopup->setCallerName(QString::fromStdString(name));
			showIncomingCallPopup = true;

			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());
	}

	incomingCallPopup->setVisible(showIncomingCallPopup);
	
	// Set hold action in correct state
    callHold->setChecked(on_hold);
	
	// Set mute action in correct state
    callMute->setChecked(is_muted);
	
	// Set transfer action in correct state
    callTransfer->setChecked(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 successful 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(' ', "&nbsp;"));
		}
		toolTip.append("</td></tr>");
	}
	toolTip.append("</table><br>");
	toolTip.append("<i>");
	toolTip.append(tr("Click to show registrations.").replace(' ', "&nbsp;"));
	toolTip.append("</i>");
	
	// Set registration status
	if (num_registered == user_list.size()) {
		// All users are registered
		statRegLabel->setPixmap(QPixmap(":/icons/images/twinkle16.png"));
	} else if (num_failed == user_list.size()) {
		// All users failed to register
		statRegLabel->setPixmap(QPixmap(":/icons/images/reg_failed.png"));
	} else if (num_registered > 0) {
		// Some users are registered
		statRegLabel->setPixmap(QPixmap(
				":/icons/images/twinkle16.png"));
	} else if (num_failed > 0) {
		// Some users failed, none are registered
		statRegLabel->setPixmap(QPixmap(":/icons/images/reg_failed.png"));
	} else {
		// No users are registered, no users failed
		statRegLabel->setPixmap(QPixmap(":/icons/images/twinkle16-disabled.png"));
	}
	
	// Set tool tip with detailed info.
    statRegLabel->setToolTip(QString());
	
	if (num_registered > 0 || num_failed > 0) {
        statRegLabel->setToolTip(toolTip);
	} else {
        statRegLabel->setToolTip(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(' ', "&nbsp;");
}

// Flash the MWI icon
void MphoneForm::flashMWI()
{
	if (mwiFlashStatus) {
		mwiFlashStatus = false;
		statMWILabel->setPixmap(QPixmap(
				":/icons/images/mwi_none16.png"));
	} else {
		mwiFlashStatus = true;
		statMWILabel->setPixmap(QPixmap(
				":/icons/images/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(' ', "&nbsp;"));
	toolTip.append("</i>");
	
	// Set MWI icon
	if (mwi_new_msgs) {
		statMWILabel->setPixmap(QPixmap(
			":/icons/images/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(
			":/icons/images/mwi_failure16.png"));
	} else if (mwi_known) {
		tmrFlashMWI.stop();
		statMWILabel->setPixmap(QPixmap(
			":/icons/images/mwi_none16.png"));
	} else {
		tmrFlashMWI.stop();
		statMWILabel->setPixmap(QPixmap(
			":/icons/images/mwi_none16_dis.png"));
	}
	
	// Set tool tip
    statMWILabel->setToolTip(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(' ', "&nbsp;");
	tipDnd += "</b><br>\n<table>";
	QString tipCf = "<b>";
	tipCf += tr("Redirection active for:").replace(' ', "&nbsp;");
	tipCf +=  "</b><br>\n<table>";
	QString tipAa = "<b>";
	tipAa += tr("Auto answer active for:").replace(' ', "&nbsp;");
	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(' ', "&nbsp;");
	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(":/icons/images/cancel.png"));
	} else if (num_dnd > 0) {
		// Some users enabled dnd
		statDndLabel->setPixmap(QPixmap(":/icons/images/cancel.png"));
	} else {
		// No users enabeld dnd
		statDndLabel->setPixmap(QPixmap(":/icons/images/cancel-disabled.png"));
	}
	
	if (num_cf == user_list.size()) {
		// All users enabled redirecton
		statCfLabel->setPixmap(QPixmap(":/icons/images/cf.png"));
	} else if (num_cf > 0) {
		// Some users enabled redirection
		statCfLabel->setPixmap(QPixmap(":/icons/images/cf.png"));
	} else {
		// No users enabled redirection
		statCfLabel->setPixmap(QPixmap(":/icons/images/cf-disabled.png"));
	}
	
	if (num_auto_answer == user_list.size()) {
		// All users enabled auto answer
		statAaLabel->setPixmap(QPixmap(":/icons/images/auto_answer.png"));
	} else if (num_auto_answer > 0) {
		// Some users enabled auto answer
		statAaLabel->setPixmap(QPixmap(
				":/icons/images/auto_answer.png"));
	} else {
		// No users enabeld auto answer
		statAaLabel->setPixmap(QPixmap(
				":/icons/images/auto_answer-disabled.png"));
	}
	
	// Set tool tip with detailed info for multiple users.
    statDndLabel->setToolTip(QString());
    statCfLabel->setToolTip(QString());
    statAaLabel->setToolTip(QString());

	QString clickToActivate("<i>");
	clickToActivate += tr("Click to activate").replace(' ', "&nbsp;");
	clickToActivate += "</i>";
	if (num_dnd > 0) {
        statDndLabel->setToolTip(tipDnd);
	} else {
		QString status("<p>");
		status += tr("Do not disturb is not active.").replace(' ', "&nbsp;");
		status += "</p>";
		status += clickToActivate;
        statDndLabel->setToolTip(status);
	}		
	
	if (num_cf > 0) {
        statCfLabel->setToolTip(tipCf);
	} else {
		QString status("<p>");
		status += tr("Redirection is not active.").replace(' ', "&nbsp;");
		status += "</p>";
		status += clickToActivate;
        statCfLabel->setToolTip(status);
	}
	
	if (num_auto_answer > 0) {
        statAaLabel->setToolTip(tipAa);
	} else {
		QString status("<p>");
		status += tr("Auto answer is not active.").replace(' ', "&nbsp;");
		status += "</p>";
		status += clickToActivate;
        statAaLabel->setToolTip(status);
	}
	
	updateSysTrayStatus();
}

void MphoneForm::updateMissedCallStatus(int num_missed_calls)
{	
	QString clickDetails("<i>");
	clickDetails += tr("Click to see call history for details.").replace(' ', "&nbsp;");
	clickDetails += "</i>";
	if (num_missed_calls == 0) {
		statMissedLabel->setPixmap(QPixmap(":/icons/images/missed-disabled.png"));
		QString status("<p>");
		status += tr("You have no missed calls.").replace(' ', "&nbsp;");
		status += "</p>";
		status += clickDetails;
        statMissedLabel->setToolTip(status);
	} else {
		statMissedLabel->setPixmap(
			QPixmap(":/icons/images/missed.png"));
		
		QString tip("<p>");
		if (num_missed_calls == 1) {
			tip += tr("You missed 1 call.").replace(' ', "&nbsp;");
		} else {
			tip += tr("You missed %1 calls.").arg(num_missed_calls).
			       replace(' ', "&nbsp;");
		}
		tip += "</p>";
		tip += clickDetails;
        statMissedLabel->setToolTip(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->setIcon(QPixmap(":/icons/images/" + 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->setCheckable(true);
        serviceDnd->setChecked(srv->is_dnd_active());
		connect(serviceDnd, SIGNAL(toggled(bool)),
			this, SLOT(srvDnd(bool)));
		
        serviceAutoAnswer->setCheckable(true);
        serviceAutoAnswer->setChecked(srv->is_auto_answer_active());
		connect(serviceAutoAnswer, SIGNAL(toggled(bool)),
			this, SLOT(srvAutoAnswer(bool)));
	} else {
        serviceDnd->setChecked(false);
        serviceDnd->setCheckable(false);
		connect(serviceDnd, SIGNAL(triggered()),
			this, SLOT(srvDnd()));
		
        serviceAutoAnswer->setChecked(false);
        serviceAutoAnswer->setCheckable(false);
		connect(serviceAutoAnswer, SIGNAL(triggered()),
			this, SLOT(srvAutoAnswer()));
	}
	
#ifdef WITH_DIAMONDCARD
	updateDiamondcardMenu();
#endif
}

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 QAction* rechargeId = nullptr;
    static QAction* balanceHistoryId = nullptr;
    static QAction* callHistoryId = nullptr;
    static QAction* adminCenterId = nullptr;
	
	// Sub menu's
    static QMenu *rechargeMenu = NULL;
    static QMenu *balanceHistoryMenu = NULL;
    static QMenu *callHistoryMenu = NULL;
    static QMenu *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->addAction(tr("Recharge..."), this, SLOT(DiamondcardRecharge()));
        rechargeId->setData(0);
        balanceHistoryId = Diamondcard->addAction(tr("Balance history..."), this, SLOT(DiamondcardBalanceHistory()));
        balanceHistoryId->setData(0);
        callHistoryId = Diamondcard->addAction(tr("Call history..."), this, SLOT(DiamondcardCallHistory()));
        callHistoryId->setData(0);
        adminCenterId = Diamondcard->addAction(tr("Admin center..."), this, SLOT(DiamondcardAdminCenter()));
        adminCenterId->setData(0);
		
		// Disable actions as there is no active Diamondcard users.
		if (diamondcard_users.empty()) {
            rechargeId->setEnabled(false);
            balanceHistoryId->setEnabled(false);
            callHistoryId->setEnabled(false);
            adminCenterId->setEnabled(false);
		}
	}
	else
	{
        // Add the Diamondcard popup menus to the main Diamondcard menu.
        rechargeMenu = Diamondcard->addMenu(tr("Recharge"));
        balanceHistoryMenu = Diamondcard->addMenu(tr("Balance history"));
        callHistoryMenu = Diamondcard->addMenu(tr("Call history"));
        adminCenterMenu = Diamondcard->addMenu(tr("Admin center"));


		// 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)
		{
            QAction* 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->addAction(user->get_profile_name().c_str(), this,
                                  SLOT(DiamondcardRecharge()));
            menuId->setData(idx);
            menuId = balanceHistoryMenu->addAction(user->get_profile_name().c_str(), this,
                                  SLOT(DiamondcardBalanceHistory()));
            menuId->setData(idx);
            menuId = callHistoryMenu->addAction(user->get_profile_name().c_str(), this,
                                  SLOT(DiamondcardCallHistory()));
            menuId->setData(idx);
            menuId = adminCenterMenu->addAction(user->get_profile_name().c_str(), this,
                                  SLOT(DiamondcardAdminCenter()));
            menuId->setData(idx);
			
			++idx;
		}
		

	}
}

void MphoneForm::removeDiamondcardAction(QAction*& act)
{
    if (act != nullptr) {
        Diamondcard->removeAction(act);
        act = nullptr;
	}
}

void MphoneForm::removeDiamondcardMenu(QMenu* &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);
        selectUserForm->setModal(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);
        selectUserForm->setModal(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);
        selectUserForm->setModal(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);
        inviteForm->setModal(true);
		MEMMAN_NEW(inviteForm);
		
		// Initialize the destination history list
		for (int i = callComboBox->count() - 1; i >= 0; i--) {
            inviteForm->addToInviteComboBox(callComboBox->itemText(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().toStdString());
	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().toStdString());
	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.toStdString(), subject.toStdString(),
				     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);
    redirectForm->setModal(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);
    transferForm->setModal(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);
    termCapForm->setModal(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().toStdString());
	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.toStdString());
}

void MphoneForm::startMessageSession(void)
{
    t_user *user = phone->ref_user_profile(userComboBox->currentText().toStdString());
	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);
    selectUserForm->setModal(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);
    selectUserForm->setModal(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);
        srvRedirectForm->setModal(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(' ', "&nbsp;"), 
		    QMessageBox::Information, 
		    QMessageBox::Ok | QMessageBox::Default,
		    QMessageBox::NoButton, QMessageBox::NoButton);
	mbAbout.setIconPixmap(QPixmap(":/icons/images/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);
        userProfileForm->setModal(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);
        sysSettingsForm->setModal(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);
        selectProfileForm->setModal(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.toStdString())) {
			add_profile_list.push_back(*i);
		}
	}
	
	// Add new phone users
    QProgressDialog progress(tr("Starting user profiles..."), "Abort", 0, add_profile_list.size(), this);
    progress.setModal(true);
    progress.setWindowTitle(PRODUCT_NAME);
	progress.setMinimumDuration(200);
	int progressStep = 0;
	for (list<string>::iterator i = add_profile_list.begin(); i != add_profile_list.end(); i++) {
        progress.setValue(progressStep);
		qApp->processEvents();
		
        if (progress.wasCanceled()) {
			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(QString::fromStdString(user_config.get_name())).toStdString();
				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.").toStdString();
				
				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.setValue(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->itemText(i) == current_user) {
            userComboBox->setCurrentIndex(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.").toStdString(),
			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();
}

QSystemTrayIcon *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().toStdString());
	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().trimmed().toStdString(),
			       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->itemText(i) == destination) {
			callComboBox->removeItem(i);
		}
	}
	
	// Add entry
    callComboBox->insertItem(0, destination);
    callComboBox->setCurrentIndex(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->clearEditText();
}

void MphoneForm::showAddressBook()
{
	if (!getAddressForm) {
        getAddressForm = new GetAddressForm(this);
        getAddressForm->setModal(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->testAttribute(Qt::WA_UnderMouse)) {
		if (phone->ref_users().size() == 1) {
            bool enable = !serviceAutoAnswer->isChecked();
			srvAutoAnswer(enable);
            serviceAutoAnswer->setChecked(enable);
		} else {
			srvAutoAnswer();
		}
    } else if (statDndLabel->testAttribute(Qt::WA_UnderMouse)) {
		if (phone->ref_users().size() == 1) {
            bool enable = !serviceDnd->isChecked();
			srvDnd(enable);
            serviceDnd->setChecked(enable);
		} else {
			srvDnd();
		}
    } else if (statCfLabel->testAttribute(Qt::WA_UnderMouse)) {
		srvRedirect();
    } else if (statMWILabel->testAttribute(Qt::WA_UnderMouse)) {
		popupMenuVoiceMail(e->globalPos());
    } else if (statMissedLabel->testAttribute(Qt::WA_UnderMouse)) {
		// Open the history form, when the user clicks on the 
		// missed calls indication.
		viewHistory();
    } else if (statRegLabel->testAttribute(Qt::WA_UnderMouse)) {
		// Fetch registration status
		phoneShowRegistrations();
    } else if (crypt1Label->testAttribute(Qt::WA_UnderMouse)) {
		processCryptLabelClick(0);
    } else if (crypt2Label->testAttribute(Qt::WA_UnderMouse)) {
		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)
{
    QMenu menu(this);
	QIcon vmIcon(QPixmap(":/icons/images/mwi_none16.png"));
    vmIcon.addPixmap(QPixmap(":/icons/images/mwi_none16_dis.png"), QIcon::Disabled);
	
	list<t_user *>user_list = phone->ref_users();
    map<QAction*, 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;
		}		
		
        QAction* act = menu.addAction(vmIcon, entry);
		if (address.isEmpty()) {
            act->setEnabled(false);
		}
		
        vm.insert(make_pair(act, *i));
	}
	
    QAction* 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 == nullptr) 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.").toStdString(),
				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.").toStdString(),
				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()).toStdString(),
				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->setChecked(on);
}

void MphoneForm::showBuddyList(bool on)
{
	if (on) {
		buddyListView->show();
	} else {
		buddyListView->hide();
	}
	
	viewBuddyList = on;
    viewBuddyListAction->setChecked(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);
	}
    buddyListView->expandAll();
}

void MphoneForm::showBuddyListPopupMenu(const QPoint &pos)
{
    QTreeWidgetItem* item = buddyListView->currentItem();
	if (!item) return;
	
	BuddyListViewItem *buddyItem = dynamic_cast<BuddyListViewItem *>(item);
	if (buddyItem) {
        buddyPopupMenu->popup(buddyListView->mapToGlobal(pos));
	} else {
        buddyListPopupMenu->popup(buddyListView->mapToGlobal(pos));
	}
}

void MphoneForm::doCallBuddy()
{
    QTreeWidgetItem *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(QTreeWidgetItem *qitem)
{	
	BuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem);
	if (!item) return;
	
	t_buddy *buddy = item->get_buddy();
	
	startMessageSession(buddy);
}

void MphoneForm::doMessageBuddy()
{
    QTreeWidgetItem *item = buddyListView->currentItem();
	doMessageBuddy(item);
}

void MphoneForm::doEditBuddy()
{
    QTreeWidgetItem *qitem = buddyListView->currentItem();
	BuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem);
	if (!item) return;
	
	t_buddy *buddy = item->get_buddy();
	
    BuddyForm *form = new BuddyForm(this);
    form->setModal(true);
    form->setAttribute(Qt::WA_DeleteOnClose, true);
	// Do not call MEMMAN as this form will be deleted automatically.	
	form->showEdit(*buddy);
}

void MphoneForm::doDeleteBuddy()
{
    QTreeWidgetItem *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.toStdString(), MSG_CRITICAL);
	}
}

void MphoneForm::doAddBuddy()
{
    QTreeWidgetItem *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);
    form->setModal(true);
    form->setAttribute( Qt::WA_DeleteOnClose, true );
	// Do not call MEMMAN as this form will be deleted automatically.
	form->showNew(*buddy_list, item);
}

void MphoneForm::doAvailabilityOffline()
{
    QTreeWidgetItem *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()
{
    QTreeWidgetItem *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);
	
    f->setModal(true);
    f->setAttribute( Qt::WA_DeleteOnClose, true );
	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.toStdString());
	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()
{
    DiamondcardAction(DC_ACT_RECHARGE, static_cast<QAction*>(sender())->data().toInt());
}

void MphoneForm::DiamondcardBalanceHistory()
{
    DiamondcardAction(DC_ACT_BALANCE_HISTORY, static_cast<QAction*>(sender())->data().toInt());
}

void MphoneForm::DiamondcardCallHistory()
{
    DiamondcardAction(DC_ACT_CALL_HISTORY, static_cast<QAction*>(sender())->data().toInt());
}

void MphoneForm::DiamondcardAdminCenter()
{
    DiamondcardAction(DC_ACT_ADMIN_CENTER, static_cast<QAction*>(sender())->data().toInt());
}

void MphoneForm::whatsThis()
{
    QWhatsThis::enterWhatsThisMode();
}

void MphoneForm::sysTrayIconClicked(QSystemTrayIcon::ActivationReason reason)
{
	if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick)
	{
		if (sys_config->get_gui_hide_on_close())
			setVisible(!isVisible());
		else
			activateWindow();
	}
}

bool MphoneForm::event(QEvent * event)
{
	if (event->type() == QEvent::WindowActivate || event->type() == QEvent::WindowDeactivate)
		osdWindow->setVisible(shouldDisplayOSD());

	return QMainWindow::event(event);
}

bool MphoneForm::shouldDisplayOSD()
{
	if (QApplication::activeWindow() == this)
		return false;

	t_line_substate ss;

	ss = phone->get_line_substate(phone->get_active_line());

	switch (ss)
	{
	case LSSUB_ANSWERING:
	case LSSUB_ESTABLISHED:
	case LSSUB_OUTGOING_PROGRESS:
		break;
	default:
		return false;
	}

	if (!sys_config->get_gui_show_call_osd())
		return false;

	return true;
}

void MphoneForm::updateOSD()
{
	t_line_substate ss;
	int line;
	bool osdDisplayed;
	struct timeval t;
	unsigned long duration;
	t_user *user_config;
	t_call_record cr;

	osdDisplayed = shouldDisplayOSD();
	osdWindow->setVisible(osdDisplayed);

	line = phone->get_active_line();
	ss = phone->get_line_substate(line);
	user_config = phone->get_line_user(line);

	cr = phone->get_call_hist(line);

	if (ss == LSSUB_ESTABLISHED)
	{
		// Calculate duration of call
		gettimeofday(&t, nullptr);
		duration = t.tv_sec - cr.time_answer;

		osdWindow->setTime(QString::fromStdString(timer2str(duration)));
		osdWindow->setMuted(phone->is_line_muted(line));
	}
	else
	{
		osdWindow->setTime(lineSubstate2str(line));
	}

	if (ss != LSSUB_IDLE && user_config != nullptr)
	{
		std::string address;

		address = (cr.direction == t_call_record::DIR_IN ?
			 ui->format_sip_address(user_config,
			  cr.from_display, cr.from_uri) :
			 ui->format_sip_address(user_config,
			  cr.to_display, cr.to_uri));

		osdWindow->setCaller(QString::fromStdString(address));
	}
}

void MphoneForm::osdMuteClicked()
{
	((t_gui *)ui)->action_mute(!phone->is_line_muted(phone->get_active_line()));
	updateState();
}
