From 0da5af5f8c3255dd681b10584acc7c0ff41a515f Mon Sep 17 00:00:00 2001 From: Jack Zielke Date: Tue, 26 May 2009 05:18:32 +0000 Subject: version 9 - journal works, message text in bold, scroll bugfix --- diff --git a/NEWS b/NEWS index 503d20b..0ade83a 100644 --- a/NEWS +++ b/NEWS @@ -7,3 +7,4 @@ October 5 2008 - version 5 - no journal, can not cancel messages October 7 2008 - version 6 - no journal, can cancel message queue, show acks October 18 2008 - version 7 - no journal, ack bugfix October 18 2008 - version 8 - no journal, 8.2.0 update +November 9 2008 - version 9 - journal works, message text in bold, scroll bugfix diff --git a/README b/README index 220de8a..c92acb1 100644 --- a/README +++ b/README @@ -1,8 +1,6 @@ -APRS for OLPC - -APRS Position Reporting for OLPC +APRS for OLPC XO This activity tries to follow the development plan outlined by Bob Bruninga, WB4APR. -http://www.ew.usna.edu/~bruninga/APRS-OLPC.html +http://aprs.org/APRS-OLPC.html diff --git a/TODO b/TODO index ca8ab62..24da9ec 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,11 @@ -save in journal - multi language +cancel individual messages +beep on message receive + cleanup timers on disconnect cleanup old acks in self.sent_acks{} cleanup old timers in self.timers[] -send a default filter command like: s//A/X ? auto reconnect? aprs.laptop.org? diff --git a/activity/activity.info b/activity/activity.info index e334971..464a9bd 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,6 +1,6 @@ [Activity] name = APRS-XO -activity_version= 8 +activity_version= 9 service_name = org.laptop.APRSXO icon = activity-aprs-xo exec = sugar-activity aprs.APRSActivity diff --git a/aprs.py b/aprs.py index 830fd02..a3178b0 100755 --- a/aprs.py +++ b/aprs.py @@ -22,6 +22,7 @@ import time import socket import random import gobject +import json from gettext import gettext as _ @@ -31,8 +32,6 @@ from sugar.bundle.activitybundle import ActivityBundle from sugar.graphics.menuitem import MenuItem from sugar.graphics.toolbutton import ToolButton -TESTING = False - HOST = 'rotate.aprs2.net' #HOST = '192.168.50.6' PORT = 14580 @@ -77,6 +76,7 @@ class APRSActivity(activity.Activity): mediumfont = pango.FontDescription('Sans 6.5') smallfont = pango.FontDescription('Sans 6') verysmallfont = pango.FontDescription('Sans 4') + firstName = profile.get_nick_name().split(None, 1)[0].capitalize() toolbox = activity.ActivityToolbox(self) @@ -84,12 +84,12 @@ class APRSActivity(activity.Activity): activity_toolbar = toolbox.get_activity_toolbar() activity_toolbar.share.props.visible = False - activity_toolbar.keep.props.visible = False +# activity_toolbar.keep.props.visible = False toolbox.show() win = gtk.HBox(False, 10) - self.set_canvas(win) +# self.set_canvas(win) leftwin = gtk.VBox(False, 10) @@ -174,6 +174,7 @@ class APRSActivity(activity.Activity): self.calltext = gtk.Entry() self.calltext.set_max_length(9) self.calltext.set_width_chars(9) + self.calltext.set_text(self.metadata.get('callsign', "")) self.calltext.connect("changed", self.disable_beacon) bottomidentbox.pack_start(self.calltext, False, False, 0) self.calltext.show() @@ -428,9 +429,13 @@ class APRSActivity(activity.Activity): self.messagebuffer.set_text("Welcome to APRS-XO.\n\nThis program sends your position information to a server that\nwill display your location on a webpage. This program requires an active Internet connection to work.\n\nSelect an APRS Site\nSelecting a button will copy the URI to the clipboard.\n\nIndentifiers\nEnter your callsign and optionally an aprsd password.\n\nStation Comment\nData in the Station Comment field will appear after your\nlocation information on the website.") + # tags for easier reading of messages + self.messagebold = self.messagebuffer.create_tag("bold", weight=pango.WEIGHT_BOLD) + self.messagewindow = gtk.ScrolledWindow() self.messagewindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) - self.messagewindow.add_with_viewport(self.messageview) +# self.messagewindow.add_with_viewport(self.messageview) + self.messagewindow.add(self.messageview) self.messageview.show() rightwin.pack_start(self.messagewindow, True, True, 0) self.messagewindow.show() @@ -477,7 +482,8 @@ class APRSActivity(activity.Activity): self.statuswindow = gtk.ScrolledWindow() self.statuswindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) - self.statuswindow.add_with_viewport(self.statusview) +# self.statuswindow.add_with_viewport(self.statusview) + self.statuswindow.add(self.statusview) self.statusview.show() rightwin.pack_start(self.statuswindow, True, True, 0) self.statuswindow.show() @@ -504,25 +510,15 @@ class APRSActivity(activity.Activity): win.pack_start(rightwin, False, False, 0) rightwin.show() - # for quick testing - if (TESTING): - self.calltext.set_text("kg4gjy") - self.passtext.set_text("18107") - self.latDDtext.set_text("35") - self.latMMtext.set_text("7") - self.latmmtext.set_text("42") - self.latcombo.set_active(0) - self.lonDDDtext.set_text("85") - self.lonMMtext.set_text("6") - self.lonmmtext.set_text("93") - self.loncombo.set_active(0) -# self.stationtext.set_text("") - self.ziptext.set_text("") - self.beaconbutton.set_active(True) - + self.set_canvas(win) win.show() self.calltext.grab_focus() + # Fix window not updating until activity after alt-tab + self.statusbuffer.create_mark("end", self.statusbuffer.get_end_iter(), False) + # Do the same for message window, without auto delete it did not have the problem + self.messagebuffer.create_mark("end", self.messagebuffer.get_end_iter(), False) + def clear_status(self, button=None): self.statusbuffer.set_text("") @@ -545,7 +541,7 @@ class APRSActivity(activity.Activity): try: iplist = socket.gethostbyname_ex(HOST)[2] except socket.error, msg: - self.status_write(msg[1]) + self.status_write("\n%s\n" % msg[1]) self.sock = None return False server = random.choice(iplist) @@ -553,7 +549,7 @@ class APRSActivity(activity.Activity): try: self.sock.connect((server, PORT)) except socket.error, msg: - self.status_write(msg[1]) + self.status_write("%s\n" % msg[1]) self.sock = None return False @@ -566,10 +562,12 @@ class APRSActivity(activity.Activity): if (response.find("javAPRSSrvr") == -1): self.status_write("invalid response.\n") self.disconnect_aprs(button) + return False if (response.find("Port Full") != -1): self.status_write("Port Full.\n") self.disconnect_aprs(button) + return False if (self.calltext.get_text() != "" and self.passtext.get_text() != ""): sendme = "user %s pass %s vers aprs_xo %d filter %s\n" % (self.calltext.get_text(), self.passtext.get_text(), VERSION, FILTER) @@ -583,6 +581,7 @@ class APRSActivity(activity.Activity): if (response.find("# logresp") == -1): self.status_write("invalid response.\n") self.disconnect_aprs(button) + return False if (response.find("unverified") != -1): sendme = "# filter %s\n" % FILTER @@ -720,17 +719,26 @@ class APRSActivity(activity.Activity): deletehere = self.statusbuffer.get_iter_at_line(statuslines - MAXLINES) self.statusbuffer.delete(self.statusbuffer.get_start_iter(), deletehere) if (not self.statusview.is_focus()): - adjustment = self.statuswindow.get_vadjustment() - adjustment.set_value(adjustment.upper) + self.statusbuffer.move_mark_by_name("end", self.statusbuffer.get_end_iter()) + self.statusview.scroll_mark_onscreen(self.statusbuffer.get_mark("end")) - def message_write(self, text): + def message_write(self, text, bold=None): if (self.messagebox): self.clear_message() self.messagebox = False - self.messagebuffer.insert(self.messagebuffer.get_end_iter(), text) + iter = self.messagebuffer.get_end_iter() + self.messagebuffer.insert(iter, text) + if (bold): + bold_end = iter.copy() + bold_start = iter.copy() + bold_end.forward_to_line_end() + bold_start = bold_end.backward_search(">", gtk.TEXT_SEARCH_TEXT_ONLY) + bold_start[0].forward_chars(2) + self.messagebuffer.apply_tag(self.messagebold, bold_start[0], bold_end) + if (not self.messageview.is_focus()): - adjustment = self.messagewindow.get_vadjustment() - adjustment.set_value(adjustment.upper) + self.messagebuffer.move_mark_by_name("end", self.messagebuffer.get_end_iter()) + self.messageview.scroll_mark_onscreen(self.messagebuffer.get_mark("end")) def set_site(self, widget, data=None): self.site = data @@ -874,53 +882,107 @@ class APRSActivity(activity.Activity): self.beaconbutton.set_active(beaconchecked) self.validating = False - def close( self ): + def can_close( self ): self.hide() if (self.sock != None): self.disconnect() - activity.Activity.close( self ) - - def write_file_broken(self, filename): - # does not appear to run - # from self.save() returns: - # TypeError: object of type 'dbus.Int32' has no len() - - self.metadata['mime_type'] = 'text/plain' - - self.metadata['callsign'] = self.calltext.get_text() - self.metadata['password'] = self.passtext.get_text() - self.metadata['latDD'] = self.latDDtext.get_text() - self.metadata['latMM'] = self.latMMtext.get_text() - self.metadata['latmm'] = self.latmmtext.get_text() - self.metadata['lat'] = self.latcombo.get_active() - self.metadata['lonDDD'] = self.lonDDDtext.get_text() - self.metadata['lonMM'] = self.lonMMtext.get_text() - self.metadata['lonmm'] = self.lonmmtext.get_text() - self.metadata['lon'] = self.loncombo.get_active() -# self.metadata['nick_name'] = self.nametext.get_text() -# self.metadata['location'] = self.location - self.metadata['stationtext'] = self.stationtext.get_text() -# self.metadata['zip'] = self.ziptext.get_text() - self.metadata['beacon'] = self.beaconbutton.get_active() - # should save list of callsigns used in outgoing messages - - def read_file_broken(self, filename): - # does not appear to run + return True + + def write_file(self, file_path): + try: + self.metadata['mime_type'] = 'text/plain' + + self.metadata['callsign'] = self.calltext.get_text() + self.metadata['password'] = self.passtext.get_text() + self.metadata['latDD'] = self.latDDtext.get_text() + self.metadata['latMM'] = self.latMMtext.get_text() + self.metadata['latmm'] = self.latmmtext.get_text() + self.metadata['lat'] = self.latcombo.get_active_text() + self.metadata['lonDDD'] = self.lonDDDtext.get_text() + self.metadata['lonMM'] = self.lonMMtext.get_text() + self.metadata['lonmm'] = self.lonmmtext.get_text() + self.metadata['lon'] = self.loncombo.get_active_text() + self.metadata['stationtext'] = self.stationtext.get_text() + if (self.beaconbutton.get_active()): + self.metadata['beacon'] = 'True' + else: + self.metadata['beacon'] = 'False' + + callsignlist = [] + model = self.messagecombo.get_model() + iter = model.get_iter_first() + while iter: + callsignlist.append(model.get(iter, 0)[0]) + iter = model.iter_next(iter) + + JournalData = {} + JournalData['callsignlist'] = callsignlist + if (self.help): + JournalData['messages'] = "Message Window" + else: + JournalData['messages'] = self.messagebuffer.get_text(self.messagebuffer.get_start_iter(), self.messagebuffer.get_end_iter()) + data = json.write(JournalData) + + f = open(file_path, 'w') + try: + f.write(data) + finally: + f.close() + + except Exception, e: + self.status_write("write_file(): %s\n" % e) + + def read_file(self, file_path): + self.statusbuffer.set_text("Status Window\n\n") self.calltext.set_text(self.metadata.get('callsign', "")) self.passtext.set_text(self.metadata.get('password', "")) - self.latDDtext.set_text(self.metadata.get('latDD', "00")) - self.latMMtext.set_text(self.metadata.get('latMM', "00")) - self.latmmtext.set_text(self.metadata.get('latmm', "00")) -# self.latcombo.set_active(self.metadata.get('lat', "")) - self.lonDDDtext.set_text(self.metadata.get('lonDDD', "000")) - self.lonMMtext.set_text(self.metadata.get('lonMM', "00")) - self.lonmmtext.set_text(self.metadata.get('lonmm', "00")) -# self.loncombo.set_active(self.metadata.get('lon', "")) -# self.nametext.set_text(self.metadata.get('nick_name', "Joe")) -# self.locbutton.set_active(self.metadata.get('location', "")) - self.stationtext.set_text(self.metadata.get('stationtext', "test text")) - self.ziptext.set_text(self.metadata.get('zip', "")) -# self.beaconbutton.set_active(self.metadata.get('beacon', "")) + self.latDDtext.set_text(self.metadata.get('latDD', "DD")) + self.latMMtext.set_text(self.metadata.get('latMM', "MM")) + self.latmmtext.set_text(self.metadata.get('latmm', "mm")) + if (self.metadata.get('lat', "N") == "N"): + self.latcombo.set_active(0) + else: + self.latcombo.set_active(1) + self.lonDDDtext.set_text(self.metadata.get('lonDDD', "DDD")) + self.lonMMtext.set_text(self.metadata.get('lonMM', "MM")) + self.lonmmtext.set_text(self.metadata.get('lonmm', "mm")) + if (self.metadata.get('lon', "W") == "W"): + self.loncombo.set_active(0) + else: + self.loncombo.set_active(1) + stationtext = self.metadata.get('stationtext', "") + if (stationtext == ""): + firstName = profile.get_nick_name().split(None, 1)[0].capitalize() + self.stationtext.set_text("%s's XO at home." % firstName) + else: + self.stationtext.set_text(stationtext) + if (self.metadata.get('beacon', "True") == "True"): + self.beaconbutton.set_active(True) + else: + self.beaconbutton.set_active(False) + + JournalData = {} + callsignlist = ["ALL", "BEACON", "CQ", "QST"] + messages = "Message Window" + try: + f = open(file_path, 'r') + JournalData = json.read(f.read()) + if JournalData.has_key('callsignlist'): + callsignlist = JournalData['callsignlist'] + if JournalData.has_key('messages'): + messages = JournalData['messages'] + except: + pass + finally: + f.close() + self.messagebuffer.set_text(messages) + self.help = False + if (messages == "Message Window"): + self.messagebox = True + else: + self.messagebox = False + for currentcall in callsignlist: + self.add_callsign(currentcall, False) def msg_check(self, data): firstcheck = data.find("::") @@ -937,7 +999,7 @@ class APRSActivity(activity.Activity): bln_id = "%s-%s" % (fromcall, strippedtocall) if (not bln_id in self.seen_bulletins): self.seen_bulletins[bln_id] = 1 - self.message_write("%s %s:%s> %s\n" % (time.strftime("%m/%d %H:%M", time.localtime()), fromcall, strippedtocall, message)) + self.message_write("%s %s:%s> %s\n" % (time.strftime("%m/%d %H:%M", time.localtime()), fromcall, strippedtocall, message), True) else: if (strippedtocall == self.calltext.get_text()): self.add_callsign(fromcall, False) @@ -980,7 +1042,7 @@ class APRSActivity(activity.Activity): self.timers.append(gobject.timeout_add(120 * 1000, self.send_ack, tocall, sequence)) else: # TODO beep? - self.message_write("%s %s> %s\n" % (time.strftime("%m/%d %H:%M", time.localtime()), fromcall, message)) + self.message_write("%s %s> %s\n" % (time.strftime("%m/%d %H:%M", time.localtime()), fromcall, message), True) self.sent_acks[id] = time.time() # to help a cleanup thread later self.sent_acks[fromcall] = sequence # to help with reply acks later else: @@ -1014,7 +1076,7 @@ class APRSActivity(activity.Activity): self.recv_acks[ackid] = 1 else: # TODO beep? - self.message_write("%s %s> %s\n" % (time.strftime("%m/%d %H:%M", time.localtime()), fromcall, message)) + self.message_write("%s %s> %s\n" % (time.strftime("%m/%d %H:%M", time.localtime()), fromcall, message), True) def disable_beacon(self, widget, data=None): if (self.sock != None): @@ -1206,6 +1268,14 @@ class APRSActivity(activity.Activity): iter.forward_chars(15 + len(call)) self.message_marks[id] = self.messagebuffer.create_mark(None, iter, True) # self.message_marks[id].set_visible(True) + + bold_end = iter.copy() + bold_start = iter.copy() + bold_end.forward_to_line_end() + bold_end = bold_end.backward_search("<", gtk.TEXT_SEARCH_TEXT_ONLY) + bold_start.forward_chars(2) + self.messagebuffer.apply_tag(self.messagebold, bold_start, bold_end[0]) + return True def send_msg_queue(self, call): @@ -1226,7 +1296,6 @@ class APRSActivity(activity.Activity): isbulletin = self.bulletin_check(call) if (isbulletin): self.send_data(":%s:%s" % (call.ljust(9), message)) - # TODO what are the correct timings for bulletins? gobject.timeout_add(600 * 1000, self.msg_timer, call, message, "", 2, 600) else: replyack = self.replyack(call) @@ -1266,6 +1335,9 @@ class APRSActivity(activity.Activity): self.messagebuffer.insert(iter, " 1/%s" % MAXRETRIES) def cancel_message(self, id): + if (self.recv_acks[id] == 1): + return + self.recv_acks[id] = 1 # remove status -- cgit v0.9.1