summaryrefslogtreecommitdiffstats
path: root/iw/timezone_map_gui.py
blob: 165bd6af49a4b90d1b510a4a1b495a579b8c3f41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#
# timezone_map_gui.py: gui timezone map widget.
#
# Copyright 2001 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

import gobject
import pango
import gtk
import gnome.canvas
import string
import re
import math
from translate import _

class Enum:
    def __init__(self, *args):
        i = 0
        for arg in args:
            self.__dict__[arg] = i
            i += 1

class TimezoneMap(gtk.VBox):
    def __init__(self, zonetab, default="America/New_York",
                 map='/usr/share/anaconda/pixmaps/map480.png'):
        gtk.VBox.__init__(self, gtk.FALSE, 5)

        # set up class member objects
        self.zonetab = zonetab
        self.markers = {}
        self.highlightedEntry = None

        # set up the map canvas
        self.canvas = gnome.canvas.Canvas()
        root = self.canvas.root()
        pixbuf = gtk.gdk.pixbuf_new_from_file(map)
        self.mapWidth = pixbuf.get_width()
        self.mapHeight = pixbuf.get_height()
        root.add(gnome.canvas.CanvasPixbuf, x=0, y=0, pixbuf=pixbuf)
        x1, y1, x2, y2 = root.get_bounds()
        self.canvas.set_scroll_region(x1, y1, x2, y2)
        self.canvas.set_size_request(x2, y2)
        self.pack_start(self.canvas, gtk.FALSE, gtk.FALSE)

        self.current = root.add(gnome.canvas.CanvasText, text='x',
                                fill_color='red', anchor=gtk.ANCHOR_CENTER,
                                weight=pango.WEIGHT_BOLD)
        
        root.connect("event", self.mapEvent)
        self.canvas.connect("event", self.canvasEvent)

        self.arrow = root.add(gnome.canvas.CanvasLine,
                              fill_color='limegreen',
                              width_pixels=2,
                              first_arrowhead=gtk.FALSE,
                              last_arrowhead=gtk.TRUE,
                              arrow_shape_a=4.0,
                              arrow_shape_b=8.0,
                              arrow_shape_c=4.0,
                              points=(0.0, 0.0, 0.0, 0.0))
        self.arrow.hide()

        # set up status bar
        self.status = gtk.Statusbar()
        self.status.set_has_resize_grip(gtk.FALSE)
        self.statusContext = self.status.get_context_id("")
        self.pack_start(self.status, gtk.FALSE, gtk.FALSE)

        self.columns = Enum("TZ", "COMMENTS", "ENTRY")
        
        # set up list of timezones
        self.listStore = gtk.ListStore(gobject.TYPE_STRING,
                                       gobject.TYPE_STRING,
                                       gobject.TYPE_PYOBJECT)
        
        for entry in zonetab.getEntries():
            iter = self.listStore.append()
            self.listStore.set_value(iter, self.columns.TZ, _(entry.tz))
            if entry.comments:
                self.listStore.set_value(iter, self.columns.COMMENTS,
                                         _(entry.comments))
            else:
                self.listStore.set_value(iter, self.columns.COMMENTS, "")
            self.listStore.set_value(iter, self.columns.ENTRY, entry)
            
            x, y = self.map2canvas(entry.lat, entry.long)
            marker = root.add(gnome.canvas.CanvasText, x=x, y=y,
                              text=u'\u00B7', fill_color='yellow',
                              anchor=gtk.ANCHOR_CENTER,
                              weight=pango.WEIGHT_BOLD)
            self.markers[entry.tz] = marker
            if entry.tz == default:
                self.currentEntry = entry

        self.listStore.set_sort_column_id(self.columns.TZ, gtk.SORT_ASCENDING)

        self.listView = gtk.TreeView(self.listStore)
        selection = self.listView.get_selection()
        selection.connect("changed", self.selectionChanged)
        self.listView.set_property("headers-visible", gtk.FALSE)
        col = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0)
        self.listView.append_column(col)
        col = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=1)
        self.listView.append_column(col)

        sw = gtk.ScrolledWindow ()
        sw.add(self.listView)
        sw.set_shadow_type(gtk.SHADOW_IN)
        self.pack_start(sw, gtk.TRUE, gtk.TRUE)

        self.setCurrent(self.currentEntry)

    def getCurrent(self):
        return self.currentEntry

    def selectionChanged(self, selection, *args):
        # XXX broken, returns None if there is no selection, so I have to
        #     check before unpacking
        rc = selection.get_selected()
        if rc is None:
            return
        model, iter = rc
        entry = self.listStore.get_value(iter, self.columns.ENTRY)
        self.setCurrent(entry, skipList=1)

    def mapEvent(self, widget, event=None):
        if event.type == gtk.gdk.MOTION_NOTIFY:
            x1, y1 = self.canvas.root().w2i(event.x, event.y)
            lat, long = self.canvas2map(x1, y1)
            last = self.highlightedEntry
            self.highlightedEntry = self.zonetab.findNearest(lat, long)
            if last != self.highlightedEntry:
                self.status.pop(self.statusContext)
                status = _(self.highlightedEntry.tz)
                if self.highlightedEntry.comments:
                    status = "%s - %s" % (status,
                                          _(self.highlightedEntry.comments))
                self.status.push(self.statusContext, status)

            x2, y2 = self.map2canvas(self.highlightedEntry.lat,
                                       self.highlightedEntry.long)
            self.arrow.set(points=(x1, y1, x2, y2))
            self.arrow.show()
        elif event.type == gtk.gdk.BUTTON_PRESS:
            if event.button == 1:
                self.setCurrent(self.highlightedEntry)
                
    def setCurrent(self, entry, skipList=0):
        self.markers[self.currentEntry.tz].show()
        self.currentEntry = entry
        self.markers[self.currentEntry.tz].hide()
        x, y = self.map2canvas(self.currentEntry.lat, self.currentEntry.long)
        self.current.set(x=x, y=y)

        if skipList:
            return

        iter = self.listStore.get_iter_root()
        next = 1
        while next:
            if self.listStore.get_value(iter, self.columns.ENTRY) == self.currentEntry:
                selection = self.listView.get_selection()
                selection.unselect_all()
                selection.select_iter(iter)
                path = self.listStore.get_path(iter)
                col = self.listView.get_column(0)
                self.listView.scroll_to_cell(path, col, gtk.TRUE, 0.5, 0.5)
                self.listView.set_cursor(path, col, gtk.FALSE)
                break
            next = self.listStore.iter_next(iter)
        
    def canvasEvent(self, widget, event=None):
        if event.type == gtk.gdk.LEAVE_NOTIFY:
            self.arrow.hide()
        
    def map2canvas(self, lat, long):
        x2 = self.mapWidth
        y2 = self.mapHeight
        x = x2 / 2.0 + (x2 / 2.0) * long / 180.0
        y = y2 / 2.0 - (y2 / 2.0) * lat / 90.0
        return (x, y)

    def canvas2map(self, x, y):
        x2 = self.mapWidth
        y2 = self.mapHeight
        long = (x - x2 / 2.0) / (x2 / 2.0) * 180.0
        lat = (y2 / 2.0 - y) / (y2 / 2.0) * 90.0
        return (lat, long)

class ZoneTabEntry:
    def __init__(self, code=None, lat=0, long=0, tz=None, comments=None):
        self.code = code
        self.lat = lat
        self.long = long
        self.tz = tz
        self.comments = comments

class ZoneTab:
    def __init__(self, fn='/usr/share/zoneinfo/zone.tab'):
        self.entries = []
        self.readZoneTab(fn)

    def getEntries(self):
        return self.entries

    def findEntryByTZ(self, tz):
        for entry in self.entries:
            if entry.tz == tz:
                return entry
        return None

    def findNearest(self, lat, long):
        nearestEntry = None
        min = -1
        for entry in self.entries:
            dx = entry.long - long
            dy = entry.lat - lat
            dist = (dy * dy) + (dx * dx)
            if dist < min or min == -1:
                min = dist
                nearestEntry = entry
        return nearestEntry

    def convertCoord(self, coord, type="lat"):
        if type != "lat" and type != "long":
            raise TypeError, "invalid coord type"
        if type == "lat":
            deg = 3
        else:
            deg = 4
        degrees = string.atoi(coord[0:deg])
        order = len(coord[deg:])
        minutes = string.atoi(coord[deg:])
        if degrees > 0:
            return degrees + minutes/math.pow(10, order)
        return degrees - minutes/math.pow(10, order)
        
    def readZoneTab(self, fn):
        f = open(fn, 'r')
        comment = re.compile("^#")
        coordre = re.compile("[\+-]")
        while 1:
            line = f.readline()
            if not line:
                break
            if comment.search(line):
                continue
            fields = string.split(line, '\t')
            if len(fields) < 3:
                continue
            code = fields[0]
            split = coordre.search(fields[1], 1)
            lat = self.convertCoord(fields[1][:split.end() - 1], "lat")
            long = self.convertCoord(fields[1][split.end() - 1:], "long")
            tz = string.strip(fields[2])
            if len(fields) > 3:
                comments = string.strip(fields[3])
            else:
                comments = None
            entry = ZoneTabEntry(code, lat, long, tz, comments)
            self.entries.append(entry)

if __name__ == "__main__":
    zonetab = ZoneTab()
    win = gtk.Window()
    win.connect('destroy', gtk.mainquit)
    map = TimezoneMap(zonetab)
    vbox = gtk.VBox()
    vbox.pack_start(map)
    button = gtk.Button("Quit")
    button.connect("pressed", gtk.mainquit)
    vbox.pack_start(button, gtk.FALSE, gtk.FALSE)
    win.add(vbox)
    win.show_all()
    gtk.mainloop()