Index: sonata.service =================================================================== --- sonata.service (revision 0) +++ sonata.service (revision 0) @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.MPD.Sonata +Exec=/usr/bin/sonata + Index: sonata/ui.py =================================================================== --- sonata/ui.py (revision 784) +++ sonata/ui.py (working copy) @@ -46,13 +46,18 @@ return tmpevbox def button(text=None, stock=None, relief=None, focus=True, \ - hidetxt=False, img=None, w=-1, h=-1): + hidetxt=False, img=None, w=-1, h=-1, stocksize=None): tmpbut = gtk.Button() if text: tmpbut.set_label(text) elif stock: - tmpbut.set_label(stock) - tmpbut.set_use_stock(True) + if stocksize is not None: + stockimg = gtk.image_new_from_stock(stock, stocksize) + tmpbut.set_image(stockimg) + tmpbut.set_label("") + else: + tmpbut.set_label(stock) + tmpbut.set_use_stock(True) tmpbut.set_use_underline(True) if img: tmpbut.set_image(img) Index: sonata/mpdhelper.py =================================================================== --- sonata/mpdhelper.py (revision 784) +++ sonata/mpdhelper.py (working copy) @@ -12,7 +12,7 @@ try: test = status['state'] except: - return {} + return None return status def currsong(client): Index: sonata/main.py =================================================================== --- sonata/main.py (revision 784) +++ sonata/main.py (working copy) @@ -120,6 +120,16 @@ HAVE_SUGAR = False VOLUME_ICON_SIZE = 4 + try: + import hildon + HAVE_STATUS_ICON = False + HAVE_HILDON = True + VOLUME_ICON_SIZE = 4 + BUTTON_ICON_SIZE = gtk.ICON_SIZE_DIALOG + except: + HAVE_HILDON = False + BUTTON_ICON_SIZE = gtk.ICON_SIZE_BUTTON + # Test pygtk version if gtk.pygtk_version < (2, 6, 0): sys.stderr.write("Sonata requires PyGTK 2.6.0 or newer. Aborting...\n") @@ -369,6 +379,7 @@ self.scrob_last_prepared = "" self.scrob_time_now = None self.sel_rows = None + self.window_fullscreened = False self.img_clicked = False # If the connection to MPD times out, this will cause the interface to freeze while # the socket.connect() calls are repeatedly executed. Therefore, if we were not @@ -441,7 +452,6 @@ ('clearmenu', gtk.STOCK_CLEAR, _('_Clear'), 'Delete', None, self.mpd_clear), ('savemenu', None, _('_New Playlist...'), 's', None, self.on_playlist_save), ('updatemenu', None, _('_Update Library'), None, None, self.on_updatedb), - ('preferencemenu', gtk.STOCK_PREFERENCES, _('_Preferences...'), 'F5', None, self.on_prefs), ('aboutmenu', None, _('_About...'), 'F1', None, self.on_about), ('newmenu', None, _('_New...'), 'n', None, self.on_streams_new), ('editmenu', None, _('_Edit...'), None, None, self.on_streams_edit), @@ -482,6 +492,18 @@ ('centerplaylistkey', None, 'Center Playlist Key', 'i', None, self.current_center_song_in_list), ('searchkey', None, 'Search Key', 'h', None, self.on_library_search_shortcut), ) + + if HAVE_HILDON: + actions = actions + ( + ('fullscreenkey', None, 'Full Screen', 'F6', None, self.toggle_fullscreen), + ('lowerkey2', None, 'Lower Volume Key', 'F8', None, self.on_volume_lower), + ('raisekey3', None, 'Raise Volume Key 2', 'F7', None, self.on_volume_raise), + ('preferencemenu', gtk.STOCK_PREFERENCES, _('_Preferences...'), None, None, self.on_prefs) + ) + else: + actions = actions + ( + ('preferencemenu', gtk.STOCK_PREFERENCES, _('_Preferences...'), 'F5', None, self.on_prefs), + ) toggle_actions = ( ('showmenu', None, _('_Show Sonata'), None, None, self.on_withdraw_app_toggle, not self.withdrawn), @@ -545,10 +567,15 @@ - + """ + + if not HAVE_HILDON: + uiDescription += """ - - + """ + + uiDescription += """ + @@ -587,6 +614,18 @@ for tab in self.all_tab_names: uiDescription = uiDescription + "" uiDescription = uiDescription + "" + + if HAVE_HILDON: + hildon_accels = """ + + + + + + + + + """ # Try to connect to MPD: self.mpd_connect(blocking=True) @@ -607,7 +646,11 @@ # Main app: if window is None: - self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + if HAVE_HILDON: + self.window = hildon.Window() + self.window.set_wmclass("sonata", "sonata") + else: + self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window_owner = True else: self.window = window @@ -623,11 +666,75 @@ self.window.stick() self.tooltips = gtk.Tooltips() self.UIManager = gtk.UIManager() + + if HAVE_HILDON: + self.hildon_UIManager = gtk.UIManager() + hildon_uiDescription = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + actions += ( + ('playlistmenu', None, _('_Playlist')), + ('librarymenu', None, _('_Library')), + ('streamsmenu', None, _('_Streams')), + ('editmenu', None, _('_Edit')), + ) + + h_actionGroup = gtk.ActionGroup('Actions') + h_actionGroup.add_actions(actions) + h_actionGroup.add_toggle_actions(toggle_actions) + self.hildon_UIManager.insert_action_group(h_actionGroup, 0) + self.hildon_UIManager.add_ui_from_string(hildon_uiDescription) + h_menu = self.hildon_UIManager.get_widget("/mainmenu") + #h_menu.unparent() + self.window.set_menu(h_menu) + self.window.add_accel_group(self.hildon_UIManager.get_accel_group()) + + self.h_shufflemenu = self.hildon_UIManager.get_widget('/mainmenu/librarymenu/shufflemenu') + self.h_repeatmenu = self.hildon_UIManager.get_widget('/mainmenu/librarymenu/repeatmenu') + actionGroup = gtk.ActionGroup('Actions') actionGroup.add_actions(actions) actionGroup.add_toggle_actions(toggle_actions) self.UIManager.insert_action_group(actionGroup, 0) self.UIManager.add_ui_from_string(uiDescription) + if HAVE_HILDON: + self.UIManager.add_ui_from_string(hildon_accels) self.populate_profiles_for_menu() self.window.add_accel_group(self.UIManager.get_accel_group()) self.mainmenu = self.UIManager.get_widget('/mainmenu') @@ -648,10 +755,10 @@ tophbox.pack_start(self.imageeventbox, False, False, 5) topvbox = gtk.VBox() toptophbox = gtk.HBox() - self.prevbutton = ui.button(stock=gtk.STOCK_MEDIA_PREVIOUS, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True) - self.ppbutton = ui.button(stock=gtk.STOCK_MEDIA_PLAY, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True) - self.stopbutton = ui.button(stock=gtk.STOCK_MEDIA_STOP, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True) - self.nextbutton = ui.button(stock=gtk.STOCK_MEDIA_NEXT, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True) + self.prevbutton = ui.button(stock=gtk.STOCK_MEDIA_PREVIOUS, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True, stocksize=BUTTON_ICON_SIZE) + self.ppbutton = ui.button(stock=gtk.STOCK_MEDIA_PLAY, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True, stocksize=BUTTON_ICON_SIZE) + self.stopbutton = ui.button(stock=gtk.STOCK_MEDIA_STOP, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True, stocksize=BUTTON_ICON_SIZE) + self.nextbutton = ui.button(stock=gtk.STOCK_MEDIA_NEXT, relief=gtk.RELIEF_NONE, focus=False, hidetxt=True, stocksize=BUTTON_ICON_SIZE) for mediabutton in (self.prevbutton, self.ppbutton, self.stopbutton, self.nextbutton): toptophbox.pack_start(mediabutton, False, False, 0) if not self.show_playback: @@ -699,6 +806,13 @@ self.filterbox.set_no_show_all(True) vbox_current = gtk.VBox() vbox_current.pack_start(self.expanderwindow, True, True) + if HAVE_HILDON: + button_box = gtk.HButtonBox() + button_box.set_property('layout-style', gtk.BUTTONBOX_START) + button = gtk.Button(stock='gtk-remove') + button.connect("clicked", self.on_remove) + button_box.add(button) + vbox_current.pack_start(button_box, False, False, 2) vbox_current.pack_start(self.filterbox, False, False, 5) playlisthbox = gtk.HBox() playlisthbox.pack_start(ui.image(stock=gtk.STOCK_CDROM), False, False, 2) @@ -706,6 +820,7 @@ playlistevbox = ui.eventbox(add=playlisthbox) playlistevbox.show_all() playlistevbox.connect("button_press_event", self.on_tab_click) + self.notebook.append_page(vbox_current, playlistevbox) current_tab = self.notebook.get_children()[0] if not self.current_tab_visible: @@ -731,6 +846,21 @@ self.searchbox.pack_start(self.searchtext, True, True, 2) self.searchbox.pack_start(self.searchbutton, False, False, 2) libraryvbox.pack_start(expanderwindow2, True, True, 2) + if HAVE_HILDON: + button_box = gtk.HButtonBox() + button_box.set_property('layout-style', gtk.BUTTONBOX_START) + button = gtk.Button(stock='gtk-add') + button.connect("clicked", self.on_add_item) + button_box.add(button) + button = gtk.Button() + image = gtk.Image() + image.set_from_stock('gtk-redo', gtk.ICON_SIZE_BUTTON) + button.set_image(image) + button.set_use_underline(True) + button.set_label(_('_Replace')) + button.connect("clicked", self.on_replace_item) + button_box.add(button) + libraryvbox.pack_start(button_box, False, False, 2) libraryvbox.pack_start(self.searchbox, False, False, 2) libraryhbox = gtk.HBox() libraryhbox.pack_start(ui.image(stock=gtk.STOCK_HARDDISK), False, False, 2) @@ -746,13 +876,30 @@ self.playlists = ui.treeview() self.playlists_selection = self.playlists.get_selection() expanderwindow3 = ui.scrollwindow(add=self.playlists) + vbox = gtk.VBox() + vbox.pack_start(expanderwindow3, True, True) + if HAVE_HILDON: + button_box = gtk.HButtonBox() + button_box.set_property('layout-style', gtk.BUTTONBOX_START) + button = gtk.Button(stock='gtk-add') + button.connect("clicked", self.on_add_item) + button_box.add(button) + button = gtk.Button() + image = gtk.Image() + image.set_from_stock('gtk-redo', gtk.ICON_SIZE_BUTTON) + button.set_image(image) + button.set_use_underline(True) + button.set_label(_('_Replace')) + button.connect("clicked", self.on_replace_item) + button_box.add(button) + vbox.pack_start(button_box, False, False, 2) playlistshbox = gtk.HBox() playlistshbox.pack_start(ui.image(stock=gtk.STOCK_JUSTIFY_CENTER), False, False, 2) playlistshbox.pack_start(ui.label(text=self.TAB_PLAYLISTS), False, False, 2) playlistsevbox = ui.eventbox(add=playlistshbox) playlistsevbox.show_all() playlistsevbox.connect("button_press_event", self.on_tab_click) - self.notebook.append_page(expanderwindow3, playlistsevbox) + self.notebook.append_page(vbox, playlistsevbox) playlists_tab = self.notebook.get_children()[2] if not self.playlists_tab_visible: ui.hide(playlists_tab) @@ -760,13 +907,22 @@ self.streams = ui.treeview() self.streams_selection = self.streams.get_selection() expanderwindow4 = ui.scrollwindow(add=self.streams) + vbox = gtk.VBox() + vbox.pack_start(expanderwindow4, True, True) + if HAVE_HILDON: + button_box = gtk.HButtonBox() + button_box.set_property('layout-style', gtk.BUTTONBOX_START) + button = gtk.Button(stock='gtk-new') + button.connect("clicked", self.on_streams_new) + button_box.add(button) + vbox.pack_start(button_box, False, False, 2) streamshbox = gtk.HBox() streamshbox.pack_start(ui.image(stock=gtk.STOCK_NETWORK), False, False, 2) streamshbox.pack_start(ui.label(text=self.TAB_STREAMS), False, False, 2) streamsevbox = ui.eventbox(add=streamshbox) streamsevbox.show_all() streamsevbox.connect("button_press_event", self.on_tab_click) - self.notebook.append_page(expanderwindow4, streamsevbox) + self.notebook.append_page(vbox, streamsevbox) streams_tab = self.notebook.get_children()[3] if not self.streams_tab_visible: ui.hide(streams_tab) @@ -948,6 +1104,18 @@ treeviewsel.connect('changed', self.on_treeview_selection_changed) for widget in [self.ppbutton, self.prevbutton, self.stopbutton, self.nextbutton, self.progresseventbox, self.expander, self.volumebutton]: widget.connect('button_press_event', self.menu_popup) + + if HAVE_HILDON: + self.h_shufflemenu.connect('toggled', self.on_shuffle_clicked) + self.h_repeatmenu.connect('toggled', self.on_repeat_clicked) + self.current.tap_and_hold_setup(None) + self.current.connect('tap-and-hold', self.on_tap_and_hold) + self.library.tap_and_hold_setup(None) + self.library.connect('tap-and-hold', self.on_tap_and_hold) + self.playlists.tap_and_hold_setup(None) + self.playlists.connect('tap-and-hold', self.on_tap_and_hold) + self.streams.tap_and_hold_setup(None) + self.streams.connect('tap-and-hold', self.on_tap_and_hold) self.systemtray_initialize() @@ -1407,13 +1575,19 @@ def populate_profiles_for_menu(self): host, port, password = self.mpd_env_vars() if host or port: return + + if HAVE_HILDON: + UIManager = self.hildon_UIManager + else: + UIManager = self.UIManager + if self.merge_id: - self.UIManager.remove_ui(self.merge_id) + UIManager.remove_ui(self.merge_id) if self.actionGroupProfiles: - self.UIManager.remove_action_group(self.actionGroupProfiles) + UIManager.remove_action_group(self.actionGroupProfiles) self.actionGroupProfiles = None self.actionGroupProfiles = gtk.ActionGroup('MPDProfiles') - self.UIManager.ensure_update() + UIManager.ensure_update() actions = [] for i in range(len(self.profile_names)): action_name = "Profile: " + self.profile_names[i].replace("&", "") @@ -1430,8 +1604,8 @@ action_name = "Profile: " + self.profile_names[len(self.profile_names)-i-1].replace("&", "") uiDescription = uiDescription + """""" uiDescription = uiDescription + """""" - self.merge_id = self.UIManager.add_ui_from_string(uiDescription) - self.UIManager.insert_action_group(self.actionGroupProfiles, 0) + self.merge_id = UIManager.add_ui_from_string(uiDescription) + UIManager.insert_action_group(self.actionGroupProfiles, 0) self.UIManager.get_widget('/hidden').set_property('visible', False) def on_profiles_click(self, radioaction, current): @@ -1531,8 +1705,12 @@ if self.status: if not self.last_repeat or self.last_repeat != self.status['repeat']: self.repeatmenu.set_active(self.status['repeat'] == '1') + if HAVE_HILDON: + self.h_repeatmenu.set_active(self.status['repeat'] == '1') if not self.last_random or self.last_random != self.status['random']: self.shufflemenu.set_active(self.status['random'] == '1') + if HAVE_HILDON: + self.h_shufflemenu.set_active(self.status['random'] == '1') if self.status['xfade'] == '0': self.xfade_enabled = False else: @@ -1613,6 +1791,16 @@ def on_topwindow_keypress(self, widget, event): shortcut = gtk.accelerator_name(event.keyval, event.state) shortcut = shortcut.replace("", "") + + if HAVE_HILDON: + keyname = gtk.gdk.keyval_name(event.keyval) + if keyname == 'F6': + self.toggle_fullscreen(None) + elif keyname == 'F7': + self.on_volume_raise(None) + elif keyname == 'F8': + self.on_volume_lower(None) + # These shortcuts were moved here so that they don't interfere with searching the library if shortcut == 'BackSpace': self.library_browse_parent(None) @@ -1648,6 +1836,12 @@ self.searchfilter_toggle(None, event.string) else: self.searchfilter_toggle(None) + + def toggle_fullscreen(self, event): + if self.window_fullscreened: + self.window.unfullscreen() + else: + self.window.fullscreen() def settings_load(self): # Load config @@ -2005,6 +2199,8 @@ edit_mode = False # Prompt user for playlist name: dialog = ui.dialog(title=None, parent=self.window, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT), role="streamsNew") + if HAVE_HILDON: + dialog.set_default_size(650,200) if edit_mode: dialog.set_title(_("Edit Stream")) else: @@ -3122,6 +3318,20 @@ # the current selection change to the current row) if widget.get_selection().count_selected_rows() > 1: return True + + def on_tap_and_hold(self, widget): + self.update_menu_visibility() + # Calling the popup in idle_add is important. It allows the menu items + # to have been shown/hidden before the menu is popped up. Otherwise, if + # the menu pops up too quickly, it can result in automatically clicking + # menu items for the user! + gobject.idle_add(self.mainmenu.popup, None, None, None, 3, 0) + # Don't change the selection for a right-click. This + # will allow the user to select multiple rows and then + # right-click (instead of right-clicking and having + # the current selection change to the current row) + if widget.get_selection().count_selected_rows() > 1: + return True def on_current_drag_begin(self, widget, context): self.sel_rows = False @@ -3378,7 +3588,7 @@ self.update_wintitle() self.info_update(True) if self.status['state'] == 'stop': - self.ppbutton.set_image(ui.image(stock=gtk.STOCK_MEDIA_PLAY, stocksize=gtk.ICON_SIZE_BUTTON)) + self.ppbutton.set_image(ui.image(stock=gtk.STOCK_MEDIA_PLAY, stocksize=BUTTON_ICON_SIZE)) self.ppbutton.get_child().get_child().get_children()[1].set_text('') self.UIManager.get_widget('/traymenu/playmenu').show() self.UIManager.get_widget('/traymenu/pausemenu').hide() @@ -3388,7 +3598,7 @@ self.eggtrayfile = self.find_path('sonata.png') self.trayimage.set_from_pixbuf(img.get_pixbuf_of_size(gtk.gdk.pixbuf_new_from_file(self.eggtrayfile), self.eggtrayheight)[0]) elif self.status['state'] == 'pause': - self.ppbutton.set_image(ui.image(stock=gtk.STOCK_MEDIA_PLAY, stocksize=gtk.ICON_SIZE_BUTTON)) + self.ppbutton.set_image(ui.image(stock=gtk.STOCK_MEDIA_PLAY, stocksize=BUTTON_ICON_SIZE)) self.ppbutton.get_child().get_child().get_children()[1].set_text('') self.UIManager.get_widget('/traymenu/playmenu').show() self.UIManager.get_widget('/traymenu/pausemenu').hide() @@ -3398,7 +3608,7 @@ self.eggtrayfile = self.find_path('sonata_pause.png') self.trayimage.set_from_pixbuf(img.get_pixbuf_of_size(gtk.gdk.pixbuf_new_from_file(self.eggtrayfile), self.eggtrayheight)[0]) elif self.status['state'] == 'play': - self.ppbutton.set_image(ui.image(stock=gtk.STOCK_MEDIA_PAUSE, stocksize=gtk.ICON_SIZE_BUTTON)) + self.ppbutton.set_image(ui.image(stock=gtk.STOCK_MEDIA_PAUSE, stocksize=BUTTON_ICON_SIZE)) self.ppbutton.get_child().get_child().get_children()[1].set_text('') self.UIManager.get_widget('/traymenu/playmenu').hide() self.UIManager.get_widget('/traymenu/pausemenu').show() @@ -4322,6 +4532,10 @@ def on_window_state_change(self, widget, event): self.volume_hide() + if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN: + self.window_fullscreened = True + else: + self.window_fullscreened = False def on_window_lost_focus(self, widget, event): self.volume_hide() @@ -5903,7 +6117,16 @@ [_("_Extras"), as_frame]] for table_name in table_names: tmplabel = ui.label(textmn=table_name[0]) - prefsnotebook.append_page(table_name[1], tmplabel) + if HAVE_HILDON: + scrolled = gtk.ScrolledWindow() + scrolled.set_size_request(650, 200) + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + viewport = gtk.Viewport() + viewport.add(table_name[1]) + scrolled.add(viewport) + prefsnotebook.append_page(scrolled, tmplabel) + else: + prefsnotebook.append_page(table_name[1], tmplabel) hbox.pack_start(prefsnotebook, False, False, 10) prefswindow.vbox.pack_start(hbox, False, False, 10) close_button = prefswindow.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) Index: setup.py =================================================================== --- setup.py (revision 784) +++ setup.py (working copy) @@ -7,6 +7,12 @@ from distutils.core import setup, Extension +try: + import hildon + HAVE_HILDON = True +except: + HAVE_HILDON = False + def capture(cmd): return os.popen(cmd).read().strip() @@ -43,6 +49,18 @@ os.mkdir("mo/" + lang + "/") print "generating", mofile os.system("msgfmt %s -o %s" % (pofile, mofile)) + +if HAVE_HILDON: + desktop_file = ('share/applications/hildon', ['sonata-hildon.desktop']) + service_file = ('share/dbus-1/services', ['sonata.service']) + ext_modules = None +else: + desktop_file = ('share/applications', ['sonata.desktop']) + service_file = ('share/dbus-1/services', []) + ext_modules = [Extension( + "mmkeys", ["mmkeys/mmkeyspy.c", "mmkeys/mmkeys.c", "mmkeys/mmkeysmodule.c"], + extra_compile_args=capture("pkg-config --cflags gtk+-2.0 pygtk-2.0").split(), + extra_link_args=capture("pkg-config --libs gtk+-2.0 pygtk-2.0").split())] # Copy script "sonata" file to sonata dir: shutil.copyfile("sonata.py", "sonata/sonata") @@ -64,14 +82,11 @@ ], packages=["sonata"], package_dir={"sonata": "sonata/"}, - ext_modules=[Extension( - "mmkeys", ["mmkeys/mmkeyspy.c", "mmkeys/mmkeys.c", "mmkeys/mmkeysmodule.c"], - extra_compile_args=capture("pkg-config --cflags gtk+-2.0 pygtk-2.0").split(), - extra_link_args=capture("pkg-config --libs gtk+-2.0 pygtk-2.0").split() - ),], + ext_modules=ext_modules, scripts = ['sonata/sonata'], data_files=[('share/sonata', ['README', 'CHANGELOG', 'TODO', 'TRANSLATORS']), - ('share/applications', ['sonata.desktop']), + desktop_file, + service_file, ('share/pixmaps', glob.glob('sonata/pixmaps/*')), ('share/man/man1', ['sonata.1']), ('share/locale/de/LC_MESSAGES', ['mo/de/sonata.mo']), Index: sonata-hildon.desktop =================================================================== --- sonata-hildon.desktop (revision 0) +++ sonata-hildon.desktop (revision 0) @@ -0,0 +1,14 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Sonata +Comment=An elegant GTK+ MPD client +TryExec=sonata.sh +Exec=/usr/bin/sonata +Terminal=false +Type=Application +Categories=GTK;AudioVideo;Player; +Icon=sonata_large +X-Osso-Service=org.MPD.Sonata +X-Osso-Type=application/x-executable +StartupWMClass=sonata +