/*
    Copyright (C) 2005-2007  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 <iostream>
#include <cstdlib>
#include <qapplication.h>
#include "gui.h"
#include "line.h"
#include "log.h"
#include "sys_settings.h"
#include "user.h"
#include "cmd_socket.h"
#include "audio/rtp_telephone_event.h"
#include "sockets/interfaces.h"
#include "threads/thread.h"
#include "audits/memman.h"
#include "authenticationform.h"
#include "mphoneform.h"
#include "selectnicform.h"
#include "selectprofileform.h"
#include "messageformview.h"
#include "twinklesystray.h"
#include "util.h"
#include "address_finder.h"
#include "yesnodialog.h"
#include "command_args.h"
#include "im/msg_session.h"

#include "qcombobox.h"
#include "qhbox.h"
#include "qlabel.h"
#include "qlayout.h"
#include "qlistbox.h"
#include "qmessagebox.h"
#include "qpixmap.h"
#include "qpushbutton.h"
#include "qsize.h"
#include "qsizepolicy.h"
#include "qstring.h"
#include "qtextcodec.h"
#include "qtextedit.h"
#include "qtoolbar.h"
#include "qtooltip.h"
#include "qvbox.h"

extern string user_host;
extern pthread_t thread_id_main;

// External command arguments
extern t_command_args g_cmd_args;

QString str2html(const QString &s)
{
	QString result(s);
	
	result.replace('&', "&amp;");
	result.replace('<', "&lt;");
	result.replace('>', "&gt;");
	
	return result;
}

void setDisabledIcon(QAction *action, const QString &icon) {
	QIconSet i = action->iconSet();
	i.setPixmap(QPixmap::fromMimeSource(icon), 
		    QIconSet::Automatic, QIconSet::Disabled);
	action->setIconSet(i);
}

void setDisabledIcon(QToolButton *toolButton, const QString &icon) {
	QIconSet i = toolButton->iconSet();
	i.setPixmap(QPixmap::fromMimeSource(icon), 
		    QIconSet::Automatic, QIconSet::Disabled);
	toolButton->setIconSet(i);
}

/////////////////////////////////////////////////
// PRIVATE
/////////////////////////////////////////////////

void t_gui::setLineFields(int line) {
	if (line == 0) {
		fromLabel = mainWindow->from1Label;
		toLabel = mainWindow->to1Label;
		subjectLabel = mainWindow->subject1Label;
		codecLabel = mainWindow->codec1TextLabel;
		photoLabel = mainWindow->photo1Label;
	} else {
		fromLabel = mainWindow->from2Label;
		toLabel = mainWindow->to2Label;
		subjectLabel = mainWindow->subject2Label;
		codecLabel = mainWindow->codec2TextLabel;
		photoLabel = mainWindow->photo2Label;
	}
}

void t_gui::clearLineFields(int line) {
	if (line >= NUM_USER_LINES) return;
	
	setLineFields(line);
	fromLabel->clear();
	QToolTip::remove(fromLabel);
	toLabel->clear();
	QToolTip::remove(toLabel);
	subjectLabel->clear();
	QToolTip::remove(subjectLabel);
	codecLabel->clear();
	photoLabel->clear();
	photoLabel->hide();
}

void t_gui::displayTo(const QString &s) {
	toLabel->setText(s);
	toLabel->setCursorPosition(0);
	QToolTip::add(toLabel, s);
}

void t_gui::displayFrom(const QString &s) {
	fromLabel->setText(s);
	fromLabel->setCursorPosition(0);
	QToolTip::add(fromLabel, s);
}

void t_gui::displaySubject(const QString &s) {
	subjectLabel->setText(s);
	subjectLabel->setCursorPosition(0);
	QToolTip::add(subjectLabel, s);
}

void t_gui::displayCodecInfo(int line) {
	if (line > NUM_USER_LINES) return;
	
	setLineFields(line);
	codecLabel->clear();
	
	t_call_info call_info = phone->get_call_info(line);
	
	if (call_info.send_codec == CODEC_NULL && call_info.recv_codec == CODEC_NULL) {
		return;
	}
	
	if (call_info.send_codec == CODEC_NULL) {
		codecLabel->setText(format_codec(call_info.recv_codec).c_str());
		return;
	}
	
	if (call_info.recv_codec == CODEC_NULL) {
		codecLabel->setText(format_codec(call_info.send_codec).c_str());
		return;
	}
	
	if (call_info.send_codec == call_info.recv_codec) {
		codecLabel->setText(format_codec(call_info.send_codec).c_str());
		return;
	}
	
	QString s = format_codec(call_info.send_codec).c_str();
	s.append('/').append(format_codec(call_info.recv_codec).c_str());
	codecLabel->setText(s);
}

void t_gui::displayPhoto(const QImage &photo) {
	if (mainWindow->getViewCompactLineStatus()) {
		// In compact line status mode, no photo can be shown
		return;
	}
	
	if (photo.isNull()) {
		photoLabel->hide();
	} else {
		QPixmap pm;
		pm.convertFromImage(photo.smoothScale(
			photoLabel->width(), photoLabel->height(), QImage::ScaleMin));
		photoLabel->setPixmap(pm);
		photoLabel->show();
	}
}

/////////////////////////////////////////////////
// PROTECTED
/////////////////////////////////////////////////
bool t_gui::do_invite(const string &destination, const string &display, 
			const string &subject, bool immediate, bool anonymous)
{
	lock();
	if (mainWindow->callInvite->isEnabled()) {
		if (immediate) {
			t_user *user = phone->ref_user_profile(
				mainWindow->userComboBox->currentText().ascii());
			t_url dst_url(expand_destination(user, destination));
			if (dst_url.is_valid()) {
				mainWindow->do_phoneInvite(user, 
						display.c_str(), dst_url, subject.c_str(),
						anonymous);
			}
		} else {
			t_url dest_url(destination);
			t_display_url du(dest_url, display);
			mainWindow->phoneInvite(du.encode().c_str(), subject.c_str(),
						anonymous);
		}
	}
	unlock();
	
	return true;
}

void t_gui::do_redial(void) {
	lock();
	if (mainWindow->callRedial->isEnabled()) {
		mainWindow->phoneRedial();
	}
	unlock();
}

void t_gui::do_answer(void) {
	lock();
	if (mainWindow->callAnswer->isEnabled()) {
		mainWindow->phoneAnswer();
	}
	unlock();
}

void t_gui::do_answerbye(void) {
	lock();
	if (mainWindow->callAnswer->isEnabled()) {
		mainWindow->phoneAnswer();
	} else if (mainWindow->callBye->isEnabled()) {
		mainWindow->phoneBye();
	}
	unlock();
}

void t_gui::do_reject(void) {
	lock();
	if (mainWindow->callReject->isEnabled()) {
		mainWindow->phoneReject();
	}
	unlock();
}

void t_gui::do_redirect(bool show_status, bool type_present, t_cf_type cf_type, 
		bool action_present, bool enable, int num_redirections,
		const list<string> &dest_strlist, bool immediate)
{
	if (show_status) {
		// Show status not supported in GUI
		return;
	}
	
	t_user *user = phone->ref_user_profile(mainWindow->
				userComboBox->currentText().ascii());
	
	list<t_display_url> dest_list;
	for (list<string>::const_iterator i = dest_strlist.begin();
	     i != dest_strlist.end(); i++)
	{
		t_display_url du;
		du.url = expand_destination(user, *i);
		du.display.clear();
		if (!du.is_valid()) return;
		dest_list.push_back(du);
	}
	
	// Enable/disable permanent redirections
	if (type_present) {
		lock();
		if (enable) {
			phone->ref_service(user)->enable_cf(cf_type, dest_list);
		} else {
			phone->ref_service(user)->disable_cf(cf_type);
		}
		mainWindow->updateServicesStatus();
		unlock();
		
		return;
	} else {
		if (action_present) {
			if (!enable) {
				lock();
				phone->ref_service(user)->disable_cf(CF_ALWAYS);
				phone->ref_service(user)->disable_cf(CF_BUSY);
				phone->ref_service(user)->disable_cf(CF_NOANSWER);
				mainWindow->updateServicesStatus();
				unlock();
			}
			
			return;
		}
	}
	
	lock();
	if (mainWindow->callRedirect->isEnabled()) {
		if (immediate) {
			mainWindow->do_phoneRedirect(dest_list);
		} else {
			mainWindow->phoneRedirect(dest_strlist);
		}
	}
	unlock();
	
	return;
}

void t_gui::do_dnd(bool show_status, bool toggle, bool enable) {
	if (show_status) {
		// Show status not supported in GUI
		return;
	}
	
	lock();
	if (phone->ref_users().size() == 1) {
		if (toggle) {
			enable = !mainWindow->serviceDnd->isOn();
		}
		mainWindow->srvDnd(enable);
		mainWindow->serviceDnd->setOn(enable);
	} else {
		t_user *user = phone->ref_user_profile(mainWindow->
				userComboBox->currentText().ascii());
		list<t_user *> l;
		l.push_back(user);
		if (toggle) {
			enable = !phone->ref_service(user)->is_dnd_active();
		}
		
		if (enable) {
			mainWindow->do_srvDnd_enable(l);
		} else {
			mainWindow->do_srvDnd_disable(l);
		}
	}
	unlock();
}

void t_gui::do_auto_answer(bool show_status, bool toggle, bool enable) {
	if (show_status) {
		// Show status not supported in GUI
		return;
	}
	
	lock();
	if (phone->ref_users().size() == 1) {
		if (toggle) {
			enable = !mainWindow->serviceAutoAnswer->isOn();
		}
		mainWindow->srvAutoAnswer(enable);
		mainWindow->serviceAutoAnswer->setOn(enable);
	} else {
		t_user *user = phone->ref_user_profile(mainWindow->
				userComboBox->currentText().ascii());
		list<t_user *> l;
		l.push_back(user);
		if (toggle) {
			enable = !phone->ref_service(user)->
				 is_auto_answer_active();
		}

		if (enable) {
			mainWindow->do_srvAutoAnswer_enable(l);
		} else {
			mainWindow->do_srvAutoAnswer_disable(l);
		}
	}
	unlock();
}

void t_gui::do_bye(void) {
	lock();
	if (mainWindow->callBye->isEnabled()) {
		mainWindow->phoneBye();
	}
	unlock();
}

void t_gui::do_hold(void) {
	lock();
	if (mainWindow->callHold->isEnabled() && !mainWindow->callHold->isOn()) {
		mainWindow->phoneHold(true);
	}
	unlock();
}

void t_gui::do_retrieve(void) {
	lock();
	if (mainWindow->callHold->isEnabled() && mainWindow->callHold->isOn()) {
		mainWindow->phoneHold(false);
	}
	unlock();
}

bool t_gui::do_refer(const string &destination, t_transfer_type transfer_type, bool immediate) {
	lock();
	if (mainWindow->callTransfer->isEnabled() &&
	    !mainWindow->callTransfer->isOn()) 
	{
		if (immediate) {
			t_display_url du;
			t_user *user = phone->ref_user_profile(mainWindow->
				userComboBox->currentText().ascii());
			du.url = expand_destination(user, destination);
			
			if (du.is_valid() || transfer_type == TRANSFER_OTHER_LINE) {
				mainWindow->do_phoneTransfer(du, transfer_type);
			}
		} else {
			mainWindow->phoneTransfer(destination, transfer_type);
		}
	} else if(mainWindow->callTransfer->isEnabled() &&
		  mainWindow->callTransfer->isOn())
	{
		if (transfer_type != TRANSFER_CONSULT) {
			mainWindow->do_phoneTransferLine();
		}
	}
	unlock();
	
	return true;
}

void t_gui::do_conference(void) {
	lock();
	if (mainWindow->callConference->isEnabled()) {
		mainWindow->phoneConference();
	}
	unlock();
}

void t_gui::do_mute(bool show_status, bool toggle, bool enable) {
	if (show_status) {
		// Show status not supported in GUI
		return;
	}
	
	lock();
	if (mainWindow->callMute->isEnabled()) {
		if (toggle) enable = !phone->is_line_muted(phone->get_active_line());
		mainWindow->phoneMute(enable);
	}
	unlock();
}

void t_gui::do_dtmf(const string &digits) {
	lock();
	if (mainWindow->callDTMF->isEnabled()) {
		mainWindow->sendDTMF(digits.c_str());
	}
	unlock();
}

void t_gui::do_register(bool reg_all_profiles) {
	lock();
	list<t_user *> l;
	
	if (reg_all_profiles) {
		l = phone->ref_users();
	} else {
		t_user *user = phone->ref_user_profile(mainWindow->
			userComboBox->currentText().ascii());
		l.push_back(user);
	}
	
	mainWindow->do_phoneRegister(l);
	unlock();
}

void t_gui::do_deregister(bool dereg_all_profiles, bool dereg_all_devices) {
	lock();
	list<t_user *> l;
	
	if (dereg_all_profiles) {
		l = phone->ref_users();
	} else {
		t_user *user = phone->ref_user_profile(mainWindow->
			userComboBox->currentText().ascii());
		l.push_back(user);
	}
	
	if (dereg_all_devices) {
		mainWindow->do_phoneDeregisterAll(l);
	} else {
		mainWindow->do_phoneDeregister(l);
	}
	unlock();
}

void t_gui::do_fetch_registrations(void) {
	lock();
	mainWindow->phoneShowRegistrations();
	unlock();
}

bool t_gui::do_options(bool dest_set, const string &destination, bool immediate) {
	lock();
	// In-dialog OPTIONS request
	int line = phone->get_active_line();
	if (phone->get_line_substate(line) == LSSUB_ESTABLISHED) {
		((t_gui *)ui)->action_options();
		return true;
	}
	
	if (immediate) {
		t_user *user = phone->ref_user_profile(mainWindow->
			userComboBox->currentText().ascii());
		t_url dst_url(expand_destination(user, destination));
		
		if (dst_url.is_valid()) {
			mainWindow->do_phoneTermCap(user, dst_url);
		}
	} else {
		mainWindow->phoneTermCap(destination.c_str());
	}
	unlock();
	
	return true;
}

void t_gui::do_line(int line) {
	phone->pub_activate_line(line - 1);
}

void t_gui::do_user(const string &profile_name) {
	lock();
	for (int i = 0; i < mainWindow->userComboBox->count(); i++) {
		if (mainWindow->userComboBox->text(i) == profile_name.c_str()) 
		{
			mainWindow->userComboBox->setCurrentItem(i);
		}
	}
	unlock();
}

bool t_gui::do_message(const string &destination, const string &display,
		const string &text)
{
	t_user *user = phone->ref_user_profile(mainWindow->
			userComboBox->currentText().ascii());
	
	t_url dest_url(expand_destination(user, destination));

	if (dest_url.is_valid()) {
		phone->pub_send_message(user, dest_url, display, text);
	}
	
	return true;
}

void t_gui::do_presence(t_presence_state::t_basic_state basic_state) {
	t_user *user = phone->ref_user_profile(mainWindow->
			userComboBox->currentText().ascii());
	
	phone->pub_publish_presence(user, basic_state);
}

void t_gui::do_zrtp(t_zrtp_cmd zrtp_cmd) {
	lock();
	
	switch (zrtp_cmd) {
	case ZRTP_ENCRYPT:
		mainWindow->phoneEnableZrtp(true);
		break;
	case ZRTP_GO_CLEAR:
		mainWindow->phoneEnableZrtp(false);
		break;
	case ZRTP_CONFIRM_SAS:
		mainWindow->phoneConfirmZrtpSas();
		break;
	case ZRTP_RESET_SAS:
		mainWindow->phoneResetZrtpSasConfirmation();
		break;
	default:
		assert(false);
	}
	
	unlock();
}



void t_gui::do_quit(void) {
	lock();
	mainWindow->fileExit();
	unlock();
}

void t_gui::do_help(const list<t_command_arg> &al) {
	// Nothing to do in GUI mode
	return;
}


/////////////////////////////////////////////////
// PUBLIC
/////////////////////////////////////////////////

t_gui::t_gui(t_phone *_phone) : t_userintf(_phone) {
	use_stdout = false;
	lastFileBrowsePath = DIR_HOME;
	
	mainWindow = new MphoneForm(0, 0, Qt::WType_TopLevel | Qt::WStyle_ContextHelp);
#ifdef HAVE_KDE
	sys_tray_popup = NULL;
#endif
	
	for (int i = 0; i < NUM_USER_LINES; i++) {
		QObject::connect(&autoShowTimer[i], SIGNAL(timeout()),
			mainWindow, SLOT(show()));
	}
	
	MEMMAN_NEW(mainWindow);
	qApp->setMainWidget(mainWindow);
}

t_gui::~t_gui() {
	destroyAllMessageSessions();
	
	MEMMAN_DELETE(mainWindow);
	delete mainWindow;
}

void t_gui::run(void) {
	// Start asynchronous event processor
	thr_process_events = new t_thread(process_events_main, NULL);
	MEMMAN_NEW(thr_process_events);
	
	QString s;
	list<t_user *> user_list = phone->ref_users();
	
	// The Qt event loop is not running yet. Explicitly take the Qt lock
	// to avoid race conditions with other threads that may call GUI call
	// backs.
	// NOTE: the t_gui::lock() method cannot be used as this method
	// will not lock from the main thread and we are running in the
	// main thread (a bit of a kludge).
	qApp->lock();
	
	// Set configuration file name in titlebar
	s = PRODUCT_NAME;
	s.append(" - ").append(user_host.c_str());
	mainWindow->setCaption(s);
	
	// Set user combo box
	mainWindow->updateUserComboBox();
	
	// Display product information
	s = PRODUCT_NAME;
	s.append(' ').append(PRODUCT_VERSION).append(", ");
	s.append(sys_config->get_product_date().c_str());
	mainWindow->display(s);
	s = "Copyright (C) 2005-2007  ";
	s.append(PRODUCT_AUTHOR);
	mainWindow->display(s);
	
	// Restore user interface state from previous session
	restore_state();
	
	// Initialize phone functions
	phone->init();
	
	// Set controls in correct status
	mainWindow->updateState();
	mainWindow->updateRegStatus();
	mainWindow->updateMwi();
	mainWindow->updateServicesStatus();
	mainWindow->updateMissedCallStatus(0);
	mainWindow->updateMenuStatus();
	
	// Clear line field info fields
	clearLineFields(0);
	clearLineFields(1);
	
	// Populate buddy list
	mainWindow->populateBuddyList();
	
	// Set width of window to width of tool bar
	int widthToolBar = mainWindow->callToolbar->width();
	QSize sizeMainWin = mainWindow->size();
	sizeMainWin.setWidth(widthToolBar);
	mainWindow->resize(sizeMainWin);
	
	// Start QApplication/KApplication
	if ((sys_config->get_start_hidden() && !g_cmd_args.cmd_show) ||
	    g_cmd_args.cmd_hide)
	{
		mainWindow->hide();
	} else {
		mainWindow->show();
	}
	
	// Activate a profile if the --set-profile option was given on the command
	// line.
	if (!g_cmd_args.cmd_set_profile.isEmpty()) {
		cmdsocket::cmd_cli(string("user ") + 
				   g_cmd_args.cmd_set_profile.ascii(), true);
	}
	
	// Execute the call command if a callto destination was specified on the
	// command line
	if (!g_cmd_args.callto_destination.isEmpty()) {
		cmdsocket::cmd_call(g_cmd_args.callto_destination.ascii(), 
			 g_cmd_args.cmd_immediate_mode);
	}
	
	// Execute a CLI command if one was given on the command line
	if (!g_cmd_args.cli_command.isEmpty()) {
		cmdsocket::cmd_cli(g_cmd_args.cli_command.ascii(), 
			g_cmd_args.cmd_immediate_mode);
	}
	
	qApp->unlock();
	
	// Start Qt application
	qApp->exec();
	
	// Terminate phone functions
	phone->terminate();
	
	// Make user interface state persistent
	save_state();
}

void t_gui::save_state(void) {
	lock();
	
	sys_config->set_last_used_profile(
			mainWindow->userComboBox->currentText().ascii());

	list<string> history;
	for (int i = 0; i < mainWindow->callComboBox->count(); i++) {
		history.push_back(mainWindow->callComboBox->text(i).ascii());
	}
	sys_config->set_dial_history(history);
	
	sys_config->set_show_display(mainWindow->getViewDisplay());
	sys_config->set_compact_line_status(mainWindow->getViewCompactLineStatus());
	sys_config->set_show_buddy_list(mainWindow->getViewBuddyList());
	
	t_userintf::save_state();
	
	unlock();
}

void t_gui::restore_state(void) {
	lock();
	
	// The last used profile is selected when the userComboBox is
	// filled by MphoneForm::updateUserComboBox
	
	mainWindow->callComboBox->clear();
	list<string> dial_history = sys_config->get_dial_history();
	for (list<string>::reverse_iterator i = dial_history.rbegin(); i != dial_history.rend(); i++)
	{
		mainWindow->addToCallComboBox(i->c_str());
	}
	
	mainWindow->showDisplay(sys_config->get_show_display());
	mainWindow->showCompactLineStatus(sys_config->get_compact_line_status());
	mainWindow->showBuddyList(sys_config->get_show_buddy_list());
	
	t_userintf::restore_state();
	
	unlock();
}

void t_gui::lock(void) {
	// To synchronize actions on the Qt widget the application lock
	// is used. The main thread running the Qt event loop takes the
	// application lock itself already. So take the lock if this is not the
	// main thread.
	// If the Qt event loop has not been started yet, then the lock
	// should also be taken from the main thread.
	t_userintf::lock();
	if (!t_thread::is_self(thread_id_main)) {
		qApp->lock();
	}
}

void t_gui::unlock(void) {
	t_userintf::lock();
	if (!t_thread::is_self(thread_id_main)) {
		qApp->unlock();
	}
}

string t_gui::select_network_intf(void) {
	string ip;
	list<t_interface> *l = get_interfaces();
	// The socket routines are not under control of MEMMAN so report
	// the allocation here.
	MEMMAN_NEW(l);
	if (l->size() == 0) {
		cb_show_msg(qApp->translate("GUI",
			    "Cannot find a network interface. Twinkle will use "
			    "127.0.0.1 as the local IP address. When you connect to "
			    "the network you have to restart Twinkle to use the correct "
			    "IP address.").ascii(),
			    MSG_WARNING);
		
		MEMMAN_DELETE(l);
		delete l;
		return "127.0.0.1";
	}
	
	if (l->size() == 1) {
		// There is only 1 interface
		ip = l->front().get_ip_addr();
	} else {
		// There are multiple interfaces
		SelectNicForm *sf = new SelectNicForm(NULL, "nic", true);
		MEMMAN_NEW(sf);
		QString item;
		for (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {
			item = i->name.c_str();
			item.append(':').append(i->get_ip_addr().c_str());
			sf->nicListBox->insertItem(
					QPixmap::fromMimeSource("kcmpci16.png"), 
					item);
		}
		
		sf->nicListBox->setCurrentItem(0);
		sf->exec();
		
		int selection = sf->nicListBox->currentItem();
		int num = 0;
		for (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {
			if (num == selection) {
				ip = i->get_ip_addr();
				break;
			}
			num++;
		}
		
		MEMMAN_DELETE(sf);
		delete sf;		
	}
	
	MEMMAN_DELETE(l);
	delete l;
	return ip;
}

bool t_gui::select_user_config(list<string> &config_files) {
	SelectProfileForm f(0, "select user profile", true);
	
	if (f.execForm()) {
		config_files = f.selectedProfiles;
		return true;
	}
	
	return false;
}

// GUI call back functions

void t_gui::cb_incoming_call(t_user *user_config, int line, const t_request *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	setLineFields(line);
	
	// Incoming call for to-header
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: incoming call for %2").arg(line + 1).arg(
			format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri).c_str());
	mainWindow->display(s);
	
	// Is this a transferred call?
	if (r->hdr_referred_by.is_populated()) {
		s = "Call transferred by ";
		s = qApp->translate("GUI", "Call transferred by %1").arg(
				format_sip_address(user_config, 
					    r->hdr_referred_by.display, 
					    r->hdr_referred_by.uri).c_str());
		mainWindow->display(s);
	}
	
	// From
	QString fromParty = format_sip_address(user_config,
				r->hdr_from.get_display_presentation(), r->hdr_from.uri).c_str();
	s = fromParty;
	QString organization("");
	if (r->hdr_organization.is_populated()) {
		organization = r->hdr_organization.name.c_str();
		s.append(", ").append(organization);
	}
	displayFrom(s);
	
	// Display photo
	QImage fromPhoto;
	
	if (sys_config->get_ab_lookup_photo()) {
		t_address_finder *af = t_address_finder::get_instance();
		fromPhoto = af->find_photo(user_config, r->hdr_from.uri);
	}
	
	displayPhoto(fromPhoto);
	
	// To
	s = "";
	s.append(format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri).c_str());
	displayTo(s);
	
	// Subject
	QString subject("");
	if (r->hdr_subject.is_populated()) {
		subject = r->hdr_subject.subject.c_str();
	}
	displaySubject(subject);
	
	cb_notify_call(line, fromParty, organization, fromPhoto, subject);
	
	unlock();
}

void t_gui::cb_call_cancelled(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: far end cancelled call.").arg(line + 1);
	mainWindow->display(s);
	
	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_far_end_hung_up(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: far end released call.").arg(line + 1);
	mainWindow->display(s);

	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_answer_timeout(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;

	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_sdp_answer_not_supported(int line, const string &reason) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: SDP answer from far end not supported.").arg(line + 1);
	mainWindow->display(s);
	
	s = reason.c_str();
	mainWindow->display(s);
	
	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_sdp_answer_missing(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;

	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: SDP answer from far end missing.").arg(line + 1);
	mainWindow->display(s);

	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_unsupported_content_type(int line, const t_sip_message *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: Unsupported content type in answer from far end.").arg(line + 1);
	mainWindow->display(s);
	
	s = r->hdr_content_type.media.type.c_str();
	s.append("/").append(r->hdr_content_type.media.subtype.c_str());
	mainWindow->display(s);
	
	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_ack_timeout(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: no ACK received, call will be terminated.").arg(line + 1);
	mainWindow->display(s);

	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_100rel_timeout(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: no PRACK received, call will be terminated.").arg(line + 1);
	mainWindow->display(s);

	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_prack_failed(int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: PRACK failed.").arg(line + 1);
	mainWindow->display(s);
	
	s = QString().setNum(r->code);
	s.append(' ').append(r->reason.c_str());
	mainWindow->display(s);

	cb_stop_call_notification(line);
	
	unlock();
}

void t_gui::cb_provisional_resp_invite(int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	mainWindow->updateState();
	unlock();
}

void t_gui::cb_cancel_failed(int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: failed to cancel call.").arg(line + 1);
	mainWindow->display(s);
	
	s = QString().setNum(r->code);
	s.append(' ').append(r->reason.c_str());
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_call_answered(t_user *user_config, int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	setLineFields(line);
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: far end answered call.").arg(line + 1);
	mainWindow->display(s);
	
	// Put far-end party in line to-field
	s = "";
	s.append(format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri).c_str());
	if (r->hdr_organization.is_populated()) {
		s.append(", ").append(r->hdr_organization.name.c_str());
	}
	displayTo(s);
	
	unlock();
}

void t_gui::cb_call_failed(t_user *user_config, int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call failed.").arg(line + 1);
	mainWindow->display(s);
	
	s = QString().setNum(r->code);
	s.append(' ').append(r->reason.c_str());
	mainWindow->display(s);
	
	// Warnings
	if (r->hdr_warning.is_populated()) {
		list<string> l = format_warnings(r->hdr_warning);
		for (list<string>::iterator i = l.begin(); i != l.end(); i++) {
			mainWindow->display(i->c_str());
		}
	}
	
	// Redirect response
	if (r->get_class() == R_3XX && r->hdr_contact.is_populated()) {
		list<t_contact_param> l = r->hdr_contact.contact_list;
		l.sort();
		mainWindow->display(qApp->translate("GUI",
				"The call can be redirected to:"));
		for (list<t_contact_param>::iterator i = l.begin();
		i != l.end(); i++)
		{
			s = format_sip_address(user_config,
					i->display, i->uri).c_str();
			mainWindow->display(s);
		}
	}
	
	// Unsupported extensions
	if (r->code == R_420_BAD_EXTENSION) {
		mainWindow->display(r->hdr_unsupported.encode().c_str());
	}

	unlock();
}

void t_gui::cb_stun_failed_call_ended(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call failed.").arg(line + 1);
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_call_ended(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call released.").arg(line + 1);
	mainWindow->display(s);

	unlock();
}

void t_gui::cb_call_established(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call established.").arg(line + 1);
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_options_response(const t_response *r) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Response on terminal capability request: %1 %2")
	    .arg(r->code).arg(r->reason.c_str());
	mainWindow->display(s);
	
	if (r->code == R_408_REQUEST_TIMEOUT) {
		// The request timed out, so no capabilities are known.
		unlock();
		return;
	}
	
	s = qApp->translate("GUI", "Terminal capabilities of %1").arg(r->hdr_to.uri.encode().c_str());
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "Accepted body types:").append(" ");
	if (r->hdr_accept.is_populated()) {
		s.append(r->hdr_accept.get_value().c_str());
	} else {
		s.append(qApp->translate("GUI", "unknown"));
	}
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "Accepted encodings:").append(" ");
	if (r->hdr_accept_encoding.is_populated()) {
		s.append(r->hdr_accept_encoding.get_value().c_str());
	} else {
		s.append(qApp->translate("GUI", "unknown"));
	}
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "Accepted languages:").append(" ");
	if (r->hdr_accept_language.is_populated()) {
		s.append(r->hdr_accept_language.get_value().c_str());
	} else {
		s.append(qApp->translate("GUI", "unknown"));
	}
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "Allowed requests:").append(" ");
	if (r->hdr_allow.is_populated()) {
		s.append(r->hdr_allow.get_value().c_str());
	} else {
		s.append(qApp->translate("GUI", "unknown"));
	}
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "Supported extensions:").append(" ");
	if (r->hdr_supported.is_populated()) {
		if (r->hdr_supported.features.empty()) {
			s.append(qApp->translate("GUI", "none"));
		} else {
			s.append(r->hdr_supported.get_value().c_str());
		}
	} else {
		s.append(qApp->translate("GUI", "unknown"));
	}
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "End point type:").append(" ");
	if (r->hdr_server.is_populated()) {
		s.append(r->hdr_server.get_value().c_str());
	} else if (r->hdr_user_agent.is_populated()) {
		// Some end-points put a User-Agent header in the response
		// instead of a Server header.
		s.append(r->hdr_user_agent.get_value().c_str());
	} else {
		s.append(qApp->translate("GUI", "unknown"));
	}
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_reinvite_success(int line, const t_response *r) {
	// Do not bother GUI user.
	return;
}

void t_gui::cb_reinvite_failed(int line, const t_response *r) {
	// Do not bother GUI user.
	return;
}

void t_gui::cb_retrieve_failed(int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call retrieve failed.").arg(line + 1);
	mainWindow->display(s);
	
	s = QString().setNum(r->code);
	s.append(' ').append(r->reason.c_str());
	mainWindow->display(s);
	
	unlock();
}


void  t_gui::cb_invalid_reg_resp(t_user *user_config, const t_response *r, const string &reason) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	qApp->translate("GUI", "%1, registration failed: %2 %3")
			.arg(user_config->get_profile_name().c_str())
			.arg(r->code)
			.arg(r->reason.c_str());
	mainWindow->display(s);
	mainWindow->display(reason.c_str());
	
	mainWindow->updateRegStatus();
	unlock();
}

void t_gui::cb_register_success(t_user *user_config, const t_response *r, unsigned long expires,
				bool first_success) 
{
	lock();
	QString s;
	
	if (first_success) {
		mainWindow->displayHeader();
		s = qApp->translate("GUI", "%1, registration succeeded (expires = %2 seconds)")
		    .arg(user_config->get_profile_name().c_str())
		    .arg(expires);
		mainWindow->display(s);
	}
	
	mainWindow->updateRegStatus();
	unlock();
}

void t_gui::cb_register_failed(t_user *user_config, const t_response *r, bool first_failure) {
	lock();
	QString s;
	
	if (first_failure) {
		mainWindow->displayHeader();
		s = qApp->translate("GUI", "%1, registration failed: %2 %3")
		    .arg(user_config->get_profile_name().c_str())
		    .arg(r->code)
		    .arg(r->reason.c_str());
		mainWindow->display(s);
	}
	
	mainWindow->updateRegStatus();
	unlock();
}

void t_gui::cb_register_stun_failed(t_user *user_config, bool first_failure) {
	lock();
	QString s;
	
	if (first_failure) {
		mainWindow->displayHeader();
		s = qApp->translate("GUI", "%1, registration failed: STUN failure")
		    .arg(user_config->get_profile_name().c_str());
		mainWindow->display(s);
	}
	
	mainWindow->updateRegStatus();
	unlock();
}

void t_gui::cb_deregister_success(t_user *user_config, const t_response *r) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "%1, de-registration succeeded: %2 %3")
	    .arg(user_config->get_profile_name().c_str())
	    .arg(r->code)
	    .arg(r->reason.c_str());
	mainWindow->display(s);
	
	mainWindow->updateRegStatus();
	unlock();
}

void t_gui::cb_deregister_failed(t_user *user_config, const t_response *r) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "%1, de-registration failed: %2 %3")
	    .arg(user_config->get_profile_name().c_str())
	    .arg(r->code)
	    .arg(r->reason.c_str());
	mainWindow->display(s);
	
	mainWindow->updateRegStatus();
	unlock();
}

void t_gui::cb_fetch_reg_failed(t_user *user_config, const t_response *r) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "%1, fetching registrations failed: %2 %3")
	    .arg(user_config->get_profile_name().c_str())
	    .arg(r->code)
	    .arg(r->reason.c_str());
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_fetch_reg_result(t_user *user_config, const t_response *r) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	
	s = user_config->get_profile_name().c_str();
	const list<t_contact_param> &l = r->hdr_contact.contact_list;
	if (l.size() == 0) {
		s += qApp->translate("GUI", ": you are not registered");
		mainWindow->display(s);
	} else {
		s += qApp->translate("GUI", ": you have the following registrations");
		mainWindow->display(s);
		for (list<t_contact_param>::const_iterator i = l.begin();
		i != l.end(); i++)
		{
			mainWindow->display(i->encode().c_str());
		}
	}
	
	unlock();
}

void t_gui::cb_register_inprog(t_user *user_config, t_register_type register_type) {
	QString s;
	
	lock();
	
	switch(register_type) {
	case REG_REGISTER:
		// Do not report registration refreshments
		if (phone->get_is_registered(user_config)) break;
		mainWindow->statRegLabel->setPixmap(
				QPixmap::fromMimeSource("gear.png"));
		break;
	case REG_DEREGISTER:
	case REG_DEREGISTER_ALL:
		mainWindow->statRegLabel->setPixmap(
				QPixmap::fromMimeSource("gear.png"));
		break;
	case REG_QUERY:
		mainWindow->displayHeader();
		s = user_config->get_profile_name().c_str();
		s += qApp->translate("GUI", ": fetching registrations...");
		mainWindow->display(s);
		break;
	}
	
	unlock();
}

void t_gui::cb_redirecting_request(t_user *user_config, int line, const t_contact_param &contact) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: redirecting request to").arg(line + 1);
	mainWindow->display(s);
	
	s = format_sip_address(user_config, contact.display, contact.uri).c_str();
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_redirecting_request(t_user *user_config, const t_contact_param &contact) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Redirecting request to: %1").arg(
			format_sip_address(user_config, contact.display, contact.uri).c_str());
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_notify_call(int line, const QString &from_party, const QString &organization,
			   const QImage &photo, const QString &subject)
{
	if (line >= NUM_USER_LINES) return;
	
	lock();
	
	// Play ringtone if the call is received on the active line.
	if (line == phone->get_active_line() &&
	    !phone->is_line_auto_answered(line))
	{
		cb_play_ringtone(line);
	}
	
	// Pop up sys tray balloon
#ifdef HAVE_KDE
	t_twinkle_sys_tray *tray = mainWindow->getSysTray();
	if (tray && !sys_tray_popup &&  !phone->is_line_auto_answered(line)) {
		QString presFromParty("");
		if (!from_party.isEmpty()) {
			presFromParty = dotted_truncate(from_party.ascii(), 40).c_str();
		}
		QString presOrganization("");
		if (!organization.isEmpty()) {
			presOrganization = dotted_truncate(organization.ascii(), 40).c_str();
		}
		QString presSubject("");
		if (!subject.isEmpty()) {
			presSubject = dotted_truncate(subject.ascii(), 40).c_str();
		}
		
		// Create photo pixmap. If no photo is available, then use
		// the Twinkle icon.
		QPixmap pm;
		QFrame::Shape photoFrameShape = QFrame::NoFrame;
		if (photo.isNull()) {
			pm = QPixmap::fromMimeSource("twinkle32.png");
		} else {
			pm.convertFromImage(photo);
			photoFrameShape = QFrame::Box;
		}
		
		// Create the popup view.
		sys_tray_popup = new KPassivePopup(tray);
		MEMMAN_NEW(sys_tray_popup);
		sys_tray_popup->setAutoDelete(false);
		sys_tray_popup->setTimeout(0);
		QVBox *popup_view = new QVBox(sys_tray_popup);
		QHBox *hb = new QHBox(popup_view);
		hb->setSpacing(5);
		QLabel *lblPhoto = new QLabel(hb);
		lblPhoto->setPixmap(pm);
		lblPhoto->setFrameShape(photoFrameShape);
		QVBox *vb = new QVBox(hb);
		QLabel *lblCaption = new QLabel("<H2>Incoming Call</H2>", vb);
		lblCaption->setAlignment(Qt::AlignTop | Qt::AlignLeft);
		lblCaption->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
		QLabel *lblFrom = new QLabel(presFromParty, vb);
		lblFrom->setAlignment(Qt::AlignTop | Qt::AlignLeft);
		lblFrom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
		QLabel *lastLabel = lblFrom;
		if (!presOrganization.isEmpty()) {
			QLabel *lblOrganization = new QLabel(presOrganization, vb);
			lblOrganization->setAlignment(Qt::AlignTop | Qt::AlignLeft);
			lblOrganization->setSizePolicy(QSizePolicy::Expanding, 
						       QSizePolicy::Minimum);
			lastLabel = lblOrganization;
		}
		if (!presSubject.isEmpty()) {
			QLabel *lblSubject = new QLabel(presSubject, vb);
			lblSubject->setAlignment(Qt::AlignTop | Qt::AlignLeft);
			lblSubject->setSizePolicy(QSizePolicy::Expanding, 
						       QSizePolicy::Minimum);
			lastLabel = lblSubject;
		}
		lastLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
		
		// Answer and reject buttons
		
		QHBox *buttonBox = new QHBox(vb);
		QIconSet iconAnswer(QPixmap::fromMimeSource("answer.png"));
		QPushButton *pbAnswer = new QPushButton(iconAnswer, 
			qApp->translate("SysTrayPopup", "Answer"), buttonBox);
		QObject::connect(pbAnswer, SIGNAL(clicked()), 
				 mainWindow, SLOT(phoneAnswerFromSystrayPopup()));
		QIconSet iconReject(QPixmap::fromMimeSource("reject.png"));
		QPushButton *pbReject = new QPushButton(iconReject, 
			qApp->translate("SysTrayPopup", "Reject"), buttonBox);
		QObject::connect(pbReject, SIGNAL(clicked()), 
				 mainWindow, SLOT(phoneRejectFromSystrayPopup()));
		
		sys_tray_popup->setView(popup_view);
		
		// Show the popup
		line_sys_tray_popup = line;
		sys_tray_popup->show();
		QObject::connect(sys_tray_popup, SIGNAL(clicked()),
			sys_tray_popup, SLOT(hide()));
	}
#endif
	
	// Show main window after a few seconds
	if (sys_config->get_gui_auto_show_incoming()) {
		autoShowTimer[line].start(
			sys_config->get_gui_auto_show_timeout() * 1000, true);
	}
	
	unlock();
}

void t_gui::cb_stop_call_notification(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	cb_stop_tone(line);
	autoShowTimer[line].stop();
	
#ifdef HAVE_KDE
	if (sys_tray_popup && line_sys_tray_popup == line) {
		sys_tray_popup->hide();
		MEMMAN_DELETE(sys_tray_popup);
		delete sys_tray_popup;
		sys_tray_popup = NULL;
	}
#endif
	unlock();
}

void t_gui::cb_dtmf_detected(int line, char dtmf_event) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: DTMF detected:").arg(line + 1).append(" ");
	
	if (VALID_DTMF_EV(dtmf_event)) {
		s.append(dtmf_ev2char(dtmf_event));
	} else {
		s.append(qApp->translate("GUI", "invalid DTMF telephone event (%1)").arg(
				(int)dtmf_event));
	}
	
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_send_dtmf(int line, char dtmf_event) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	if (!VALID_DTMF_EV(dtmf_event)) return;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: send DTMF %2").arg(line + 1).arg(
			dtmf_ev2char(dtmf_event));
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_dtmf_not_supported(int line) {
	if (line >= NUM_USER_LINES) return;
	
	QString s;
	
	if (throttle_dtmf_not_supported) return;
	
	lock();
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: far end does not support DTMF telephone events.").arg(line + 1);
	mainWindow->display(s);
	
	// Throttle subsequent call backs
	throttle_dtmf_not_supported = true;
	
	unlock();
}

void t_gui::cb_dtmf_supported(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	mainWindow->updateState();
	unlock();
}

void t_gui::cb_line_state_changed(void) {
	lock();
	mainWindow->updateState();
	unlock();
}

void t_gui::cb_send_codec_changed(int line, t_audio_codec codec) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	displayCodecInfo(line);
	unlock();
}

void t_gui::cb_recv_codec_changed(int line, t_audio_codec codec) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	displayCodecInfo(line);
	unlock();
}

void t_gui::cb_notify_recvd(int line, const t_request *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: received notification.").arg(line+1);
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "Event: %1").arg(r->hdr_event.event_type.c_str());
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "State: %1").arg(r->hdr_subscription_state.substate.c_str());
	mainWindow->display(s);
	
	if (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {
		s = qApp->translate("GUI", "Reason: %1").arg(r->hdr_subscription_state.reason.c_str());
		mainWindow->display(s);
	}
	
	t_response *sipfrag = (t_response *)((t_sip_body_sipfrag *)r->body)->sipfrag;
	s = qApp->translate("GUI", "Progress: %1 %2").arg(sipfrag->code).arg(sipfrag->reason.c_str());
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_refer_failed(int line, const t_response *r) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call transfer failed.").arg(line + 1);
	mainWindow->display(s);
	
	s = QString().setNum(r->code);
	s.append(' ').append(r->reason.c_str());
	mainWindow->display(s);
	
	// The refer state has changed, so update the main window.
	mainWindow->updateState();
	
	unlock();
}

void t_gui::cb_refer_result_success(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call succesfully transferred.").arg(line + 1);
	mainWindow->display(s);
	
	// The refer state has changed, so update the main window.
	mainWindow->updateState();
	
	unlock();
}

void t_gui::cb_refer_result_failed(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call transfer failed.").arg(line + 1);
	mainWindow->display(s);
	
	// The refer state has changed, so update the main window.
	mainWindow->updateState();
	
	unlock();
}

void t_gui::cb_refer_result_inprog(int line) {
	if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call transfer still in progress.").arg(line + 1);
	mainWindow->display(s);
	
	s = qApp->translate("GUI", "No further notifications will be received.");
	mainWindow->display(s);
	
	// The refer state has changed, so update the main window.
	mainWindow->updateState();
	
	unlock();
}

void t_gui::cb_call_referred(t_user *user_config, int line, t_request *r) {
	if (line >= NUM_USER_LINES) return;
	
	QString s;
	
	lock();
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: transferring call to %2").arg(line +1).arg(
			format_sip_address(user_config,
			r->hdr_refer_to.display, r->hdr_refer_to.uri).c_str());
	mainWindow->display(s);
	
	if (r->hdr_referred_by.is_populated()) {
		s = qApp->translate("GUI", "Transfer requested by %1").arg(
				format_sip_address(user_config,
					    r->hdr_referred_by.display, 
					    r->hdr_referred_by.uri).c_str());
		mainWindow->display(s);
	}
	
	setLineFields(line);
	s = format_sip_address(user_config, 
			       user_config->get_display(false),  user_config->create_user_uri(false)).c_str();
	displayFrom(s);
	photoLabel->hide();
	
	s = format_sip_address(user_config,
			       r->hdr_refer_to.display, r->hdr_refer_to.uri).c_str();
	displayTo(s);
	
	subjectLabel->clear();
	codecLabel->clear();
	
	unlock();
}

void t_gui::cb_retrieve_referrer(t_user *user_config, int line) {
	if (line >= NUM_USER_LINES) return;
	
	QString s;
	
	lock();
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: Call transfer failed. Retrieving original call.").arg(line + 1);
	mainWindow->display(s);
	
	setLineFields(line);
	const t_call_info call_info = phone->get_call_info(line);
	
	s = format_sip_address(user_config, call_info.get_from_display_presentation(), 
			       call_info.from_uri).c_str();
	if (!call_info.from_organization.empty()) {
		s += ", ";
		s += call_info.from_organization.c_str();
	}
	displayFrom(s);
	
	// Display photo
	QImage fromPhoto;
	
	if (sys_config->get_ab_lookup_photo()) {
		t_address_finder *af = t_address_finder::get_instance();
		fromPhoto = af->find_photo(user_config, call_info.from_uri);
	}
	
	displayPhoto(fromPhoto);	
	
	s = format_sip_address(user_config, call_info.to_display, call_info.to_uri).c_str();
	if (!call_info.to_organization.empty()) {
		s += ", ";
		s += call_info.to_organization.c_str();
	}
	displayTo(s);
	
	displaySubject(call_info.subject.c_str());
	codecLabel->clear();
	
	unlock();
}

void t_gui::cb_consultation_call_setup(t_user *user_config, int line) {
	if (line >= NUM_USER_LINES) return;
	
	QString s;
	
	lock();
	
	setLineFields(line);
	const t_call_info call_info = phone->get_call_info(line);
	
	s = format_sip_address(user_config, call_info.get_from_display_presentation(), 
			       call_info.from_uri).c_str();
	if (!call_info.from_organization.empty()) {
		s += ", ";
		s += call_info.from_organization.c_str();
	}
	displayFrom(s);
	
	photoLabel->hide();
	
	s = format_sip_address(user_config, call_info.to_display, call_info.to_uri).c_str();
	if (!call_info.to_organization.empty()) {
		s += ", ";
		s += call_info.to_organization.c_str();
	}
	displayTo(s);
	
	displaySubject(call_info.subject.c_str());
	codecLabel->clear();
	
	unlock();
}

void t_gui::cb_stun_failed(t_user *user_config, int err_code, const string &err_reason) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "%1, STUN request failed: %2 %3")
	    .arg(user_config->get_profile_name().c_str())
	    .arg(err_code).arg(err_reason.c_str());
	mainWindow->display(s);

	unlock();
}

void t_gui::cb_stun_failed(t_user *user_config) {
	lock();
	QString s;
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "%1, STUN request failed.")
	    .arg(user_config->get_profile_name().c_str());
	mainWindow->display(s);

	unlock();
}

bool t_gui::cb_ask_user_to_redirect_invite(t_user *user_config, const t_url &destination,
					   const string &display)
{
	QString s;
	QString title;
	
	lock();
	
	title = PRODUCT_NAME;
	title.append(" - ").append(qApp->translate("GUI", "Redirecting call"));
	
	s = qApp->translate("GUI", "User profile:").append(" <b>");
	s.append(user_config->get_profile_name().c_str());
	s.append("</b><br>").append(qApp->translate("GUI", "User:")).append(" <b>");
	s.append(str2html(user_config->get_display_uri().c_str()));
	s.append("</b><br><br>");
	
	s.append(qApp->translate("GUI", "Do you allow the call to be redirected to the following destination?"));
	s.append("<br><br>");
	s.append(str2html(ui->format_sip_address(user_config, display, destination).c_str()));
	s.append("<br><br>");
	s.append(qApp->translate("GUI", 
		"If you don't want to be asked this anymore, then you must change "
		"the settings in the SIP protocol section of the user profile."));
	QMessageBox *mb = new QMessageBox(title, s,
					  QMessageBox::Warning,
					  QMessageBox::Yes,
					  QMessageBox::No,
					  QMessageBox::NoButton,
					  mainWindow);
	MEMMAN_NEW(mb);
	bool permission = (mb->exec() == QMessageBox::Yes);
	MEMMAN_DELETE(mb);
	delete mb;
	
	unlock();
	
	return permission;
}

bool t_gui::cb_ask_user_to_redirect_request(t_user *user_config,
					    const t_url &destination,
					    const string &display, t_method method)
{
	QString s;
	QString title;
	
	lock();
	
	title = PRODUCT_NAME;
	title.append(" - ").append(qApp->translate("GUI", "Redirecting request"));
	
	s = qApp->translate("GUI", "User profile:").append(" <b>");
	s.append(user_config->get_profile_name().c_str());
	s.append("</b><br>").append(qApp->translate("GUI", "User:")).append(" <b>");
	s.append(str2html(user_config->get_display_uri().c_str()));
	s.append("</b><br><br>");
	
	s.append(qApp->translate("GUI", 
		"Do you allow the %1 request to be redirected to the following destination?").arg(
		method2str(method).c_str()));
	s.append("<br><br>");
	s.append(str2html(ui->format_sip_address(user_config, display, destination).c_str()));
	s.append("<br><br>");
	s.append(qApp->translate("GUI",
		"If you don't want to be asked this anymore, then you must change "
		"the settings in the SIP protocol section of the user profile."));
	QMessageBox *mb = new QMessageBox(title, s,
					  QMessageBox::Warning,
					  QMessageBox::Yes,
					  QMessageBox::No,
					  QMessageBox::NoButton,
					  mainWindow);
	MEMMAN_NEW(mb);
	bool permission = (mb->exec() == QMessageBox::Yes);
	MEMMAN_DELETE(mb);
	delete mb;
	
	unlock();
	
	return permission;
}

bool t_gui::cb_ask_credentials(t_user *user_config, const string &realm, string &username,
			       string &password)
{
	QString user(username.c_str());
	QString passwd(password.c_str());
	
	lock();
	
	AuthenticationForm *af = new AuthenticationForm(mainWindow, "authentication",
							true);
	MEMMAN_NEW(af);
	if (!af->exec(user_config, QString(realm.c_str()), user, passwd)) {
		MEMMAN_DELETE(af);
		delete af;
		unlock();
		return false;
	}
	
	username = user.ascii();
	password = passwd.ascii();
	MEMMAN_DELETE(af);
	delete af;
	
	unlock();
	return true;
}

void t_gui::cb_ask_user_to_refer(t_user *user_config, const t_url &refer_to_uri,
				 const string &refer_to_display,
				 const t_url &referred_by_uri,
				 const string &referred_by_display)
{
	QString s;
	QString title;
	
	lock();
	
	title = PRODUCT_NAME;
	title.append(" - ").append(qApp->translate("GUI", "Transferring call"));
	
	s = qApp->translate("GUI", "User profile:").append(" <b>");
	s.append(user_config->get_profile_name().c_str());
	s.append("</b><br>").append(qApp->translate("GUI", "User:")).append(" <b>");
	s.append(str2html(user_config->get_display_uri().c_str()));
	s.append("</b><br><br>");
	
	if (referred_by_uri.is_valid()) {
		s.append(qApp->translate("GUI","Request to transfer call received from:"));
		s.append("<br>");
		s.append(str2html(format_sip_address(user_config, referred_by_display,
					    referred_by_uri).c_str()));
		s.append("<br>");
	} else {
		s.append(qApp->translate("GUI", "Request to transfer call received."));
		s.append("<br>");
	}
	s.append("<br>");
	
	s.append(qApp->translate("GUI",
		"Do you allow the call to be transferred to the following destination?"));
	s.append("<br><br>");
	s.append(str2html(ui->format_sip_address(user_config, refer_to_display, 
					refer_to_uri).c_str()));
	s.append("<br><br>");
	s.append(qApp->translate("GUI",
		"If you don't want to be asked this anymore, then you must change "
		"the settings in the SIP protocol section of the user profile."));
	
	ReferPermissionDialog *dialog = new ReferPermissionDialog(mainWindow, title, s);
	// Do not report to MEMMAN as Qt will auto destruct this dialog on close.
	dialog->show();
	
	unlock();
}

void t_gui::cb_show_msg(const string &msg, t_msg_priority prio) {
	cb_show_msg(NULL, msg, prio);
}

void t_gui::cb_show_msg(QWidget *parent, const string &msg, t_msg_priority prio) {
	lock();
	
	switch (prio) {
	case MSG_INFO:
		QMessageBox::information(parent, PRODUCT_NAME, msg.c_str());
		break;
	case MSG_WARNING:
		QMessageBox::warning(parent, PRODUCT_NAME, msg.c_str());
		break;
	case MSG_CRITICAL:
	default:
		QMessageBox::critical(parent, PRODUCT_NAME, msg.c_str());
		break;
	}
	
	unlock();
}

bool t_gui::cb_ask_msg(const string &msg, t_msg_priority prio) {
	return cb_ask_msg(NULL, msg, prio);
}

bool t_gui::cb_ask_msg(QWidget *parent, const string &msg, t_msg_priority prio) {
	lock();
	
	int button = QMessageBox::No;
	switch (prio) {
	case MSG_INFO:
		button = QMessageBox::information(parent, PRODUCT_NAME, msg.c_str(),
			QMessageBox::Yes,
			QMessageBox::No | QMessageBox::Escape | QMessageBox::Default);
		break;
	case MSG_WARNING:
		button = QMessageBox::warning(parent, PRODUCT_NAME, msg.c_str(),
			QMessageBox::Yes,
			QMessageBox::No | QMessageBox::Escape | QMessageBox::Default);
		break;
	case MSG_CRITICAL:
	default:
		button = QMessageBox::critical(parent, PRODUCT_NAME, msg.c_str(),
			QMessageBox::Yes,
			QMessageBox::No | QMessageBox::Escape | QMessageBox::Default);
		break;
	}
	
	unlock();
	
	return (button == QMessageBox::Yes);
}

void t_gui::cb_display_msg(const string &msg, t_msg_priority prio) {
	QString s;
	
	lock();
	
	switch (prio) {
	case MSG_NO_PRIO:
		break;
	case MSG_INFO:
		s = qApp->translate("GUI", "Info:");
		break;
	case MSG_WARNING:
		s = qApp->translate("GUI", "Warning:");
		break;
	case MSG_CRITICAL:
	default:
		s = qApp->translate("GUI", "Critical:");
		break;
	}	
	
	if (prio == MSG_NO_PRIO) {
		s = msg.c_str();
	} else {
		s.append(" ").append(msg.c_str());
	}
	mainWindow->displayHeader();
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_log_updated(bool log_zapped) {
	lock();
	mainWindow->updateLog(log_zapped);
	unlock();
}

void t_gui::cb_call_history_updated(void) {
	lock();
	mainWindow->updateCallHistory();
	unlock();
}

void  t_gui::cb_missed_call(int num_missed_calls) {
	lock();
	mainWindow->updateMissedCallStatus(num_missed_calls);
	unlock();
}

void t_gui::cb_nat_discovery_progress_start(int num_steps) {
	natDiscoveryProgressDialog = new QProgressDialog(
			qApp->translate("GUI", "Firewall / NAT discovery..."), 
			qApp->translate("GUI", "Abort"), 
			num_steps, NULL,
			"nat discovery progress", true);
	MEMMAN_NEW(natDiscoveryProgressDialog);
	natDiscoveryProgressDialog->setCaption(PRODUCT_NAME);
	natDiscoveryProgressDialog->setMinimumDuration(200);
}

void t_gui::cb_nat_discovery_progress_step(int step) {
	natDiscoveryProgressDialog->setProgress(step);
	qApp->processEvents();
}

void t_gui::cb_nat_discovery_finished(void) {
	MEMMAN_DELETE(natDiscoveryProgressDialog);
	delete natDiscoveryProgressDialog;
}

bool t_gui::cb_nat_discovery_cancelled(void) {
	return natDiscoveryProgressDialog->wasCancelled();
}

void t_gui::cb_line_encrypted(int line, bool encrypted, const string &cipher_mode) {
	// Nothing todo in GUI
	// Encryption state is shown by the line state updata methods on
	// MphoneForm
}

void t_gui::cb_show_zrtp_sas(int line, const string &sas) {
		if (line >= NUM_USER_LINES) return;
	
	lock();
	QString s;
	
	setLineFields(line);
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1").arg(line + 1);
	s.append(": SAS = ").append(sas.c_str());
	mainWindow->display(s);
	s = qApp->translate("GUI", "Click the padlock to confirm a correct SAS.");
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_zrtp_confirm_go_clear(int line) {
	t_user *user_config = phone->get_line_user(line);
	if (!user_config) return;
	
	QString msg(qApp->translate("GUI", "The remote user on line %1 disabled the encryption.")
			.arg(line + 1));
	if (user_config->get_zrtp_goclear_warning()) {
		cb_show_msg(msg.ascii(), MSG_WARNING);
	} else {
		cb_display_msg(msg.ascii(), MSG_WARNING);
	}
	
	action_zrtp_go_clear_ok(line);
}

void t_gui::cb_zrtp_sas_confirmed(int line) {
	lock();
	QString s;
	
	setLineFields(line);
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: SAS confirmed.").arg(line + 1);
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_zrtp_sas_confirmation_reset(int line) {
	lock();
	QString s;
	
	setLineFields(line);
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: SAS confirmation reset.").arg(line + 1);
	mainWindow->display(s);
	
	unlock();
}

void t_gui::cb_update_mwi(void) {
	lock();
	mainWindow->updateMwi();
	unlock();
}

void t_gui::cb_mwi_subscribe_failed(t_user *user_config, t_response *r, bool first_failure) {
	lock();
	QString s;
	
	if (first_failure) {
		mainWindow->displayHeader();
		s = qApp->translate("GUI", "%1, voice mail status failure.")
		    .arg(user_config->get_profile_name().c_str());
		mainWindow->display(s);
	}
	
	unlock();
}

void t_gui::cb_mwi_terminated(t_user *user_config, const string &reason) {
	lock();
	QString s;
	
	if (reason == "EV_REASON_REJECTED") {
		s = qApp->translate("GUI", "%1, voice mail status rejected.");
	} else if (reason == "EV_REASON_NORESOURCE") {
		s = qApp->translate("GUI", "%1, voice mailbox does not exist.");
	} else {
		s = qApp->translate("GUI", "%1, voice mail status terminated.");
	}

	mainWindow->displayHeader();
	mainWindow->display(s.arg(user_config->get_profile_name().c_str()));

	unlock();
}

bool t_gui::cb_message_request(t_user *user_config, t_request *r) {	
	string text;
	im::t_text_format text_format;
	
	if (r->body && r->body->get_type() == BODY_PLAIN_TEXT) {
		t_sip_body_plain_text *sb = dynamic_cast<t_sip_body_plain_text *>(r->body);
		text = sb->text;
		text_format = im::TXT_PLAIN;
	} else if (r->body && r->body->get_type() == BODY_HTML_TEXT) {
		t_sip_body_html_text *sb = dynamic_cast<t_sip_body_html_text *>(r->body);
		text = sb->text;
		text_format = im::TXT_HTML;
	} else {
		log_file->write_header("t_gui::cb_message_request",
			LOG_NORMAL, LOG_CRITICAL);
		log_file->write_raw("Unsupported content type: ");
		log_file->write_raw(r->body->get_type());
		log_file->write_endl();
		log_file->write_footer();
		return true;
	}
	
	string charset = r->hdr_content_type.media.charset;
	if (!charset.empty() && cmp_nocase(charset, "utf-8") != 0) {
		// Try to decode the text
		QTextCodec *c = QTextCodec::codecForName(charset.c_str());
		if (c) {
			text = c->toUnicode(text.c_str());
		} else {
			log_file->write_header(
					"t_gui::cb_message_request",
					LOG_NORMAL, LOG_WARNING);
			log_file->write_raw("Cannot decode charset: ");
			log_file->write_raw(charset);
			log_file->write_endl();
			log_file->write_footer();
		}
	}
	
	lock();
	
	// Find an existing session
	im::t_msg_session *session = getMessageSession(user_config, r->hdr_from.uri,
				r->hdr_from.get_display_presentation());
	if (!session) {
		// There is no session yet.
		if (messageSessions.size() >= user_config->get_im_max_sessions()) {
			log_file->write_report(
				"Maximum number of message sessions reached. Reject message",
				"t_gui::cb_message_request");
			unlock();
			return false;
		}
		
		// Create a new session.
		session = new im::t_msg_session(user_config, t_display_url(r->hdr_from.uri, 
				r->hdr_from.get_display_presentation()));
		MEMMAN_NEW(session);
		addMessageSession(session);
		MessageFormView *view = new MessageFormView(NULL, session);
		MEMMAN_NEW(view);
		view->show();
		view->raise();
	}
	
	session->recv_msg(text, text_format);
	
	unlock();
	return true;
}

void t_gui::cb_message_response(t_user *user_config, t_response *r) {
	// Only report failure responses to the user
	if (r->is_success()) return;
	
	lock();
	
	// Find session associated with the response
	im::t_msg_session *session = getMessageSession(user_config, r->hdr_to.uri,
				r->hdr_from.get_display_presentation());
	
	if (session) {
		string s = int2str(r->code);
		s += ' ';
		s += r->reason;
		session->set_error(s);
	}
	// If there is no session anymore, then discard the response
	
	unlock();
}

void t_gui::cmd_call(const string &destination, bool immediate) {
	string subject;
	string dst_no_headers;
	t_display_url du;
	
	t_user *user = phone->ref_user_profile(
			mainWindow->userComboBox->currentText().ascii());
	expand_destination(user, destination, du, subject, dst_no_headers);
	if (!du.is_valid()) return;
	
	lock();
	if (immediate) {
		mainWindow->do_phoneInvite(user, du.display.c_str(), du.url, 
					   subject.c_str(), false);
	} else {
		mainWindow->phoneInvite(dst_no_headers.c_str(), subject.c_str(), false);
	}
	unlock();
}

void t_gui::cmd_quit(void) {
	lock();
	mainWindow->fileExit();
	unlock();
}

void t_gui::cmd_show(void) {
	lock();
	if (mainWindow->isMinimized()) {
		mainWindow->setWindowState(mainWindow->windowState() & ~Qt::WindowMinimized | Qt::WindowActive);
		mainWindow->raise();
	} else {
		mainWindow->show();
		mainWindow->raise();
		mainWindow->setActiveWindow();
	}
	unlock();
}

void t_gui::cmd_hide(void) {
	lock();
	if (sys_config->get_gui_use_systray()) {
		mainWindow->hide();
	} else {
		mainWindow->setWindowState(mainWindow->windowState() | Qt::WindowMinimized);
	}
	unlock();
}

string t_gui::get_name_from_abook(t_user *user_config, const t_url &u) {
	string name;
	
	lock();
	// Search local address book first
	name = t_userintf::get_name_from_abook(user_config, u);
	
	// Search KAddressBook
	if (name.empty()) {
		t_address_finder *af = t_address_finder::get_instance();
		name = af->find_name(user_config, u);
	}
	unlock();
	
	return name;
}

// User invoked actions on the phone object

void t_gui::action_register(list<t_user *> user_list) {
	for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
		phone->pub_registration(*i, REG_REGISTER, 
			DUR_REGISTRATION(*i));
	}
}

void t_gui::action_deregister(list<t_user *> user_list, bool dereg_all) {
	for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
		if (dereg_all) {
			phone->pub_registration(*i, REG_DEREGISTER_ALL);
		} else {
			phone->pub_registration(*i, REG_DEREGISTER);
		}
	}
}

void t_gui::action_show_registrations(list<t_user *> user_list) {
	for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
		phone->pub_registration(*i, REG_QUERY);
	}
}

void t_gui::action_invite(t_user *user_config, const t_url &destination,
			  const string &display, const string &subject, bool anonymous) 
{
	QString s;
	
	// Call can only be made if line is idle
	int line = phone->get_active_line();
	if (phone->get_line_state(line) == LS_BUSY) return;
	
	t_url vm_url(expand_destination(user_config, user_config->get_mwi_vm_address()));
	if (destination != vm_url) {
		// Store call info for redial
		last_called_url = destination;
		last_called_display = display;
		last_called_subject = subject;
		last_called_profile = user_config->get_profile_name();
		last_called_hide_user = anonymous;
	}
	
	setLineFields(line);
	
	// Set party and subject line fields
	s = "";
	s.append(format_sip_address(user_config, display, destination).c_str());
	displayTo(s);
	
	s = "";
	s.append(format_sip_address(user_config, user_config->get_display(false), 
				    user_config->create_user_uri(false)).c_str());
	displayFrom(s);
	
	displaySubject(subject.c_str());
	
	phone->pub_invite(user_config, destination, display, subject.c_str(), anonymous);
}

void t_gui::action_answer(void) {
	cb_stop_call_notification(phone->get_active_line());
	phone->pub_answer();
}

void t_gui::action_bye(void) {
	phone->pub_end_call();
}

void t_gui::action_reject(void) {
	QString s;
	
	cb_stop_call_notification(phone->get_active_line());
	phone->pub_reject();
	
	int line = phone->get_active_line();
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call rejected.").arg(line + 1);
	mainWindow->display(s);
}

void t_gui::action_reject(unsigned short line) {
	QString s;
	
	cb_stop_call_notification(line);
	phone->pub_reject(line);
	
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call rejected.").arg(line + 1);
	mainWindow->display(s);
}

void t_gui::action_redirect(const list<t_display_url> &contacts) {
	QString s;
	
	cb_stop_call_notification(phone->get_active_line());
	phone->pub_redirect(contacts, 302);
	
	int line = phone->get_active_line();
	mainWindow->displayHeader();
	s = qApp->translate("GUI", "Line %1: call redirected.");
	mainWindow->display(s);
}

void t_gui::action_refer(const t_url &destination, const string &display) {
	phone->pub_refer(destination, display);
}

void t_gui::action_refer(unsigned short line_from, unsigned short line_to) {
	phone->pub_refer(line_from, line_to);
}

void t_gui::action_setup_consultation_call(const t_url &destination, const string &display) {
	phone->pub_setup_consultation_call(destination, display);
}

void t_gui::action_hold(void) {
	phone->pub_hold();
}

void t_gui::action_retrieve(void) {
	phone->pub_retrieve();
}

void t_gui::action_conference(void) {
	if (!phone->join_3way(0, 1)) {
		mainWindow->display(qApp->translate("GUI", "Failed to start conference."));
	}
}

void t_gui::action_mute(bool on) {
	phone->mute(on);
}

void t_gui::action_options(void) {
	phone->pub_options();
}

void t_gui::action_options(t_user *user_config, const t_url &contact) {
	phone->pub_options(user_config, contact);
}

void t_gui::action_dtmf(const string &digits) {
	const t_call_info call_info = phone->get_call_info(phone->get_active_line());
	throttle_dtmf_not_supported = false;
	
	if (!call_info.dtmf_supported) return;
	
	for (string::const_iterator i = digits.begin(); i != digits.end(); i++) {
		if (VALID_DTMF_SYM(*i)) {
			phone->pub_send_dtmf(*i, call_info.dtmf_inband, call_info.dtmf_info);
		}
	}
}

void t_gui::action_activate_line(unsigned short line) {
	phone->pub_activate_line(line);
}

bool t_gui::action_seize(void) {
	return phone->pub_seize();
}

void t_gui::action_unseize(void) {
	phone->pub_unseize();
}

void t_gui::action_confirm_zrtp_sas(int line) {
	phone->pub_confirm_zrtp_sas(line);
}

void t_gui::action_confirm_zrtp_sas() {
	phone->pub_confirm_zrtp_sas();
}

void t_gui::action_reset_zrtp_sas_confirmation(int line) {
	phone->pub_reset_zrtp_sas_confirmation(line);
}

void t_gui::action_reset_zrtp_sas_confirmation() {
	phone->pub_reset_zrtp_sas_confirmation();
}

void  t_gui::action_enable_zrtp(void) {
	phone->pub_enable_zrtp();
}

void  t_gui::action_zrtp_request_go_clear(void) {
	phone->pub_zrtp_request_go_clear();
}

void  t_gui::action_zrtp_go_clear_ok(unsigned short line) {
	phone->pub_zrtp_go_clear_ok(line);
}

void t_gui::srv_dnd(list<t_user *> user_list, bool on) {
	for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
		if (on) {
			phone->ref_service(*i)->enable_dnd();
		} else {
			phone->ref_service(*i)->disable_dnd();
		}
	}
}

void t_gui::srv_enable_cf(t_user *user_config,
		t_cf_type cf_type, const list<t_display_url> &cf_dest) 
{
	phone->ref_service(user_config)->enable_cf(cf_type, cf_dest);
}

void t_gui::srv_disable_cf(t_user *user_config, t_cf_type cf_type) {
	phone->ref_service(user_config)->disable_cf(cf_type);
}

void t_gui::srv_auto_answer(list<t_user *> user_list, bool on) {
	for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
		phone->ref_service(*i)->enable_auto_answer(on);
	}
}

void t_gui::fill_user_combo(QComboBox *cb) {
	cb->clear();
	
	list<t_user *> user_list = phone->ref_users();
	for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
		// OLD code: used display uri
		// cb->insertItem((*i)->get_display_uri().c_str());
		cb->insertItem((*i)->get_profile_name().c_str());
	}
	
	cb->setCurrentItem(0);
}

QString t_gui::get_last_file_browse_path(void) const {
	return lastFileBrowsePath;
}

void t_gui::set_last_file_browse_path(QString path) {
	lock();
	lastFileBrowsePath = path;
	unlock();
}

#ifdef HAVE_KDE
unsigned short t_gui::get_line_sys_tray_popup(void) const {
	return line_sys_tray_popup;
}
#endif

im::t_msg_session *t_gui::getMessageSession(t_user *user_config, 
			const t_url &remote_url, const string &display) const 
{
	for (list<im::t_msg_session *>::const_iterator it = messageSessions.begin();
	it != messageSessions.end(); ++it)
	{
		if ((*it)->match(user_config, remote_url)) {
			(*it)->set_display_if_empty(display);
			return *it;
		}
	}
	
	return NULL;
}
	
void t_gui::addMessageSession(im::t_msg_session *s) {
	messageSessions.push_back(s);
}

void t_gui::removeMessageSession(im::t_msg_session *s) {
	messageSessions.remove(s);
}

void t_gui::destroyAllMessageSessions(void) {
	for (list<im::t_msg_session *>::iterator it = messageSessions.begin();
	it != messageSessions.end(); ++it)
	{
		MEMMAN_DELETE(*it);
		delete *it;
	}
	
	messageSessions.clear();
}


syntax highlighted by Code2HTML, v. 0.9.1