GTK UI: Configurable columns in episode list

This commit is contained in:
Thomas Perl 2011-04-11 13:09:50 +02:00
parent 6f893a1e87
commit 1a813a4c90
6 changed files with 106 additions and 12 deletions

View File

@ -77,6 +77,7 @@ gPodderSettings = {
# Display list filter configuration
'episode_list_view_mode': 1,
'episode_list_columns': int('101', 2), # bitfield of visible columns
'podcast_list_view_mode': 1,
'podcast_list_hide_boring': False,

View File

@ -55,13 +55,13 @@ class DownloadStatusModel(download.DownloadStatusModel):
class EpisodeListModel(gtk.GenericTreeModel):
N_COLUMNS = 16
N_COLUMNS = 17
C_URL, C_TITLE, C_FILESIZE_TEXT, C_EPISODE, C_STATUS_ICON, \
C_PUBLISHED_TEXT, C_DESCRIPTION, C_TOOLTIP, \
C_VIEW_SHOW_UNDELETED, C_VIEW_SHOW_DOWNLOADED, \
C_VIEW_SHOW_UNPLAYED, C_FILESIZE, C_PUBLISHED, \
C_TIME, C_TIME_VISIBLE, \
C_TIME, C_TIME_VISIBLE, C_TOTAL_TIME, \
C_LOCKED = range(N_COLUMNS)
DATA_TYPES = (str, str, str, object, str , str, str, \
@ -158,6 +158,8 @@ class EpisodeListModel(gtk.GenericTreeModel):
elif column == self.C_TIME:
return episode.get_play_info_string()
elif column == self.C_TIME_VISIBLE:
return bool(episode.total_time)
elif column == self.C_TOTAL_TIME:
return episode.total_time
elif column == self.C_LOCKED:
return episode.archive and \

View File

@ -421,6 +421,7 @@ class TreeViewHelper(object):
LAST_TOOLTIP = '_gpodder_last_tooltip'
CAN_TOOLTIP = '_gpodder_can_tooltip'
ROLE = '_gpodder_role'
COLUMNS = '_gpodder_columns'
# Enum for the role attribute
ROLE_PODCASTS, ROLE_EPISODES, ROLE_DOWNLOADS = range(3)
@ -453,3 +454,15 @@ class TreeViewHelper(object):
return True
return func
@classmethod
def register_column(cls, treeview, column):
if not hasattr(treeview, cls.COLUMNS):
setattr(treeview, cls.COLUMNS, [])
columns = getattr(treeview, cls.COLUMNS)
columns.append(column)
@classmethod
def get_columns(cls, treeview):
return getattr(treeview, cls.COLUMNS, [])

View File

@ -98,8 +98,8 @@ class EpisodeListModel(gtk.ListStore):
C_PUBLISHED_TEXT, C_DESCRIPTION, C_TOOLTIP, \
C_VIEW_SHOW_UNDELETED, C_VIEW_SHOW_DOWNLOADED, \
C_VIEW_SHOW_UNPLAYED, C_FILESIZE, C_PUBLISHED, \
C_TIME, C_TIME_VISIBLE, \
C_LOCKED = range(16)
C_TIME, C_TIME_VISIBLE, C_TOTAL_TIME, \
C_LOCKED = range(17)
VIEW_ALL, VIEW_UNDELETED, VIEW_DOWNLOADED, VIEW_UNPLAYED = range(4)
@ -109,7 +109,7 @@ class EpisodeListModel(gtk.ListStore):
def __init__(self, on_filter_changed=lambda has_episodes: None):
gtk.ListStore.__init__(self, str, str, str, object, \
str, str, str, str, bool, bool, bool, \
int, int, str, bool, bool, bool)
int, int, str, bool, int, bool)
# Callback for when the filter / list changes, gets one parameter
# (has_episodes) that is True if the list has any episodes
@ -269,8 +269,8 @@ class EpisodeListModel(gtk.ListStore):
episode.file_size, \
episode.published, \
episode.get_play_info_string(), \
episode.total_time and not episode.current_position, \
episode.total_time and episode.current_position, \
bool(episode.total_time), \
episode.total_time, \
episode.archive))
self.update_by_iter(iter, downloading, include_description, \
@ -398,8 +398,9 @@ class EpisodeListModel(gtk.ListStore):
self.C_VIEW_SHOW_UNPLAYED, view_show_unplayed, \
self.C_DESCRIPTION, description, \
self.C_TOOLTIP, tooltip, \
self.C_TIME, episode.get_play_info_string(), \
self.C_TIME_VISIBLE, episode.total_time, \
self.C_TIME, episode.get_play_info_string(duration_only=True), \
self.C_TIME_VISIBLE, bool(episode.total_time), \
self.C_TOTAL_TIME, episode.total_time, \
self.C_LOCKED, episode.archive, \
self.C_FILESIZE_TEXT, self._format_filesize(episode), \
self.C_FILESIZE, episode.file_size)

View File

@ -232,6 +232,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.gPodder.connect('key-press-event', self.on_key_press)
self.preferences_dialog = None
self.episode_columns_menu = None
self.config.add_observer(self.on_config_changed)
self.episode_shownotes_window = None
@ -943,6 +944,34 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.entry_search_episodes.grab_focus()
self.entry_search_episodes.set_position(-1)
def set_episode_list_column(self, index, new_value):
mask = (1 << index)
if new_value:
self.config.episode_list_columns |= mask
else:
self.config.episode_list_columns &= ~mask
def update_episode_list_columns_visibility(self):
if gpodder.ui.fremantle:
return
columns = TreeViewHelper.get_columns(self.treeAvailable)
for index, column in enumerate(columns):
visible = bool(self.config.episode_list_columns & (1 << index))
column.set_visible(visible)
self.treeAvailable.columns_autosize()
if self.episode_columns_menu is not None:
children = self.episode_columns_menu.get_children()
for index, child in enumerate(children):
active = bool(self.config.episode_list_columns & (1 << index))
child.set_active(active)
def on_episode_list_header_clicked(self, button, event):
if self.episode_columns_menu is not None:
self.episode_columns_menu.popup(None, None, None, event.button, \
event.time, None)
def init_episode_list_treeview(self):
# For loading the list model
self.episode_list_model = EpisodeListModel(self.on_episode_list_filter_changed)
@ -1009,6 +1038,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
sizecolumn = gtk.TreeViewColumn(_('Size'), sizecell, text=EpisodeListModel.C_FILESIZE_TEXT)
sizecolumn.set_sort_column_id(EpisodeListModel.C_FILESIZE)
timecell = gtk.CellRendererText()
timecell.set_property('xalign', 1)
timecolumn = gtk.TreeViewColumn(_('Duration'), timecell, text=EpisodeListModel.C_TIME)
timecolumn.set_sort_column_id(EpisodeListModel.C_TOTAL_TIME)
releasecell = gtk.CellRendererText()
releasecolumn = gtk.TreeViewColumn(_('Released'), releasecell, text=EpisodeListModel.C_PUBLISHED_TEXT)
releasecolumn.set_sort_column_id(EpisodeListModel.C_PUBLISHED)
@ -1017,9 +1051,48 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.treeAvailable.append_column(namecolumn)
if gpodder.ui.desktop:
for itemcolumn in (sizecolumn, releasecolumn):
for itemcolumn in (sizecolumn, timecolumn, releasecolumn):
itemcolumn.set_reorderable(True)
self.treeAvailable.append_column(itemcolumn)
TreeViewHelper.register_column(self.treeAvailable, itemcolumn)
# Add context menu to all tree view column headers
for column in self.treeAvailable.get_columns():
label = gtk.Label(column.get_title())
label.show_all()
column.set_widget(label)
w = column.get_widget()
while w is not None and not isinstance(w, gtk.Button):
w = w.get_parent()
w.connect('button-release-event', self.on_episode_list_header_clicked)
# Create a new menu for the visible episode list columns
for child in self.mainMenu.get_children():
if child.get_name() == 'menuView':
submenu = child.get_submenu()
item = gtk.MenuItem(_('Visible columns'))
submenu.append(gtk.SeparatorMenuItem())
submenu.append(item)
submenu.show_all()
self.episode_columns_menu = gtk.Menu()
item.set_submenu(self.episode_columns_menu)
break
# For each column that can be shown/hidden, add a menu item
columns = TreeViewHelper.get_columns(self.treeAvailable)
for index, column in enumerate(columns):
item = gtk.CheckMenuItem(column.get_title())
self.episode_columns_menu.append(item)
def on_item_toggled(item, index):
self.set_episode_list_column(index, item.get_active())
item.connect('toggled', on_item_toggled, index)
self.episode_columns_menu.show_all()
# Update the visibility of the columns and the check menu items
self.update_episode_list_columns_visibility()
# Set up type-ahead find for the episode list
def on_key_press(treeview, event):
@ -1371,6 +1444,8 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.update_podcast_list_model()
if gpodder.ui.fremantle:
hildon.hildon_gtk_window_set_progress_indicator(self.main_window, False)
elif name == 'episode_list_columns':
self.update_episode_list_columns_visibility()
def on_treeview_query_tooltip(self, treeview, x, y, keyboard_tooltip, tooltip):
# With get_bin_window, we get the window that contains the rows without

View File

@ -591,9 +591,11 @@ class PodcastEpisode(PodcastModelObject):
(self.current_position + 10 >= self.total_time or \
self.current_position >= self.total_time*.99)
def get_play_info_string(self):
def get_play_info_string(self, duration_only=False):
duration = util.format_time(self.total_time)
if self.current_position > 0 and \
if duration_only and self.total_time > 0:
return duration
elif self.current_position > 0 and \
self.current_position != self.total_time:
position = util.format_time(self.current_position)
return '%s / %s' % (position, duration)