~ubuntu-branches/ubuntu/wily/d-rats/wily

« back to all changes in this revision

Viewing changes to d_rats/ui/main_stations.py

  • Committer: Bazaar Package Importer
  • Author(s): Steve Conklin
  • Date: 2011-02-18 18:17:24 UTC
  • Revision ID: james.westby@ubuntu.com-20110218181724-kaxw0ceawnu3wgw6
Tags: upstream-0.3.3~b5
ImportĀ upstreamĀ versionĀ 0.3.3~b5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#
 
3
# Copyright 2009 Dan Smith <dsmith@danplanet.com>
 
4
#
 
5
# This program is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation, either version 3 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 
 
18
import gtk
 
19
import gobject
 
20
 
 
21
import time
 
22
import os
 
23
 
 
24
from d_rats.ui.main_common import MainWindowTab
 
25
from d_rats.ui import main_events
 
26
from d_rats.ui import conntest
 
27
from d_rats.sessions import rpc
 
28
from d_rats import station_status
 
29
from d_rats import signals
 
30
from d_rats import image
 
31
from d_rats import miscwidgets
 
32
from d_rats import inputdialog
 
33
from d_rats import utils
 
34
 
 
35
def prompt_for_account(config):
 
36
    accounts = {}
 
37
    for section in config.options("incoming_email"):
 
38
        info = config.get("incoming_email", section).split(",")
 
39
        key = "%s on %s" % (info[1], info[0])
 
40
        accounts[key] = info
 
41
 
 
42
    wl2k_call = config.get("user", "callsign")
 
43
    wl2k_ssid = config.get("prefs", "msg_wl2k_ssid").strip()
 
44
    if wl2k_ssid:
 
45
        wl2k_call = "%s-%s" % (wl2k_call, wl2k_ssid)
 
46
 
 
47
    accounts["Other"] = ["", "", "", "", "", "110"]
 
48
    accounts["WL2K"] = ["@WL2K", wl2k_call, "", "", "", "0"]
 
49
    default = accounts.keys()[0]
 
50
 
 
51
    account = miscwidgets.make_choice(accounts.keys(), False, default)
 
52
    host = gtk.Entry()
 
53
    user = gtk.Entry()
 
54
    pasw = gtk.Entry()
 
55
    ussl = gtk.CheckButton()
 
56
    port = gtk.SpinButton(gtk.Adjustment(110, 1, 65535, 1), digits=0)
 
57
 
 
58
    disable = [host, user, pasw, ussl, port]
 
59
 
 
60
    pasw.set_visibility(False)
 
61
 
 
62
    def choose_account(box):
 
63
        info = accounts[box.get_active_text()]
 
64
        for i in disable:
 
65
            i.set_sensitive(not info[0])
 
66
        host.set_text(info[0])
 
67
        user.set_text(info[1])
 
68
        pasw.set_text(info[2])
 
69
        ussl.set_active(info[4] == "True")
 
70
        port.set_value(int(info[5]))
 
71
    account.connect("changed", choose_account)
 
72
    choose_account(account)
 
73
 
 
74
    d = inputdialog.FieldDialog(title="Select account")
 
75
    d.add_field("Account", account)
 
76
    d.add_field("Server", host)
 
77
    d.add_field("Username", user)
 
78
    d.add_field("Password", pasw)
 
79
    d.add_field("Use SSL", ussl)
 
80
    d.add_field("Port", port)
 
81
    r = d.run()
 
82
    d.destroy()
 
83
    if r == gtk.RESPONSE_CANCEL:
 
84
        return None
 
85
 
 
86
    return host.get_text(), user.get_text(), pasw.get_text(), \
 
87
        str(ussl.get_active()), str(int(port.get_value()))
 
88
 
 
89
class StationsList(MainWindowTab):
 
90
    __gsignals__ = {
 
91
        "event" : signals.EVENT,
 
92
        "notice" : signals.NOTICE,
 
93
        "get-station-list" : signals.GET_STATION_LIST,
 
94
        "ping-station" : signals.PING_STATION,
 
95
        "ping-station-echo" : signals.PING_STATION_ECHO,
 
96
        "incoming-chat-message" : signals.INCOMING_CHAT_MESSAGE,
 
97
        "submit-rpc-job" : signals.SUBMIT_RPC_JOB,
 
98
        "user-send-file" : signals.USER_SEND_FILE,
 
99
        }
 
100
 
 
101
    _signals = __gsignals__
 
102
 
 
103
    def _update(self):
 
104
        self.__view.queue_draw()
 
105
 
 
106
        return True
 
107
 
 
108
    def _mh(self, _action, station, port):
 
109
        action = _action.get_name()
 
110
 
 
111
        model = self.__view.get_model()
 
112
        iter = model.get_iter_first()
 
113
        while iter:
 
114
            _station, = model.get(iter, 0)
 
115
            if _station == station:
 
116
                break
 
117
            iter = model.iter_next(iter)
 
118
 
 
119
        if action == "ping":
 
120
            # FIXME: Use the port we saw the user on
 
121
            self.emit("ping-station", station, port)
 
122
        elif action == "conntest":
 
123
            ct = conntest.ConnTestAssistant(station, port)
 
124
            ct.connect("ping-echo-station",
 
125
                       lambda a, *v: self.emit("ping-station-echo", *v))
 
126
            ct.run()
 
127
        elif action == "remove":
 
128
            self.__calls.remove(station)
 
129
            self._update_station_count()
 
130
            model.remove(iter)
 
131
        elif action == "reset":
 
132
            model.set(iter, 1, time.time())
 
133
        elif action == "reqpos":
 
134
            job = rpc.RPCPositionReport(station, "Position Request")
 
135
            def log_result(job, state, result):
 
136
                msg = result.get("rc", "(Error)")
 
137
                if msg != "OK":
 
138
                    event = main_events.Event(None,
 
139
                                              "%s %s: %s" % (station,
 
140
                                                             _("says"),
 
141
                                                             msg))
 
142
                    self.emit("event", event)
 
143
                print "Result: %s" % str(result)
 
144
            job.set_station(station)
 
145
            job.connect("state-change", log_result)
 
146
 
 
147
            # FIXME: Send on the port where we saw this user
 
148
            self.emit("submit-rpc-job", job, port)
 
149
        elif action == "clearall":
 
150
            model.clear()
 
151
            self.__calls = []
 
152
            self._update_station_count()
 
153
        elif action == "pingall":
 
154
            stationlist = self.emit("get-station-list")
 
155
            for port in stationlist.keys():
 
156
                print "Doing CQCQCQ ping on port %s" % port
 
157
                self.emit("ping-station", "CQCQCQ", port)
 
158
        elif action == "reqposall":
 
159
            job = rpc.RPCPositionReport("CQCQCQ", "Position Request")
 
160
            job.set_station(".")
 
161
            stationlist = self.emit("get-station-list")
 
162
            for port in stationlist.keys():
 
163
                self.emit("submit-rpc-job", job, port)
 
164
        elif action == "sendfile":
 
165
            fn = self._config.platform.gui_open_file()
 
166
            if not fn:
 
167
                return
 
168
 
 
169
            fnl = fn.lower()
 
170
            if fnl.endswith(".jpg") or fnl.endswith(".jpeg") or \
 
171
                    fnl.endswith(".png") or fnl.endswith(".gif"):
 
172
                fn = image.send_image(fn)
 
173
                if not fn:
 
174
                    return
 
175
 
 
176
            name = os.path.basename(fn)
 
177
            self.emit("user-send-file", station, port, fn, name)
 
178
        elif action == "version":
 
179
            def log_result(job, state, result):
 
180
                if state == "complete":
 
181
                    msg = "Station %s running D-RATS %s on %s" % (\
 
182
                        job.get_dest(),
 
183
                        result.get("version", "Unknown"),
 
184
                        result.get("os", "Unknown"))
 
185
                    print "Station %s reports version info: %s" % (\
 
186
                        job.get_dest(), result)
 
187
 
 
188
                else:
 
189
                    msg = "No version response from %s" % job.get_dest()
 
190
                event = main_events.Event(None, msg)
 
191
                self.emit("event", event)
 
192
 
 
193
            job = rpc.RPCGetVersion(station, "Version Request")
 
194
            job.connect("state-change", log_result)
 
195
            self.emit("submit-rpc-job", job, port)
 
196
        elif action == "mcheck":
 
197
            def log_result(job, state, result):
 
198
                msg = "Mail check via %s: %s" % (job.get_dest(),
 
199
                                                 result.get("msg",
 
200
                                                            "No response"))
 
201
                event = main_events.Event(None, msg)
 
202
                self.emit("event", event)
 
203
 
 
204
            vals = prompt_for_account(self._config)
 
205
            if vals is None:
 
206
                return
 
207
 
 
208
            job = rpc.RPCCheckMail(station, "Mail Check")
 
209
            job.set_account(vals[0], vals[1], vals[2], vals[4], vals[3])
 
210
            job.connect("state-change", log_result)
 
211
            self.emit("submit-rpc-job", job, port)
 
212
 
 
213
    def _make_station_menu(self, station, port):
 
214
        xml = """
 
215
<ui>
 
216
  <popup name="menu">
 
217
    <menuitem action="ping"/>
 
218
    <menuitem action="conntest"/>
 
219
    <menuitem action="reqpos"/>
 
220
    <menuitem action="sendfile"/>
 
221
    <menuitem action="version"/>
 
222
    <menuitem action="mcheck"/>
 
223
    <separator/>
 
224
    <menuitem action="remove"/>
 
225
    <menuitem action="reset"/>
 
226
    <separator/>
 
227
    <menuitem action="clearall"/>
 
228
    <menuitem action="pingall"/>
 
229
    <menuitem action="reqposall"/>
 
230
  </popup>
 
231
</ui>
 
232
"""
 
233
        ag = gtk.ActionGroup("menu")
 
234
        actions = [("ping", _("Ping"), None),
 
235
                   ("conntest", _("Test Connectivity"), None),
 
236
                   ("reqpos", _("Request Position"), None),
 
237
                   ("sendfile", _("Send file"), None),
 
238
                   ("remove", _("Remove"), gtk.STOCK_DELETE),
 
239
                   ("reset", _("Reset"), gtk.STOCK_JUMP_TO),
 
240
                   ("version", _("Get version"), gtk.STOCK_ABOUT),
 
241
                   ("mcheck", _("Request mail check"), None)]
 
242
 
 
243
        for action, label, stock in actions:
 
244
            a = gtk.Action(action, label, None, stock)
 
245
            a.connect("activate", self._mh, station, port)
 
246
            a.set_sensitive(station is not None)
 
247
            ag.add_action(a)
 
248
 
 
249
        actions = [("clearall", _("Clear All"), gtk.STOCK_CLEAR),
 
250
                   ("pingall", _("Ping All Stations"), None),
 
251
                   ("reqposall", _("Request all positions"), None)]
 
252
        for action, label, stock in actions:
 
253
            a = gtk.Action(action, label, None, stock)
 
254
            a.connect("activate", self._mh, station, port)
 
255
            ag.add_action(a)
 
256
 
 
257
        uim = gtk.UIManager()
 
258
        uim.insert_action_group(ag, 0)
 
259
        uim.add_ui_from_string(xml)
 
260
 
 
261
        return uim.get_widget("/menu")
 
262
 
 
263
    def _mouse_cb(self, view, event):
 
264
        if event.button != 3:
 
265
            return
 
266
 
 
267
        if event.window == view.get_bin_window():
 
268
            x, y = event.get_coords()
 
269
            pathinfo = view.get_path_at_pos(int(x), int(y))
 
270
            if pathinfo is None:
 
271
                station = None
 
272
                port = None
 
273
            else:
 
274
                view.set_cursor_on_cell(pathinfo[0])
 
275
                (model, iter) = view.get_selection().get_selected()
 
276
                station, port = model.get(iter, 0, 5)
 
277
 
 
278
        menu = self._make_station_menu(station, port)
 
279
        menu.popup(None, None, None, event.button, event.time)
 
280
 
 
281
    def __init__(self, wtree, config):
 
282
        MainWindowTab.__init__(self, wtree, config, "main")
 
283
 
 
284
        frame, self.__view, = self._getw("stations_frame", "stations_view")
 
285
 
 
286
        store = gtk.ListStore(gobject.TYPE_STRING,  # Station
 
287
                              gobject.TYPE_INT,     # Timestamp
 
288
                              gobject.TYPE_STRING,  # Message
 
289
                              gobject.TYPE_INT,     # Status
 
290
                              gobject.TYPE_STRING,  # Status message
 
291
                              gobject.TYPE_STRING)  # Port
 
292
        store.set_sort_column_id(1, gtk.SORT_DESCENDING)
 
293
        self.__view.set_model(store)
 
294
 
 
295
        try:
 
296
            self.__view.set_tooltip_column(2)
 
297
        except AttributeError:
 
298
            print "This version of GTK is old; disabling station tooltips"
 
299
 
 
300
        self.__view.connect("button_press_event", self._mouse_cb)
 
301
 
 
302
        def render_call(col, rend, model, iter):
 
303
            call, ts, status = model.get(iter, 0, 1, 3)
 
304
            sec = time.time() - ts
 
305
 
 
306
            hour = 3600
 
307
            day = (hour*24)
 
308
 
 
309
            if sec < 60:
 
310
                msg = call
 
311
            elif sec < hour:
 
312
                msg = "%s (%im)" % (call, (sec / 60))
 
313
            elif sec < day:
 
314
                msg = "%s (%ih %im)" % (call, sec / 3600, (sec % 3600) / 60)
 
315
            else:
 
316
                msg = "%s (%id %ih)" % (call, sec / day, (sec % day) / 3600)
 
317
 
 
318
            if status == station_status.STATUS_ONLINE:
 
319
                color = "blue"
 
320
            elif status == station_status.STATUS_UNATTENDED:
 
321
                color = "#CC9900"
 
322
            elif status == station_status.STATUS_OFFLINE:
 
323
                color = "grey"
 
324
            else:
 
325
                color = "black"
 
326
 
 
327
            rend.set_property("markup", "<span color='%s'>%s</span>" % (color,
 
328
                                                                        msg))
 
329
 
 
330
        r = gtk.CellRendererText()
 
331
        col = gtk.TreeViewColumn(_("Stations"), r, text=0)
 
332
        col.set_cell_data_func(r, render_call)
 
333
        self.__view.append_column(col)
 
334
 
 
335
        self.__calls = []
 
336
        self._update_station_count()
 
337
 
 
338
        status, msg = self._getw("stations_status", "stations_smsg")
 
339
 
 
340
        try:
 
341
            status.set_tooltip_text(_("This is the state other stations will " +
 
342
                                      "see when requesting your status"))
 
343
            msg.set_tooltip_text(_("This is the message other stations will " +
 
344
                                   "see when requesting your status"))
 
345
        except AttributeError:
 
346
            pass
 
347
 
 
348
        def set_status(cb):
 
349
            self.__status = cb.get_active_text()
 
350
            self._config.set("state", "status_state", self.__status)
 
351
 
 
352
        def set_smsg(e):
 
353
            self.__smsg = e.get_text()
 
354
            self._config.set("state", "status_msg", self.__smsg)
 
355
 
 
356
        for s in sorted(station_status.get_status_msgs().values()):
 
357
            status.append_text(s)
 
358
 
 
359
        status.connect("changed", set_status)
 
360
        msg.connect("changed", set_smsg)
 
361
 
 
362
        prev_status = self._config.get("state", "status_state")
 
363
        if not utils.combo_select(status, prev_status):
 
364
            utils.combo_select(status,
 
365
                               station_status.get_status_msgs().values()[0])
 
366
        msg.set_text(self._config.get("state", "status_msg"))
 
367
        set_status(status)
 
368
        set_smsg(msg)
 
369
 
 
370
        gobject.timeout_add(30000, self._update)
 
371
 
 
372
    def _update_station_count(self):
 
373
            hdr, = self._getw("stations_header")
 
374
            hdr.set_markup("<b>Stations (%i)</b>" % len(self.__calls))
 
375
 
 
376
    def saw_station(self, station, port, status=0, smsg=""):
 
377
        status_changed = False
 
378
 
 
379
        if station == "CQCQCQ":
 
380
            return
 
381
 
 
382
        store = self.__view.get_model()
 
383
 
 
384
        ts = time.time()
 
385
        msg = "%s <b>%s</b> %s <i>%s</i>\r\n%s: <b>%s</b>" % \
 
386
            (_("Station"),
 
387
             station,
 
388
             _("last seen at"),
 
389
             time.strftime("%X %x",
 
390
                           time.localtime(ts)),
 
391
             _("Port"),
 
392
             port)
 
393
 
 
394
        status_val = station_status.get_status_msgs().get(status, "Unknown")
 
395
        if station not in self.__calls:
 
396
            if smsg:
 
397
                msg += "\r\nStatus: <b>%s</b> (<i>%s</i>)" % (status_val, smsg)
 
398
            self.__calls.append(station)
 
399
            store.append((station, ts, msg, status, smsg, port))
 
400
            self.__view.queue_draw()
 
401
            status_changed = True
 
402
            self._update_station_count()
 
403
        else:
 
404
            iter = store.get_iter_first()
 
405
            while iter:
 
406
                call, _status, _smsg = store.get(iter, 0, 3, 4)
 
407
                if call == station:
 
408
                    status_changed = (status and (_status != status) or \
 
409
                                          (smsg and (_smsg != smsg)))
 
410
 
 
411
                    if _status > 0 and status == 0:
 
412
                        status = _status
 
413
                    if not smsg:
 
414
                        smsg = _smsg
 
415
 
 
416
                    msg += "\r\nStatus: <b>%s</b> (<i>%s</i>)" % (status_val,
 
417
                                                                  smsg)
 
418
                    store.set(iter, 1, ts, 2, msg, 3, status, 4, smsg, 5, port)
 
419
                    break
 
420
                iter = store.iter_next(iter)
 
421
 
 
422
        if status_changed and status > 0 and \
 
423
                self._config.getboolean("prefs", "chat_showstatus"):
 
424
            self.emit("incoming-chat-message",
 
425
                      station,
 
426
                      "CQCQCQ",
 
427
                      "%s %s: %s (%s %s)" % (_("Now"), status_val, smsg,
 
428
                                             _("Port"), port))
 
429
            
 
430
    def get_status(self):
 
431
        sval = station_status.get_status_vals()[self.__status]
 
432
 
 
433
        return sval, self.__smsg
 
434
 
 
435
    def get_stations(self):
 
436
        stations = []
 
437
        store = self.__view.get_model()
 
438
        iter = store.get_iter_first()
 
439
        while iter:
 
440
            call, ts, port = store.get(iter, 0, 1, 5)
 
441
            station = station_status.Station(call)
 
442
            station.set_heard(ts)
 
443
            station.set_port(port)
 
444
            stations.append(station)
 
445
            iter = store.iter_next(iter)
 
446
 
 
447
        return stations