#!/usr/bin/env python

import copy
import sys
import math
import dbus
import os
import subprocess
import ConfigParser
from dbus.mainloop.glib import DBusGMainLoop

import pygtk
pygtk.require("2.0")

import gtk
import gtk.glade
import gobject
import pango

import gettext
_ = gettext.gettext

class GtkAlerter:
	def __getitem__(self, key):
		return self.widgets.get_widget(key)
	def __init__(self, gladefile, ag):
		self.widgets = gtk.glade.XML(gladefile)
		dic = {
			"CallAlertAccept_clicked_cb" :
			self.action_accept_clicked,
			"CallAlertAcceptHold_clicked_cb" :
			self.action_accept_hold_clicked,
			"CallAlertAcceptDrop_clicked_cb" :
			self.action_accept_drop_clicked,
			"CallAlertReject_clicked_cb" :
			self.action_reject_clicked,
			"CallAlertBusy_clicked_cb" :
			self.action_busy_clicked,
			"CallAlertIgnore_clicked_cb" :
			self.action_ignore_clicked,
			}
		self.widgets.signal_autoconnect(dic)
		self.ag = ag
		self.completed = False
		self.removed = False

		if ag.can_drop_waiting():
			self['CallAlertReject'].show()
		else:
			self['CallAlertReject'].hide()
		# The UDUB feature seems to be implemented very
		# inconsistently across phones and handset makers.
		# Presenting it to the user is almost silly.
		if False and ag.can_drop_held_udub():
			self['CallAlertBusy'].show()
		else:
			self['CallAlertBusy'].hide()
		self['CallAlertIgnore'].show()
		self.update_state()
		self['CallAlertDialog'].show_all()

	def update_state(self):
		callstate = self.ag.callstate
		(callid, callphbook) = self.ag.waiting_caller_info()
		if callid:
			if callphbook:
				self['CallAlertInfoLabel'].set_text(
					"From: %s (%s)" % (callphbook, callid))
			else:
				self['CallAlertInfoLabel'].set_text(
					"From: %s" % callid)
		else:
			self['CallAlertInfoLabel'].set_text('')
		if callstate == 5:
			self['CallAlertAccept'].hide()
		else:
			self['CallAlertAcceptHold'].hide()
			self['CallAlertAcceptDrop'].hide()
			self['CallAlertReject'].hide()
		if callstate == 5:
			self['CallAlertAcceptHold'].show()
			self['CallAlertAcceptDrop'].show()
		else:
			self['CallAlertAccept'].show()
			if self.ag.can_drop_waiting():
				self['CallAlertReject'].show()

	def remove(self):
		self.complete()
		del self.widgets
		del self.ag
		self.removed = True
	def complete(self):
		if not self.completed:
			self.completed = True
			self['CallAlertDialog'].hide()
	def action_accept_clicked(self, widget):
		if not self.removed:
			self.complete()
			self.ag.answer()
	def action_accept_hold_clicked(self, widget):
		if not self.removed:
			self.complete()
			self.ag.swap_hold_active()
	def action_accept_drop_clicked(self, widget):
		if not self.removed:
			self.complete()
			self.ag.swap_drop_active()
	def action_reject_clicked(self, widget):
		if not self.removed:
			self.complete()
			self.ag.hangup()
	def action_busy_clicked(self, widget):
		if not self.removed:
			self.complete()
			self.ag.drop_held_udub()
	def action_ignore_clicked(self, widget):
		if not self.removed:
			self.complete()

# Telephone number processing is a serious pain in the rear.
# In order to be able to compare telephone numbers, we must convert them
# to a normalized form, which will be international form.
class TelNormalizer:
	"""A hacky telephone number normalizer"""
	def __init__(self):
		pass
	def _isnum(self, c):
		x = ord(c)
		return (x >= ord('0') and x <= ord('9'))
	def _isalpha(self, c):
		x = ord(c)
		return ((x >= ord('a') and x <= ord('z')) or
			(x >= ord('A') and x <= ord('Z')))
	def _alpha_convert(self, c):
		x = ord(c)
		if x >= ord('a') and x <= ord('z'):
			c = c.upper()
			x = ord(c)
		if x >= ord('A') and x <= ord('C'):
			c = '2'
		elif x >= ord('D') and x <= ord('F'):
			c = '3'
		elif x >= ord('G') and x <= ord('I'):
			c = '4'
		elif x >= ord('J') and x <= ord('L'):
			c = '5'
		elif x >= ord('M') and x <= ord('O'):
			c = '6'
		elif x >= ord('P') and x <= ord('S'):
			c = '7'
		elif x >= ord('T') and x <= ord('V'):
			c = '8'
		elif x >= ord('W') and x <= ord('Z'):
			c = '9'
		return c
	def _simplify(self, tel):
		new = ''
		if not tel:
			return new
		if tel[0] == '+':
			new = '+'
			tel = tel[1:]
		for c in tel:
			if self._isnum(c) or c == '#' or c == '*':
				new = new + c
			elif self._isalpha(c):
				new = new + self._alpha_convert(c)
			elif (self._iswsp(c) or c == '-' or c == '.' or
			      c == '(' or c == ')' or c == '~' or c == '/'):
				continue
			else:
				print "Odd character \"%s\" in tel" % c
		return new
	def normalize(self, tel, teltype):
		return self._simplify(tel)
	def compare(self, tel1, tel2):
		# FIXME: this is a horrible hack!
		if ((tel1[0] == '+' and tel2[0] == '+') or
		    (tel1[0] != '+' and tel2[0] != '+')):
			return tel1 == tel2
		if tel2[0] == '+':
			tmp = tel1
			tel1 = tel2
			tel2 = tmp
		if len(tel1) < len(tel2):
			return False
		start = len(tel1) - len(tel2)
		return tel1[start:] == tel2


# This is really _really_ stupid
# If there was a vCard parser that was part of the standard
# Python distribution, it would be used here.  But there isn't
# and the job isn't very complicated.
class VCard:
	"""A hacky vCard representation"""
	def __init__(self, fillbasic):
		self.vtype = 'vcard'
		self.attribs = []
		if fillbasic:
			self.attribs.append(['version', None, None, '3.0'])
			self.attribs.append(['n', None, None, ';;;;'])
			self.attribs.append(['fn', None, None, ''])
	def findattrs(self, name):
		opts = []
		for x in self.attribs:
			if x[0] == name:
				opts.append(x)
		return opts
	def findtype(self, attrs, xtype):
		opts = []
		for x in attrs:
			if not xtype:
				opts.append(x)
			elif 'type' in x[2]:
				types = x[2]['type']
				for y in types:
					if y == xtype:
						opts.append(x)
		return opts
	def findtels(self, teltype):
		return self.findtype(self.findattrs('tel'), teltype)
	def getuid(self):
		opts = self.findattrs('uid')
		if opts:
			return opts[0][3]
		return None
	def getname(self):
		opts = self.findattrs('name')
		if not opts:
			opts = self.findattrs('fn')
		opt = opts[0]
		# fixme: decode the name!
		return opt[3]
	def setname(self, name):
		opts = self.findattrs('name')
		if not opts:
			opts = self.findattrs('fn')
		opt = opts[0]
		# fixme: encode the name!
		opt[3] = name
	def gettel(self, teltype):
		opts = self.findtels(teltype)
		if opts:
			return opts[0][3]
		return ''
	def settel(self, teltype, num):
		opts = self.findtels(teltype)
		if opts:
			opts[0][3] = num
		else:
			self.attribs.append(
				['tel', None, {'type': teltype}, num])
	def remove(self):
		if hasattr(self, '_container'):
			self._container.remove(self)
	def merge(self, object):
		# TODO: merge changes to attributes
		print "Merge entry %s" % self.getuid()
	def _parse_error(self, message):
		raise Exception('VCARD object: %s' % message)
	def _parse_case_insensitive_param(self, name):
		return (name == 'type' or
			name == 'charset' or
			name == 'encoding')
	def _parse_case_list(self, list):
		out = []
		for x in list:
			out.append(x.lower())
		return out
	def _parse_add_content(self, name, groups, params, value):
		self.attribs.append([name, groups, params, value])
		#print 'Name:%s groups:%s Value:%s' % (name, groups, value)
		for x in params:
			if self._parse_case_insensitive_param(x):
				params[x] = self._parse_case_list(params[x])
			#print '\tParam: %s Value:%s' % (x, params[x])
	def _parse_get_single(self, attr, reqd):
		al = self.findattrs(attr)
		v = None
		if not len(al):
			if not reqd:
				return None
			self._parse_error(_('Missing \"%s\" attribute') % attr)
		elif len(al) > 1:
			self._parse_error(_('Multiple \"%s\" attributes') %
					  attr)
		else:
			v = al[0][3]
		return v
	def _parse_end(self):
		v = self._parse_get_single('n', True)
		v = self._parse_get_single('fn', True)
		v = self._parse_get_single('version', True)
		if v != '2.1' and v != '3.0':
			self._parse_error(_('Unrecognized version \'%s\'') % v)
		v = self._parse_get_single('uid', False)

class VCardSet:
	"""A hacky vCard container/manager object"""

	# This module implements a parser for vCard 2.1 and 3.0.
	# It does not parse each format strictly, as there are low-level
	# differences in the following areas:
	# - Whitespace is permitted between content tokens in 2.1, not 3.0
	# - All parameters must be explicitly named in 3.0, not in 2.1
	#
	# Input that claims to be of one standard but does not strictly
	# comply with that standard is silently accepted.

	def __init__(self):
		self._file_line = 0
		self._save_line = None
		self._current_object = None
		self._objects = []
		self._uids = {}
		self._object_list = gtk.ListStore(gobject.TYPE_PYOBJECT,
						  gobject.TYPE_STRING)
		self.normalizer = TelNormalizer()

	def _isalpha(self, c):
		x = ord(c)
		return ((x >= ord('a') and x <= ord('z')) or
			(x >= ord('A') and x <= ord('Z')))
	def _isnum(self, c):
		x = ord(c)
		return (x >= ord('0') and x <= ord('9'))
	def _iswsp(self, c):
		return c == ' ' or c == '\t'
	def _issafe(self, c):
		x = ord(c)
		return (self._iswsp(c) or
			x == 0x21 or
			(x >= 0x23 and x <= 0x2b) or
			(x >= 0x2d and x <= 0x39) or
			(x >= 0x3c and x <= 0x7e) or
			x > 0x7f)
	def _isqsafe(self, c):
		x = ord(c)
		return (_iswsp(c) or
			x == 0x21 or
			(x >= 0x23 and x <= 0x7e) or
			x > 0x7f)
	def _create_object(self, vtype):
		if vtype == 'vcard':
			return VCard(False)
		else:
			self._parse_error(_('Unknown value type %s') % vtype)
	def _add_object(self, object):
		if not hasattr(object, '_container'):
			uid = object.getuid()
			if uid and uid in self._uids:
				mergeit = self._uids[uid]
				mergeit.merge(object)
			else:
				object._container = self
				self._objects.append(object)
				self._uids[uid] = object
				iterx = self._object_list.append(
					row = [object, object.getname()])
				object._iter = iterx
	def _remove_object(self, object):
		if hasattr(object, '_container'):
			i = 0
			while i < len(self._objects):
				if self._objects[i] == object:
					del self._objects[i]
				else:
					i += 1
			uid = object.getuid()
			if uid:
				del self._uids[uid]
			del object._container
			self._object_list.remove(object._iter)
			del object._iter
	def _process_line(self, name, groups, params, value):
		if name == 'begin':
			if self._current_object:
				self._parse_error(_('Nested directory object'))
			else:
				self._current_object = (
					self._create_object(value.lower()))
		elif name == 'end':
			if not self._current_object:
				self._parse_error(_('Mismatched directory '
						    'object END'))
			elif self._current_object.vtype != value.lower():
				self._parse_error(_('Mismatched directory '
						    'object type for END'))
			else:
				self._current_object._parse_end()
				self._add_object(self._current_object)
				self._current_object = None
		elif not self._current_object:
			self._parse_error(_('Content line outside '
					    'directory object'))
		else:
			self._current_object._parse_add_content(name, groups,
								params, value)

	def _parse_error(self, message):
		raise Exception(_('Line %d: %s') % (self._file_line, message))

	def _parse_skip_ws(self, line):
		pos = 0
		while pos < len(line) and self._iswsp(line[pos]):
			pos += 1
		return line[pos:]
	def _parse_name_groups(self, line):
		groups = []
		name = None
		sep = 0
		while True:
			if len(line) > sep:
				x = line[sep]
				if (self._isalpha(x) or
				    self._isnum(x) or
				    x == '-'):
					sep += 1
					continue
				elif x == '.':
					if not sep:
						self._parse_error(
						_('Empty group definition'))
					groups.append(line[:sep])
					line = line[(sep + 1):]
					sep = 0
					continue
			if not sep:
				self._parse_error(_("Empty content name"))
			name = line[:sep]
			line = line[sep:]
			return (groups, name.lower(), line)
	def _parse_param_value(self, line):
		sep = 0
		if line[0] == '"':
			line = line[1:]
			while True:
				if len(line) <= sep:
					self._parse_error(
						_("Malformed quoted "
						  "parameter value"))
				x = line[sep]
				if self._isqsafe(x):
					sep += 1
					continue
				elif x == '"':
					break
				else:
					self._parse_error(
						_("Malformed quoted "
						  "parameter value"))

			value = ''
			if sep:
				value = line[:sep]
			line = line[(sep + 1):]
			return (value, line)

		while True:
			if len(line) > sep:
				x = line[sep]
				if self._issafe(x):
					sep += 1
					continue
			break
		value = ''
		if sep:
			value = line[:sep]
			line = line[sep:]
		return (value, line)
		
	def _parse_param(self, line):
		name = None
		values = []
		sep = 0
		while True:
			if len(line) <= sep:
				self._parse_error(_("Malformed parameter"))
			x = line[sep]
			if self._isalpha(x) or self._isnum(x) or x == '-':
				sep += 1
			else:
				if not sep:
					self._parse_error(
						_("Empty parameter name"))
				name = line[:sep]
				# vCard 2.1 hack
				line = self._parse_skip_ws(line[sep:])
				if x == '=':
					# vCard 2.1 hack
					line = self._parse_skip_ws(line[1:])
					break

				# vCard 2.1 hack
				# Property names without '=' are equivalent
				# to type=name
				values.append(name)
				return ('type', values, line)

		name = name.lower()
		while True:
			(x, line) = self._parse_param_value(line)
			values.append(x)
			if not len(line):
				return (name, values, line)
			# vCard 2.1 hack
			line = self._parse_skip_ws(line)
			x = line[0]
			if x != ',':
				return (name, values, line)
			line = line[1:]
	def _parse_line(self, line):
		(groups, name, line) = self._parse_name_groups(line)
		params = {}
		if not line:
			self._parse_error(_('Malformed content line'))
		# vCard 2.1 hack
		line = self._parse_skip_ws(line)
		while line[0] == ';':
			(param, values, line) = self._parse_param(line[1:])
			if param in params:
				params[param].extend(values)
			else:
				params[param] = values
			# vCard 2.1 hack
			line = self._parse_skip_ws(line)
		if line[0] != ':':
			self._parse_error(_('Malformed content line'))
		# vCard 2.1 hack
		line = self._parse_skip_ws(line[1:])
		self._process_line(name, groups, params, line)

	def _feed_line(self, line):
		if not line or not self._iswsp(line[0]):
			if self._save_line:
				self._parse_line(self._save_line)
			self._save_line = line
		elif self._save_line:
			self._save_line += line[1:]
		else:
			self._save_line = line[1:]
		self._file_line += 1
	def _feed_done(self):
		self._feed_line(None)
		if self._current_object:
			self._parse_error(_('Unterminated object in stream'))
	def import_string(self, str):
		self._file_line = 0
		line = ''
		for x in str.splitlines():
			self._feed_line(x)
		self._feed_done()
	def import_file(self, path):
		f = open(os.path.expanduser(path), 'r')
		contents = f.read()
		f.close()
		self.import_string(contents)

	def search_tel(self, num_norm):
		print "Search for %s" % num_norm
		if not num_norm:
			return (None, None)
		for vc in self._objects:
			if vc.vtype != 'vcard':
				continue
			nums = vc.findtels(None)
			for ent in nums:
				xnum = self.normalizer.normalize(ent[3], 129)
				print "Check: %s" % xnum
				if self.normalizer.compare(num_norm, xnum):
					types = None
					if 'type' in ent[2]:
						types = ent[2]['type']
					return (vc, types)
		return (None, None)

	def get_gtk_model(self):
		return self._object_list


class HfConsole:
	"""This is a user interface for the hfpd Bluetooth hands-free
	profile server"""

	DBUS_SERVICE_NAME = 'org.freedesktop.DBus'
	DBUS_INTERFACE_DBUS = 'org.freedesktop.DBus'
	DBUS_INTERFACE_PROPERTIES = 'org.freedesktop.DBus.Properties'
	DBUS_BUS_OBJECT = '/org/freedesktop/DBus'
	HFPD_HANDSFREE_INTERFACE_NAME = 'net.sf.nohands.hfpd.HandsFree'
	HFPD_SOUNDIO_INTERFACE_NAME = 'net.sf.nohands.hfpd.SoundIo'
	HFPD_AUDIOGATEWAY_INTERFACE_NAME = 'net.sf.nohands.hfpd.AudioGateway'
	HFPD_SERVICE_NAME = 'net.sf.nohands.hfpd'
	HFPD_HANDSFREE_OBJECT = '/net/sf/nohands/hfpd'
	HFPD_SOUNDIO_OBJECT = '/net/sf/nohands/hfpd/soundio'

	def addr_transform(self, addr):
		val = (str(addr[0:2]) + str(addr[3:5]) + str(addr[6:8]) +
		       str(addr[9:11]) + str(addr[12:14]) + str(addr[15:17]))
		return val.upper()
	def addr_untransform(self, addr):
		val = (addr[0:2] + ':' + addr[2:4] + ':' + addr[4:6] + ':' +
		       addr[6:8] + ':' + addr[8:10] + ':' + addr[10:12])
		return val.upper()

	def create_cellrenderertext(self):
		cell = gtk.CellRendererText()
		cell.set_property('scale-set', True)
		cell.set_property('scale', pango.SCALE_X_LARGE)
		return cell

	def __init__(self):
		
		# Set the Glade file
		self.share_path = ['.', './data', '/usr/local/share/hfconsole']
		gladefile = 'hfconsole.glade'
		found = False
		for x in self.share_path:
			try:
				self.widgets = gtk.glade.XML(x + '/' +
							     gladefile)
				self.gladefile = x + '/' + gladefile
				found = True
			except:
				continue
			if found:
				break

		if not found:
			print (_('Could not find required Glade XML file %s') %
			       gladefile)
			exit(1)
		
		# Create our dictionay and connect it
		dic = {
			# Callbacks for the main window
			"AgSelector_button_press_event_cb" :
			self.ag_selector_open,
			"AgSelector_toggled_cb" : self.ag_selector_toggle,
			"AgScan_clicked_cb" : self.bar_ag_scan_clicked,
			"AgConnect_clicked_cb" : self.bar_ag_connect_clicked,
			"BarDialPad_clicked_cb" : self.bar_dialpad_clicked,
			"BarContacts_clicked_cb" : self.bar_contacts_clicked,
			"AgAudioToggle_toggled_cb" : self.bar_ag_audio_toggled,
			"AgVoiceRecogToggle_toggled_cb" :
			self.bar_ag_voice_recognition_toggled,
			"AgHangUp_clicked_cb" : self.bar_ag_hangup_clicked,
			"AgHoldCall_clicked_cb" : self.bar_ag_swaphold_clicked,
			"AgSwapCall_clicked_cb" : self.bar_ag_swaphold_clicked,
			"AgDial_clicked_cb" : self.bar_ag_dial_clicked,
			"AgRedial_clicked_cb" : self.bar_ag_redial_clicked,
			"ConfigOpen_clicked_cb" : self.bar_config_clicked,
			"DigitButton_pressed_cb" : self.digit_button_pressed,
			"DigitButton_released_cb" : self.digit_button_released,
			"DigitButton_clicked_cb" : self.digit_button_clicked,
			"PhoneNumEntry_changed_cb" : self.phone_num_changed,
			"PhoneNumBs_clicked_cb" : self.phone_num_bs,
			"Mute_toggled_cb" : self.mute_toggled,
			"on_MainWindow_destroy" : gtk.main_quit,
			"ContactsDelete_clicked_cb" :
			self.contacts_delete_clicked,
			"ContactsNew_clicked_cb" : self.contacts_new_clicked,
			"ContactsEdit_clicked_cb" : self.contacts_edit_clicked,

			# Known Devices tab
			"DevicesClose_clicked_cb" : self.devices_close_clicked,
			"DevicesAdd_clicked_cb" : self.devices_add_clicked,
			"DevicesDelete_clicked_cb" :
			self.devices_delete_clicked,
			"DevicesConnect_clicked_cb" :
			self.devices_connect_clicked,
			"DevicesDisconnect_clicked_cb" :
			self.devices_disconnect_clicked,
			"DevicesAutoConnectNever_toggled_cb" :
			self.devices_autoconnect_never_toggled,
			"DevicesAutoConnectDisabled_toggled_cb" :
			self.devices_autoconnect_disabled_toggled,
			"DevicesAutoConnectEnabled_toggled_cb" :
			self.devices_autoconnect_enabled_toggled,

			# New Device tab
			"NewDeviceClose_clicked_cb" :
			self.newdevice_close_clicked,
			"NewDeviceAdd_clicked_cb" :
			self.newdevice_add_clicked,
			"NewDeviceClear_clicked_cb" :
			self.newdevice_clear_clicked,

			# Callbacks for the scan tab
			"ScanAdd_clicked_cb" : self.scan_add_clicked,
			"ScanClose_clicked_cb" : self.scan_close_clicked,
			"ScanHistory_clicked_cb" : self.scan_history_clicked,
			"ScanRestart_clicked_cb" : self.scan_restart_clicked,

			# Callbacks for the history tab
			"HistoryOK_clicked_cb" : self.history_ok_clicked,
			"HistoryCancel_clicked_cb" :
			self.history_cancel_clicked,

			# Callbacks for the choose number tab
			"ChooseNumberCancel_clicked_cb" :
			self.choose_number_cancel_clicked,
			"ChooseNumberMobile_clicked_cb" :
			self.choose_number_mobile_clicked,
			"ChooseNumberHome_clicked_cb" :
			self.choose_number_home_clicked,
			"ChooseNumberWork_clicked_cb" :
			self.choose_number_work_clicked,

			# Callbacks for the config tab
			"ConfigOK_clicked_cb" : self.config_ok_clicked,
			"ConfigCancel_clicked_cb" : self.config_cancel_clicked,
			"ConfigInit_clicked_cb" : self.config_init_clicked,
			"ConfigDevices_clicked_cb" :
			self.config_devices_clicked,
			"ConfigDriver_changed_cb" :
			self.config_driver_changed,
			"ConfigPacketIntervalHint_toggled_cb" :
			self.config_packet_interval_toggled,
			"ConfigPacketInterval_value_changed_cb" :
			self.config_packet_interval_changed,
			"ConfigJitterWindowHint_toggled_cb" :
			self.config_jitter_window_toggled,
			"ConfigJitterWindow_value_changed_cb" :
			self.config_jitter_window_changed,
			"ConfigMinOutBufferHint_toggled_cb" :
			self.config_min_out_buffer_toggled,
			"ConfigMinOutBuffer_value_changed_cb" :
			self.config_min_out_buffer_changed,
			"ConfigFeedbackTest_toggled_cb" :
			self.config_loopback_toggled,
			"ConfigDenoise_toggled_cb" :
			self.config_denoise_changed,
			"ConfigEchoCancel_toggled_cb" :
			self.config_echo_cancel_changed,
			"ConfigEchoCancelTail_value_changed_cb" :
			self.config_echo_cancel_tail_changed,
			"ConfigAutoGain_value_changed_cb" :
			self.config_auto_gain_changed,
			"ConfigDereverb_toggled_cb" :
			self.config_dereverb_toggled,
			"ConfigDereverbValue_value_changed_cb" :
			self.config_dereverb_value_changed,
			"ConfigDereverbDecay_value_changed_cb" :
			self.config_dereverb_decay_changed,
			"ConfigRingerFile_file_set_cb" :
			self.config_ringer_file_set,
			"ConfigDspTest_clicked_cb" : self.config_dsptest_open,
			"on_ConfigDialog_close" : gtk.main_quit,

			# Callbacks for the DSP test
			"DspTestForward_clicked_cb" :
			self.config_dsptest_forward,
			"DspTestBack_clicked_cb" :
			self.config_dsptest_back,
			"DspTestClose_clicked_cb" :
			self.config_dsptest_close,
			"DspTestNotebook_switch_page_cb" :
			self.config_dsptest_switch_page,
			"DspTestRecStart1_toggled_cb" :
			self.config_dsptest_recstart1_toggled,
			"DspTestRecStart2_toggled_cb" :
			self.config_dsptest_recstart2_toggled,
			"DspTestPlayStart_toggled_cb" :
			self.config_dsptest_playstart_toggled,

			# Callbacks for the initial configuration page
			"InitConfDone_clicked_cb" : self.initconf_done_clicked,
			"InitConfBack_clicked_cb" : self.initconf_back_clicked,
			"InitConfForward_clicked_cb" :
			self.initconf_forward_clicked,
			"InitConfNotebook_switch_page_cb" :
			self.initconf_switch_page,

			}
		self.widgets.signal_autoconnect(dic)

		self.ag_list = gtk.Menu()
		self.ag_list_separator = gtk.SeparatorMenuItem()
		self.ag_list_separator.set_no_show_all(True)
		self.ag_list.append(self.ag_list_separator)
		self.ag_list_scan = gtk.MenuItem(_('Search for Device...'))
		self.ag_list_scan.connect('activate',
					  self.ag_selector_scan_activate)
		self.ag_list.append(self.ag_list_scan)
		self.ag_list_manage = gtk.MenuItem(_('Manage Devices...'))
		self.ag_list_manage.connect('activate',
					    self.ag_selector_manage_activate)
		self.ag_list.append(self.ag_list_manage)
		self.ag_list.connect('selection-done', self.ag_selector_close)
		self.ag_list.show_all()

		self.ag_list_detail = gtk.ListStore(gobject.TYPE_STRING,
						    gobject.TYPE_STRING,
						    gobject.TYPE_PYOBJECT)
		self['DevicesList'].set_model(self.ag_list_detail)
		tvcolumn = gtk.TreeViewColumn(_('Name'))
		self['DevicesList'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 0)
		tvcolumn = gtk.TreeViewColumn(_('Address'))
		self['DevicesList'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 1)
		self['DevicesList'].get_selection().connect('changed',
						    self.devices_select)
		self.devices_ag = None

		self.ag_list_new = gtk.ListStore(gobject.TYPE_STRING,
						 gobject.TYPE_STRING,
						 gobject.TYPE_PYOBJECT)
		self['NewDeviceList'].set_model(self.ag_list_new)
		tvcolumn = gtk.TreeViewColumn(_('Name'))
		self['NewDeviceList'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 0)
		tvcolumn = gtk.TreeViewColumn(_('Address'))
		self['NewDeviceList'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 1)
		self['NewDeviceList'].get_selection().connect('changed',
						      self.newdevice_select)
		self.ag_list_new.connect('row-inserted',
					 self.newdevice_row_added)
		self.ag_list_new.connect('row-deleted',
					 self.newdevice_row_removed)
		self.newdevice_ag = None

		self['ConfigDriverDevList'].set_model(
			gtk.ListStore(gobject.TYPE_STRING,
				      gobject.TYPE_STRING))
		self['ConfigDriverDevList'].set_text_column(0)

		# Set a file filter for the ringer chooser
		filt = gtk.FileFilter()
		filt.set_name(_('WAV Audio Files'))
		filt.add_pattern('*.wav')
		self["ConfigRingerFile"].add_filter(filt)

		self.inquiry = False
		self.scanresults = gtk.ListStore(gobject.TYPE_STRING,
						 gobject.TYPE_UINT,
						 gobject.TYPE_STRING)
		self['ScanResults'].set_model(self.scanresults)
		tvcolumn = gtk.TreeViewColumn(_('Device'))
		self['ScanResults'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 0)
		self.scanselect = self['ScanResults'].get_selection()
		self.scanselect.connect('changed', self.scan_select)

		self.historyresults = gtk.ListStore(gobject.TYPE_STRING,
						    gobject.TYPE_UINT,
						    gobject.TYPE_STRING)
		self['HistoryResults'].set_model(self.historyresults)
		tvcolumn = gtk.TreeViewColumn(_('Device'))
		self['HistoryResults'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 0)
		self.historyselect = self['HistoryResults'].get_selection()
		self.historyselect.connect('changed', self.history_select)

		# Configure D-Bus
		loop = DBusGMainLoop()
		self.dbus = dbus.SessionBus(mainloop=loop)

		self.ags = {}
		self.ags_added = {}
		self.soundmonitor = False
		self.selected_ag = None
		self.selected_ag_lock = False
		self.command_audio_attach_state = False
		self.nobt = False
		self.nested = False

		self.frontpage = 0
		self.frontnotebook_set(self.frontpage)

		self.alerter_factory = (lambda x:
					GtkAlerter(self.gladefile, x))

		self.vcards = VCardSet()
		sort = gtk.TreeModelSort(self.vcards.get_gtk_model())
		def name_sort(model, iter1, iter2):
			n1 = model.get_value(iter1, 1)
			n2 = model.get_value(iter2, 1)
			if n1 < n2:
				return -1
			if n1 == n2:
				return 0
			return 1
		sort.set_sort_func(0, name_sort)
		sort.set_sort_column_id(0, gtk.SORT_ASCENDING)
		self['ContactsList'].set_model(sort)
		tvcolumn = gtk.TreeViewColumn(_('Name'))
		self['ContactsList'].append_column(tvcolumn)
		cell = self.create_cellrenderertext()
		tvcolumn.pack_start(cell, True)
		tvcolumn.add_attribute(cell, 'text', 1)
		contactselect = self['ContactsList'].get_selection()
		contactselect.connect('changed', self.contacts_select)

	def command_audio_attach(self, state):
		if state == self.command_audio_attach_state:
			return
		self.command_audio_attach_state = state
		if state:
			exc = self.command_audio_attach_acmd
		else:
			exc = self.command_audio_attach_dcmd

		if exc:
			p = subprocess.Popen(exc, shell=True)

	def getprop_hfpd(self, prop):
		return self.hfpd_props.Get(self.HFPD_HANDSFREE_INTERFACE_NAME,
					   prop)
	def getprop_soundio(self, prop):
		return self.soundio_props.Get(self.HFPD_SOUNDIO_INTERFACE_NAME,
					      prop)
	def setprop_hfpd(self, name, value):
		self.hfpd_props.Set(self.HFPD_HANDSFREE_INTERFACE_NAME,
				    name, value)
	def setprop_soundio(self, name, value):
		self.soundio_props.Set(self.HFPD_SOUNDIO_INTERFACE_NAME,
				       name, value)

	def Start(self):
		try:
			self.busctl = dbus.Interface(
				self.dbus.get_object(self.DBUS_SERVICE_NAME,
						     self.DBUS_BUS_OBJECT),
				dbus_interface=self.DBUS_INTERFACE_DBUS)
		except dbus.exceptions.DBusException, (ex):
			self.fatal(_('Could not connect to D-Bus:\n%s') %
				   str(ex))
			return

		try:
			self.hfpd = dbus.Interface(
				self.dbus.get_object(self.HFPD_SERVICE_NAME,
					     self.HFPD_HANDSFREE_OBJECT),
				dbus_interface =
				self.HFPD_HANDSFREE_INTERFACE_NAME)
			self.hfpd_props = dbus.Interface(
				self.dbus.get_object(self.HFPD_SERVICE_NAME,
					     self.HFPD_HANDSFREE_OBJECT),
			      dbus_interface=self.DBUS_INTERFACE_PROPERTIES)
			self.soundio = dbus.Interface(
				self.dbus.get_object(self.HFPD_SERVICE_NAME,
						     self.HFPD_SOUNDIO_OBJECT),
				dbus_interface =
				self.HFPD_SOUNDIO_INTERFACE_NAME)
			self.soundio_props = dbus.Interface(
				self.dbus.get_object(self.HFPD_SERVICE_NAME,
						     self.HFPD_SOUNDIO_OBJECT),
			      dbus_interface=self.DBUS_INTERFACE_PROPERTIES)
		except dbus.exceptions.DBusException, (ex):
			self.fatal(_('Could not connect to hfpd:\n%s\n\n'
				     'Ensure that hfpd and its D-Bus '
				     'service file are installed correctly.\n'
				     'If the problem persists, try starting '
				     'hfpd manually, e.g. \"hfpd\", or out of '
				     'your build directory, e.g. '
				     '\"hfpd/hfpd\"') % str(ex))
			return

		try:
			v = self.getprop_hfpd('Version')
		except:
			v = 0

		my_version = 4
		if v < my_version:
			self.fatal(_('Version mismatch with hfpd!\n'
				     'hfpd version: %(hfpdver)d\n'
				     'hfconsole version: %(hfconsolever)d\n\n'
				     'Ensure that hfpd is installed '
				     'correctly.\n'
				     'If the problem persists, try '
				     '\"killall hfpd\"') %
				   {'hfpdver': v, 'hfconsolever': my_version})
			return
		elif v != my_version:
			self.fatal(_('Version mismatch with hfpd!\n'
				    'hfpd version: %(hfpdver)d\n'
				    'hfconsole version: %(hfconsolever)d\n\n'
		    'Ensure that hfconsole is installed correctly.\n'
		    'If the problem persists, try running hfconsole '
		    'out of your build directory, e.g. '
				    '\"data/hfconsole\"') %
				   {'hfpdver': v, 'hfconsolever': my_version})
			return

		self.configfile_name = os.path.expanduser('~/.hfconsolerc')
		self.configfile = ConfigParser.ConfigParser()
		self.configfile.read([self.configfile_name])
		if not self.configfile.has_section('options'):
			self.configfile.add_section('options')
		if not self.configfile.has_section('devices'):
			self.configfile.add_section('devices')
		if not self.configfile.has_section('history'):
			self.configfile.add_section('history')
		cmd = None
		if self.configfile.has_option('options', 'attach_command'):
			cmd = self.configfile.get('options', 'attach_command')
		self.command_audio_attach_acmd = cmd
		cmd = None
		if self.configfile.has_option('options', 'detach_command'):
			cmd = self.configfile.get('options', 'detach_command')
		self.command_audio_attach_dcmd = cmd
		cmd = None
		if self.configfile.has_option('options', 'default_ringtone'):
			cmd = self.configfile.get('options',
						  'default_ringtone')
		if not cmd:
			# Search for the default ringtone file
			for x in self.share_path:
				cmd = x + '/hfconsole_ring.wav'
				cmd = os.path.normpath(
					os.path.join(os.getcwd(),
						     os.path.expanduser(cmd)))

				if os.access(cmd, os.R_OK):
					break
				cmd = None
		self.default_ringtone = cmd

		loadags = self.configfile.items('devices')
		for x in loadags:
			x = self.addr_untransform(x[0])
			self.add_audiogateway(x, False)

		try:
			pass
			#self.vcards.import_file('~/.hfconsole.vcf')
		except Exception, (ex):
			print 'Load contacts: %s' % str(ex)

		self.busctl.connect_to_signal("NameOwnerChanged",
					      self.hfpd_lost)
		self.hfpd.connect_to_signal("SystemStateChanged",
					    self.hfpd_system_state)
		self.hfpd.connect_to_signal("InquiryResult",
					    self.hfpd_inquiry_result)
		self.hfpd.connect_to_signal("InquiryStateChanged",
					    self.hfpd_inquiry_complete)
		self.hfpd.connect_to_signal("AudioGatewayAdded",
					    self.hfpd_ag_added)
		self.hfpd.connect_to_signal("AudioGatewayRemoved",
					    self.hfpd_ag_removed)
		self.hfpd.connect_to_signal("LogMessage",
					    self.hfpd_log_message)
		self.soundio.connect_to_signal("StateChanged",
					       self.soundio_state_changed)
		self.soundio.connect_to_signal("StreamAborted",
					       self.soundio_stream_aborted)
		self.soundio.connect_to_signal("MuteChanged",
					       self.soundio_mute_changed)
		self.soundio.connect_to_signal("SkewNotify",
					       self.soundio_skew_notify)

		self['Mute'].set_active(self.getprop_soundio('Mute'))

		self.ag_select_changed(None)
		self.system_ctx = self['StatusBar'].get_context_id(
			'SystemState')
		self.soundio_status_ctx = self['StatusBar'].get_context_id(
			'SoundIoStatus')
		self.soundio_skew_monitor = None
		self.nobt = not self.getprop_hfpd('SystemState')
		self.hfpd_system_state(not self.nobt)
		if self.nobt:
			try:
				self.hfpd.Start()
				self.nobt = False
			except dbus.exceptions.DBusException, (ex):
				if (ex.get_dbus_name() !=
				    'net.sf.nohands.hfpd.Error'):
					self.fatal(str(ex))
					return
			self.setprop_hfpd('AutoRestart', True)
		ags = self.getprop_hfpd('AudioGateways')
		for x in ags:
			self.hfpd_ag_added(str(x))
		if not self.selected_ag:
			self.ag_none_selected()

		self.soundio_state_changed(self.getprop_soundio('State'))

		hwg['MainWindow'].show()

	def __getitem__(self, key):
		return self.widgets.get_widget(key)

	def error_closed(self, dlg, response):
		dlg.hide()
	def error_message(self, msg):
		dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
					gtk.MESSAGE_ERROR,
					gtk.BUTTONS_OK,
					msg)
		window = self['MainWindow']
		dlg.set_title(window.get_title())
		dlg.set_transient_for(window)
		dlg.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
		dlg.connect('response', self.error_closed)
		dlg.show()
		return dlg

	def fatal_closed(self, widget, response):
		# dbus-python needs to fix its exception handler
		os._exit(1)
	def fatal(self, msg):
		dlg = self.error_message(msg)
		dlg.connect('response', self.fatal_closed)

	class AudioGateway:
		def reset_indicators(self):
			self.signal = -1
			self.battery = -1
			self.message = False
			self.vm = False
			self.noservice = False
			self.voicerecog = False
			self.inbandringtone = False
		def __init__(self, hfc, sbus, hfpd, agpath, box):
			self.hfc = hfc
			self.path = agpath
			self.hfpd = hfpd
			self.alerter = None
			self.caller_id = None
			self.reset_indicators()
			self.autoreconnect = 1
			self.autoreconnect_flag = False
			self.normalizer = TelNormalizer()

			self.state = 0
			self.callstate = 0
			self.audiostate = 0
			self.disp = hfc.ag_disp_new(self)

			self.dbusobj = sbus.get_object(
				hfc.HFPD_SERVICE_NAME,
				agpath)
			self.ag = dbus.Interface(self.dbusobj,
						 dbus_interface =
					 hfc.HFPD_AUDIOGATEWAY_INTERFACE_NAME)
			self.agprops = dbus.Interface(self.dbusobj,
					      dbus_interface =
					      hfc.DBUS_INTERFACE_PROPERTIES)

			self.sigs = []
			sig = self.ag.connect_to_signal("NameResolved",
							self.name_resolved)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal("StateChanged",
							self.state_changed)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal("CallStateChanged",
						self.call_state_changed)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal("AudioStateChanged",
						self.audio_state_changed)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal("AutoReconnectChanged",
						self.autoreconnect_changed)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal("IndicatorChanged",
						self.indicator_changed)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal("Ring",
						self.ring_notify)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal(
				"VoiceRecognitionActiveChanged",
				self.voice_recognition_changed)
			self.sigs.append(sig)
			sig = self.ag.connect_to_signal(
				"InBandRingToneEnableChanged",
				self.inbandringtone_changed)
			self.sigs.append(sig)

			self.addr = self.getprop('Address')
			self.name = self.getprop('Name')
			if self.name == self.addr:
				self.name = self.hfc.ag_history_getname(
					self.addr)
			self.state = self.getprop('State')
			self.callstate = self.getprop('CallState')
			self.audiostate = self.getprop('AudioState')
			self.voicerecog = self.getprop(
				'VoiceRecognitionActive')
			self.inbandringtone = self.getprop(
				'InBandRingToneEnable')
			self.state_changed(self.state, False)
			self.call_state_changed(self.callstate)
			self.audio_state_changed(self.audiostate)
			self.disp.set_name(self.name)
			self.hfc.ag_update_history(self.addr, None)

		def has_feature(self, feat):
			return (hasattr(self, 'features') and
				self.features[feat])

		def is_connecting(self):
			return self.state == 3
		def is_connected(self):
			return self.state == 4
		def can_dial(self):
			return self.is_connected() and (self.callstate == 1 or
							self.callstate == 4)
		def can_dtmf(self):
			return self.is_connected() and (self.callstate == 2 or
							self.callstate == 3 or
							self.callstate == 5)
		def can_hold(self):
			return (self.is_connected() and
				self.has_feature('SwapHoldActive') and
				((self.callstate == 3) or
				 (self.callstate == 5)))
		def has_any_call(self):
			return self.is_connected() and (self.callstate != 1)
		def has_waiting_call(self):
			return self.is_connected() and (self.callstate == 4 or
							self.callstate == 5)
		def has_message(self):
			return self.is_connected() and (self.message or self.vm)
		def can_drop_waiting(self):
			return self.has_feature('RejectCall')
		def can_drop_held_udub(self):
			return self.has_feature('DropHeldUdub')
		def has_voice_recognition(self):
			return (self.is_connected() and
				self.has_feature('VoiceRecognition'))
		def get_voice_recognition(self):
			return self.voicerecog
		def set_voice_recognition(self, st):
			self.ag.SetVoiceRecognition(dbus.Boolean(st),
				     reply_handler=lambda : None,
				     error_handler=self.command_failed)
		def voice_recognition_changed(self, st):
			self.voicerecog = st
			self.disp.set_state()
		def selectable(self):
			return self.is_mine()
		def select_priority(self):
			if self.has_audio():
				return 4
			if self.has_any_call():
				return 3
			if self.has_service():
				return 2
			if self.is_connected():
				return 1
			return 0
		def is_mine(self):
			return self.hfc.ags_added.has_key(self.addr)
		def load_config(self):
			ar = True
			if self.hfc.ag_config_has_option(self.addr,
							 'autoreconnect'):
				ar = self.hfc.ag_config_get_option(self.addr,
							   'autoreconnect')
				try: ar = int(ar)
				except: ar = 2
			if ar != self.autoreconnect:
				self.autoreconnect = ar
				if ar == 2: self.set_autoreconnect_flag(True)
				else: self.set_autoreconnect_flag(False)
		def save_config(self):
			self.hfc.ag_config_set_option(self.addr,
						      'autoreconnect',
						      self.autoreconnect)
		def update_mine(self):
			self.disp.set_name(None)
			self.disp.set_name(self.name)
		def get_autoreconnect(self):
			return self.autoreconnect
		def set_autoreconnect(self, value):
			if value == self.autoreconnect:
				return
			self.autoreconnect = value
			if value == 2: self.set_autoreconnect_flag(True)
			else: self.set_autoreconnect_flag(False)
			self.save_config()
			self.disp.set_state()
		def get_autoreconnect_flag(self):
			return self.autoreconnect_flag
		def set_autoreconnect_flag(self, value):
			value = bool(value)
			if value != self.autoreconnect_flag:
				self.setprop('AutoReconnect',
					     dbus.Boolean(value))
				self.autoreconnect_flag = value
		def has_service(self):
			return self.is_connected() and not self.noservice
		def signal_level(self):
			return self.signal
		def battery_level(self):
			return self.battery
		def audio_connecting(self):
			return self.audiostate == 2
		def has_audio(self):
			return self.audiostate == 3
		def wants_audio(self):
			return (self.callstate == 2 or
				self.callstate == 3 or
				self.callstate == 5)

		def name_resolved(self, name):
			self.name = name
			self.disp.set_name(self.name)
			self.hfc.ag_update_history(self.addr, self.name)
		def state_changed(self, state, voluntary):
			self.state = int(state)
			if self.is_connected():
				self.features = self.getprop('Features')
				if (self.is_mine() and
				    self.get_autoreconnect() == 1):
					self.set_autoreconnect(2)
			else:
				self.reset_indicators()
				self.call_state_changed(1)
				if hasattr(self, 'features'):
					del self.features
			self.disp.set_state()
		def call_state_changed(self, state):
			self.callstate = int(state)
			self.disp.set_state()
			if (self.callstate != 4 and self.callstate != 5):
				self.alert_close()
		def audio_state_changed(self, state):
			self.audiostate = int(state)
			self.disp.set_state()
		def soundio_state_changed(self):
			self.disp.set_state()
		def inbandringtone_changed(self, st):
			self.inbandringtone = st
			# So far we don't really care about this,
			# because audio gateways get priority for the
			# sound card.
		def autoreconnect_changed(self, value):
			value = bool(value)
			if self.autoreconnect_flag != value:
				self.autoreconnect_flag = value
				if value and self.get_autoreconnect() == 0:
					self.set_autoreconnect(2)
				elif not value and self.get_autoreconnect() ==2:
					self.set_autoreconnect(1)
		def indicator_changed(self, name, val):
			#print "Indicator %s -> %d" % (name, int(val))
			if name == 'service':
				self.noservice = not val
				self.disp.set_state()
			elif name == 'signal':
				self.signal = int(val)
				self.disp.set_state()
			elif name == 'battchg':
				self.battery = int(val)
				self.disp.set_state()
			elif name == 'voice mail':
				self.vm = int(val)
				self.disp.set_state()
			elif name == 'message':
				self.vm = int(val)
				self.disp.set_state()

		def alert_close(self):
			if self.alerter:
				self.alerter.remove()
				self.alerter = None
				self.hfc.ag_ring_stop(self)
			self.caller_id = None
			self.caller_phbook = None
		def alert_open(self):
			if not self.alerter:
				self.alerter = self.hfc.alerter_factory(self)
			self.alerter.update_state()
		def ring_notify(self, number, number_type, subaddr, satype,
				phbook_ent):
			if self.caller_id != number:
				self.caller_id = number
				if not phbook_ent:
					num_norm = self.normalizer.normalize(
						number, number_type)
					phbook_ent = self.hfc.contacts_lookup(
						num_norm)
				self.caller_phbook = phbook_ent
			self.alert_open()
			if (self.alerter and
			    not self.alerter.completed and
			    self.callstate == 4):
				self.hfc.ag_ring(self)
		def waiting_caller_info(self):
			return (self.caller_id, self.caller_phbook)
			
		def connect(self):
			self.ag.Connect()
		def disconnect(self):
			self.ag.Disconnect()
		def command_failed(self, reason):
			print _("Command failed: %s") % reason
		def open_audio(self):
			self.ag.OpenAudio()
		def close_audio(self):
			self.ag.CloseAudio()
		def dial(self, number):
			self.ag.Dial(number,
				     reply_handler=lambda : None,
				     error_handler=self.command_failed)
		def redial(self):
			self.ag.Redial(reply_handler=lambda : None,
				       error_handler=self.command_failed)
		def hangup(self):
			self.ag.HangUp(reply_handler=lambda : None,
				       error_handler=self.command_failed)
		def answer(self):
			self.ag.Answer(reply_handler=lambda : None,
				       error_handler=self.command_failed)
		def drop_held_udub(self):
			self.ag.CallDropHeldUdub(reply_handler=lambda : None,
				       error_handler=self.command_failed)
		def swap_hold_active(self):
			self.ag.CallSwapHoldActive(reply_handler=lambda : None,
				       error_handler=self.command_failed)
		def swap_drop_active(self):
			self.ag.CallSwapDropActive(reply_handler=lambda : None,
				       error_handler=self.command_failed)
		def call_link(self):
			self.ag.CallLink(reply_handler=lambda : None,
					 error_handler=self.command_failed)
		def call_transfer(self):
			self.ag.CallTransfer(reply_handler=lambda : None,
					     error_handler=self.command_failed)
		def dtmf(self, char):
			self.ag.SendDtmf(char[0],
					 reply_handler=lambda : None,
					 error_handler=self.command_failed)
		def do_remove(self):
			self.hfc.ag_remove_from_config(self.addr)
			if self.hfc.ags_added.has_key(self.addr):
				del self.hfc.ags_added[self.addr]
			x = self.setprop('Known', False)
			self.hfpd.RemoveDevice(self.addr)
		def remove(self):
			self.disp.remove()
			del self.disp
			while self.sigs:
				sig = self.sigs.pop()
				sig.remove()
		def getprop(self, prop):
			return self.agprops.Get(
				self.hfc.HFPD_AUDIOGATEWAY_INTERFACE_NAME,
				prop)
		def setprop(self, prop, val):
			return self.agprops.Set(
				self.hfc.HFPD_AUDIOGATEWAY_INTERFACE_NAME,
				prop, val)

	class AgStateDisplay:
		def __init__(self, hf, ag):
			self.hf = hf
			self.ag = ag
			self.item = None
			self.row_detail = None
			self.row_new = None
		def selector_toggle(self, widget):
			if widget.get_active():
				self.hf.ag_selector_changed(self.ag)
		def remove_item(self):
			if self.item:
				self.hf.ag_list.remove(self.item)
				self.item = None
				kids = self.hf.ag_list.get_children()
				count = len(kids)
				if count == 3:
					self.hf.ag_list_separator.hide()
		def set_name(self, name):
			if not name:
				self.remove_item()
				if self.row_detail:
					self.hf.ag_list_detail.remove(
						self.row_detail)
					self.row_detail = None
				if self.row_new:
					self.hf.ag_list_new.remove(
						self.row_new)
					self.row_new = None
				return

			if not self.ag.is_mine():
				if not self.row_new:
					self.row_new = (self.hf.ag_list_new.
							append(
					row=[name, self.ag.addr, self]))
				else:
					self.hf.ag_list_new.set(self.row_new,
								0, name)
				return

			self.remove_item()
			group = None
			kids = self.hf.ag_list.get_children()
			count = len(kids)
			if count > 3:
				group = kids[0]
			self.hf.ag_list_separator.show()
			self.item = gtk.RadioMenuItem(group, name)
			self.item.connect('toggled', self.selector_toggle)
			self.item.show_all()
			if self.ag == self.hf.selected_ag:
				self.item.set_active(True)
			self.hf.ag_list.insert(self.item, count - 3)
			if self.ag == self.hf.selected_ag:
				self.hf['AgSelectorLabel'].set_text(name)

			if not self.row_detail:
				self.row_detail = (self.hf.ag_list_detail.
						   append(
					row=[name, self.ag.addr, self]))
			else:
				self.hf.ag_list_detail.set(self.row_detail,
							   0, name)
		def remove(self):
			if self.ag == self.hf.selected_ag:
				self.hf.selected_ag = None
				self.hf.ag_none_selected()
			self.ag = None
			self.set_name(None)

		def do_mainwindow_state(self, string):
			self.hf['AudioGatewayStateLabel'].set_markup(
					string)
		def mainwindow_state(self):
			if self.hf.selected_ag != self.ag:
				return
			state = self.ag.state
			callstate = self.ag.callstate
			lab = 'Invalid State'
			if not self.ag.is_connected():
				if state == 1:
					lab = _('Destroyed')
				if state == 2:
					lab = _('Detached')
				if state == 3:
					lab = _('Attaching')
				lab += '\n'
			else:
				if callstate == 1:
					lab = _('Ready')
					if not self.ag.has_service():
						lab = _('No Service')
				if callstate == 2:
					lab = _('Dialing')
				if callstate == 3:
					lab = _('Call Established')
				if callstate == 4:
					lab = _('Ringing')
				if callstate == 5:
					lab = _('Waiting Call')
				lab += '\n'
				if self.ag.has_audio():
					lab += _('Audio Open')
				elif self.ag.audio_connecting():
					lab += _('Audio Connecting')
			self.do_mainwindow_state(lab)
			self.hf.ag_configure_gui(self.ag)

		def devices_state(self):
			if self.hf.devices_ag != self.ag:
				return
			self.hf.nested = True
			self.hf['DevicesProperties'].set_sensitive(True)
			self.hf['DevicesDevAddr'].set_text(self.ag.addr)
			v = self.ag.get_autoreconnect()
			if v == 0: v = 'DevicesAutoConnectNever'
			elif v == 1: v = 'DevicesAutoConnectDisabled'
			else: v = 'DevicesAutoConnectEnabled'
			self.hf[v].set_active(True)
			if self.ag.is_connected():
				self.hf['DevicesConnect'].hide()
				self.hf['DevicesDisconnect'].show()
			else:
				self.hf['DevicesConnect'].show()
				self.hf['DevicesConnect'].set_sensitive(
					not self.ag.is_connecting())
				self.hf['DevicesDisconnect'].hide()
			self.hf.nested = False

		def update_state(self):
			self.mainwindow_state()
			self.devices_state()

		def set_state(self):
			self.hf.ag_state_changed(self.ag)
			self.update_state()

	def ag_disp_new(self, ag):
		disp = self.AgStateDisplay(self, ag)
		return disp

	def ag_selector_set(self, ag):
		do_arrow = False
		if ag:
			lab = ag.name
			ag.disp.item.set_active(True)
			self['AgSelectorArrow'].show()
		else:
			lab = _('Search for Device...')
			self['AgSelectorArrow'].hide()
		self['AgSelectorLabel'].set_text(lab)
	def ag_selector_menu_position(self, menu):
		widget = self['AgSelector']
		alloc = widget.get_allocation()
		(x, y) = widget.get_parent_window().get_origin()
		x += alloc.x
		y += alloc.y + alloc.height
		return (x, y, True)
	def ag_selector_open(self, widget, event):
		kids = self.ag_list.get_children()
		count = len(kids)
		if count == 3:
			return
		self.nested = True
		self['AgSelector'].set_active(True)
		self.nested = False
		self.ag_list.popup(None, None,
				   self.ag_selector_menu_position,
				   event.button,
				   event.get_time())
				   
	def ag_selector_close(self, widget):
		self.nested = True
		self['AgSelector'].set_active(False)
		self.nested = False
	def ag_selector_toggle(self, widget):
		if self.nested:
			return
		self.nested = True
		if widget.get_active():
			widget.set_active(False)
			self.scan_open()
		self.nested = False
	def ag_selector_scan_activate(self, widget):
		self.scan_open()
	def ag_selector_manage_activate(self, widget):
		self.devices_open()
	def ag_selector_changed(self, ag):
		if not ag.selectable():
			ag = None
			if self.selected_ag and self.selected_ag.selectable():
				self.selected_ag.item.set_active(True)
				ag = self.selected_ag
			self.ag_selector_set(ag)
			return

		# If the user explicitly picks an audio gateway of lesser
		# priority than the currently selected AG, consider the
		# selection to be locked to that audio gateway.
		lock = False
		if self.selected_ag and (self.selected_ag.select_priority() >
					 ag.select_priority()):
			lock = True
		self.ag_select_changed(ag, lock)

	def hfpd_lost(self, name, old_owner, new_owner):
		if name != self.HFPD_SERVICE_NAME or new_owner != '':
			return
		msg = _('HFPD Process Disconnected')
		print msg
		self.fatal(msg)

	def hfpd_ag_added(self, agpath):
		if agpath not in self.ags:
			# This can fail if the signals get
			# stacked up
			ag = self.AudioGateway(self, self.dbus, self.hfpd,
					       agpath,
					       self['AudioGatewayBox'])
			self.ags[agpath] = ag
			if ag.is_mine():
				ag.load_config()
			else:
				self.newdevice_open()
			if (self.ags_added.has_key(ag.addr) and
			    self.ags_added[ag.addr] == 2):
				if (not self.selected_ag or
				    not self.selected_ag.wants_audio()):
					self.ags_added[ag.addr] = 1
					self.ag_select_changed(ag)

	def hfpd_ag_removed(self, agpath):
		if agpath in self.ags:
			self.ags[agpath].remove()
			del self.ags[agpath]
			if not self.selected_ag:
				self.ag_none_selected()
	def ag_none_selected(self):
		(ag, prio) = self.ag_best()
		self.ag_select_changed(ag)
	def ag_best(self):
		best_ag = None
		best_prio = -1
		for xag in self.ags.values():
			if xag.selectable() and (
				not best_ag or (best_prio <
						xag.select_priority())):
				best_ag = xag
				best_prio = xag.select_priority()
		return (best_ag, best_prio)
	def ag_stop_audio_unselected(self):
		for xag in self.ags.values():
			if xag != self.selected_ag and xag.has_audio():
				xag.close_audio()
	def ag_push_audio(self):
		# This process is messy
		# We let the audio gateway connect to us, then
		# we configure the pipe.  Passing False as the
		# second parameter to AudioGatewayStart ensures that
		# we don't initiate audio connections.
		if (self.selected_ag and
		    self.selected_ag.has_audio() and
		    (self.soundstate == 2 or
		     self.soundstate == 5 or
		     self.soundstate == 6 or
		     self.soundstate == 7)):
			try:
				self.soundio.AudioGatewayStart(
					self.selected_ag.path, False)
				self.command_audio_attach(True)
			except:
				self.soundio_status_msg(
					_('Failed to Open Primary Sound Card'),
					5000)
				if self.selected_ag.has_audio():
					self.selected_ag.close_audio()
		self.ag_stop_audio_unselected()
	def ag_state_changed(self, ag):
		#print "AG state changed %s %slock" % (ag.name, self.selected_ag_lock)
		if ag and ag == self.selected_ag:
			self.ag_push_audio()
		if self.selected_ag_lock and self.selected_ag.selectable():
			self.ag_stop_audio_unselected()
			return
		# Evaluate all AGs and maybe change selection
		(best, prio) = self.ag_best()
		if (not self.selected_ag or
		    not self.selected_ag.selectable() or
		    (self.selected_ag.select_priority() < prio)):
			self.ag_select_changed(best)
		if not self.selected_ag:
			self.ag_stop_audio_unselected()

	def ag_select_changed(self, ag, lock = False):
		if not ag:
			lock = False
		elif not lock and self.selected_ag == ag:
			lock = self.selected_ag_lock
		self.selected_ag = ag
		self.selected_ag_lock = lock
		self.ag_selector_set(ag)
		if ag:
			ag.disp.update_state()
		else:
			self.ag_configure_gui(None)
		if ag:
			self.ag_push_audio()

	def ag_ring(self, ag):
		if (self.default_ringtone and
		    self.soundstate == 2):
			# Try to play the ring tone
			try:
				self.soundio.FileStart(self.default_ringtone,
						       False)
				self.ag_play_target = ag
			except: pass
	def ag_ring_stop(self, ag):
		if (hasattr(self, 'ag_play_target') and
		    self.ag_play_target == ag):
			del self.ag_play_target
			self.soundio.Stop()

	def ag_config_has_option(self, addr, name):
		section = 'ag_' + self.addr_transform(addr)
		if not self.configfile.has_section(section):
			return False
		return self.configfile.has_option(section, name)

	def ag_config_get_option(self, addr, name):
		section = 'ag_' + self.addr_transform(addr)
		if not self.configfile.has_section(section):
			return None
		return self.configfile.get(section, name, raw=True)

	def ag_config_set_option(self, addr, name, value):
		section = 'ag_' + self.addr_transform(addr)
		if not self.configfile.has_section(section):
			self.configfile.add_section(section)
		self.configfile.set(section, name, value)
		self.configfile.write(open(self.configfile_name, 'w+'))

	def ag_add_to_config(self, addr):
		self.configfile.set('devices', self.addr_transform(addr), True)
		self.configfile.write(open(self.configfile_name, 'w+'))

	def ag_remove_from_config(self, addr):
		self.configfile.remove_option('devices',
					      self.addr_transform(addr))
		section = 'ag_' + self.addr_transform(addr)
		self.configfile.remove_section(section)
		self.configfile.write(open(self.configfile_name, 'w+'))

	def ag_update_history(self, addr, name):
		addrx = self.addr_transform(addr)
		has = self.configfile.has_option('history', addrx)
		if not name and has:
			return
		if has and name == self.configfile.get('history', addrx):
			return
		if not name:
			name = addr
		self.configfile.set('history', addrx, name)
		self.configfile.write(open(self.configfile_name, 'w+'))

	def ag_history_getname(self, addr):
		addrx = self.addr_transform(addr)
		if not self.configfile.has_option('history', addrx):
			return None
		return self.configfile.get('history', addrx)

	def add_audiogateway(self, addr, selectit):
		agpath = None
		try:
			if selectit:
				self.ags_added[addr] = 2
			else:
				self.ags_added[addr] = 1
			# We will use the accompanying AudioGatewayAdded
			# signal to construct the AudioGateway object above
			agpath = self.hfpd.AddDevice(addr, False)
		except:
			del self.ags_added[addr]
			self.error_message(_('Could not attach Audio Gateway'))

	def hfpd_log_message(self, level, msg):
		print 'HFPD: ' + msg

	def digit_button_char(self, but):
		assert but.get_name()[:11] == 'DigitButton'
		text = but.get_name()[11:]
		if text == 'Ast':
			text = '*'
		elif text == 'Pnd':
			text = '#'
		return text

	def digit_button_output_char(self, char):
		entry = self['PhoneNumEntry']
		entry.set_text(entry.get_text() + char)
	def digit_button_plus_timeout_cancel(self):
		if hasattr(self, 'digit_plus_timeout'):
			gobject.source_remove(self.digit_plus_timeout)
			del self.digit_plus_timeout
	def digit_button_plus_timeout_set(self, timeout):
		self.digit_plus_timeout = gobject.timeout_add(
			timeout, self.digit_button_plus_timeout)
	def digit_button_plus_timeout(self):
		self.digit_button_plus_timeout_cancel()
		self.nested = True
		self['DigitButton0'].released()
		self.nested = False
		self.digit_button_output_char('+')
		return False
	def digit_button_pressed(self, but):
		ag = self.selected_ag
		text = self.digit_button_char(but)
		if not ag or not ag.can_dtmf():
			if text == '0' and ag and ag.can_dial():
				self.digit_button_plus_timeout_set(1000)
			return
		ag.dtmf(text)
	def digit_button_released(self, but):
		if self.nested:
			return
		text = self.digit_button_char(but)
		if text == '0':
			self.digit_button_plus_timeout_cancel()
	def digit_button_clicked(self, but):
		if self.nested:
			return
		ag = self.selected_ag
		if not ag or not ag.can_dial():
			return
		text = self.digit_button_char(but)
		self.digit_button_output_char(text)
	def phone_num_changed(self, widget):
		self.ag_configure_gui(self.selected_ag)
	def phone_num_bs(self, but):
		entry = self['PhoneNumEntry']
		entry.set_text(entry.get_text()[:-1])
	def ag_configure_gui(self, ag):
		sel_dis = False
		connect = False
		connect_ip = False
		digits = False
		do_notebook = False
		dial = False
		dial_gray = False
		hold = False
		swap = False
		hangup = False
		audio = False
		audio_ip = False
		audio_dn = False
		voicerecog = False
		voicerecog_dn = False
		record = False
		record_dn = False
		mainstatus = False
		status_page = 0
		battstatus = False
		batt_page = 0
		message = False
		if ag:
			mainstatus = True
			if not ag.is_connected():
				connect = True
				connect_ip = ag.is_connecting() or self.nobt
				status_page = 8
			else:
				status_page = 7
				if ag.has_service():
					status_page = ag.signal_level()
					if status_page < 0 or status_page > 5:
						status_page = 6
				digits = ag.can_dial() or ag.can_dtmf()
				dial = ag.can_dial()
				hold = ag.can_hold()
				do_notebook = not ag.can_dial()
				hangup = not dial
				if hold and ag.has_waiting_call():
					swap = True
					hold = False
				audio = True
				if ag.has_audio():
					audio_dn = True
				elif ag.audio_connecting():
					audio_ip = True
				message = ag.has_message()
				batt_page = ag.battery_level()
				battstatus = True
				if batt_page < 0 or batt_page > 5:
					batt_page = 0
					battstatus = False
				if (ag.has_voice_recognition() and
				    ag.can_dial()):
					voicerecog = True
					voicerecog_dn = (
						ag.get_voice_recognition())

		# Display the "no bluetooth" icon if BT is disconnected
		# and the AG is not connected
		if self.nobt and (not ag or (not ag.is_connected() and
					     not ag.is_connecting())):
			mainstatus = True
			status_page = 9
			sel_dis = True

		# Gray out the dial button if the UI does not have a
		# phone number entered or a contact selected
		if dial:
			pad = True
			if not do_notebook and self.frontpage == 1:
				pad = False
			if pad:
				dial_gray = not bool(self['PhoneNumEntry'].
						     get_text())
			else:
				sel = self['ContactsList'].get_selection()
				dial_gray = (sel.count_selected_rows() == 0)

		def set_vis(widget, visible):
			if visible: widget.show()
			else: widget.hide()

		self.nested = True
		self['AgSelector'].set_sensitive(not sel_dis)
		self['DigitButtonBox'].set_sensitive(digits)
		self['PhoneNumEntryBox'].set_sensitive(dial)
		set_vis(self['AgConnect'], connect)
		self['AgConnect'].set_sensitive(not connect_ip)
		set_vis(self['AgHangUp'], hangup)
		set_vis(self['AgHoldCall'], hold)
		set_vis(self['AgSwapCall'], swap)
		set_vis(self['AgDial'], dial)
		self['AgDial'].set_sensitive(not dial_gray)
		set_vis(self['AgRedial'], dial)
		set_vis(self['AgAudioToggle'], audio)
		self['AgAudioToggle'].set_active(audio_dn)
		self['AgAudioToggle'].set_sensitive(not audio_ip)
		set_vis(self['AgVoiceRecogToggle'], voicerecog)
		self['AgVoiceRecogToggle'].set_active(voicerecog_dn)
		set_vis(self['RecordCall'], record)
		self['RecordCall'].set_active(record_dn)
		set_vis(self['AgMainStatus'], mainstatus)
		self['AgMainStatus'].set_current_page(status_page)
		set_vis(self['AgBatteryStatus'], battstatus)
		self['AgBatteryStatus'].set_current_page(batt_page)
		set_vis(self['MessageIndicator'], message)
		self.nested = False
		if do_notebook:
			self.frontnotebook_set(0)
		else:
			self.frontnotebook_set(self.frontpage)

	def bar_ag_dial_clicked(self, widget):
		ag = self.selected_ag
		if not ag:
			return
		if not ag.can_dial():
			return
		if self.frontpage == 0:
			entry = self['PhoneNumEntry']
			num = entry.get_text()
			entry.set_text('')
			if not num:
				return
			ag.dial(num)
		else:
			sel = self['ContactsList'].get_selection()
			(model, iterx) = sel.get_selected()
			if not iterx:
				return
			vcard = model.get_value(iterx, 0)
			nums = vcard.findtels(None)
			if not len(nums):
				pass
			elif len(nums) == 1:
				num = vcard.gettel(None)
				ag.dial(num)
			else:
				# Ask the user to pick a number
				self.choose_number_open(vcard)

	def bar_ag_scan_clicked(self, widget):
		self.scan_open()
	def bar_ag_connect_clicked(self, widget):
		try:
			if self.selected_ag:
				self.selected_ag.connect()
		except dbus.exceptions.DBusException, (ex):
			self.error_message(
				_('Could not attach Audio Gateway: %s') %
				str(ex))

	def frontnotebook_set(self, page):
		dialpad_but = False
		contacts_but = False
		page = 0
		if page == 0:
			#contacts_but = True
			if (self.selected_ag and
			    self.selected_ag.is_connected() and
			    not self.selected_ag.can_dial()):
				contacts_but = False
				pass
		elif page == 1:
			dialpad_but = True
			
		def set_vis(widget, visible):
			if visible: widget.show()
			else: widget.hide()

		self['FrontNotebook'].set_current_page(page)
		set_vis(self['BarDialPad'], dialpad_but)
		set_vis(self['BarContacts'], contacts_but)

	def bar_dialpad_clicked(self, widget):
		self.frontpage = 0
		self.ag_configure_gui(self.selected_ag)

	def bar_contacts_clicked(self, widget):
		self.frontpage = 1
		self.ag_configure_gui(self.selected_ag)

	def bar_ag_hangup_clicked(self, widget):
		ag = self.selected_ag
		if not ag:
			return
		ag.hangup()
	def bar_ag_swaphold_clicked(self, widget):
		ag = self.selected_ag
		if not ag or not ag.can_hold():
			return
		ag.swap_hold_active()
	def bar_ag_redial_clicked(self, widget):
		ag = self.selected_ag
		if not ag or not ag.can_dial():
			return
		ag.redial()
	def bar_ag_audio_toggled(self, widget):
		if not self.selected_ag:
			return
		if not widget.get_active():
			if (self.selected_ag.has_audio() or
			    self.selected_ag.audio_connecting()):
				self.selected_ag.close_audio()
			return
		if (self.selected_ag.has_audio() or
		    self.selected_ag.audio_connecting()):
			return
		try:
			self.selected_ag.open_audio()
		except dbus.exceptions.DBusException, (ex):
			self.error_message(
				_('Could not open audio connection: %s') %
				str(ex))
			widget.set_active(False)
	def bar_ag_voice_recognition_toggled(self, widget):
		if (self.nested or
		    not self.selected_ag or
		    not self.selected_ag.has_voice_recognition()):
			return
		self.selected_ag.set_voice_recognition(widget.get_active())
	def bar_config_clicked(self, widget):
		self.config_open()

	def contacts_new_clicked(self, widget):
		pass
	def contacts_edit_clicked(self, widget):
		pass
	def contacts_delete_clicked(self, widget):
		pass
	def contacts_select(self, widget):
		self.ag_configure_gui(self.selected_ag)
	def contacts_lookup(self, num_norm):
		(vcard, types) = self.vcards.search_tel(num_norm)
		if not vcard:
			return None
		nfmt = _('%s (Home)')
		for x in types:
			if x == 'cell':
				nfmt = _('%s (Mobile)')
			elif x == 'work':
				nfmt = _('%s (Work)')
		return nfmt % vcard.getname()

	def choose_number_open(self, vcard):
		tel_cell = vcard.gettel('cell')
		tel_home = vcard.gettel('home')
		tel_work = vcard.gettel('work')

		def fix_button(prefix, tel):
			prefix = 'ChooseNumber' + prefix
			if tel:
				self[prefix + 'Label'].set_text(tel)
				self[prefix].show()
			else:
				self[prefix].hide()
		fix_button('Mobile', tel_cell)
		fix_button('Home', tel_home)
		fix_button('Work', tel_work)
		self.topnotebook_set(self['ChooseNumberTab'])
	def choose_number_done(self):
		self.topnotebook_set(None)
	def choose_number_cancel_clicked(self, widget):
		self.choose_number_done()
	def choose_number_finish(self, num):
		self.choose_number_done()
		if self.selected_ag and self.selected_ag.can_dial():
			self.selected_ag.dial(num)
	def choose_number_mobile_clicked(self, widget):
		num = self['ChooseNumberMobileLabel'].get_text()
		self.choose_number_finish(num)
	def choose_number_home_clicked(self, widget):
		num = self['ChooseNumberHomeLabel'].get_text()
		self.choose_number_finish(num)
	def choose_number_work_clicked(self, widget):
		num = self['ChooseNumberWorkLabel'].get_text()
		self.choose_number_finish(num)
	def hfpd_system_msg_clear(self):
		if hasattr(self, 'system_msg'):
			self['StatusBar'].remove(self.system_ctx,
						 self.system_msg)
			del self.system_msg
		if hasattr(self, 'system_msg_timeout'):
			gobject.source_remove(self.system_msg_timeout)
			del self.system_msg_timeout
		return False
	def hfpd_system_msg(self, msg, timeout):
		self.hfpd_system_msg_clear()
		self.system_msg = self['StatusBar'].push(self.system_ctx, msg)
		if timeout:
			self.system_msg_timeout = gobject.timeout_add(
				timeout, self.hfpd_system_msg_clear)

	def hfpd_system_state(self, state):
		if not state:
			self.nobt = True
			self.hfpd_system_msg(_('Bluetooth Unavailable'), None)
		else:
			self.nobt = False
			self.hfpd_system_msg_clear()
		self.ag_configure_gui(self.selected_ag)

	def topnotebook_set(self, page):
		nb = self['HiddenNotebook']
		if not hasattr(self, 'notebook_stack'):
			self.notebook_stack = []
		if page:
			self.notebook_stack.append(nb.get_current_page())
			nb.set_current_page(nb.page_num(page))
		elif len(self.notebook_stack):
			nb.set_current_page(self.notebook_stack.pop())
		else:
			nb.set_current_page(0)
	def topnotebook_get(self):
		nb = self['HiddenNotebook']
		return nb.get_nth_page(nb.get_current_page())

	def devices_open(self):
		self.topnotebook_set(self['DevicesTab'])
	def devices_close_clicked(self, widget):
		self.topnotebook_set(None)
	def devices_add_clicked(self, widget):
		self.scan_open()
	def devices_delete_clicked(self, widget):
		if self.devices_ag:
			self.ag_remove_from_config(self.devices_ag.addr)
			self.devices_ag.do_remove()
	def devices_connect_clicked(self, widget):
		if self.devices_ag:
			self.devices_ag.connect()
	def devices_disconnect_clicked(self, widget):
		if self.devices_ag:
			self.devices_ag.disconnect()
	def devices_autoconnect_toggled(self, widget, value):
		if self.nested or not widget.get_active():
			return
		if self.devices_ag:
			self.devices_ag.set_autoreconnect(value)
	def devices_autoconnect_never_toggled(self, widget):
		self.devices_autoconnect_toggled(widget, 0)
	def devices_autoconnect_disabled_toggled(self, widget):
		self.devices_autoconnect_toggled(widget, 1)
	def devices_autoconnect_enabled_toggled(self, widget):
		self.devices_autoconnect_toggled(widget, 2)
	def devices_select(self, selection):
		if not selection.count_selected_rows():
			self.devices_ag = None
			self['DevicesDevAddr'].set_text('')
			self['DevicesConnect'].hide()
			self['DevicesDisconnect'].show()
			self['DevicesProperties'].set_sensitive(False)
			return
		(store, iter) = selection.get_selected()
		disp = store.get_value(iter, 2)
		self.devices_ag = disp.ag
		disp.devices_state()
		self['DevicesProperties'].show()

	def newdevice_open(self):
		tab = self['NewDeviceTab']
		if self.topnotebook_get() == tab:
			return
		itx = self.ag_list_new.get_iter_root()
		if itx:
			self['NewDeviceList'].get_selection().select_iter(itx)
		self.topnotebook_set(tab)
	def newdevice_close_clicked(self, widget):
		self.topnotebook_set(None)
	def newdevice_add_clicked(self, widget):
		ag = self.newdevice_ag
		self.add_audiogateway(ag.addr, False)
		ag.update_mine()
		self.ag_add_to_config(ag.addr)
		ag.set_autoreconnect(2)
		self.ag_select_changed(ag)
	def newdevice_clear_clicked(self, widget):
		xiter = self.ag_list_new.get_iter_root()
		while xiter:
			disp = self.ag_list_new.get_value(xiter, 2)
			disp.ag.do_remove()
			xiter = self.ag_list_new.iter_next(xiter)
	def newdevice_select(self, selection):
		if not selection.count_selected_rows():
			self['NewDeviceAdd'].set_sensitive(False)
			return
		(store, iter) = selection.get_selected()
		disp = store.get_value(iter, 2)
		self.newdevice_ag = disp.ag
		self['NewDeviceAdd'].set_sensitive(True)
	def newdevice_row_added(self, list, path, iter):
		self['NewDeviceClear'].set_sensitive(True)
	def newdevice_row_removed(self, list, path):
		if not list.get_iter_root():
			self['NewDeviceClear'].set_sensitive(False)
			tab = self['NewDeviceTab']
			if self.topnotebook_get() == tab:
				self.topnotebook_set(None)

	def inquiry_show_activity(self):
		self['ScanActivityIndicator'].pulse()
		return True
	def inquiry_started(self):
		self.inquiry = True
		self['ScanRestart'].set_sensitive(False)
		self['ScanActivityIndicator'].set_fraction(0)
		self['ScanActivity'].show()
		self.inquiry_animation_timeout = gobject.timeout_add(
			175, self.inquiry_show_activity)

	def inquiry_stopped(self):
		self.inquiry = False
		self['ScanActivity'].hide()
		self['ScanRestart'].set_sensitive(True)
		if hasattr(self, 'inquiry_animation_timeout'):
			gobject.source_remove(self.inquiry_animation_timeout)
			del self.inquiry_animation_timeout

	def scan_restart(self):
		self.scanresults.clear()
		self['ScanAdd'].set_sensitive(False)
		try: self.hfpd.StartInquiry()
		except: return False
		self.inquiry_started()
		return True

	def scan_open(self):
		if not self.scan_restart():
			self.error_message(_('Could not start inquiry'))
			return
		self.topnotebook_set(self['ScanTab'])
	def scan_close(self):
		self.topnotebook_set(None)
		if self.inquiry:
			try:
				self.hfpd.StopInquiry()
			except:
				pass
			self.inquiry_stopped()
	def scan_create_session(self, store, iter):
		# Create a session for the selected bdaddr
		self.scan_close()
		assert store and iter
		addr = store.get_value(iter, 2)
		self.ag_add_to_config(addr)
		self.ag_config_set_option(addr, 'autoreconnect', 2)
		self.add_audiogateway(addr, True)
	def scan_add_clicked(self, widget):
		assert (self.scanselect.count_selected_rows() <= 1)
		(store, iter) = self.scanselect.get_selected()
		self.scan_create_session(store, iter)
	def scan_close_clicked(self,widget):
		self.scan_close()
	def scan_history_clicked(self, widget):
		self.history_open()
	def scan_restart_clicked(self, widget):
		if not self.scan_restart():
			self.error_message(_('Could not start inquiry'))
	def scan_select(self, selection):
		if not selection.count_selected_rows():
			self['ScanAdd'].set_sensitive(False)
		else:
			self['ScanAdd'].set_sensitive(True)	
	class NameResolveReq:
		def __init__(self, hfc, addr, store):
			self.addr = addr
			self.store = store
			self.hfc = hfc
		def complete(self, name):
			ix = self.store.get_iter_first()
			while ix:
				if self.store.get_value(ix, 2) == self.addr:
					self.store.set_value(ix, 0, name)
				ix = self.store.iter_next(ix)
			self.hfc.ag_update_history(self.addr, name)
			self.hfc = None
		def failed(self, name):
			ix = self.store.get_iter_first()
			while ix:
				if self.store.get_value(ix, 2) == self.addr:
					name = self.store.get_value(ix, 2)
					name += ' ' + _('(unknown name)')
					self.store.set_value(ix, 0, name)
				ix = self.store.iter_next(ix)
			self.hfc = None
	def hfpd_inquiry_result(self, addr, devclass):
		if self.inquiry:
			self.scanresults.append(row=[addr, devclass, addr])
		self.ag_update_history(addr, None)
	def hfpd_inquiry_complete(self, sval):
		if sval:
			return
		if not self.inquiry:
			return
		self.inquiry_stopped()
		ix = self.scanresults.get_iter_first()
		while ix:
			addr = self.scanresults.get_value(ix, 2)
			req = self.NameResolveReq(self, addr, self.scanresults)
			self.hfpd.GetName(addr,
					  reply_handler=req.complete,
					  error_handler=req.failed)
			ix = self.scanresults.iter_next(ix)

	# History box
	def history_open(self):
		self.historyresults.clear()
		self['HistoryOK'].set_sensitive(False)

		# Populate the device history results from the config file
		ags = self.configfile.items('history')
		for x in ags:
			addr = self.addr_untransform(x[0])
			name = x[1]
			if not name:
				name = addr
			else:
				name = "%s (%s)" % (name, addr)
			self.historyresults.append(row=[name, 0, addr])
		self.topnotebook_set(self['HistoryTab'])
	def history_close(self):
		self.topnotebook_set(None)
	def history_ok_clicked(self, widget):
		self.history_close()
		assert self.historyselect.count_selected_rows() <= 1
		(store, iter) = self.historyselect.get_selected()
		self.scan_create_session(store, iter)
	def history_cancel_clicked(self, widget):
		self.history_close()
	def history_select(self, selection):
		if not selection.count_selected_rows():
			self['HistoryOK'].set_sensitive(False)
		else:
			self['HistoryOK'].set_sensitive(True)	

	# Configuration dialog related methods
	def config_open(self):
		savecfg = self.config_get_vals()
		savecfg['packetintervalsave'] = savecfg['packetinterval']
		savecfg['jitterwindowsave'] = savecfg['jitterwindow']
		savecfg['minbufferfillsave'] = savecfg['minbufferfill']
		savecfg['echocancelsave'] = savecfg['echocancel']
		if savecfg['echocancelsave'] == 0:
			savecfg['echocancelsave'] = 100
		savecfg['dereverbsave'] = savecfg['dereverb']
		savecfg['dereverbdecaysave'] = savecfg['dereverbdecay']
		self.config = copy.copy(savecfg)
		if savecfg['secmode'] == 0:
			self['ConfigSecNone'].set_active(True)
		elif savecfg['secmode'] == 1:
			self['ConfigSecAuth'].set_active(True)
		else:
			self['ConfigSecCrypt'].set_active(True)
		self['ConfigAcceptUnknown'].set_active(
			savecfg['acceptunknown'])
		self['ConfigVoicePersist'].set_active(
			savecfg['voicepersist'])
		self['ConfigDriver'].get_model().clear()
		for x in savecfg['drivers']:
			self['ConfigDriver'].append_text(x)
		self['ConfigDriver'].set_active(savecfg['driver'])
		self['ConfigDriverDevList'].child.set_text(
			savecfg['driveropts'])
		self.config_packet_interval(savecfg['packetinterval'])
		self.config_jitter_window(savecfg['jitterwindow'])
		self.config_min_out_buffer(savecfg['minbufferfill'])
		self['ConfigDenoise'].set_active(savecfg['denoise'])
		self.config_echo_cancel(savecfg['echocancel'])
		self['ConfigAutoGain'].set_value(savecfg['autogain'])
		if self.config['dereverb'] == 0:
			self['ConfigDereverb'].set_active(False)
			self['ConfigDereverbValue'].set_sensitive(False)
			self['ConfigDereverbValue'].set_value(0.0)
			self['ConfigDereverbDecay'].set_sensitive(False)
			self['ConfigDereverbDecay'].set_value(0.0)
			self['ConfigDereverbValueLabel'].set_text('')
			self['ConfigDereverbDecayLabel'].set_text('')
		else:
			self['ConfigDereverb'].set_active(True)
			self['ConfigDereverbValue'].set_sensitive(True)
			self['ConfigDereverbValue'].set_value(
				self.config['dereverb'])
			self['ConfigDereverbDecay'].set_sensitive(True)
			self['ConfigDereverbDecay'].set_value(
				self.config['dereverbdecay'])
		cmd = self.command_audio_attach_acmd
		if cmd:
			self['ConfigAudioAttachCommand'].set_text(cmd)
		cmd = self.command_audio_attach_dcmd
		if cmd:
			self['ConfigAudioDetachCommand'].set_text(cmd)
		cmd = self.default_ringtone
		if cmd:
			self['ConfigRingerFile'].set_filename(cmd)
		self.config_savecfg = savecfg
		self.topnotebook_set(self['ConfigTab'])
	def config_close(self):
		self['ConfigFeedbackTest'].set_active(False)
		del self.config
		if hasattr(self, 'loopback_active'):
			del self.loopback_active
		if hasattr(self, 'config_savecfg'):
			del self.config_savecfg
		self.topnotebook_set(None)
	def config_ok_clicked(self, widget):
		try: self.config_save_vals()
		except dbus.exceptions.DBusException, (ex):
			self.error_message(str(ex))
		self.config_close()
	def config_cancel_clicked(self, widget):
		# Restore the save values
		try: self.config_restore_vals(self.config_savecfg)
		except dbus.exceptions.DBusException, (ex):
			self.error_message(str(ex))
		self.config_close()
	def config_init_clicked(self, widget):
		self.initconf_open()
	def config_devices_clicked(self, widget):
		self.devices_open()
	def config_get_vals(self):
		spr = self.soundio_props.GetAll(
			self.HFPD_SOUNDIO_INTERFACE_NAME)
		hfpr = self.hfpd_props.GetAll(
			self.HFPD_HANDSFREE_INTERFACE_NAME)

		vals = dict()
		vals['secmode'] = int(hfpr['SecMode'])
		vals['acceptunknown'] = hfpr['AcceptUnknown']
		vals['voicepersist'] = hfpr['VoicePersist']
		vals['drivers'] = []
		vals['driver'] = 0
		index = 0
		for x in spr['Drivers']:
			vals['drivers'].append(str(x[0]))
			if (vals['drivers'][index].lower() ==
			    str(spr['DriverName']).lower()):
				vals['driver'] = index
			index += 1
		vals['driveropts'] = str(spr['DriverOpts'])
		vals['packetinterval'] = int(spr['PacketIntervalHint'])
		vals['jitterwindow'] = spr['JitterWindowHint']
		vals['minbufferfill'] = spr['MinBufferFillHint']
		vals['denoise'] = spr['Denoise']
		vals['echocancel'] = int(spr['EchoCancelTail'])
		vals['autogain'] = int(spr['AutoGain']) / 1000
		vals['dereverb'] = float(spr['DereverbLevel'])
		vals['dereverbdecay'] = float(spr['DereverbDecay'])
		vals['ringer'] = ''
		return vals
	def config_save_vals(self):
		self['ConfigFeedbackTest'].set_active(False)
		if self['ConfigSecNone'].get_active():
			secmode = 0
		elif self['ConfigSecAuth'].get_active():
			secmode = 1
		else:
			secmode = 2
		self.setprop_hfpd('SecMode', dbus.Byte(secmode))
		self.setprop_hfpd('AcceptUnknown', dbus.Boolean(
			self['ConfigAcceptUnknown'].get_active()))
		self.setprop_hfpd('VoicePersist', dbus.Boolean(
			self['ConfigVoicePersist'].get_active()))
		self.soundio.SetDriver(self['ConfigDriver'].get_active_text(),
				       self['ConfigDriverDevList'].child.
				       get_text())
		self.hfpd.SaveSettings()
		cmd = self['ConfigAudioAttachCommand'].get_text()
		self.command_audio_attach_acmd = cmd
		self.configfile.set('options', 'attach_command', cmd)
		cmd = self['ConfigAudioDetachCommand'].get_text()
		self.command_audio_attach_dcmd = cmd
		self.configfile.set('options', 'detach_command', cmd)
		cmd = self['ConfigRingerFile'].get_filename()
		if self.default_ringtone != cmd:
			self.default_ringtone = cmd
			self.configfile.set('options', 'default_ringtone', cmd)
		self.configfile.write(open(self.configfile_name, 'w+'))
	def config_restore_vals(self, vals):
		self['ConfigFeedbackTest'].set_active(False)
		self.soundio.SetDriver(self.config['drivers'][
			self.config['driver']],
				       self.config['driveropts'])
		self.setprop_soundio('PacketIntervalHint',
				      dbus.UInt32(vals['packetinterval']))
		self.setprop_soundio('JitterWindowHint',
				      dbus.UInt32(vals['jitterwindow']))
		self.setprop_soundio('MinBufferFillHint',
				      dbus.UInt32(vals['minbufferfill']))
		self.setprop_soundio('Denoise', dbus.Boolean(vals['denoise']))
		self.setprop_soundio('EchoCancelTail',
				      dbus.UInt32(vals['echocancel']))
		self.setprop_soundio('AutoGain',
				      dbus.UInt32(vals['autogain'] * 1000))
		self.setprop_soundio('DereverbLevel',
				      dbus.Double(vals['dereverb']))
		self.setprop_soundio('DereverbDecay',
				      dbus.Double(vals['dereverbdecay']))
	def config_driver_changed(self, widget):
		# Ask for the device list
		model = self['ConfigDriverDevList'].get_model()
		model.clear()
		drivername = self['ConfigDriver'].get_active_text()
		devlist = None
		if not drivername:
			return
		try:
			devlist = self.soundio.ProbeDevices(drivername)
		except dbus.exceptions.DBusException, (ex):
			print _('Device probe failed: %s') % str(ex)
		if devlist:
			for x in devlist:
				model.append([x[0], x[1]])
	def constrain(self, val, min, max):
		if val < min:
			val = min
		elif val > max:
			val = max
		return val
	def value_transform(self, val, exp, minv, maxv):
		val = self.constrain(val, 0.0, 1.0)
		return int(minv + (pow(val, exp) * (maxv - minv)))
	def value_untransform(self, val, exp, minv, maxv):
		val = self.constrain(val, minv, maxv)
		return pow(float(val - minv) / (maxv - minv), 1.0 / exp)
	def format_ms(self, value):
		return _('%dms') % value
	def config_packet_interval(self, value):
		self.nested = True
		if value != 0:
			value = self.constrain(value, 5, 1000)
			self['ConfigPacketIntervalHint'].set_active(True)
			self['ConfigPacketIntervalLabel'].set_text(
				self.format_ms(value))
			self['ConfigPacketInterval'].set_sensitive(True)
			self['ConfigPacketInterval'].set_value(
				self.value_untransform(value, 3, 5, 1000))
		else:
			value = 0
			self['ConfigPacketIntervalHint'].set_active(False)
			self['ConfigPacketIntervalLabel'].set_text('')
			self['ConfigPacketInterval'].set_sensitive(False)
			self['ConfigPacketInterval'].set_value(0)
		if self.config['packetinterval'] != value:
			self.config['packetinterval'] = value
			self.setprop_soundio('PacketIntervalHint',
					      dbus.UInt32(value))
		self.nested = False
	def config_packet_interval_toggled(self, widget):
		if widget.get_active():
			value = self.config['packetintervalsave']
			value = self.constrain(value, 5, 1000)
		else:
			self.config['packetintervalsave'] = (
				self.config['packetinterval'])
			value = 0
		if not self.nested:
			self.config_packet_interval(value)
	def config_packet_interval_changed(self, widget):
		value = self.value_transform(widget.get_value(), 3, 5, 1000)
		if not self.nested:
			self.config_packet_interval(value)
	def config_jitter_window(self, value):
		self.nested = True
		if value != 0:
			value = self.constrain(value, 5, 1000)
			self['ConfigJitterWindowHint'].set_active(True)
			self['ConfigJitterWindowLabel'].set_text(
				self.format_ms(value))
			self['ConfigJitterWindow'].set_sensitive(True)
			self['ConfigJitterWindow'].set_value(
				self.value_untransform(value, 3, 5, 1000))
		else:
			value = 0
			self['ConfigJitterWindowHint'].set_active(False)
			self['ConfigJitterWindowLabel'].set_text('')
			self['ConfigJitterWindow'].set_sensitive(False)
			self['ConfigJitterWindow'].set_value(0)
		if self.config['jitterwindow'] != value:
			self.config['jitterwindow'] = value
			self.setprop_soundio('JitterWindowHint',
					      dbus.UInt32(value))
		self.nested = False
	def config_jitter_window_toggled(self, widget):
		if widget.get_active():
			value = self.config['jitterwindowsave']
			value = self.constrain(value, 5, 1000)
		else:
			self.config['jitterwindowsave'] = (
				self.config['jitterwindow'])
			value = 0
		if not self.nested:
			self.config_jitter_window(value)
	def config_jitter_window_changed(self, widget):
		value = self.value_transform(widget.get_value(), 3, 5, 1000)
		if not self.nested:
			self.config_jitter_window(value)
	def config_min_out_buffer(self, value):
		self.nested = True
		if value != 0:
			value = self.constrain(value, 5, 1000)
			self['ConfigMinOutBufferHint'].set_active(True)
			self['ConfigMinOutBufferLabel'].set_text(
				self.format_ms(value))
			self['ConfigMinOutBuffer'].set_sensitive(True)
			self['ConfigMinOutBuffer'].set_value(
				self.value_untransform(value, 3, 5, 1000))
		else:
			value = 0
			self['ConfigMinOutBufferHint'].set_active(False)
			self['ConfigMinOutBufferLabel'].set_text('')
			self['ConfigMinOutBuffer'].set_sensitive(False)
			self['ConfigMinOutBuffer'].set_value(0)
		if self.config['minbufferfill'] != value:
			self.config['minbufferfill'] = value
			self.setprop_soundio('MinBufferFillHint',
					      dbus.UInt32(value))
		self.nested = False
	def config_min_out_buffer_toggled(self, widget):
		if widget.get_active():
			value = self.config['minbufferfillsave']
			value = self.constrain(value, 5, 1000)
		else:
			self.config['minbufferfillsave'] = (
				self.config['minbufferfill'])
			value = 0
		if not self.nested:
			self.config_min_out_buffer(value)
	def config_min_out_buffer_changed(self, widget):
		value = self.value_transform(widget.get_value(), 3, 5, 1000)
		if not self.nested:
			self.config_min_out_buffer(value)
	def config_loopback_toggled(self, widget):
		if not hasattr(self, 'loopback_active'):
			self.loopback_active = False
		if widget.get_active():
			if not self.loopback_active:
				try:
					self.soundio.SetDriver(self[
						'ConfigDriver'].
					       get_active_text(),
				       self['ConfigDriverDevList'].child.
							       get_text())
				except:
					widget.set_active(False)
					self.error_message(_('Cannot Apply '
						     'Sound Driver Settings'))
					return

				try:
					if self['Mute'].get_active():
						self.setprop_soundio(
							'Mute', False)
					self.soundio.LoopbackStart()
				except dbus.exceptions.DBusException, (ex):
					widget.set_active(False)
					self.error_message(_('Could not open '
							     'Audio Device:\n'
							     '%s') % str(ex))
					return

				self.loopback_active = True
				self.soundio_skew_monitor = (
					self.config_skew_notify)
				pkt = self.getprop_soundio('PacketInterval')
				self['ConfigRealPacketSizeLabel'].set_text(
					self.format_ms(pkt))
		else:
			if self.loopback_active:
				self.soundio.Stop()
				self.soundio_skew_monitor = None
				self.config_skew_clear(True)
				self.loopback_active = False
				self['ConfigRealPacketSizeLabel'].set_text('')
	def config_skew_clear(self, do_hide):
		if do_hide:
			self['ConfigSkewNotice'].hide()
			self['ConfigSkewNoticeLabel'].hide()
			self['ConfigSkewNoticeLabel'].set_text('')
		if hasattr(self, 'config_skew_timeout'):
			gobject.source_remove(self.config_skew_timeout)
			del self.config_skew_timeout
		return False
	def config_skew_notify(self, skewtype, count):
		timeout = 1500
		if skewtype == 0:
			msg = _('Sound Card Failure')
			timeout = 5000
		elif skewtype == 1:
			msg = _('Sound Card Overrun/Underrun: %d/sec') % count
		elif skewtype == 2:
			if count < 0:
				submsg = _('(Playback Slower)')
				count = -count
			else:
				submsg = _('(Capture Slower)')
			msg = ((_('Sound Card Capture/Playback Skew: '
				  '%1.3f%%') % count) + '\n' + submsg)
		else:
			return
		self.config_skew_clear(False)
		self['ConfigSkewNotice'].show()
		self['ConfigSkewNoticeLabel'].show()
		self['ConfigSkewNoticeLabel'].set_text(msg)
		self.config_skew_timeout = gobject.timeout_add(
			timeout, self.config_skew_clear, True)

	def config_denoise_changed(self, widget):
		value = widget.get_active()	
		if bool(self.config['denoise']) != value:
			self.config['denoise'] = value
			self.setprop_soundio('Denoise', dbus.Boolean(value))
	def config_echo_cancel(self, value):
		self.nested = True
		if value != 0:
			value = self.constrain(value, 5, 1000)
			self['ConfigEchoCancel'].set_active(True)
			self['ConfigEchoCancelTailLabel'].set_text(
				self.format_ms(value))
			self['ConfigEchoCancelTail'].set_sensitive(True)
			self['ConfigEchoCancelTail'].set_value(
				self.value_untransform(value, 3, 5, 1000))
		else:
			value = 0
			self['ConfigEchoCancel'].set_active(False)
			self['ConfigEchoCancelTailLabel'].set_text('')
			self['ConfigEchoCancelTail'].set_sensitive(False)
			self['ConfigEchoCancelTail'].set_value(0)
		if self.config['echocancel'] != value:
			self.config['echocancel'] = value
			self.setprop_soundio('EchoCancelTail',
					      dbus.UInt32(value))
		self.nested = False
	def config_echo_cancel_changed(self, widget):
		if widget.get_active():
			value = self.config['echocancelsave']
			value = self.constrain(value, 5, 1000)
		else:
			self.config['echocancelsave'] = (
				self.config['echocancel'])
			value = 0
		if not self.nested:
			self.config_echo_cancel(value)
	def config_echo_cancel_tail_changed(self, widget):
		value = self.value_transform(widget.get_value(), 3, 5, 1000)
		if not self.nested:
			self.config_echo_cancel(value)
	def config_auto_gain_changed(self, widget):
		value = int(widget.get_value())
		self['ConfigAutoGainLabel'].set_text(str(value))
		if self.config['autogain'] != value:
			self.config['autogain'] = value
			self.setprop_soundio('AutoGain',
					      dbus.UInt32(value * 1000))
	def config_dereverb_toggled(self, widget):
		value = widget.get_active()
		self['ConfigDereverbValue'].set_sensitive(value)
		self['ConfigDereverbDecay'].set_sensitive(value)
		if value:
			self['ConfigDereverbValue'].set_value(
				self.config['dereverbsave'])
			self.config_dereverb_value_changed(
				self['ConfigDereverbValue'])
			self['ConfigDereverbDecay'].set_value(
				self.config['dereverbdecaysave'])
			self.config_dereverb_decay_changed(
				self['ConfigDereverbDecay'])
		else:
			self.config['dereverbsave'] = self.config['dereverb']
			self['ConfigDereverbValue'].set_value(0.0)
			self['ConfigDereverbValueLabel'].set_text('')
			self.config['dereverb'] = 0.0
			self.config['dereverbdecaysave'] = self.config[
				'dereverbdecay']
			self['ConfigDereverbDecay'].set_value(0.0)
			self['ConfigDereverbDecayLabel'].set_text('')
	def config_dereverb_value_changed(self, widget):
		value = widget.get_value()	
		self['ConfigDereverbValueLabel'].set_text('%0.2f' % value)
		if self.config['dereverb'] != value:
			self.config['dereverb'] = value
			self.setprop_soundio('DereverbLevel',
					      dbus.Double(value))
	def config_dereverb_decay_changed(self, widget):
		value = widget.get_value()	
		self['ConfigDereverbDecayLabel'].set_text('%0.2f' % value)
		if self.config['dereverbdecay'] != value:
			self.config['dereverbdecay'] = value
			self.setprop_soundio('DereverbDecay',
					      dbus.Double(value))
	def config_ringer_file_set(self, widget):
		pass

	def config_dsptest_pageid(self, widget):
		return widget.get_current_page()
	def config_dsptest_open(self, widget):
		self['ConfigFeedbackTest'].set_active(False)
		try:
			self.soundio.SetDriver(self['ConfigDriver'].
					       get_active_text(),
				       self['ConfigDriverDevList'].child.
					       get_text())
		except:
			self.error_message(_('Cannot Apply Sound Driver '
					     'Settings'))
			return
		self['DspTestNotebook'].set_current_page(1)
		self['DspTestNotebook'].set_current_page(0)
		self.topnotebook_set(self['DspTestTab'])
		self['DspTestForward'].grab_focus()
	def connect_soundmonitor(self, to):
		if self.soundmonitor:
			self.soundmonitor.remove()
			self.soundmonitor = None
		if to:
			self.soundmonitor = self.soundio.connect_to_signal(
				"MonitorNotify", to)
	def config_dsptest_mon1(self, pos, amp):
		self['DspTestRecPosition1'].set_fraction(float(pos)/80000)
		self['DspTestMicVolume1'].set_fraction(
			pow(float(amp) / 65535, 0.33))
		if amp > self.maxvol:
			self.maxvol = amp
	def config_dsptest_mon2(self, pos, amp):
		self['DspTestRecPosition2'].set_fraction(float(pos)/80000)
		self['DspTestMicVolume2'].set_fraction(
			pow(float(amp) / 65535, 0.33))
	def config_dsptest_mon3(self, pos, amp):
		self['DspTestPlayPosition'].set_fraction(float(pos)/80000)
	def config_dsptest_set_buttons(self, back, fwd):
		self['DspTestBack'].set_sensitive(back)
		self['DspTestForward'].set_sensitive(fwd)
	def config_dsptest_back(self, widget):
		self['DspTestNotebook'].prev_page()
	def config_dsptest_forward(self, widget):
		self['DspTestNotebook'].next_page()
	def config_dsptest_setup(self, pageid):
		if not hasattr(self, 'membuf_active'):
			self.membuf_active = False
		if self.membuf_active:
			self.soundio.Stop()
		if pageid == 0:
			self.maxvol = 0
			self.config_dsptest_set_buttons(False, True)
		elif pageid == 1:
			self.config_dsptest_set_buttons(True, False)
			self['DspTestRecPosition1'].set_fraction(0.0)
			self.connect_soundmonitor(self.config_dsptest_mon1)
			self['DspTestRecStart1'].set_active(True)
		elif pageid == 2:
			self.config_dsptest_set_buttons(True, True)
			self['DspTestRecPosition2'].set_fraction(0.0)
			self.connect_soundmonitor(self.config_dsptest_mon2)
		elif pageid == 3:
			self.config_dsptest_set_buttons(True, True)
			self['DspTestPlayPosition'].set_fraction(0.0)
			self.connect_soundmonitor(self.config_dsptest_mon3)
			self['DspTestPlayStart'].set_active(True)
		else:
			self.config_dsptest_set_buttons(True, False)
	def config_dsptest_close(self, widget):
		self['DspTestRecStart1'].set_active(False)
		self['DspTestRecStart2'].set_active(False)
		self['DspTestPlayStart'].set_active(False)
		self.topnotebook_set(None)
		if hasattr(self, 'membuf_active'):
			if self.membuf_active:
				self.soundio.Stop()
			del self.membuf_active
		self.connect_soundmonitor(None)
		if hasattr(self, 'maxvol'):
			del self.maxvol
	def config_dsptest_switch_page(self, widget, page, page_num):
		self.config_dsptest_setup(page_num)
	def config_dsptest_xxx_toggled(self, widget, page, doin, doout):
		if widget.get_active():
			if doin and not doout:
				self.soundio.MembufClear()
			try:
				# 8KHz sample rate is standard
				# We request a 10 second, 80Ksamp buffer,
				# and 16 updates/sec.
				self.soundio.MembufStart(doin, doout,
							 80000, 500)
			except:
				widget.set_active(False)
				self.error_message(_('Could not open '
						     'Audio Device'))
				return
				
			self.membuf_active = True
			self.config_dsptest_set_buttons(True, True)
			return
		if self.membuf_active:
			try:
				self.soundio.Stop()
			except:
				pass
			self.membuf_active = False
	def config_dsptest_recstart1_toggled(self, widget):
		self.config_dsptest_xxx_toggled(widget,
						self['DspTestPage1'],
						True, False)
	def config_dsptest_recstart2_toggled(self, widget):
		self.config_dsptest_xxx_toggled(widget,
						self['DspTestPage2'],
						True, True)
	def config_dsptest_playstart_toggled(self, widget):
		self.config_dsptest_xxx_toggled(widget,
						self['DspTestPage3'],
						False, True)
	def soundio_state_changed(self, state):
		agpath = self.getprop_soundio('AudioGateway')
		self.soundstate = int(state)
		if self.soundstate == 2:
			if hasattr(self, 'ag_play_target'):
				del self.ag_play_target
			self.command_audio_attach(False)
			if (hasattr(self, 'loopback_active') and
			    self.loopback_active):
				self.loopback_active = False
				self['ConfigFeedbackTest'].set_active(False)
				self['ConfigRealPacketSizeLabel'].set_text('')
			if (hasattr(self, 'membuf_active') and
			    self.membuf_active):
				self.membuf_active = False
				self['DspTestRecStart1'].set_active(False)
				self['DspTestRecStart2'].set_active(False)
				self['DspTestPlayStart'].set_active(False)
				self['DspTestMicVolume1'].set_fraction(0.0)
				self['DspTestMicVolume2'].set_fraction(0.0)
			self.ag_push_audio()
		if self.soundstate == 3 or self.soundstate == 4:
			agpath = self.getprop_soundio('AudioGateway')
			if agpath:
				ag = self.ags[agpath]
				if ag:
					ag.soundio_state_changed()
	def soundio_stream_aborted(self, excode, descr):
		if (excode ==
		    'net.sf.nohands.hfpd.Error.SoundIoSoundCardFailed'):
			self.soundio_status_msg(
				_('Primary Sound Card Failure'), 5000)
		if self.soundio_skew_monitor:
			self.soundio_skew_monitor(0, 0)
		self.soundio_state_changed(2)

	def soundio_mute_changed(self, state):
		self['Mute'].set_active(bool(state))
	def mute_toggled(self, but):
		self.setprop_soundio('Mute', but.get_active())

	def soundio_status_msg_clear(self):
		if hasattr(self, 'soundio_status_msgid'):
			self['StatusBar'].remove(self.soundio_status_ctx,
						 self.soundio_status_msgid)
			del self.soundio_status_msgid
		if hasattr(self, 'soundio_status_timeout'):
			gobject.source_remove(self.soundio_status_timeout)
			del self.soundio_status_timeout
		if hasattr(self, 'soundio_skew_type'):
			del self.soundio_skew_type
		return False
	def soundio_status_msg(self, msg, timeout):
		self.soundio_status_msg_clear()
		self.soundio_status_msgid = self['StatusBar'].push(
			self.soundio_status_ctx,
			msg)
		if timeout:
			self.soundio_status_timeout = gobject.timeout_add(
				timeout, self.soundio_status_msg_clear)

	def soundio_skew_notify(self, skewtype, count):
		if (hasattr(self, 'soundio_skew_type') and
		    self.soundio_skew_type < skewtype):
			return
		if skewtype == 1:
			msg = _('Sound Card Overrun/Underrun')
		elif skewtype == 2:
			msg = _('Sound Card Playback/Capture Clock Skew')
		elif skewtype == 3:
			msg = _('Severe Bluetooth Playback/Capture Clock Skew')
		elif skewtype == 4:
			msg = _('Severe Sound Card / Bluetooth Clock Skew')
		else:
			return
		self.soundio_status_msg(msg, 5000)
		self.soundio_skew_type = skewtype
		if self.soundio_skew_monitor:
			self.soundio_skew_monitor(skewtype, count)

	def initconf_open(self):
		self['InitConfNotebook'].set_current_page(1)
		self['InitConfNotebook'].set_current_page(0)
		self.topnotebook_set(self['InitConfTab'])
		self['InitConfForward'].grab_focus()
	def initconf_close(self):
		self.topnotebook_set(None)

	def initconf_set_buttons(self, back, fwd):
		self['InitConfBack'].set_sensitive(back)
		self['InitConfForward'].set_sensitive(fwd)
	def initconf_done_clicked(self, widget):
		self.initconf_close()
	def initconf_back_clicked(self, widget):
		self['InitConfNotebook'].prev_page()
	def initconf_forward_clicked(self, widget):
		self['InitConfNotebook'].next_page()
	def initconf_switch_page(self, widget, page, page_num):
		if page_num == 0:
			self.initconf_set_buttons(False, True)
		elif page_num == 1:
			self.initconf_set_buttons(True, False)

if __name__ == "__main__":
	hwg = HfConsole()
	hwg.Start()
	gtk.main()
