GTK UI: Configurable columns in episode list
This commit is contained in:
parent
6f893a1e87
commit
1a813a4c90
|
@ -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,
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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, [])
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue