Update structure to include the build
authorJon <jmstaley@gmail.com>
Sun, 16 May 2010 17:55:32 +0000 (18:55 +0100)
committerJon <jmstaley@gmail.com>
Sun, 16 May 2010 17:55:32 +0000 (18:55 +0100)
LICENSE [deleted file]
build/build_gigfinder.py [new file with mode: 0644]
gigfinder.png [deleted file]
gigfinder.py [deleted file]
locator.py [deleted file]
src/opt/gigfinder/LICENSE [new file with mode: 0644]
src/opt/gigfinder/gigfinder.py [new file with mode: 0755]
src/opt/gigfinder/locator.py [new file with mode: 0644]
src/usr/share/applications/hildon/gigfinder.desktop [new file with mode: 0644]
src/usr/share/icons/hicolor/48x48/hildon/gigfinder.png [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
deleted file mode 100644 (file)
index 0cff5cc..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2010 Jon Staley jmstaley@gmail.com
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
diff --git a/build/build_gigfinder.py b/build/build_gigfinder.py
new file mode 100644 (file)
index 0000000..e52111c
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.5
+# -*- coding: utf-8 -*-
+# 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; version 2 only.
+#
+# 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.
+#
+import py2deb
+import os
+if __name__ == "__main__":
+    try:
+        os.chdir(os.path.dirname(sys.argv[0]))
+    except:
+        pass
+    print
+    p=py2deb.Py2deb("gigfinder")   
+    p.description="Simple app that uses last.fm event feeds to find gigs nearby."
+    p.author="Jon Staley"
+    p.mail="jmstaley@gmail.com"
+    p.depends = "python2.5, python2.5-gtk2, python-location"
+    p.section="user/navigation"
+    #p.icon = "/home/user/MyDocs/mclock/mClock.png"
+    p.arch="all"                #should be all for python, any for all arch
+    p.urgency="low"             #not used in maemo onl for deb os
+    p.distribution="fremantle"
+    p.repository="extras-devel"
+    p.xsbc_bugtracker="https://garage.maemo.org/tracker/?group_id=1496"
+    #  p.postinstall="""#!/bin/sh
+    #  chmod +x /usr/bin/mclock.py""" #Set here your post install script
+    #  p.postremove="""#!/bin/sh
+    #  chmod +x /usr/bin/mclock.py""" #Set here your post remove script
+    #  p.preinstall="""#!/bin/sh
+    #  chmod +x /usr/bin/mclock.py""" #Set here your pre install script
+    #  p.preremove="""#!/bin/sh
+    #  chmod +x /usr/bin/mclock.py""" #Set here your pre remove script
+    version = "0.0.1"           #Version of your software, e.g. "1.2.0" or "0.8.2"
+    build = "2"                 #Build number, e.g. "1" for the first build of this version of your software. Increment for later re-builds of the same version of your software.
+                                #Text with changelog information to be displayed in the package "Details" tab of the Maemo Application Manager
+    changeloginformation = "fix dependency" 
+    dir_name = "src"            #Name of the subfolder containing your package source files (e.g. usr\share\icons\hicolor\scalable\myappicon.svg, usr\lib\myapp\somelib.py). We suggest to leave it named src in all projects and will refer to that in the wiki article on maemo.org
+    #Thanks to DareTheHair from talk.maemo.org for this snippet that recursively builds the file list 
+    for root, dirs, files in os.walk(dir_name):
+        real_dir = root[len(dir_name):]
+        fake_file = []
+        for f in files:
+            fake_file.append(root + os.sep + f + "|" + f)
+        if len(fake_file) > 0:
+            p[real_dir] = fake_file
+    print p
+    r = p.generate(version,build,changelog=changeloginformation,tar=True,dsc=True,changes=True,build=False,src=True)
diff --git a/gigfinder.png b/gigfinder.png
deleted file mode 100644 (file)
index 89dfc28..0000000
Binary files a/gigfinder.png and /dev/null differ
diff --git a/gigfinder.py b/gigfinder.py
deleted file mode 100755 (executable)
index 998e609..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-#!/usr/bin/env python
-
-"""Simple program to display local gigs
-
-Intended for use on the N900, uses the devices gps to find local gigs.
-"""
-
-__authors__ = ["Jon Staley"]
-__copyright__ = "Copyright 2010 Jon Staley"
-__license__ = "MIT"
-__version__ = "0.0.1"
-
-from xml.dom.minidom import parseString
-from datetime import datetime, date
-import urllib
-import time
-import gtk
-import hildon
-import location
-import time
-import gobject
-from threading import Thread
-import thread
-
-from locator import LocationUpdater
-
-gtk.gdk.threads_init()
-
-class GigParser:
-
-    def parse_xml(self, xml, lat, long):
-        """ Parse xml into a dict """
-        events_list = []
-        today = date.today()
-        dom = parseString(xml)
-
-        events = dom.getElementsByTagName('event')
-        for event in events:
-            start_date = self.parse_date(event.getElementsByTagName('startDate')[0].childNodes[0].data)
-            if start_date.date() == today:
-                title = event.getElementsByTagName('title')[0].childNodes[0].data
-                
-                artists_element = event.getElementsByTagName('artists')[0]
-                artist_list = []
-                for artist in artists_element.getElementsByTagName('artist'):
-                    artist_list.append(artist.childNodes[0].data)
-                artists = ', '.join(artist_list)
-
-                venue_details = event.getElementsByTagName('venue')[0]
-                venue_name = venue_details.getElementsByTagName('name')[0].childNodes[0].data
-                address = self.get_address(venue_details.getElementsByTagName('location')[0])
-                geo_data = venue_details.getElementsByTagName('geo:point')[0]
-                venue_lat = geo_data.getElementsByTagName('geo:lat')[0].childNodes[0].data
-                venue_long = geo_data.getElementsByTagName('geo:long')[0].childNodes[0].data
-                distance = location.distance_between(float(lat), 
-                                                     float(long), 
-                                                     float(venue_lat), 
-                                                     float(venue_long))
-                
-                events_list.append({'title': title,
-                                    'venue': venue_name,
-                                    'address': address,
-                                    'distance': distance,
-                                    'artists': artists,
-                                    'date': start_date})
-        return events_list
-    
-    def get_address(self, location):
-        """ Return the venues address details from the xml element """
-        street = ''
-        city = ''
-        country = ''
-        postalcode = ''
-        if location.getElementsByTagName('street')[0].childNodes:
-            street = location.getElementsByTagName('street')[0].childNodes[0].data
-        if location.getElementsByTagName('city')[0].childNodes:
-            city = location.getElementsByTagName('city')[0].childNodes[0].data
-        if location.getElementsByTagName('country')[0].childNodes:
-            country = location.getElementsByTagName('country')[0].childNodes[0].data
-        if location.getElementsByTagName('postalcode')[0].childNodes:
-            postalcode = location.getElementsByTagName('postalcode')[0].childNodes[0].data
-        return '\n'.join([street, city, country, postalcode])
-
-    def parse_date(self, date_string):
-        """ Parse date string into datetime object """
-        fmt =  "%a, %d %b %Y %H:%M:%S"
-        result = time.strptime(date_string, fmt)
-        return datetime(result.tm_year, 
-                        result.tm_mon, 
-                        result.tm_mday, 
-                        result.tm_hour, 
-                        result.tm_min, 
-                        result.tm_sec)
-
-
-class Events:
-
-    def __init__(self):
-        self.api_key = "1928a14bdf51369505530949d8b7e1ee"
-        self.url_base = "http://ws.audioscrobbler.com/2.0/"
-
-    def get_events(self, lat, long, distance):
-        """ Retrieve xml and parse into events list """
-        xml = self.get_xml(lat, long, distance)
-        events = self.parser.parse_xml(xml, 
-                                       lat,
-                                       long)
-        return self.sort_events(events)
-
-    def sort_events(self, events):
-        """ Sort gig by distance """
-        events.sort(cmp=self.distance_cmp, key=lambda x: x['distance'])
-        return events
-        
-    def get_xml(self, lat, long, distance):
-        """ Return xml from lastfm """
-        method = "geo.getevents"
-        params = urllib.urlencode({'method': method,
-                                   'api_key': self.api_key,
-                                   'distance': distance,
-                                   'long': long,
-                                   'lat': lat})
-        response = urllib.urlopen(self.url_base, params)
-        return response.read()
-    
-    def distance_cmp(self, x, y):
-        """ Compare distances for list sort """
-        if x > y:
-            return 1
-        elif x == y:
-            return 0
-        else:
-            return -1
-
-
-class GigFinder:
-
-    def __init__(self):
-        self.lat = None
-        self.long = None
-        self.distance = '10'
-        self.banner = None
-        self.parser = GigParser()
-        self.location = LocationUpdater()
-        self.events = Events()
-        self.win = hildon.StackableWindow()
-        self.app_title = "Gig Finder"
-        # TODO: 
-        # Add user settings for distance, date
-        # refactor gui code, 
-        # maybe do km to mile conversions
-
-    def main(self):
-        """ Build the gui and start the update thread """
-        program = hildon.Program.get_instance()
-        menu = self.create_menu()
-
-        self.win.set_title(self.app_title)
-        self.win.connect("destroy", gtk.main_quit, None)
-        self.win.set_app_menu(menu)
-
-        Thread(target=self.update_gigs).start()
-
-        self.win.show_all()
-        gtk.main()
-
-    def show_about(self, widget, data):
-        """ Show about dialog """
-        dialog = gtk.AboutDialog()
-        dialog.set_name('Gig Finder')
-        dialog.set_version(__version__)
-        dialog.set_authors(__authors__)
-        dialog.set_comments('Display gigs close by.\nUsing the http://www.last.fm api.')
-        dialog.set_license('Distributed under the MIT license.\nhttp://www.opensource.org/licenses/mit-license.php')
-        dialog.set_copyright(__copyright__)
-        dialog.show_all()
-
-    def update(self, widget, data):
-        """ Start update process """
-        self.win.set_title(self.app_title)
-        self.location.reset()
-        self.win.remove(self.pannable_area)
-        Thread(target=self.update_gigs).start()
-
-    def update_gigs(self):
-        """ Get gig info """
-        gobject.idle_add(self.show_message, "Getting events")
-        gobject.idle_add(self.location.update_location)
-
-        # if no gps fix wait
-        # TODO: needs a timeout
-        while not self.location.lat or not self.location.long:
-            time.sleep(1)
-
-        events = self.events.get_events(self.location.lat, 
-                                        self.location.long, 
-                                        self.distance)
-        gobject.idle_add(self.hide_message)
-        gobject.idle_add(self.show_events, events)
-        thread.exit()
-
-    def show_message(self, message):
-        """ Set window progress indicator and show message """
-        hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
-        self.banner = hildon.hildon_banner_show_information(self.win,
-                                                            '', 
-                                                            message)
-
-    def hide_message(self):
-        """ Hide banner and sete progress indicator """
-        self.banner.hide()
-        hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
-
-    def show_events(self, events):
-        """ Sort events, set new window title and add events to table """
-        if events:
-            self.win.set_title('%s (%s)' % (self.app_title, len(events)))
-            self.add_events(events)
-        else:
-            label = gtk.Label('No events available')
-            vbox = gtk.VBox(False, 0)
-            vbox.pack_start(label, True, True, 0)
-            vbox.show_all()
-            self.win.add(vbox)
-
-    def create_menu(self):
-        """ Build application menu """
-        update_button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        update_button.set_label('Update')
-        update_button.connect('clicked',
-                              self.update,
-                              None)
-
-        about_button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        about_button.set_label('About')
-        about_button.connect('clicked',
-                             self.show_about,
-                             None)
-
-        menu = hildon.AppMenu()
-        menu.append(update_button)
-        menu.append(about_button)
-        menu.show_all()
-        return menu
-
-    def show_details(self, widget, data):
-        """ Open new window showing gig details """
-        win = hildon.StackableWindow()
-        win.set_title(data['title'])
-
-        win.vbox = gtk.VBox()
-        win.add(win.vbox)
-
-        scroll = hildon.PannableArea()
-        win.vbox.pack_start(scroll, True, True, 0)
-
-        view = hildon.TextView()
-        view.set_editable(False)
-        view.unset_flags(gtk.CAN_FOCUS)
-        view.set_wrap_mode(gtk.WRAP_WORD)
-        buffer = view.get_buffer()
-        end = buffer.get_end_iter()
-        buffer.insert(end, '%s\n' % data['title'])
-        buffer.insert(end, 'Artists: %s\n' % data['artists'])
-        buffer.insert(end, 'Venue: %s\n' % data['venue'])
-        buffer.insert(end, '%s\n' % data['address'])
-        buffer.insert(end, 'When: %s\n' % data['date'].strftime('%H:%M %d/%M/%Y'))
-        buffer.insert(end, '\n')
-        scroll.add_with_viewport(view)
-
-        win.show_all()
-
-    def add_table(self):
-        """ Add table for events """
-        self.table = gtk.Table(columns=1)
-        self.table.set_row_spacings(10)
-        self.table.set_col_spacings(10)
-
-        self.pannable_area = hildon.PannableArea()
-        self.pannable_area.add_with_viewport(self.table)
-        self.pannable_area.show_all()
-        self.win.add(self.pannable_area)
-        
-    def add_events(self, events):
-        """ Add a table of buttons """
-        self.add_table()
-        pos = 0
-        for event in events:
-            button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, 
-                                   hildon.BUTTON_ARRANGEMENT_VERTICAL)
-            button.set_text(event['title'], "distance: %0.02f km" % event['distance'])
-            button.connect("clicked", self.show_details, event)
-            self.table.attach(button, 0, 1, pos, pos+1)
-            pos += 1
-        self.table.show_all()
-   
-if __name__ == "__main__":
-    finder = GigFinder()
-    finder.main()
diff --git a/locator.py b/locator.py
deleted file mode 100644 (file)
index 089a822..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-import gobject
-import location
-
-class LocationUpdater:
-
-    def __init__(self):
-        self.lat = None
-        self.long = None
-        self.loop = gobject.MainLoop()
-
-        self.control = location.GPSDControl.get_default()
-        self.control.set_properties(preferred_method=location.METHOD_AGNSS,
-                               preferred_interval=location.INTERVAL_DEFAULT)
-        self.control.connect("error-verbose", self.on_error, self.loop)
-        self.control.connect("gpsd-stopped", self.on_stop, self.loop)
-
-        self.device = location.GPSDevice()
-        self.device.connect("changed", self.on_changed, self.control)
-
-    def update_location(self):
-        """ Run the loop and update lat and long """
-        self.reset()
-        gobject.idle_add(self.start_location, self.control)
-        self.loop.run()
-
-    def on_error(self, control, error, data):
-        """ Handle errors """
-        print "location error: %d... quitting" % error
-        data.quit()
-
-    def on_changed(self, device, data):
-        """ Set long and lat """
-        if not device:
-            return
-        if device.fix:
-            # once fix is found and long, lat available set long lat
-            if device.fix[1] & location.GPS_DEVICE_LATLONG_SET:
-                self.lat, self.long = device.fix[4:6]
-                data.stop()
-
-    def on_stop(self, control, data):
-        """ Stop the location service """
-        print "quitting"
-        data.quit()
-
-    def start_location(self, data):
-        """ Start the location service """
-        data.start()
-        return False
-
-    def reset(self):
-        """ Reset coordinates """
-        self.lat = None
-        self.long = None
-        self.device.reset_last_known()
-
diff --git a/src/opt/gigfinder/LICENSE b/src/opt/gigfinder/LICENSE
new file mode 100644 (file)
index 0000000..0cff5cc
--- /dev/null
@@ -0,0 +1,23 @@
+Copyright (c) 2010 Jon Staley jmstaley@gmail.com
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/src/opt/gigfinder/gigfinder.py b/src/opt/gigfinder/gigfinder.py
new file mode 100755 (executable)
index 0000000..998e609
--- /dev/null
@@ -0,0 +1,299 @@
+#!/usr/bin/env python
+
+"""Simple program to display local gigs
+
+Intended for use on the N900, uses the devices gps to find local gigs.
+"""
+
+__authors__ = ["Jon Staley"]
+__copyright__ = "Copyright 2010 Jon Staley"
+__license__ = "MIT"
+__version__ = "0.0.1"
+
+from xml.dom.minidom import parseString
+from datetime import datetime, date
+import urllib
+import time
+import gtk
+import hildon
+import location
+import time
+import gobject
+from threading import Thread
+import thread
+
+from locator import LocationUpdater
+
+gtk.gdk.threads_init()
+
+class GigParser:
+
+    def parse_xml(self, xml, lat, long):
+        """ Parse xml into a dict """
+        events_list = []
+        today = date.today()
+        dom = parseString(xml)
+
+        events = dom.getElementsByTagName('event')
+        for event in events:
+            start_date = self.parse_date(event.getElementsByTagName('startDate')[0].childNodes[0].data)
+            if start_date.date() == today:
+                title = event.getElementsByTagName('title')[0].childNodes[0].data
+                
+                artists_element = event.getElementsByTagName('artists')[0]
+                artist_list = []
+                for artist in artists_element.getElementsByTagName('artist'):
+                    artist_list.append(artist.childNodes[0].data)
+                artists = ', '.join(artist_list)
+
+                venue_details = event.getElementsByTagName('venue')[0]
+                venue_name = venue_details.getElementsByTagName('name')[0].childNodes[0].data
+                address = self.get_address(venue_details.getElementsByTagName('location')[0])
+                geo_data = venue_details.getElementsByTagName('geo:point')[0]
+                venue_lat = geo_data.getElementsByTagName('geo:lat')[0].childNodes[0].data
+                venue_long = geo_data.getElementsByTagName('geo:long')[0].childNodes[0].data
+                distance = location.distance_between(float(lat), 
+                                                     float(long), 
+                                                     float(venue_lat), 
+                                                     float(venue_long))
+                
+                events_list.append({'title': title,
+                                    'venue': venue_name,
+                                    'address': address,
+                                    'distance': distance,
+                                    'artists': artists,
+                                    'date': start_date})
+        return events_list
+    
+    def get_address(self, location):
+        """ Return the venues address details from the xml element """
+        street = ''
+        city = ''
+        country = ''
+        postalcode = ''
+        if location.getElementsByTagName('street')[0].childNodes:
+            street = location.getElementsByTagName('street')[0].childNodes[0].data
+        if location.getElementsByTagName('city')[0].childNodes:
+            city = location.getElementsByTagName('city')[0].childNodes[0].data
+        if location.getElementsByTagName('country')[0].childNodes:
+            country = location.getElementsByTagName('country')[0].childNodes[0].data
+        if location.getElementsByTagName('postalcode')[0].childNodes:
+            postalcode = location.getElementsByTagName('postalcode')[0].childNodes[0].data
+        return '\n'.join([street, city, country, postalcode])
+
+    def parse_date(self, date_string):
+        """ Parse date string into datetime object """
+        fmt =  "%a, %d %b %Y %H:%M:%S"
+        result = time.strptime(date_string, fmt)
+        return datetime(result.tm_year, 
+                        result.tm_mon, 
+                        result.tm_mday, 
+                        result.tm_hour, 
+                        result.tm_min, 
+                        result.tm_sec)
+
+
+class Events:
+
+    def __init__(self):
+        self.api_key = "1928a14bdf51369505530949d8b7e1ee"
+        self.url_base = "http://ws.audioscrobbler.com/2.0/"
+
+    def get_events(self, lat, long, distance):
+        """ Retrieve xml and parse into events list """
+        xml = self.get_xml(lat, long, distance)
+        events = self.parser.parse_xml(xml, 
+                                       lat,
+                                       long)
+        return self.sort_events(events)
+
+    def sort_events(self, events):
+        """ Sort gig by distance """
+        events.sort(cmp=self.distance_cmp, key=lambda x: x['distance'])
+        return events
+        
+    def get_xml(self, lat, long, distance):
+        """ Return xml from lastfm """
+        method = "geo.getevents"
+        params = urllib.urlencode({'method': method,
+                                   'api_key': self.api_key,
+                                   'distance': distance,
+                                   'long': long,
+                                   'lat': lat})
+        response = urllib.urlopen(self.url_base, params)
+        return response.read()
+    
+    def distance_cmp(self, x, y):
+        """ Compare distances for list sort """
+        if x > y:
+            return 1
+        elif x == y:
+            return 0
+        else:
+            return -1
+
+
+class GigFinder:
+
+    def __init__(self):
+        self.lat = None
+        self.long = None
+        self.distance = '10'
+        self.banner = None
+        self.parser = GigParser()
+        self.location = LocationUpdater()
+        self.events = Events()
+        self.win = hildon.StackableWindow()
+        self.app_title = "Gig Finder"
+        # TODO: 
+        # Add user settings for distance, date
+        # refactor gui code, 
+        # maybe do km to mile conversions
+
+    def main(self):
+        """ Build the gui and start the update thread """
+        program = hildon.Program.get_instance()
+        menu = self.create_menu()
+
+        self.win.set_title(self.app_title)
+        self.win.connect("destroy", gtk.main_quit, None)
+        self.win.set_app_menu(menu)
+
+        Thread(target=self.update_gigs).start()
+
+        self.win.show_all()
+        gtk.main()
+
+    def show_about(self, widget, data):
+        """ Show about dialog """
+        dialog = gtk.AboutDialog()
+        dialog.set_name('Gig Finder')
+        dialog.set_version(__version__)
+        dialog.set_authors(__authors__)
+        dialog.set_comments('Display gigs close by.\nUsing the http://www.last.fm api.')
+        dialog.set_license('Distributed under the MIT license.\nhttp://www.opensource.org/licenses/mit-license.php')
+        dialog.set_copyright(__copyright__)
+        dialog.show_all()
+
+    def update(self, widget, data):
+        """ Start update process """
+        self.win.set_title(self.app_title)
+        self.location.reset()
+        self.win.remove(self.pannable_area)
+        Thread(target=self.update_gigs).start()
+
+    def update_gigs(self):
+        """ Get gig info """
+        gobject.idle_add(self.show_message, "Getting events")
+        gobject.idle_add(self.location.update_location)
+
+        # if no gps fix wait
+        # TODO: needs a timeout
+        while not self.location.lat or not self.location.long:
+            time.sleep(1)
+
+        events = self.events.get_events(self.location.lat, 
+                                        self.location.long, 
+                                        self.distance)
+        gobject.idle_add(self.hide_message)
+        gobject.idle_add(self.show_events, events)
+        thread.exit()
+
+    def show_message(self, message):
+        """ Set window progress indicator and show message """
+        hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
+        self.banner = hildon.hildon_banner_show_information(self.win,
+                                                            '', 
+                                                            message)
+
+    def hide_message(self):
+        """ Hide banner and sete progress indicator """
+        self.banner.hide()
+        hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
+
+    def show_events(self, events):
+        """ Sort events, set new window title and add events to table """
+        if events:
+            self.win.set_title('%s (%s)' % (self.app_title, len(events)))
+            self.add_events(events)
+        else:
+            label = gtk.Label('No events available')
+            vbox = gtk.VBox(False, 0)
+            vbox.pack_start(label, True, True, 0)
+            vbox.show_all()
+            self.win.add(vbox)
+
+    def create_menu(self):
+        """ Build application menu """
+        update_button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+        update_button.set_label('Update')
+        update_button.connect('clicked',
+                              self.update,
+                              None)
+
+        about_button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+        about_button.set_label('About')
+        about_button.connect('clicked',
+                             self.show_about,
+                             None)
+
+        menu = hildon.AppMenu()
+        menu.append(update_button)
+        menu.append(about_button)
+        menu.show_all()
+        return menu
+
+    def show_details(self, widget, data):
+        """ Open new window showing gig details """
+        win = hildon.StackableWindow()
+        win.set_title(data['title'])
+
+        win.vbox = gtk.VBox()
+        win.add(win.vbox)
+
+        scroll = hildon.PannableArea()
+        win.vbox.pack_start(scroll, True, True, 0)
+
+        view = hildon.TextView()
+        view.set_editable(False)
+        view.unset_flags(gtk.CAN_FOCUS)
+        view.set_wrap_mode(gtk.WRAP_WORD)
+        buffer = view.get_buffer()
+        end = buffer.get_end_iter()
+        buffer.insert(end, '%s\n' % data['title'])
+        buffer.insert(end, 'Artists: %s\n' % data['artists'])
+        buffer.insert(end, 'Venue: %s\n' % data['venue'])
+        buffer.insert(end, '%s\n' % data['address'])
+        buffer.insert(end, 'When: %s\n' % data['date'].strftime('%H:%M %d/%M/%Y'))
+        buffer.insert(end, '\n')
+        scroll.add_with_viewport(view)
+
+        win.show_all()
+
+    def add_table(self):
+        """ Add table for events """
+        self.table = gtk.Table(columns=1)
+        self.table.set_row_spacings(10)
+        self.table.set_col_spacings(10)
+
+        self.pannable_area = hildon.PannableArea()
+        self.pannable_area.add_with_viewport(self.table)
+        self.pannable_area.show_all()
+        self.win.add(self.pannable_area)
+        
+    def add_events(self, events):
+        """ Add a table of buttons """
+        self.add_table()
+        pos = 0
+        for event in events:
+            button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, 
+                                   hildon.BUTTON_ARRANGEMENT_VERTICAL)
+            button.set_text(event['title'], "distance: %0.02f km" % event['distance'])
+            button.connect("clicked", self.show_details, event)
+            self.table.attach(button, 0, 1, pos, pos+1)
+            pos += 1
+        self.table.show_all()
+   
+if __name__ == "__main__":
+    finder = GigFinder()
+    finder.main()
diff --git a/src/opt/gigfinder/locator.py b/src/opt/gigfinder/locator.py
new file mode 100644 (file)
index 0000000..089a822
--- /dev/null
@@ -0,0 +1,56 @@
+import gobject
+import location
+
+class LocationUpdater:
+
+    def __init__(self):
+        self.lat = None
+        self.long = None
+        self.loop = gobject.MainLoop()
+
+        self.control = location.GPSDControl.get_default()
+        self.control.set_properties(preferred_method=location.METHOD_AGNSS,
+                               preferred_interval=location.INTERVAL_DEFAULT)
+        self.control.connect("error-verbose", self.on_error, self.loop)
+        self.control.connect("gpsd-stopped", self.on_stop, self.loop)
+
+        self.device = location.GPSDevice()
+        self.device.connect("changed", self.on_changed, self.control)
+
+    def update_location(self):
+        """ Run the loop and update lat and long """
+        self.reset()
+        gobject.idle_add(self.start_location, self.control)
+        self.loop.run()
+
+    def on_error(self, control, error, data):
+        """ Handle errors """
+        print "location error: %d... quitting" % error
+        data.quit()
+
+    def on_changed(self, device, data):
+        """ Set long and lat """
+        if not device:
+            return
+        if device.fix:
+            # once fix is found and long, lat available set long lat
+            if device.fix[1] & location.GPS_DEVICE_LATLONG_SET:
+                self.lat, self.long = device.fix[4:6]
+                data.stop()
+
+    def on_stop(self, control, data):
+        """ Stop the location service """
+        print "quitting"
+        data.quit()
+
+    def start_location(self, data):
+        """ Start the location service """
+        data.start()
+        return False
+
+    def reset(self):
+        """ Reset coordinates """
+        self.lat = None
+        self.long = None
+        self.device.reset_last_known()
+
diff --git a/src/usr/share/applications/hildon/gigfinder.desktop b/src/usr/share/applications/hildon/gigfinder.desktop
new file mode 100644 (file)
index 0000000..7d1d679
--- /dev/null
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Version=1.0.0  
+Encoding=UTF-8
+Name=Gig Finder
+Comment=Gig finder
+Exec=/home/jon/applications/gigfinder/gigfinder.py
+Icon=gigfinder
+X-Icon-path=/usr/share/icons
+X-Window-Icon=gigfinder
+Type=Application
+X-Osso-Type=application/x-executable
+
diff --git a/src/usr/share/icons/hicolor/48x48/hildon/gigfinder.png b/src/usr/share/icons/hicolor/48x48/hildon/gigfinder.png
new file mode 100644 (file)
index 0000000..89dfc28
Binary files /dev/null and b/src/usr/share/icons/hicolor/48x48/hildon/gigfinder.png differ