Fri, 02 May 2008 17:28:22 +0200 <thp@perli.net>

Change "Channel" to "Podcast"; new main menu; URL entry updated; +niceties

	* data/gpodder.glade: Change "Channel" to "Podcast"; re-structure main
	menu in gPodder window
	* src/gpodder/config.py: Add "show_podcast_url_entry" configuration
	option that controls whether the podcast url entry box is shown in the
	main window or not; add observer functionality to the configuration
	manager, so UI elements can "watch" the configuration manager for
	changes of UI-related configuration options
	* src/gpodder/console.py: Change "Channel" to "Podcast"
	* src/gpodder/gui.py: Hildon-specific file open/save dialogs; default
	"Enter podcast URL..." test for the URL entry box; size-dependent
	showing and hiding of podcast icon and downloaded count pixmap also
	for the Desktop version; change "Channel" to "Podcast"; offer
	first-time users to see a list of example podcasts to subscribe to;
	dynamic main menu; code supporting the main menu changes; add code for
	sending the subscription list via e-mail; import from OPML file in
	addition to import from OPML URL; remove unneeded callbacks (wishlist,
	select all, ...); optionally set title and hide url entry in the
	gPodderOpmlLister (OPML import GUI); Add Frank Harper to list of
	contributors (initial reporter of bug #82)
	* src/gpodder/libgpodder.py: Add "send_subscriptions" function that
	sends the user's channels.opml file via E-Mail (using xdg-email);
	rename "Channel" to "Podcast"
	(Closes: http://bugs.gpodder.org/show_bug.cgi?id=82)
	(Closes: http://bugs.gpodder.org/show_bug.cgi?id=103)



git-svn-id: svn://svn.berlios.de/gpodder/trunk@700 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2008-05-02 15:36:43 +00:00
parent 42a3b5be44
commit 40cef59a49
6 changed files with 570 additions and 506 deletions

View file

@ -1,3 +1,31 @@
Fri, 02 May 2008 17:28:22 +0200 <thp@perli.net>
Change "Channel" to "Podcast"; new main menu; URL entry updated; +niceties
* data/gpodder.glade: Change "Channel" to "Podcast"; re-structure main
menu in gPodder window
* src/gpodder/config.py: Add "show_podcast_url_entry" configuration
option that controls whether the podcast url entry box is shown in the
main window or not; add observer functionality to the configuration
manager, so UI elements can "watch" the configuration manager for
changes of UI-related configuration options
* src/gpodder/console.py: Change "Channel" to "Podcast"
* src/gpodder/gui.py: Hildon-specific file open/save dialogs; default
"Enter podcast URL..." test for the URL entry box; size-dependent
showing and hiding of podcast icon and downloaded count pixmap also
for the Desktop version; change "Channel" to "Podcast"; offer
first-time users to see a list of example podcasts to subscribe to;
dynamic main menu; code supporting the main menu changes; add code for
sending the subscription list via e-mail; import from OPML file in
addition to import from OPML URL; remove unneeded callbacks (wishlist,
select all, ...); optionally set title and hide url entry in the
gPodderOpmlLister (OPML import GUI); Add Frank Harper to list of
contributors (initial reporter of bug #82)
* src/gpodder/libgpodder.py: Add "send_subscriptions" function that
sends the user's channels.opml file via E-Mail (using xdg-email);
rename "Channel" to "Podcast"
(Closes: http://bugs.gpodder.org/show_bug.cgi?id=82)
(Closes: http://bugs.gpodder.org/show_bug.cgi?id=103)
Fri, 02 May 2008 15:52:31 +0200 <thp@perli.net> Fri, 02 May 2008 15:52:31 +0200 <thp@perli.net>
Updated Portuguese translation by João Trindade Updated Portuguese translation by João Trindade

File diff suppressed because it is too large Load diff

View file

@ -106,6 +106,7 @@ gPodderSettings = {
'create_m3u_playlists': (bool, False), 'create_m3u_playlists': (bool, False),
'max_episodes_per_feed': (int, 200), 'max_episodes_per_feed': (int, 200),
'mp3_player_use_scrobbler_log': (bool, False), 'mp3_player_use_scrobbler_log': (bool, False),
'show_podcast_url_entry': (bool, True),
# Window and paned positions # Window and paned positions
'main_window_x': ( int, 100 ), 'main_window_x': ( int, 100 ),
@ -127,6 +128,7 @@ class Config(dict):
self.__filename = filename self.__filename = filename
self.__section = 'gpodder-conf-1' self.__section = 'gpodder-conf-1'
self.__ignore_window_events = False self.__ignore_window_events = False
self.__observers = []
# Name, Type, Value, Type(python type), Editable?, Font weight # Name, Type, Value, Type(python type), Editable?, Font weight
self.__model = gtk.ListStore(str, str, str, object, bool, int) self.__model = gtk.ListStore(str, str, str, object, bool, int)
@ -141,6 +143,22 @@ class Config(dict):
else: else:
raise AttributeError raise AttributeError
def add_observer(self, callback):
"""
Add a callback function as observer. This callback
will be called when a setting changes. It should
have this signature:
observer(name, old_value, new_value)
The "name" is the setting name, the "old_value" is
the value that has been overwritten with "new_value".
"""
if callback not in self.__observers:
self.__observers.append(callback)
else:
log('Observer already added: %s', repr(callback), sender=self)
def connect_gtk_editable( self, name, editable): def connect_gtk_editable( self, name, editable):
if name in self.Settings: if name in self.Settings:
editable.delete_text( 0, -1) editable.delete_text( 0, -1)
@ -326,7 +344,14 @@ class Config(dict):
try: try:
if self[name] != fieldtype(value): if self[name] != fieldtype(value):
log( 'Update: %s = %s', name, value, sender = self) log( 'Update: %s = %s', name, value, sender = self)
old_value = self[name]
self[name] = fieldtype(value) self[name] = fieldtype(value)
for observer in self.__observers:
try:
# Notify observer about config change
observer(name, old_value, self[name])
except:
log('Error while calling observer: %s', repr(observer), sender=self)
for row in self.__model: for row in self.__model:
if row[0] == name: if row[0] == name:
row[2] = str(fieldtype(value)) row[2] = str(fieldtype(value))

View file

@ -33,8 +33,8 @@ import urllib
def list_channels(): def list_channels():
for channel in load_channels( load_items = False): for channel in load_channels(load_items=False):
msg( 'channel', urllib.unquote( channel.url)) msg('podcast', urllib.unquote(channel.url))
def add_channel( url): def add_channel( url):
@ -56,7 +56,7 @@ def add_channel( url):
save_channels( channels) save_channels( channels)
msg( 'add', urllib.unquote( url)) msg( 'add', urllib.unquote( url))
else: else:
msg( 'error', _('Could not add channel.')) msg('error', _('Could not add podcast.'))
def del_channel( url): def del_channel( url):
@ -73,7 +73,7 @@ def del_channel( url):
if len(keep_channels) < len(channels): if len(keep_channels) < len(channels):
save_channels( keep_channels) save_channels( keep_channels)
else: else:
msg( 'error', _('Could not remove channel.')) msg('error', _('Could not remove podcast.'))
def update(): def update():
@ -111,7 +111,7 @@ def sync_device():
for channel in load_channels(): for channel in load_channels():
if not channel.sync_to_devices: if not channel.sync_to_devices:
msg('info', _('Skipping channel: %s') % channel.title) msg('info', _('Skipping podcast: %s') % channel.title)
continue continue
episodes_to_sync = [] episodes_to_sync = []

View file

@ -78,8 +78,8 @@ app_authors = [
'Antonio Roversi', 'Aravind Seshadri', 'Atte André Jensen', 'Antonio Roversi', 'Aravind Seshadri', 'Atte André Jensen',
'Bernd Schlapsi', 'Bill Barnard', 'Bjørn Rasmussen', 'Camille Moncelier', 'Bernd Schlapsi', 'Bill Barnard', 'Bjørn Rasmussen', 'Camille Moncelier',
'Carlos Moffat', 'Chris', 'Chris Arnold', 'Clark Burbidge', 'Carlos Moffat', 'Chris', 'Chris Arnold', 'Clark Burbidge',
'Doug Hellmann', 'FFranci72', 'Doug Hellmann', 'FFranci72', 'Florian Richter', 'Frank Harper',
'Florian Richter', 'FriedBunny', 'Gerrit Sangel', 'Götz Waschk', 'FriedBunny', 'Gerrit Sangel', 'Götz Waschk',
'Haim Roitgrund', 'Hex', 'Holger Bauer', 'Holger Leskien', 'Jens Thiele', 'Haim Roitgrund', 'Hex', 'Holger Bauer', 'Holger Leskien', 'Jens Thiele',
'Jérôme Chabod', 'Jessica Henline', 'João Trindade', 'Joel Calado', 'John Ferguson', 'Jérôme Chabod', 'Jessica Henline', 'João Trindade', 'Joel Calado', 'John Ferguson',
'José Luis Fustel', 'Joseph Bleau', 'Julio Acuña', 'Junio C Hamano', 'José Luis Fustel', 'Joseph Bleau', 'Julio Acuña', 'Junio C Hamano',
@ -221,15 +221,17 @@ class GladeWidget(SimpleGladeApp.SimpleGladeApp):
if not dst_filename.endswith( extension): if not dst_filename.endswith( extension):
dst_filename += extension dst_filename += extension
dlg = gtk.FileChooserDialog( title = title, parent = GladeWidget.gpodder_main_window, action = gtk.FILE_CHOOSER_ACTION_SAVE) if gpodder.interface == gpodder.GUI:
dlg.set_do_overwrite_confirmation( True) dlg = gtk.FileChooserDialog(title=title, parent=GladeWidget.gpodder_main_window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
dlg.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_OK)
elif gpodder.interface == gpodder.MAEMO:
dlg = hildon.FileChooserDialog(GladeWidget.gpodder_main_window, gtk.FILE_CHOOSER_ACTION_SAVE)
dlg.set_do_overwrite_confirmation( True)
dlg.set_current_name( os.path.basename( dst_filename)) dlg.set_current_name( os.path.basename( dst_filename))
dlg.set_current_folder( dst_directory) dlg.set_current_folder( dst_directory)
dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
dlg.add_button( gtk.STOCK_SAVE, gtk.RESPONSE_OK)
if dlg.run() == gtk.RESPONSE_OK: if dlg.run() == gtk.RESPONSE_OK:
dst_filename = dlg.get_filename() dst_filename = dlg.get_filename()
if not dst_filename.endswith( extension): if not dst_filename.endswith( extension):
@ -248,6 +250,7 @@ class GladeWidget(SimpleGladeApp.SimpleGladeApp):
class gPodder(GladeWidget): class gPodder(GladeWidget):
finger_friendly_widgets = ['btnUpdateFeeds', 'btnCancelFeedUpdate', 'treeAvailable', 'label2', 'labelDownloads'] finger_friendly_widgets = ['btnUpdateFeeds', 'btnCancelFeedUpdate', 'treeAvailable', 'label2', 'labelDownloads']
ENTER_URL_TEXT = _('Enter podcast URL...')
def new(self): def new(self):
if gpodder.interface == gpodder.MAEMO: if gpodder.interface == gpodder.MAEMO:
@ -288,15 +291,27 @@ class gPodder(GladeWidget):
# do some widget hiding # do some widget hiding
self.toolbar.remove(self.toolTransfer) self.toolbar.remove(self.toolTransfer)
self.itemTransferSelected.hide_all() self.itemTransferSelected.hide_all()
self.separator11.hide_all() self.item_show_url_entry.hide_all()
self.hboxAddChannel.hide_all()
# Feed cache update button # Feed cache update button
self.label120.set_text(_('Update')) self.label120.set_text(_('Update'))
self.treeChannels.connect('size-allocate', self.on_tree_channels_resize)
# get screen real estate # get screen real estate
self.hboxContainer.set_border_width(0) self.hboxContainer.set_border_width(0)
self.treeChannels.connect('size-allocate', self.on_tree_channels_resize)
if gpodder.interface == gpodder.MAEMO or not gl.config.show_podcast_url_entry:
self.hboxAddChannel.hide_all()
if not gl.config.show_toolbar:
self.toolbar.hide_all()
gl.config.add_observer(self.on_config_changed)
self.default_entry_text_color = self.entryAddChannel.get_style().text[gtk.STATE_NORMAL]
self.entryAddChannel.connect('focus-in-event', self.entry_add_channel_focus)
self.entryAddChannel.connect('focus-out-event', self.entry_add_channel_unfocus)
self.entry_add_channel_unfocus(self.entryAddChannel, None)
self.uar = None self.uar = None
self.tray_icon = None self.tray_icon = None
@ -307,11 +322,11 @@ class gPodder(GladeWidget):
self.already_notified_new_episodes = [] self.already_notified_new_episodes = []
self.show_hide_tray_icon() self.show_hide_tray_icon()
self.no_episode_selected.set_sensitive(False)
self.episode_description_shown = gl.config.episode_list_descriptions
self.itemShowToolbar.set_active(gl.config.show_toolbar) self.itemShowToolbar.set_active(gl.config.show_toolbar)
self.itemShowDescription.set_active(gl.config.episode_list_descriptions) self.itemShowDescription.set_active(gl.config.episode_list_descriptions)
self.update_view_settings() self.item_show_url_entry.set_active(gl.config.show_podcast_url_entry)
if self.tray_icon: if self.tray_icon:
if gl.config.start_iconified: if gl.config.start_iconified:
@ -344,7 +359,7 @@ class gPodder(GladeWidget):
gtk.about_dialog_set_url_hook(lambda dlg, link, data: util.open_website(link), None) gtk.about_dialog_set_url_hook(lambda dlg, link, data: util.open_website(link), None)
# cell renderers for channel tree # cell renderers for channel tree
namecolumn = gtk.TreeViewColumn( _('Channel')) namecolumn = gtk.TreeViewColumn( _('Podcast'))
iconcell = gtk.CellRendererPixbuf() iconcell = gtk.CellRendererPixbuf()
namecolumn.pack_start( iconcell, False) namecolumn.pack_start( iconcell, False)
@ -479,13 +494,43 @@ class gPodder(GladeWidget):
self.delete_episode_list(old_episodes, confirm=False) self.delete_episode_list(old_episodes, confirm=False)
self.updateComboBox() self.updateComboBox()
# First-time users should be asked if they want to see the OPML
if len(self.channels) == 0:
util.idle_add(self.on_itemUpdate_activate, None)
def on_tree_channels_resize(self, widget, allocation): def on_tree_channels_resize(self, widget, allocation):
window_allocation = self.gPodder.get_allocation() window_allocation = self.gPodder.get_allocation()
percentage = 100. * float(allocation.width) / float(window_allocation.width) percentage = 100. * float(allocation.width) / float(window_allocation.width)
if hasattr(self, 'cell_channel_icon'): if hasattr(self, 'cell_channel_icon'):
self.cell_channel_icon.set_property('visible', bool(percentage > 24.)) self.cell_channel_icon.set_property('visible', bool(percentage > 22.))
if hasattr(self, 'cell_channel_pill'): if hasattr(self, 'cell_channel_pill'):
self.cell_channel_pill.set_property('visible', bool(percentage > 28.)) self.cell_channel_pill.set_property('visible', bool(percentage > 25.))
def entry_add_channel_focus(self, widget, event):
widget.modify_text(gtk.STATE_NORMAL, self.default_entry_text_color)
if widget.get_text() == self.ENTER_URL_TEXT:
widget.set_text('')
def entry_add_channel_unfocus(self, widget, event):
if widget.get_text() == '':
widget.set_text(self.ENTER_URL_TEXT)
widget.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse('#aaaaaa'))
def on_config_changed(self, name, old_value, new_value):
if name == 'show_toolbar':
if new_value:
self.toolbar.show_all()
else:
self.toolbar.hide_all()
elif name == 'episode_list_descriptions':
for channel in self.channels:
channel.force_update_tree_model()
self.updateTreeView()
elif name == 'show_podcast_url_entry' and gpodder.interface != gpodder.MAEMO:
if new_value:
self.hboxAddChannel.show_all()
else:
self.hboxAddChannel.hide_all()
def read_apps(self): def read_apps(self):
time.sleep(3) # give other parts of gpodder a chance to start up time.sleep(3) # give other parts of gpodder a chance to start up
@ -590,7 +635,10 @@ class gPodder(GladeWidget):
def on_itemClose_activate(self, widget): def on_itemClose_activate(self, widget):
if self.tray_icon is not None: if self.tray_icon is not None:
self.iconify_main_window() if gpodder.interface == gpodder.MAEMO:
self.gPodder.set_property('visible', False)
else:
self.iconify_main_window()
else: else:
self.on_gPodder_delete_event(widget) self.on_gPodder_delete_event(widget)
@ -832,12 +880,20 @@ class gPodder(GladeWidget):
return False return False
return True return True
def change_menu_item(self, menuitem, icon=None, label=None):
(label_widget, icon_widget) = menuitem.get_children()
if icon is not None:
icon_widget.set_from_icon_name(icon, gtk.ICON_SIZE_MENU)
if label is not None:
label_widget.set_text(label)
def play_or_download( self): def play_or_download( self):
if self.wNotebook.get_current_page() > 0: if self.wNotebook.get_current_page() > 0:
return return
( can_play, can_download, can_transfer, can_cancel ) = (False,)*4 ( can_play, can_download, can_transfer, can_cancel ) = (False,)*4
(is_played, is_locked) = (False,)*2
selection = self.treeAvailable.get_selection() selection = self.treeAvailable.get_selection()
if selection.count_selected_rows() > 0: if selection.count_selected_rows() > 0:
@ -849,6 +905,8 @@ class gPodder(GladeWidget):
if os.path.exists( local_filename): if os.path.exists( local_filename):
can_play = True can_play = True
is_played = gl.history_is_played(url)
is_locked = gl.history_is_locked(url)
else: else:
if services.download_status_manager.is_download_in_progress( url): if services.download_status_manager.is_download_in_progress( url):
can_cancel = True can_cancel = True
@ -867,6 +925,43 @@ class gPodder(GladeWidget):
self.toolTransfer.set_sensitive( can_transfer) self.toolTransfer.set_sensitive( can_transfer)
self.toolCancel.set_sensitive( can_cancel) self.toolCancel.set_sensitive( can_cancel)
if can_cancel:
self.item_cancel_download.show_all()
else:
self.item_cancel_download.hide_all()
if can_download:
self.itemDownloadSelected.show_all()
else:
self.itemDownloadSelected.hide_all()
if can_play:
self.itemPlaySelected.show_all()
self.itemDeleteSelected.show_all()
self.item_toggle_played.show_all()
self.item_toggle_lock.show_all()
self.separator9.show_all()
if is_played:
self.change_menu_item(self.item_toggle_played, gtk.STOCK_CANCEL, _('Mark as unplayed'))
else:
self.change_menu_item(self.item_toggle_played, gtk.STOCK_APPLY, _('Mark as played'))
if is_locked:
self.change_menu_item(self.item_toggle_lock, gtk.STOCK_DIALOG_AUTHENTICATION, _('Allow deletion'))
else:
self.change_menu_item(self.item_toggle_lock, gtk.STOCK_DIALOG_AUTHENTICATION, _('Prohibit deletion'))
else:
self.itemPlaySelected.hide_all()
self.itemDeleteSelected.hide_all()
self.item_toggle_played.hide_all()
self.item_toggle_lock.hide_all()
self.separator9.hide_all()
if can_play or can_download or can_cancel:
self.item_episode_details.show_all()
self.separator16.show_all()
self.no_episode_selected.hide_all()
else:
self.item_episode_details.hide_all()
self.separator16.hide_all()
self.no_episode_selected.show_all()
return ( can_play, can_download, can_transfer, can_cancel ) return ( can_play, can_download, can_transfer, can_cancel )
def download_status_updated( self): def download_status_updated( self):
@ -927,7 +1022,7 @@ class gPodder(GladeWidget):
if result: if result:
for old_channel in self.channels: for old_channel in self.channels:
if old_channel.url == result: if old_channel.url == result:
self.show_message( _('You have already subscribed to this channel: %s') % ( saxutils.escape( old_channel.title), ), _('Already added')) self.show_message( _('You have already subscribed to this podcast: %s') % ( saxutils.escape( old_channel.title), ), _('Already added'))
log( 'Channel already exists: %s', result) log( 'Channel already exists: %s', result)
# Select the existing channel in combo box # Select the existing channel in combo box
for i in range( len( self.channels)): for i in range( len( self.channels)):
@ -961,8 +1056,8 @@ class gPodder(GladeWidget):
if ask_download_new: if ask_download_new:
self.on_btnDownloadNewer_clicked( None) self.on_btnDownloadNewer_clicked( None)
else: else:
title = _('Error adding channel') title = _('Error adding podcast')
message = _('The channel could not be added. Please check the spelling of the URL or try again later.') message = _('The podcast could not be added. Please check the spelling of the URL or try again later.')
self.show_message( message, title) self.show_message( message, title)
else: else:
if result: if result:
@ -1067,7 +1162,7 @@ class gPodder(GladeWidget):
self.playback_episode( current_channel, current_podcast) self.playback_episode( current_channel, current_podcast)
return return
if widget.get_name() == 'treeAvailable': if widget.get_name() == 'treeAvailable' or widget.get_name() == 'item_episode_details':
play_callback = lambda: self.playback_episode( current_channel, current_podcast) play_callback = lambda: self.playback_episode( current_channel, current_podcast)
download_callback = lambda: self.download_podcast_by_url( url, want_message_dialog, None) download_callback = lambda: self.download_podcast_by_url( url, want_message_dialog, None)
gpe = gPodderEpisode( episode = current_podcast, channel = current_channel, download_callback = download_callback, play_callback = play_callback) gpe = gPodderEpisode( episode = current_podcast, channel = current_channel, download_callback = download_callback, play_callback = play_callback)
@ -1142,7 +1237,7 @@ class gPodder(GladeWidget):
""" """
if self.channels: if self.channels:
if not save_channels(self.channels): if not save_channels(self.channels):
self.show_message(_('Please check your permissions and free disk space.'), _('Error saving channel list')) self.show_message(_('Please check your permissions and free disk space.'), _('Error saving podcast list'))
services.download_status_manager.cancel_all() services.download_status_manager.cancel_all()
@ -1189,7 +1284,7 @@ class gPodder(GladeWidget):
def on_itemRemoveOldEpisodes_activate( self, widget): def on_itemRemoveOldEpisodes_activate( self, widget):
columns = ( columns = (
('title', _('Episode')), ('title', _('Episode')),
('channel_prop', _('Channel')), ('channel_prop', _('Podcast')),
('filesize_prop', _('Size')), ('filesize_prop', _('Size')),
('pubdate_prop', _('Released')), ('pubdate_prop', _('Released')),
('played_prop', _('Status')), ('played_prop', _('Status')),
@ -1240,13 +1335,23 @@ class gPodder(GladeWidget):
self.for_each_selected_episode_url(callback) self.for_each_selected_episode_url(callback)
def on_item_email_subscriptions_activate(self, widget):
if not self.channels:
self.show_message(_('Your subscription list is empty.'), _('Could not send list'))
elif not gl.send_subscriptions():
self.show_message(_('There was an error sending your subscription list via e-mail.'), _('Could not send list'))
def on_item_show_url_entry_activate(self, widget):
gl.config.show_podcast_url_entry = self.item_show_url_entry.get_active()
def on_itemUpdate_activate(self, widget, notify_no_new_episodes=False): def on_itemUpdate_activate(self, widget, notify_no_new_episodes=False):
if self.channels: if self.channels:
self.update_feed_cache(notify_no_new_episodes=notify_no_new_episodes) self.update_feed_cache(notify_no_new_episodes=notify_no_new_episodes)
else: else:
title = _('No channels available') title = _('Import podcasts from the web')
message = _('You need to subscribe to some podcast feeds before you can start downloading podcasts. Use your favorite search engine to look for interesting podcasts.') message = _('Your podcast list is empty. Do you want to see a list of example podcasts you can subscribe to?')
self.show_message( message, title) if self.show_confirmation(message, title):
self.on_itemImportChannels_activate(self, widget)
def download_episode_list( self, episodes): def download_episode_list( self, episodes):
for episode in episodes: for episode in episodes:
@ -1258,7 +1363,7 @@ class gPodder(GladeWidget):
def new_episodes_show(self, episodes): def new_episodes_show(self, episodes):
columns = ( columns = (
('title', _('Episode')), ('title', _('Episode')),
('channel_prop', _('Channel')), ('channel_prop', _('Podcast')),
('filesize_prop', _('Size')), ('filesize_prop', _('Size')),
('pubdate_prop', _('Released')), ('pubdate_prop', _('Released')),
) )
@ -1404,31 +1509,17 @@ class gPodder(GladeWidget):
elif self.tray_icon: elif self.tray_icon:
self.tray_icon.set_visible(True) self.tray_icon.set_visible(True)
def update_view_settings(self):
if gl.config.show_toolbar:
self.toolbar.show_all()
else:
self.toolbar.hide_all()
if self.episode_description_shown != gl.config.episode_list_descriptions:
for channel in self.channels:
channel.force_update_tree_model()
self.updateTreeView()
self.episode_description_shown = gl.config.episode_list_descriptions
def on_itemShowToolbar_activate(self, widget): def on_itemShowToolbar_activate(self, widget):
gl.config.show_toolbar = self.itemShowToolbar.get_active() gl.config.show_toolbar = self.itemShowToolbar.get_active()
self.update_view_settings()
def on_itemShowDescription_activate(self, widget): def on_itemShowDescription_activate(self, widget):
gl.config.episode_list_descriptions = self.itemShowDescription.get_active() gl.config.episode_list_descriptions = self.itemShowDescription.get_active()
self.update_view_settings()
def update_item_device( self): def update_item_device( self):
if gl.config.device_type != 'none': if gl.config.device_type != 'none':
self.itemDevice.show_all() self.itemDevice.show_all()
( label, image ) = self.itemDevice.get_children() (label,) = self.itemDevice.get_children()
label.set_text( gl.get_device_name()) label.set_text(gl.get_device_name())
else: else:
self.itemDevice.hide_all() self.itemDevice.hide_all()
@ -1444,18 +1535,17 @@ class gPodder(GladeWidget):
gPodderMaemoPreferences() gPodderMaemoPreferences()
def on_itemAddChannel_activate(self, widget, *args): def on_itemAddChannel_activate(self, widget, *args):
if gpodder.interface == gpodder.MAEMO: if gpodder.interface == gpodder.MAEMO or not gl.config.show_podcast_url_entry:
gPodderAddPodcastDialog(url_callback=self.add_new_channel) gPodderAddPodcastDialog(url_callback=self.add_new_channel)
else: else:
if self.channelPaned.get_position() < 200: if self.channelPaned.get_position() < 200:
self.channelPaned.set_position( 200) self.channelPaned.set_position( 200)
self.entryAddChannel.set_text( _('Enter podcast URL'))
self.entryAddChannel.grab_focus() self.entryAddChannel.grab_focus()
def on_itemEditChannel_activate(self, widget, *args): def on_itemEditChannel_activate(self, widget, *args):
if self.active_channel is None: if self.active_channel is None:
title = _('No channel selected') title = _('No podcast selected')
message = _('Please select a channel in the channels list to edit.') message = _('Please select a podcast in the podcasts list to edit.')
self.show_message( message, title) self.show_message( message, title)
return return
@ -1500,7 +1590,7 @@ class gPodder(GladeWidget):
dialog.add_button(gtk.STOCK_NO, gtk.RESPONSE_NO) dialog.add_button(gtk.STOCK_NO, gtk.RESPONSE_NO)
dialog.add_button(gtk.STOCK_YES, gtk.RESPONSE_YES) dialog.add_button(gtk.STOCK_YES, gtk.RESPONSE_YES)
title = _('Remove channel and episodes?') title = _('Remove podcast and episodes?')
message = _('Do you really want to remove <b>%s</b> and all downloaded episodes?') % saxutils.escape(self.active_channel.title) message = _('Do you really want to remove <b>%s</b> and all downloaded episodes?') % saxutils.escape(self.active_channel.title)
dialog.set_title(title) dialog.set_title(title)
@ -1512,7 +1602,7 @@ class gPodder(GladeWidget):
affirmative = gtk.RESPONSE_YES affirmative = gtk.RESPONSE_YES
elif gpodder.interface == gpodder.MAEMO: elif gpodder.interface == gpodder.MAEMO:
cb_ask = gtk.CheckButton('') # dummy check button cb_ask = gtk.CheckButton('') # dummy check button
dialog = hildon.Note('confirmation', (self.gPodder, _('Do you really want to remove this channel and all downloaded episodes?'))) dialog = hildon.Note('confirmation', (self.gPodder, _('Do you really want to remove this podcast and all downloaded episodes?')))
affirmative = gtk.RESPONSE_OK affirmative = gtk.RESPONSE_OK
result = dialog.run() result = dialog.run()
@ -1537,16 +1627,43 @@ class gPodder(GladeWidget):
except: except:
log('There has been an error removing the channel.', traceback=True, sender=self) log('There has been an error removing the channel.', traceback=True, sender=self)
def get_opml_filter(self):
filter = gtk.FileFilter()
filter.add_pattern('*.opml')
filter.add_pattern('*.xml')
filter.set_name(_('OPML files')+' (*.opml, *.xml)')
return filter
def on_item_import_from_file_activate(self, widget):
if gpodder.interface == gpodder.GUI:
dlg = gtk.FileChooserDialog(title=_('Import from OPML'), parent=None, action=gtk.FILE_CHOOSER_ACTION_OPEN)
dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
dlg.add_button(gtk.STOCK_OPEN, gtk.RESPONSE_OK)
elif gpodder.interface == gpodder.MAEMO:
dlg = hildon.FileChooserDialog(self.gPodder, gtk.FILE_CHOOSER_ACTION_OPEN)
dlg.set_filter(self.get_opml_filter())
response = dlg.run()
filename = None
if response == gtk.RESPONSE_OK:
filename = dlg.get_filename()
dlg.destroy()
if filename is not None:
gPodderOpmlLister(custom_title=_('Import podcasts from OPML file'), hide_url_entry=True).get_channels_from_url(filename, lambda url: self.add_new_channel(url,False), lambda: self.on_itemDownloadAllNew_activate(self.gPodder))
def on_itemExportChannels_activate(self, widget, *args): def on_itemExportChannels_activate(self, widget, *args):
if not self.channels: if not self.channels:
title = _('Nothing to export') title = _('Nothing to export')
message = _('Your list of channel subscriptions is empty. Please subscribe to some podcasts first before trying to export your subscription list.') message = _('Your list of podcast subscriptions is empty. Please subscribe to some podcasts first before trying to export your subscription list.')
self.show_message( message, title) self.show_message( message, title)
return return
dlg = gtk.FileChooserDialog( title=_("Export to OPML"), parent = None, action = gtk.FILE_CHOOSER_ACTION_SAVE) if gpodder.interface == gpodder.GUI:
dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) dlg = gtk.FileChooserDialog(title=_('Export to OPML'), parent=self.gPodder, action=gtk.FILE_CHOOSER_ACTION_SAVE)
dlg.add_button( gtk.STOCK_SAVE, gtk.RESPONSE_OK) dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
dlg.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_OK)
elif gpodder.interface == gpodder.MAEMO:
dlg = hildon.FileChooserDialog(self.gPodder, gtk.FILE_CHOOSER_ACTION_SAVE)
dlg.set_filter(self.get_opml_filter())
response = dlg.run() response = dlg.run()
if response == gtk.RESPONSE_OK: if response == gtk.RESPONSE_OK:
filename = dlg.get_filename() filename = dlg.get_filename()
@ -1565,9 +1682,6 @@ class gPodder(GladeWidget):
def on_homepage_activate(self, widget, *args): def on_homepage_activate(self, widget, *args):
util.open_website(app_website) util.open_website(app_website)
def on_wishlist_activate(self, widget, *args):
util.open_website('http://www.amazon.de/gp/registry/2PD2MYGHE6857')
def on_wiki_activate(self, widget, *args): def on_wiki_activate(self, widget, *args):
util.open_website('http://wiki.gpodder.org/') util.open_website('http://wiki.gpodder.org/')
@ -1635,7 +1749,7 @@ class gPodder(GladeWidget):
self.updateTreeView() self.updateTreeView()
def on_entryAddChannel_changed(self, widget, *args): def on_entryAddChannel_changed(self, widget, *args):
active = self.entryAddChannel.get_text() not in ('', _('Enter podcast URL')) active = self.entryAddChannel.get_text() not in ('', self.ENTER_URL_TEXT)
self.btnAddChannel.set_sensitive( active) self.btnAddChannel.set_sensitive( active)
def on_btnAddChannel_clicked(self, widget, *args): def on_btnAddChannel_clicked(self, widget, *args):
@ -1688,11 +1802,6 @@ class gPodder(GladeWidget):
def on_btnDownloadNewer_clicked(self, widget, *args): def on_btnDownloadNewer_clicked(self, widget, *args):
self.new_episodes_show(self.active_channel.get_new_episodes()) self.new_episodes_show(self.active_channel.get_new_episodes())
def on_btnSelectAllAvailable_clicked(self, widget, *args):
self.treeAvailable.get_selection().select_all()
self.on_treeAvailable_row_activated( self.toolDownload, args)
self.treeAvailable.get_selection().unselect_all()
def auto_update_procedure(self, first_run=False): def auto_update_procedure(self, first_run=False):
log('auto_update_procedure() got called', sender=self) log('auto_update_procedure() got called', sender=self)
if not first_run and gl.config.auto_update_feeds and self.minimized: if not first_run and gl.config.auto_update_feeds and self.minimized:
@ -1806,11 +1915,6 @@ class gPodder(GladeWidget):
self.active_channel.force_update_tree_model() self.active_channel.force_update_tree_model()
self.updateTreeView() self.updateTreeView()
def on_btnDeleteAll_clicked(self, widget, *args):
self.treeAvailable.get_selection().select_all()
self.on_btnDownloadedDelete_clicked( widget, args)
self.treeAvailable.get_selection().unselect_all()
def on_key_press(self, widget, event): def on_key_press(self, widget, event):
# Currently, we only handle Maemo hardware keys here, # Currently, we only handle Maemo hardware keys here,
# so if we are not a Maemo app, we don't do anything! # so if we are not a Maemo app, we don't do anything!
@ -1961,7 +2065,7 @@ class gPodderChannel(GladeWidget):
channel_url = self.channel.url channel_url = self.channel.url
if entered_url != channel_url: if entered_url != channel_url:
if self.show_confirmation(_('Do you really want to move this channel to <b>%s</b>?') % (saxutils.escape(entered_url),), _('Really change URL?')): if self.show_confirmation(_('Do you really want to move this podcast to <b>%s</b>?') % (saxutils.escape(entered_url),), _('Really change URL?')):
if hasattr(self, 'callback_change_url'): if hasattr(self, 'callback_change_url'):
self.gPodderChannel.hide_all() self.gPodderChannel.hide_all()
self.callback_change_url(channel_url, entered_url) self.callback_change_url(channel_url, entered_url)
@ -2507,13 +2611,18 @@ class gPodderOpmlLister(GladeWidget):
self.callback_for_channel = None self.callback_for_channel = None
self.callback_finished = None self.callback_finished = None
if hasattr(self, 'custom_title'):
self.gPodderOpmlLister.set_title(self.custom_title)
if hasattr(self, 'hide_url_entry'):
self.hbox25.hide_all()
togglecell = gtk.CellRendererToggle() togglecell = gtk.CellRendererToggle()
togglecell.set_property( 'activatable', True) togglecell.set_property( 'activatable', True)
togglecell.connect( 'toggled', self.callback_edited) togglecell.connect( 'toggled', self.callback_edited)
togglecolumn = gtk.TreeViewColumn( '', togglecell, active=0) togglecolumn = gtk.TreeViewColumn( '', togglecell, active=0)
titlecell = gtk.CellRendererText() titlecell = gtk.CellRendererText()
titlecolumn = gtk.TreeViewColumn( _('Channel'), titlecell, markup=1) titlecolumn = gtk.TreeViewColumn(_('Podcast'), titlecell, markup=1)
for itemcolumn in ( togglecolumn, titlecolumn ): for itemcolumn in ( togglecolumn, titlecolumn ):
self.treeviewChannelChooser.append_column( itemcolumn) self.treeviewChannelChooser.append_column( itemcolumn)

View file

@ -199,6 +199,15 @@ class gPodderLib(object):
def history_is_locked( self, url): def history_is_locked( self, url):
return (url in self.__locked_history) return (url in self.__locked_history)
def send_subscriptions(self):
try:
subprocess.Popen(['xdg-email', '--subject', _('My podcast subscriptions'),
'--attach', self.channel_opml_file])
except:
return False
return True
def playback_episode( self, channel, episode): def playback_episode( self, channel, episode):
self.history_mark_played( episode.url) self.history_mark_played( episode.url)
@ -234,7 +243,7 @@ class gPodderLib(object):
def image_download_thread( self, url, callback_pixbuf = None, callback_status = None, callback_finished = None, cover_file = None): def image_download_thread( self, url, callback_pixbuf = None, callback_status = None, callback_finished = None, cover_file = None):
if callback_status is not None: if callback_status is not None:
util.idle_add(callback_status, _('Downloading channel cover...')) util.idle_add(callback_status, _('Downloading podcast cover...'))
pixbuf = gtk.gdk.PixbufLoader() pixbuf = gtk.gdk.PixbufLoader()
if cover_file is None: if cover_file is None: