Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py	(revision 4664)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py	(revision 4665)
@@ -35,24 +35,9 @@
 import buddy
 import endpoint
+import settings
 
 import os
 import traceback
 
-class SipTransportConfig:
-	def __init__(self):
-		#pj.PersistentObject.__init__(self)
-		self.type = pj.PJSIP_TRANSPORT_UNSPECIFIED;
-		self.cfg = pj.TransportConfig()
-		
-	def readObject(self, node):
-		child_node = node.readContainer("SipTransportConfig")
-		self.type = child_node.readInt("type")
-		self.cfg.readObject(child_node)
-	
-	def writeObject(self, node):
-		child_node = node.writeNewContainer("SipTransportConfig")
-		child_node.writeInt("type", self.type)
-		self.cfg.writeObject(child_node)
-	 
 	
 class Application(ttk.Frame):
@@ -88,99 +73,61 @@
 		
 		# Default config
-		self.epCfg = pj.EpConfig()
-		self.epCfg.uaConfig.threadCnt = 0;
-		self.epCfg.logConfig.writer = self.logger
-		self.epCfg.logConfig.filename = "pygui.log"
-		self.epCfg.logConfig.fileFlags = pj.PJ_O_APPEND
-		self.epCfg.logConfig.level = 5
-		self.epCfg.logConfig.consoleLevel = 5
-		
-		self.transportCfgs = []
-		t = SipTransportConfig()
-		t.type = pj.PJSIP_TRANSPORT_UDP
-		t.cfg.port = 0
-		self.transportCfgs.append(t)
-		t = SipTransportConfig()
-		t.type = pj.PJSIP_TRANSPORT_TCP
-		t.cfg.port = 0
-		self.transportCfgs.append(t)
-		
-	
+		self.appConfig = settings.AppConfig()
+		self.appConfig.epConfig.uaConfig.threadCnt = 0;
+		self.appConfig.epConfig.logConfig.writer = self.logger
+		self.appConfig.epConfig.logConfig.filename = "pygui.log"
+		self.appConfig.epConfig.logConfig.fileFlags = pj.PJ_O_APPEND
+		self.appConfig.epConfig.logConfig.level = 5
+		self.appConfig.epConfig.logConfig.consoleLevel = 5
+		
 	def saveConfig(self, filename='pygui.js'):
-		json = pj.JsonDocument()
-		
-		# Write endpoint config
-		json.writeObject(self.epCfg)
-		
-		# Write transport config
-		node = json.writeNewArray("transports")
-		for t in self.transportCfgs:
-			t.writeObject(node);
-		
-		# Write account configs
-		node = json.writeNewArray("accounts")
+		# Save disabled accounts since they are not listed in self.accList
+		disabled_accs = [ac for ac in self.appConfig.accounts if not ac.enabled]
+		self.appConfig.accounts = []
+		
+		# Get account configs from active accounts
 		for acc in self.accList:
-			acc_node = node.writeNewContainer("Account")
-			acc_node.writeObject(acc.cfg);
-
-			# Write buddy configs
-			buddy_node = acc_node.writeNewArray("buddies")
+			acfg = settings.AccConfig()
+			acfg.enabled = True
+			acfg.config = acc.cfg
 			for bud in acc.buddyList:
-				buddy_node.writeObject(bud.cfg)
-				
-		json.saveFile(filename)
+				acfg.buddyConfigs.append(bud.cfg)
+			self.appConfig.accounts.append(acfg)
+		
+		# Put back disabled accounts
+		self.appConfig.accounts.extend(disabled_accs)
+		# Save
+		self.appConfig.saveFile(filename)
 	
 	def start(self, cfg_file='pygui.js'):
 		# Load config
-		acc_cfgs = []
 		if cfg_file and os.path.exists(cfg_file):
-			json = pj.JsonDocument()
-			json.loadFile(cfg_file)
-			
-			# Load endpoint config
-			json.readObject(self.epCfg)
-			
-			# Load transport configs
-			node = json.readArray("transports")
-			if node.hasUnread():
-				self.transportCfgs = []
-				while node.hasUnread():
-					t = SipTransportConfig()
-					t.readObject(node)
-					self.transportCfgs.append(t)
-			
-			# Load account configs
-			node = json.readArray("accounts")
-			while node.hasUnread():
-				acc_node = node.readContainer("Account")
-				acc_cfg = pj.AccountConfig()
-				acc_cfg.readObject(acc_node)
-		
-				# Load buddy configs
-				buddy_cfgs = []
-				buddy_node = acc_node.readArray("buddies")
-				while buddy_node.hasUnread():
-					buddy_cfg = pj.BuddyConfig()
-					buddy_cfg.readObject(buddy_node)
-					buddy_cfgs.append(buddy_cfg)
-
-				acc_cfgs.append((acc_cfg, buddy_cfgs))
-				
+			self.appConfig.loadFile(cfg_file)
+
+		self.appConfig.epConfig.uaConfig.threadCnt = 0;
+		self.appConfig.epConfig.logConfig.writer = self.logger
+		self.appConfig.epConfig.logConfig.level = 5
+		self.appConfig.epConfig.logConfig.consoleLevel = 5
 				
 		# Initialize library
-		self.epCfg.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
-		self.ep.libInit(self.epCfg)
+		self.appConfig.epConfig.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
+		self.ep.libInit(self.appConfig.epConfig)
 		self.master.title('pjsua2 Demo version ' + self.ep.libVersion().full)
 		
 		# Create transports
-		for t in self.transportCfgs:
-			self.ep.transportCreate(t.type, t.cfg)
+		if self.appConfig.udp.enabled:
+			self.ep.transportCreate(self.appConfig.udp.type, self.appConfig.udp.config)
+		if self.appConfig.tcp.enabled:
+			self.ep.transportCreate(self.appConfig.tcp.type, self.appConfig.tcp.config)
+		if self.appConfig.tls.enabled:
+			self.ep.transportCreate(self.appConfig.tls.type, self.appConfig.tls.config)
 			
 		# Add accounts
-		for cfg, buddy_cfgs in acc_cfgs:
-			self._createAcc(cfg)
-			acc = self.accList[-1]
-			for buddy_cfg in buddy_cfgs:
-				self._createBuddy(acc, buddy_cfg)
+		for cfg in self.appConfig.accounts:
+			if cfg.enabled:
+				self._createAcc(cfg.config)
+				acc = self.accList[-1]
+				for buddy_cfg in cfg.buddyConfigs:
+					self._createBuddy(acc, buddy_cfg)
 				
 		# Start library
@@ -294,4 +241,5 @@
 		self.buddyMenu = tk.Menu(top, tearoff=False)
 		labels = ['Video call', 'Audio call', 'Send instant message', '-',
+			  'Subscribe', 'Unsubscribe', '-',
 			  'Settings...', '-',
 			  'Delete...']
@@ -420,4 +368,8 @@
 		elif label=='Send instant message':
 			pass
+		elif label=='Subscribe':
+			bud.subscribePresence(True)
+		elif label=='Unsubscribe':
+			bud.subscribePresence(False)
 		elif label=='Settings...':
 			subs = bud.cfg.subscribe
@@ -477,5 +429,7 @@
 	
 	def _onMenuSettings(self):
-		msgbox.showinfo(self.master.title(), 'Settings')
+		dlg = settings.Dialog(self, self.appConfig)
+		if dlg.doModal():
+			msgbox.showinfo(self.master.title(), 'You need to restart for new settings to take effect')
 	
 	def _onMenuSaveSettings(self):
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/settings.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/settings.py	(revision 4665)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/settings.py	(revision 4665)
@@ -0,0 +1,362 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 Teluu Inc. (http://www.teluu.com)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	import tkinter as tk
+	from tkinter import ttk
+	from tkinter import messagebox as msgbox
+else:
+	import Tkinter as tk
+	import tkMessageBox as msgbox
+	import ttk
+
+import pjsua2 as pj
+#import application
+
+# Transport setting
+class SipTransportConfig:
+	def __init__(self, type, enabled):
+		#pj.PersistentObject.__init__(self)
+		self.type = type
+		self.enabled = enabled
+		self.config = pj.TransportConfig()
+	def readObject(self, node):
+		child_node = node.readContainer("SipTransport")
+		self.type = child_node.readInt("type")
+		self.enabled = child_node.readBool("enabled")
+		self.config.readObject(child_node)
+	def writeObject(self, node):
+		child_node = node.writeNewContainer("SipTransport")
+		child_node.writeInt("type", self.type)
+		child_node.writeBool("enabled", self.enabled)
+		self.config.writeObject(child_node)
+
+# Account setting with buddy list
+class AccConfig:
+	def __init__(self):
+		self.enabled = True
+		self.config = pj.AccountConfig()
+		self.buddyConfigs = []
+	def readObject(self, node):
+		acc_node = node.readContainer("Account")
+		self.enabled = acc_node.readBool("enabled")
+		self.config.readObject(acc_node)
+		buddy_node = acc_node.readArray("buddies")
+		while buddy_node.hasUnread():
+			buddy_cfg = pj.BuddyConfig()
+			buddy_cfg.readObject(buddy_node)
+			self.buddyConfigs.append(buddy_cfg)
+	def writeObject(self, node):
+		acc_node = node.writeNewContainer("Account")
+		acc_node.writeBool("enabled", self.enabled)
+		self.config.writeObject(acc_node)
+		buddy_node = acc_node.writeNewArray("buddies")
+		for buddy in self.buddyConfigs:
+			buddy_node.writeObject(buddy)
+
+	
+# Master settings
+class AppConfig:
+	def __init__(self):
+		self.epConfig = pj.EpConfig()	# pj.EpConfig()
+		self.udp = SipTransportConfig(pj.PJSIP_TRANSPORT_UDP, True)
+		self.tcp = SipTransportConfig(pj.PJSIP_TRANSPORT_TCP, True)
+		self.tls = SipTransportConfig(pj.PJSIP_TRANSPORT_TLS, False)
+		self.accounts = []		# Array of AccConfig
+		
+	def loadFile(self, file):
+		json = pj.JsonDocument()
+		json.loadFile(file)
+		root = json.getRootContainer()
+		self.epConfig = pj.EpConfig()
+		self.epConfig.readObject(root)
+		
+		tp_node = root.readArray("transports")
+		self.udp.readObject(tp_node)
+		self.tcp.readObject(tp_node)
+		if tp_node.hasUnread():
+			self.tls.readObject(tp_node)
+			
+		acc_node = root.readArray("accounts")
+		while acc_node.hasUnread():
+			acfg = AccConfig()
+			acfg.readObject(acc_node)
+			self.accounts.append(acfg)
+			
+	def saveFile(self,file):
+		json = pj.JsonDocument()
+		
+		# Write endpoint config
+		json.writeObject(self.epConfig)
+		
+		# Write transport config
+		tp_node = json.writeNewArray("transports")
+		self.udp.writeObject(tp_node)
+		self.tcp.writeObject(tp_node)
+		self.tls.writeObject(tp_node)
+		
+		# Write account configs
+		node = json.writeNewArray("accounts")
+		for acc in self.accounts:
+			acc.writeObject(node)
+				
+		json.saveFile(file)
+		
+
+# Settings dialog
+class Dialog(tk.Toplevel):
+	"""
+	This implements account settings dialog to manipulate account settings.
+	"""
+	def __init__(self, parent, cfg):
+		tk.Toplevel.__init__(self, parent)
+		self.transient(parent)
+		self.parent = parent
+		self.title('Settings')
+		
+		self.frm = ttk.Frame(self)
+		self.frm.pack(expand='yes', fill='both')
+		
+		self.isOk = False
+		self.cfg = cfg
+		
+		self.createWidgets()
+	
+	def doModal(self):
+		if self.parent:
+			self.parent.wait_window(self)
+		else:
+			self.wait_window(self)
+		return self.isOk
+		
+	def createWidgets(self):
+		# The notebook
+		self.frm.rowconfigure(0, weight=1)
+		self.frm.rowconfigure(1, weight=0)
+		self.frm.columnconfigure(0, weight=1)
+		self.frm.columnconfigure(1, weight=1)
+		self.wTab = ttk.Notebook(self.frm)
+		self.wTab.grid(column=0, row=0, columnspan=2, padx=10, pady=10, ipadx=20, ipady=20, sticky=tk.N+tk.S+tk.W+tk.E)
+		
+		# Main buttons
+		btnOk = ttk.Button(self.frm, text='Ok', command=self.onOk)
+		btnOk.grid(column=0, row=1, sticky=tk.E, padx=20, pady=10)
+		btnCancel = ttk.Button(self.frm, text='Cancel', command=self.onCancel)
+		btnCancel.grid(column=1, row=1, sticky=tk.W, padx=20, pady=10)
+		
+		# Tabs
+		self.createBasicTab()
+		self.createNetworkTab()
+		self.createMediaTab()
+
+	def createBasicTab(self):
+		# Prepare the variables to set/receive values from GUI
+		self.cfgLogFile = tk.StringVar(value=self.cfg.epConfig.logConfig.filename)
+		self.cfgLogAppend = tk.BooleanVar(value=True if (self.cfg.epConfig.logConfig.fileFlags & pj.PJ_O_APPEND) else False)
+		
+		# Build the tab page
+		frm = ttk.Frame(self.frm)
+		frm.columnconfigure(0, weight=1)
+		frm.columnconfigure(1, weight=2)
+		row = 0
+		ttk.Label(frm, text='User Agent:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Label(frm, text=self.cfg.epConfig.uaConfig.userAgent).grid(row=row, column=1, sticky=tk.W, pady=2, padx=6)
+		row += 1
+		ttk.Label(frm, text='Max calls:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Label(frm, text=str(self.cfg.epConfig.uaConfig.maxCalls)).grid(row=row, column=1, sticky=tk.W, pady=2, padx=6)
+		row += 1
+		ttk.Label(frm, text='Log file:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgLogFile, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='Append log file', variable=self.cfgLogAppend).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+
+		self.wTab.add(frm, text='Basic')
+		
+	def createNetworkTab(self):
+		self.cfgNameserver = tk.StringVar()
+		if len(self.cfg.epConfig.uaConfig.nameserver):
+			self.cfgNameserver.set(self.cfg.epConfig.uaConfig.nameserver[0])
+		self.cfgStunServer = tk.StringVar()
+		if len(self.cfg.epConfig.uaConfig.stunServer):
+			self.cfgStunServer.set(self.cfg.epConfig.uaConfig.stunServer[0])
+		self.cfgStunIgnoreError = tk.BooleanVar(value=self.cfg.epConfig.uaConfig.stunIgnoreFailure)
+		
+		self.cfgUdpEnabled = tk.BooleanVar(value=self.cfg.udp.enabled)
+		self.cfgUdpPort = tk.IntVar(value=self.cfg.udp.config.port)
+		self.cfgTcpEnabled = tk.BooleanVar(value=self.cfg.tcp.enabled)
+		self.cfgTcpPort = tk.IntVar(value=self.cfg.tcp.config.port)
+		self.cfgTlsEnabled = tk.BooleanVar(value=self.cfg.tls.enabled)
+		self.cfgTlsPort = tk.IntVar(value=self.cfg.tls.config.port)
+		
+		self.cfgTlsCaFile = tk.StringVar(value=self.cfg.tls.config.tlsConfig.CaListFile)
+		self.cfgTlsCertFile = tk.StringVar(value=self.cfg.tls.config.tlsConfig.certFile)
+		self.cfgTlsVerifyClient = tk.BooleanVar(value=self.cfg.tls.config.tlsConfig.verifyClient)
+		self.cfgTlsVerifyServer = tk.BooleanVar(value=self.cfg.tls.config.tlsConfig.verifyServer)
+		
+		# Build the tab page
+		frm = ttk.Frame(self.frm)
+		frm.columnconfigure(0, weight=1)
+		frm.columnconfigure(1, weight=2)
+		row = 0
+		#ttk.Label(frm, text='UDP transport:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable UDP transport', variable=self.cfgUdpEnabled).grid(row=row, column=0, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='UDP port:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgUdpPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0 for any)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		#ttk.Label(frm, text='TCP transport:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable TCP transport', variable=self.cfgTcpEnabled).grid(row=row, column=0, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='TCP port:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgTcpPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0 for any)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		#ttk.Label(frm, text='TLS transport:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable TLS transport', variable=self.cfgTlsEnabled).grid(row=row, column=0, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='TLS port:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgTlsPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0 for any)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		ttk.Label(frm, text='TLS CA file:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgTlsCaFile, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='TLS cert file:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgTlsCertFile, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='TLS verify server', variable=self.cfgTlsVerifyServer).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Checkbutton(frm, text='TLS verify client', variable=self.cfgTlsVerifyClient).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='DNS and STUN:').grid(row=row, column=0, sticky=tk.W, pady=2, padx=8)
+		row += 1
+		ttk.Label(frm, text='Nameserver:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgNameserver, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='STUN Server:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgStunServer, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='Ignore STUN failure at startup', variable=self.cfgStunIgnoreError).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		
+		self.wTab.add(frm, text='Network')
+
+	def createMediaTab(self):
+		self.cfgClockrate = tk.IntVar(value=self.cfg.epConfig.medConfig.clockRate)
+		self.cfgSndClockrate = tk.IntVar(value=self.cfg.epConfig.medConfig.sndClockRate)
+		self.cfgAudioPtime = tk.IntVar(value=self.cfg.epConfig.medConfig.audioFramePtime)
+		self.cfgMediaQuality = tk.IntVar(value=self.cfg.epConfig.medConfig.quality)
+		self.cfgCodecPtime = tk.IntVar(value=self.cfg.epConfig.medConfig.ptime)
+		self.cfgVad = tk.BooleanVar(value=not self.cfg.epConfig.medConfig.noVad)
+		self.cfgEcTailLen = tk.IntVar(value=self.cfg.epConfig.medConfig.ecTailLen)
+		
+		# Build the tab page
+		frm = ttk.Frame(self.frm)
+		frm.columnconfigure(0, weight=1)
+		frm.columnconfigure(1, weight=2)
+		row = 0
+		ttk.Label(frm, text='Max media ports:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Label(frm, text=str(self.cfg.epConfig.medConfig.maxMediaPorts)).grid(row=row, column=1, sticky=tk.W, pady=2, padx=6)
+		row += 1
+		ttk.Label(frm, text='Core clock rate:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=8000, to=48000, increment=8000, textvariable=self.cfgClockrate, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='Snd device clock rate:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=48000, increment=8000, textvariable=self.cfgSndClockrate, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0: follow core)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		ttk.Label(frm, text='Core ptime:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=10, to=400, increment=10, textvariable=self.cfgAudioPtime, width=3).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='RTP ptime:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=20, to=400, increment=10, textvariable=self.cfgCodecPtime, width=3).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='Media quality (1-10):').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=1, to=10, textvariable=self.cfgMediaQuality, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='VAD:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable', variable=self.cfgVad).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='Echo canceller tail length:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=400, increment=10, textvariable=self.cfgEcTailLen, width=3).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(ms, 0 to disable)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		
+		self.wTab.add(frm, text='Media')
+
+	def onOk(self):
+		# Check basic settings
+		errors = "";
+		if errors:
+			msgbox.showerror("Error detected:", errors)
+			return
+		
+		# Basic settings
+		self.cfg.epConfig.logConfig.filename = self.cfgLogFile.get()
+		flags = pj.PJ_O_APPEND if self.cfgLogAppend.get() else 0
+		self.cfg.epConfig.logConfig.fileFlags = self.cfg.epConfig.logConfig.fileFlags | flags 
+		
+		# Network settings
+		self.cfg.epConfig.uaConfig.nameserver.clear()
+		if len(self.cfgNameserver.get()): 
+			self.cfg.epConfig.uaConfig.nameserver.append(self.cfgNameserver.get())
+		self.cfg.epConfig.uaConfig.stunServer.clear()
+		if len(self.cfgStunServer.get()):
+			self.cfg.epConfig.uaConfig.stunServer.append(self.cfgStunServer.get())
+			
+		self.cfg.epConfig.uaConfig.stunIgnoreFailure = self.cfgStunIgnoreError.get()
+		
+		self.cfg.udp.enabled 	= self.cfgUdpEnabled.get()
+		self.cfg.udp.config.port = self.cfgUdpPort.get()
+		self.cfg.tcp.enabled 	= self.cfgTcpEnabled.get()
+		self.cfg.tcp.config.port = self.cfgTcpPort.get()
+		self.cfg.tls.enabled 	= self.cfgTlsEnabled.get()
+		self.cfg.tls.config.port = self.cfgTlsPort.get()
+		
+		self.cfg.tls.config.tlsConfig.CaListFile = self.cfgTlsCaFile.get()
+		self.cfg.tls.config.tlsConfig.certFile   = self.cfgTlsCertFile.get()
+		self.cfg.tls.config.tlsConfig.verifyClient = self.cfgTlsVerifyClient.get()
+		self.cfg.tls.config.tlsConfig.verifyServer = self.cfgTlsVerifyServer.get()
+
+		# Media
+		self.cfg.epConfig.medConfig.clockRate	= self.cfgClockrate.get()
+		self.cfg.epConfig.medConfig.sndClockRate = self.cfgSndClockrate.get()
+		self.cfg.epConfig.medConfig.audioFramePtime = self.cfgAudioPtime.get()
+		self.cfg.epConfig.medConfig.quality	= self.cfgMediaQuality.get()
+		self.cfg.epConfig.medConfig.ptime	= self.cfgCodecPtime.get()
+		self.cfg.epConfig.medConfig.noVad	= not self.cfgVad.get()
+		self.cfg.epConfig.medConfig.ecTailLen	= self.cfgEcTailLen.get()
+		
+		self.isOk = True
+		self.destroy()
+		
+	def onCancel(self):
+		self.destroy()
+
+
+if __name__ == '__main__':
+	#application.main()
+	acfg = AppConfig()
+	acfg.loadFile('pygui.js')
+
+	dlg = Dialog(None, acfg)
+	if dlg.doModal():
+		acfg.saveFile('pygui.js')
+		
