#!/usr/bin/python
# -*- coding: koi8-r -*-
# This file is part of the metromap
#
# Copyright(c) 2010, Sergey Pinaev
# http://metromap.antex.ru/
#
# This file may be licensed under the terms of of the
# GNU General Public License Version 2 (the ``GPL'').
#
# Software distributed under the License is distributed
# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
# express or implied. See the GPL for the specific language
# governing rights and limitations.
#
# You should have received a copy of the GPL along with this
# program. If not, go to http://www.gnu.org/licenses/gpl.html
# or write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

PROGRAM = 'metromap'
PROGRAM_VERSION = "0.1.3"

try:
        import hildon
	import osso
	HILDON = True
except:
	import pygtk
	pygtk.require('2.0')
	HILDON=False

import pygtk
pygtk.require('2.0')

import gtk
import gobject
import pango

import os
import sys
import time
import zipfile
import StringIO
import copy

PYGTK_MIN_VER = (2, 8, 0)

if gtk.pygtk_version < PYGTK_MIN_VER:
        print "PyGTK version >= %s required, but you have only %s" % (
                ".".join(map(lambda a: str(a), PYGTK_MIN_VER)),
                ".".join(map(lambda a: str(a), gtk.pygtk_version))
                )
        sys.exit(1)

from optparse import OptionParser

MAPINI = 'metro.ini'
MAPMAP = 'metro.map'
MAPTRP = 'metro.trp'
MAPCTY = '.cty'
MAPVEC = 'metro.vec'
MAPGRD = '.grd'
VECTORS = 'VectorsMetro.txt'
VECTORS_FALLBACK = 'Vectors.txt'

one_button = None

MD = None
MetroMap = None
Finder = None

DTlist = dict()
DeletedTransfers = dict()

AddInfo = dict()

scalelist = list()
for sc in xrange(5, 21):
        scalelist.append([str(sc * 10) + '%', sc / 10.0])
scale = 1.0

XSIZE, YSIZE = None, None
compact = False
dw, dh = gtk.gdk.screen_width(), gtk.gdk.screen_height()
if dw < 800 or HILDON:
        compact = True
delta = 5

dtime = 0
h = time.localtime()[3]
if h >= 21 or h <= 7:
        dtime = 1

CITY = None

import gettext

binpath = os.path.realpath(os.path.dirname(sys.argv[0]))
if binpath.endswith('/bin'): #FHS
        PREFIX = binpath[:binpath.rfind('/bin')]
        DATAPATH = PREFIX + '/share/' + PROGRAM + '/data/'
        MODULEPATH = PREFIX + '/share/' + PROGRAM + '/modules/'
        gettext.bindtextdomain(PROGRAM, PREFIX + '/share/locale/')
else:
        PREFIX = binpath
        DATAPATH = PREFIX + '/data/'
        MODULEPATH = PREFIX + '/modules/'
        gettext.bindtextdomain(PROGRAM, PREFIX + '/locale/')

sys.path.insert(1, MODULEPATH)
gettext.textdomain(PROGRAM)
_ = lambda a: unicode(gettext.gettext(a), "utf8")

if sys.version_info < (2, 5):
        class UOptionParser(OptionParser):
                def print_help(self, file=None):
                        if file is None:
                                file = sys.stdout
                        file.write(self.format_help().encode(file.encoding, 'replace'))
        oparser = UOptionParser()
else:
        oparser = OptionParser()

oparser.add_option("-d", "--debug", dest = "debug",  action="store_true", default = False,
                  help = _("Enable debugging messages"))
oparser.add_option("-n", "--no-cache", dest = "nocache",  action="store_true", default = False,
                  help = _("Disable image caching"),
                  )
def ob_cb(option, opt, value, parser):
        global one_button
        one_button = True

oparser.add_option("-o", "--one-button", action = "callback", callback = ob_cb,
                  help = _("select start/end stations with one mouse button (useful when used with stylus)"),
                  )
(options, args) = oparser.parse_args()

DEBUG = options.debug

if HILDON:
	osso_c = osso.Context("maemo_" + PROGRAM, PROGRAM_VERSION, False)

import ReadMap
ReadMap._ = _
ReadMap.HILDON = HILDON
from ReadMap import ReadMap, GetMapName, GetMapVec, ReadAddInfo, NullUpdater, parse_vectors

from FindPath import FindPath

import MapDisplay
MapDisplay._ = _
from MapDisplay import MapDisplay, StationDisplay

import Interface
Interface._ = _
Interface.HILDON = HILDON
Interface.PROGRAM = 'metromap'
Interface.PROGRAM_VERSION = "0.1.3"
if HILDON:
        Interface.hildon = hildon
        Interface.osso = osso

RCDIR = os.environ.get('HOME') + "/." + PROGRAM
CACHEDIR = RCDIR + '/cache'

if not os.path.isdir(RCDIR):
        try:
                os.mkdir(RCDIR)
        except:
                pass

if not os.path.isdir(CACHEDIR):
        try:
                os.mkdir(CACHEDIR)
        except:
                pass

if options.nocache:
        CACHEDIR = None

if DEBUG:
        print "Started"

#load config
if os.path.isfile(RCDIR + "/rc"):
        f = file(RCDIR + "/rc", 'r')
        done = False
        while not done:
                s = f.readline()
                if not s:
                        break
                p = s.split('=')
                if len(p) == 2:
                        if p[0] == 'city':
                                CITY = p[1].rstrip()
                        elif p[0] == 'zoom':
                                scale = float(p[1].rstrip())
                                if not scale in map(lambda a: a[1], scalelist):
                                        scale = 1
                        elif p[0] == 'xsize':
                                XSIZE = int(p[1].rstrip())
                        elif p[0] == 'ysize':
                                YSIZE = int(p[1].rstrip())
                        elif p[0] == 'delta':
                                delta = int(p[1].rstrip())
                                if delta < 0:
                                        delta = 0
                                elif delta > 99:
                                        delta = 99
                        elif p[0] == 'compact':
                                if p[1].rstrip().lower() == "true":
                                        compact = True
                        elif p[0] == 'one_button_mode':
                                if one_button == None: #do not overwrite if specifed on command line
                                        if p[1].rstrip().lower() == "true":
                                                one_button = True
                                        else:
                                                one_button = False
                        elif p[0].startswith('dt[') and p[0][-1] == ']':
                                DTlist[p[0][3:-1]] = list()
                                sp = p[1].rstrip().split(',')
                                for pair in sp:
                                        l = pair.rstrip().lstrip().split('-', 2)
                                        if len(l) == 2 and l[0].isdigit() and l[1].isdigit():
                                                DTlist[p[0][3:-1]].append((int(l[0]), int(l[1])))
        f.close()

if one_button == None: #not set from command line and/or config file, set default value
        if HILDON:
	        one_button = True
        else:
                one_button = False

#save config
def save_config():
        try:
                f = file(RCDIR + "/rc", 'w')
                f.write('city=' + CITY + '\n')
                f.write('xsize=' + str(Iface.xsize) + '\n')
                f.write('ysize=' + str(Iface.ysize) + '\n')
                f.write('delta=' + str(delta) + '\n')
                if Iface.compact_mode:
                        f.write('compact=true\n')
                else:
                        f.write('compact=false\n')
                if Iface.one_button:
                        f.write('one_button_mode=true\n')
                else:
                        f.write('one_button_mode=false\n')
                f.write('zoom=' + str(scale) + '\n')
                for c in DTlist.keys():
                        if len(DTlist[c]):
                                st = 'dt[' + c + ']='
                                for p in DTlist[c]:
                                        st += str(p[0]) + '-' + str(p[1]) + ','
                                st = st[:-1] + '\n'
                                f.write(st)
                f.close()
        except:
                pass

def sync_to_dt():
        DTlist[CITY] = list()
        for dt in DeletedTransfers.keys():
                if not (dt[0], dt[1]) in DTlist[CITY] and not (dt[1], dt[0]) in DTlist[CITY]:
                        DTlist[CITY].append((dt[0], dt[1]))

def sync_from_dt():
        global DeletedTransfers

        DeletedTransfers = dict()
        if DTlist.has_key(CITY):
                Finder.set_graph(dt_disable(Finder.graph, DTlist[CITY]))

def atexit(foo):
        save_config()
        gtk.main_quit()

st_start = None
st_end = None
path_list = list()

def ciext_file_find(path, name, ext):
        for f in os.listdir(path):
                if f.startswith(name) and len(f) == len(name) + len(ext) + 1 and \
                   f[-len(ext):].lower() == ext.lower():
                        return f
        #not found, return some shit
        return name + '.' + ext

def nocase_find(name, namelist):
        ll = map(lambda a: a.lower(), namelist)
        if name.lower() in ll:
                return namelist[ll.index(name.lower())]
        return None

def get_data_full(datapath, city, filename, ui_update_func = None):
        f = None
        n = None
        flist = list()
        try:
                flist = os.listdir(datapath + city + '/')
                if ui_update_func:
                        ui_update_func()
                filename = nocase_find(filename, flist)
                n = datapath + city + '/' + filename
                f = open(n)
        except:
                try:
                        n = datapath + ciext_file_find(datapath, city, 'pmz')
                        z = zipfile.ZipFile(n, 'r')
                        if ui_update_func:
                                ui_update_func()
                        flist = z.namelist()
                        filename = nocase_find(filename, flist)
                        f = StringIO.StringIO(z.read(filename))
                        z.close()
                        if ui_update_func:
                                ui_update_func()
                except:
                        try:
                                n = datapath + ciext_file_find(datapath, city, 'zip')
                                z = zipfile.ZipFile(n, 'r')
                                if ui_update_func:
                                        ui_update_func()
                                intn = city + '.pmz'
                                for zn in z.namelist():
                                        if zn.lower() == city.lower() + '.pmz':
                                                intn = zn
                                                break
                                pmzf = StringIO.StringIO(z.read(intn))
                                z.close()
                                if ui_update_func:
                                        ui_update_func()
                                z = zipfile.ZipFile(pmzf, 'r')
                                flist = z.namelist()
                                filename = nocase_find(filename, flist)
                                f = StringIO.StringIO(z.read(filename))
                                z.close()
                                if ui_update_func:
                                        ui_update_func()
                        except:
                                n = _("unknown")
                                f = None
        return n, f, flist

def get_data(city, filename, ui_update_func = None):
        for dp in DATAPATH, RCDIR + '/':
                n, f, flist = get_data_full(dp, city, filename, ui_update_func)
                if f:
                        break
        return n, f, flist

if DEBUG:
        start_time = time.time()
citylist = list()
for dp in DATAPATH, RCDIR + '/':
        if not os.path.isdir(dp):
                continue
        for c in os.listdir(dp):
                cn = c
                if c.lower().endswith('.zip') or c.lower().endswith('.pmz'):
                        cn = c[:-4]
                n, f, flist = get_data_full(dp, cn, MAPINI)
                if not f:
                        n, f, flist = get_data_full(dp, cn, cn + MAPCTY)
                if f:
                        name = GetMapName(f)
                        f.close()
                        if name:
                                citylist.append((name, cn))
if DEBUG:
        print _("List data files time:"), time.time() - start_time

if len(citylist) == 0:
        print unicode(_('No data files found, please install some.'))
        sys.exit(0)

if not CITY or not len(filter(lambda a: a[1] == CITY, citylist)):
        if len(filter(lambda a: a[1] == "Moscow", citylist)):
                CITY = "Moscow"
        else:
                CITY = citylist[0][1]

def station_selected(station, isstart):
        global st_start, st_end, path_list

        if isstart:
                st_start = station
                Iface.set_from(station)
                MD.set_start_station(station)
                if st_end == station:
                        st_end = None
        else:
                st_end = station
                Iface.set_to(station)
                MD.set_stop_station(station)
                if st_start == station:
                        st_start = None

        if st_start != None and st_end != None:
                path_list = Finder.waves_accurate(st_start, st_end, MetroMap.WaitLen)
                path_list.sort()
        else:
                path_list = []

        if len(path_list):
                MD.set_path_list(path_list[0][2:])
                Iface.set_path_list(map(lambda a: _("Minutes: %s Changes: %d Stations: %d") %
                                                    (("%.2f" % (a[0])).rstrip("0").rstrip("."), a[1], len(a) - a[1] - 3),
                                        path_list))
        else:
                MD.set_path_list(None)
                Iface.set_path_list([])


def list_selected(num):
        global path_list

        if path_list and num >= 0 and num < len(path_list):
                MD.set_path_list(path_list[num][2:])

def zoom_changed(zoom):
        global scale

        if scale != zoom:
                scale = zoom
                city_changed(None)

def city_changed(name):
        global st_start, st_end, path_list, MetroMap, MD, Finder, prefs, CITY, dtime, delta, scale, AddInfo, DEBUG

        if MD and MD.da:
                MD.da.hide()

        if name != None:
                Iface.progress_start(_("Reading map %s") % (name))
                if DEBUG:
                        start_time = time.time()
                CITY = name
                st_start = st_end = None
                path_list = list()
                n, f, flist = get_data(CITY, MAPINI, Iface.update)
                if not f:
                        #probably "new" map format
                        n, fcty, flist = get_data(CITY, CITY + MAPCTY, Iface.update)
                        if fcty:
                                n, fmap, flist = get_data(CITY, MAPMAP, Iface.update)
                                if fmap:
					mapvec = GetMapVec(fmap)
					if mapvec.find(",") != -1:
						raster, mapvec = mapvec.split(",", 1)
					if not mapvec:
						mapvec = MAPVEC
                                        n, ftrp, flist = get_data(CITY, MAPTRP, Iface.update)
                                        if ftrp:
                                                n1, fvec, fl1 = get_data(CITY, mapvec, Iface.update)
                                                f = (fcty, fmap, ftrp, fvec)
                if not f:
                        print unicode(_('Cannot find ') + MAPINI + _(' for city "') + CITY + '"')
                        sys.exit(0)
                grd_n, grd_f, grd_flist = get_data(CITY, CITY + MAPGRD, Iface.update)

                Iface.progress_set_percentage(25)
                Iface.progress_set_text(_("Loading vectors"))
                vn, vf, devnull = get_data(CITY, VECTORS, Iface.update)
                if vf == None:
                        vn, vf, devnull = get_data(CITY, VECTORS_FALLBACK, Iface.update)
                if DEBUG:
                        print _("Load data time:"), time.time() - start_time
                        start_time = time.time()
                Iface.progress_set_percentage(50)
                Iface.progress_set_text(_("Parsing map data"))

                MetroMap = ReadMap(f, vf, grd_f, Iface.update)
                if DEBUG:
                        print _("ReadMap time:"), time.time() - start_time
                Iface.progress_set_percentage(75)
                Iface.progress_set_text(_("Loading additional information"))
                if vf:
                        vf.close()
                if type(f) == tuple:
                        for fd in f:
                                fd.close()
                else:
                        f.close()

                if DEBUG:
                        start_time = time.time()
                AddInfo = dict()
                txt_list = filter(lambda a: a.lower().endswith('.txt'), flist)
		txt_list.sort()
                for t in txt_list:
                        Iface.progress_set_percentage(75 + 15.0 / len(txt_list))
                        vn, vf, devnull = get_data(CITY, t, Iface.update)
                        if vf:
                                ai = ReadAddInfo(vf, MetroMap.MapEncoding)
                                if ai.name:
					if AddInfo.has_key(ai.name):
						for k in ai.data.keys():
							if AddInfo[ai.name].data.has_key(k):
								AddInfo[ai.name].data[k] += "\n" + ai.data[k]
							else:
								AddInfo[ai.name].data[k] = ai.data[k]
					else:
						AddInfo[ai.name] = ai
                                else:
                                        #some 'special' file
                                        pass
                if DEBUG:
                        print _("Load txt's time:"), time.time() - start_time

                MetroMap.SetDelayTime(dtime)
                Iface.update()

        if name == None:
                Iface.progress_start(_("Calculating map %s") % (name))
        snames = map(lambda a: ["%s (%s)" % (a['name'], MetroMap.Lines[a['line']]['name']), a['number']], MetroMap.Stations.values())
        snames.sort()

        sh = False #hack for StPetersburg map
        if CITY.lower() == 'peterburg':
                sh = True

        mdlines = copy.deepcopy(MetroMap.Lines)
        mdstations = copy.deepcopy(MetroMap.Stations)
        mdvectors = copy.deepcopy(MetroMap.Map['vectors'])
        Iface.update()

        if DEBUG:
                start_time = time.time()
        if scale != 1:
                Iface.progress_set_text(_("Scaling"))
                for k in mdlines.keys():
                        Iface.update()
                        mdlines[k]['diameter'] = int(round(mdlines[k]['diameter'] * scale))
                for k in mdstations.keys():
                        Iface.update()
                        mdstations[k]['diameter'] = int(round(mdstations[k]['diameter'] * scale))
                        for ak in mdstations[k]['add'].keys():
                                for i in xrange(len(mdstations[k]['add'][ak]['coords'])):
                                        foo = mdstations[k]['add'][ak]['coords'][i]
                                        mdstations[k]['add'][ak]['coords'][i] = (int(round(foo[0] * scale)), int(round(foo[1] * scale)))
                        mdstations[k]['x'] = int(round(mdstations[k]['x'] * scale))
                        mdstations[k]['y'] = int(round(mdstations[k]['y'] * scale))
                        mdstations[k]['namerect'][0] = int(round(mdstations[k]['namerect'][0] * scale))
                        mdstations[k]['namerect'][1] = int(round(mdstations[k]['namerect'][1] * scale))
                        mdstations[k]['namerect'][2] = int(round(mdstations[k]['namerect'][2] * scale))
                        mdstations[k]['namerect'][3] = int(round(mdstations[k]['namerect'][3] * scale))
                if mdvectors.has_key('lineswidth'):
                        Iface.update()
                        mdvectors['lineswidth'] = int(round(mdvectors['lineswidth'] * scale))
                for i in xrange(len(mdvectors['instructions'])):
                        Iface.update()
                        if mdvectors['instructions'][i][0] == 'spline':
                                mdvectors['instructions'][i][1] = int(round(mdvectors['instructions'][i][1] * scale))
                                for j in xrange(len(mdvectors['instructions'][i][2])):
                                        foo = mdvectors['instructions'][i][2][j]
                                        mdvectors['instructions'][i][2][j] = (int(round(foo[0] * scale)), int(round(foo[1] * scale)))
                        elif mdvectors['instructions'][i][0] == 'text':
                                mdvectors['instructions'][i][1]['size'] = int(round(mdvectors['instructions'][i][1]['size'] * scale))
                                mdvectors['instructions'][i][1]['x'] = int(round(mdvectors['instructions'][i][1]['x'] * scale))
                                mdvectors['instructions'][i][1]['y'] = int(round(mdvectors['instructions'][i][1]['y'] * scale))
        Iface.progress_set_percentage(100)
        Iface.progress_set_text(_("Starting"))
        if DEBUG:
                print _("Scale time:"), time.time() - start_time

        if DEBUG:
                start_time = time.time()
        MD = MapDisplay(mdlines, mdstations, mdvectors, station_selected, sh, cachedir = CACHEDIR, debug = DEBUG, pb = Iface, one_button = Iface.one_button)
        Iface.update()
        if DEBUG:
                print _("New MapDisplay time:"), time.time() - start_time
                start_time = time.time()
        MD.set_menu(Iface.menu_callback, Iface.menu_items)
        MD.set_dtcb(transfers_cb)
        Iface.progress_end()

        if name != None:
                Iface.set_path_list([])
        Iface.set_da(MD.da)
        Iface.set_st_list(snames)
        Iface.set_dtime(dtime)
        Iface.set_win_title(PROGRAM + ' - ' + MetroMap.Map['city'])
        Iface.set_one_button_cb(MD.set_one_button)
        if name == None:
                MD.set_start_station(st_start)
                MD.set_stop_station(st_end)
                Iface.set_from(st_start)
                Iface.set_to(st_end)
                Iface.path_selected(None)
        if name != None:
                if not Finder:
                        Finder = FindPath(MetroMap.Graph, delta)
                else:
                        Finder.set_graph(MetroMap.Graph)
        sync_from_dt()
        if DEBUG:
                print _("Rest of city changed time:"), time.time() - start_time

def ground_tr_selected(treeselection, metromap, tr_name, ss_label, info_label, route_view):
        if treeselection:
                ls, ti = treeselection.get_selected()
                tr_num = ls.get_value(ti, 0)
                tr_idx = ls.get_value(ti, 1)
                ss = MetroMap.get_transport_start_end(tr_name, tr_num, tr_idx)
                if not ss:
                        ss_label.get_parent().hide()
                else:
                        ss_label.set_text(ss)
                        ss_label.get_parent().show()
                info = MetroMap.get_transport_info(tr_name, tr_num, tr_idx)
                if not info:
                        info_label.get_parent().hide()
                else:
                        info_label.get_buffer().set_text(info)
                        info_label.get_parent().show()

                route_list = route_view.get_model()
                route_list.clear()
                slist = metromap.get_transport_stations_list(tr_name, tr_num, tr_idx)
                if slist:
                        for s in slist:
                                route_list.append([s])
                        route_view.get_parent().show()
                else:
                        route_view.get_parent().hide()

def show_info(st):
    #for st in MetroMap.Stations.keys():
        if st != None:
                sd = None
                if MetroMap.Stations[st].has_key('vfile'):
                        n, f, devnull = get_data(CITY, MetroMap.Stations[st]['vfile'])
                        if not f:
                                Interface.SimpleMessageBox('error', PROGRAM, _('File not found'),
                                                           _('Cannot find ') + MetroMap.Stations[st]['vfile'],
                                                           gtk.STOCK_OK, Iface.win)
                                return

                        nu = NullUpdater()
                        sd = StationDisplay(parse_vectors(f.readlines(), nu, MetroMap.MapEncoding))
                        f.close()

		if HILDON:
			win = hildon.Window()
			win.set_resizable(False)
			Iface.program.add_window(win)
                else:
                        win = gtk.Window()
                        win.set_resizable(True)
                win.set_title(MetroMap.Stations[st]['name'])
                win.set_wmclass(PROGRAM + 'StInfo', PROGRAM)
                win.set_border_width(5)
                if sd:
                        Iface.s_swin = Interface.SWin(sd.da)

                win.set_default_size(Iface.Swin.swin.get_allocation().width, Iface.Swin.swin.get_allocation().height)

                nb = gtk.Notebook()
		if HILDON:
			nb.set_tab_pos(gtk.POS_TOP)
		else:
			nb.set_tab_pos(gtk.POS_LEFT)
                nb.set_scrollable(True)

                numpages = 0
                s = MetroMap.Stations[st]['name'].lower()
                l = MetroMap.Lines[MetroMap.Stations[st]['line']]['name'].lower()
                transports = MetroMap.get_transport_names(l, s)
                if transports:
                        monospace = pango.FontDescription('monospace')
                        transports.sort()
                        tr_nb = gtk.Notebook()
                        tr_nb.set_tab_pos(gtk.POS_TOP)
                        tr_nb.set_border_width(5)
                        tr_nb.set_scrollable(True)
                        for transport in transports:
                                numbers = MetroMap.get_transport_numbers(transport, l, s)
                                if numbers:
                                        ls = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT)
                                        for num in numbers:
                                                ls.append([num[0], num[1]])
                                        tvcol = gtk.TreeViewColumn('', gtk.CellRendererText(), text = 0)
                                        tv = gtk.TreeView(ls)
                                        tv.set_headers_visible(False)
                                        tv.append_column(tvcol)
                                        swin = gtk.ScrolledWindow()
                                        swin.set_shadow_type(gtk.SHADOW_IN)
                                        swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                                        swin.add(tv)
                                        tv_s = tv.size_request()
                                        swin.set_size_request(tv_s[0], 0)

                                        gt_start_stop = gtk.Label()
                                        gt_info = gtk.TextView()
                                        gt_info.set_wrap_mode(gtk.WRAP_WORD_CHAR)
                                        gt_info.set_editable(False)
                                        gt_info.set_cursor_visible(False)
                                        gt_info_swin = gtk.ScrolledWindow()
                                        gt_info_swin.set_shadow_type(gtk.SHADOW_IN)
                                        gt_info_swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                                        gt_info_swin.add(gt_info)

                                        route_ls = gtk.ListStore(gobject.TYPE_STRING)
                                        route_col = gtk.TreeViewColumn('', gtk.CellRendererText(), text = 0)
                                        route_tv = gtk.TreeView(route_ls)
                                        route_tv.modify_font(monospace)
                                        route_tv.set_headers_visible(False)
                                        route_tv.append_column(route_col)
                                        route_swin = gtk.ScrolledWindow()
                                        route_swin.set_shadow_type(gtk.SHADOW_IN)
                                        route_swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                                        route_swin.add(route_tv)

                                        gt_vb = gtk.VBox()
                                        gt_vb.pack_start(gt_start_stop, expand = False, padding = 5)

                                        gt_info_vpan = gtk.VPaned()
                                        gt_vb.pack_start(gt_info_vpan, expand = True, fill = True)

                                        gt_info_vpan.add1(gt_info_swin)
                                        gt_info_vpan.add2(route_swin)
                                        hpan = gtk.HPaned()
                                        hpan.add1(swin)
                                        hpan.add2(gt_vb)

                                        sel = tv.get_selection()
                                        sel.set_mode(gtk.SELECTION_BROWSE)
                                        sel.connect('changed', ground_tr_selected, MetroMap, transport, gt_start_stop, gt_info, route_tv)
                                        sel.select_path("0")

                                        tr_nb.append_page(hpan, gtk.Label(transport))
                        nb.append_page(tr_nb, gtk.Label(_('Ground transport')))
                        numpages += 1

                a_keys = AddInfo.keys()
                a_keys.sort()
                for ai in a_keys:
                        if AddInfo[ai].data.has_key((l, s)):
                                if sd and AddInfo[ai].data[(l, s)] == MetroMap.Stations[st]['vfile']:
                                        nb.append_page(Iface.s_swin.swin, gtk.Label(ai))
                                else:
                                        view = gtk.TextView()
                                        view.set_wrap_mode(gtk.WRAP_WORD_CHAR)
                                        view.set_editable(False)
                                        view.set_cursor_visible(False)
                                        view.get_buffer().set_text(AddInfo[ai].data[(l, s)].replace(r"\n", chr(0x0a)))
                                        swin = gtk.ScrolledWindow()
                                        swin.set_shadow_type(gtk.SHADOW_IN)
                                        swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                                        swin.add(view)
                                        nb.append_page(swin, gtk.Label(ai))
                                numpages += 1

                if numpages:
                        win.add(nb)
                else:
                        win.add(gtk.Label(_("Sorry, no info available")))

                win.show_all()

def dt_enable(g, pairs):
        for p in pairs:
                for tr in DeletedTransfers[(p[0], p[1])]:
                        g[p[0]].append(tr)
                        g[p[1]].append(tr)
                del DeletedTransfers[(p[0], p[1])]
                del DeletedTransfers[(p[1], p[0])]
        return g

def dt_disable(g, pairs):
        for p in pairs:
                DeletedTransfers[(p[0], p[1])] = list()
                DeletedTransfers[(p[1], p[0])] = list()
                for tr in g[p[0]]:
                        if tr[0] == p[1]:
                                DeletedTransfers[(p[0], p[1])].append(tr)
                                DeletedTransfers[(p[1], p[0])].append(tr)
                                g[p[0]].remove(tr)
                                break
                for tr in g[p[1]]:
                        if tr[0] == p[0]:
                                DeletedTransfers[(p[0], p[1])].append(tr)
                                DeletedTransfers[(p[1], p[0])].append(tr)
                                g[p[1]].remove(tr)
                                break
        return g

def transfers_cb(act, sfrom = None, sto = None):
        if act == 'get':
                return DeletedTransfers
        elif act == 'set':
                if sfrom == None or sto == None: #enable all disabled transfers
                        Finder.set_graph(dt_enable(Finder.graph, DTlist[CITY]))
                else:
                        if DeletedTransfers.has_key((sfrom, sto)):
                                Finder.set_graph(dt_enable(Finder.graph, [[sfrom, sto]]))
                        else:
                                Finder.set_graph(dt_disable(Finder.graph, [[sfrom, sto]]))
                recalc_paths()
                sync_to_dt()

def recalc_paths():
        global st_start

        if st_start != None:
                station = st_start
                st_start = None
                station_selected(station, True)

def dtime_changed(num):
        global MetroMap, dtime

        if num != dtime:
                dtime = num
                MetroMap.SetDelayTime(dtime)
                recalc_paths()

def delta_changed(num):
        global delta

        if num != delta:
                delta = num
                Finder.maxerror = delta
                recalc_paths()

Iface = Interface.Interface(PROGRAM, None, citylist = citylist, compact = compact, delta = delta,
                            citynow = CITY, xsize = XSIZE, ysize = YSIZE,
                            zoomlist = scalelist, zoomnow = scale, one_button = one_button)
Iface.win.connect('destroy', atexit)
Iface.set_path_cb(list_selected)
Iface.set_station_cb(station_selected)
Iface.set_city_cb(city_changed)
Iface.set_dtime_cb(dtime_changed)
Iface.set_delta_cb(delta_changed)
Iface.set_zoom_cb(zoom_changed)
Iface.quit_cb = atexit
Iface.show_info_cb = show_info

if DEBUG:
        start_time = time.time()
city_changed(CITY)
if DEBUG:
        print _("City changed total time:"), time.time() - start_time

Iface.run()
