Strings: Merge "manual only" to "manually" (bug 1215)
[gpodder.git] / src / gpodder / gtkui / desktop / preferences.py
blob2c27888bc3487ff10d2eadbb826a3f65baeb6065
1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2010 Thomas Perl and the gPodder Team
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 import gtk
21 import pango
22 import threading
24 import gpodder
26 _ = gpodder.gettext
27 N_ = gpodder.ngettext
29 from gpodder import util
31 from gpodder.gtkui.interface.common import BuilderWidget
32 from gpodder.gtkui.interface.configeditor import gPodderConfigEditor
34 from gpodder.gtkui.desktopfile import PlayerListModel
36 class NewEpisodeActionList(gtk.ListStore):
37 C_CAPTION, C_AUTO_DOWNLOAD, C_HIDE_DIALOG = range(3)
39 ACTION_NONE, ACTION_ASK, ACTION_MINIMIZED, ACTION_ALWAYS = range(4)
41 def __init__(self, config):
42 gtk.ListStore.__init__(self, str, str, bool)
43 self._config = config
44 self.append((_('Do nothing'), 'never', True))
45 self.append((_('Show episode list'), 'never', False))
46 self.append((_('Add to download list'), 'queue', False))
47 self.append((_('Download if minimized'), 'minimized', False))
48 self.append((_('Download immediately'), 'always', False))
50 def get_index(self):
51 if self._config.do_not_show_new_episodes_dialog:
52 return 0
53 else:
54 for index, row in enumerate(self):
55 if row[self.C_HIDE_DIALOG]:
56 continue
58 if self._config.auto_download == \
59 row[self.C_AUTO_DOWNLOAD]:
60 return index
62 return 1 # Some sane default
64 def set_index(self, index):
65 self._config.do_not_show_new_episodes_dialog = self[index][self.C_HIDE_DIALOG]
66 self._config.auto_download = self[index][self.C_AUTO_DOWNLOAD]
69 class DeviceTypeActionList(gtk.ListStore):
70 C_CAPTION, C_DEVICE_TYPE = range(2)
71 ACTION_NONE, ACTION_ASK, ACTION_MINIMIZED, ACTION_ALWAYS = range(4)
73 def __init__(self, config):
74 gtk.ListStore.__init__(self, str, str)
75 self._config = config
76 self.append((_('None'), 'none'))
77 self.append((_('iPod'), 'ipod'))
78 self.append((_('Filesystem-based'), 'filesystem'))
79 self.append((_('MTP'), 'mtp'))
81 def get_index(self):
82 for index, row in enumerate(self):
83 if self._config.device_type == row[self.C_DEVICE_TYPE]:
84 return index
85 return 0 # Some sane default
87 def set_index(self, index):
88 self._config.device_type = self[index][self.C_DEVICE_TYPE]
90 class OnSyncActionList(gtk.ListStore):
91 C_CAPTION, C_ON_SYNC_DELETE, C_ON_SYNC_MARK_PLAYED = range(3)
92 ACTION_NONE, ACTION_ASK, ACTION_MINIMIZED, ACTION_ALWAYS = range(4)
94 def __init__(self, config):
95 gtk.ListStore.__init__(self, str, bool, bool)
96 self._config = config
97 self.append((_('Do nothing'), False, False))
98 self.append((_('Mark it as played'), False, True))
99 self.append((_('Delete it from gPodder'), True, False))
101 def get_index(self):
102 for index, row in enumerate(self):
103 if self._config.on_sync_delete and row[self.C_ON_SYNC_DELETE]:
104 return index
105 if self._config.on_sync_mark_played and row[self.C_ON_SYNC_MARK_PLAYED] \
106 and not self._config.on_sync_delete:
107 return index
108 return 0 # Some sane default
110 def set_index(self, index):
111 self._config.on_sync_delete = self[index][self.C_ON_SYNC_DELETE]
112 self._config.on_sync_mark_played = self[index][self.C_ON_SYNC_MARK_PLAYED]
115 class gPodderPreferences(BuilderWidget):
116 def new(self):
117 if not hasattr(self, 'callback_finished'):
118 self.callback_finished = None
120 for cb in (self.combo_audio_player_app, self.combo_video_player_app):
121 cellrenderer = gtk.CellRendererPixbuf()
122 cb.pack_start(cellrenderer, False)
123 cb.add_attribute(cellrenderer, 'pixbuf', PlayerListModel.C_ICON)
124 cellrenderer = gtk.CellRendererText()
125 cellrenderer.set_property('ellipsize', pango.ELLIPSIZE_END)
126 cb.pack_start(cellrenderer, True)
127 cb.add_attribute(cellrenderer, 'markup', PlayerListModel.C_NAME)
128 cb.set_row_separator_func(PlayerListModel.is_separator)
130 self.audio_player_model = self.user_apps_reader.get_model('audio')
131 self.combo_audio_player_app.set_model(self.audio_player_model)
132 index = self.audio_player_model.get_index(self._config.player)
133 self.combo_audio_player_app.set_active(index)
135 self.video_player_model = self.user_apps_reader.get_model('video')
136 self.combo_video_player_app.set_model(self.video_player_model)
137 index = self.video_player_model.get_index(self._config.videoplayer)
138 self.combo_video_player_app.set_active(index)
140 self._config.connect_gtk_togglebutton('enable_notifications', self.checkbutton_enable_notifications)
141 self._config.connect_gtk_togglebutton('display_tray_icon', self.checkbutton_show_tray_icon)
143 self.update_interval_presets = [0, 10, 30, 60, 2*60, 6*60, 12*60]
144 adjustment_update_interval = self.hscale_update_interval.get_adjustment()
145 adjustment_update_interval.upper = len(self.update_interval_presets)-1
146 if self._config.auto_update_frequency in self.update_interval_presets:
147 index = self.update_interval_presets.index(self._config.auto_update_frequency)
148 self.hscale_update_interval.set_value(index)
149 else:
150 # Patch in the current "custom" value into the mix
151 self.update_interval_presets.append(self._config.auto_update_frequency)
152 self.update_interval_presets.sort()
154 adjustment_update_interval.upper = len(self.update_interval_presets)-1
155 index = self.update_interval_presets.index(self._config.auto_update_frequency)
156 self.hscale_update_interval.set_value(index)
158 self._config.connect_gtk_togglebutton('update_on_startup', self.checkbutton_update_on_startup)
159 self._config.connect_gtk_spinbutton('max_episodes_per_feed', self.spinbutton_episode_limit)
161 self.auto_download_model = NewEpisodeActionList(self._config)
162 self.combo_auto_download.set_model(self.auto_download_model)
163 cellrenderer = gtk.CellRendererText()
164 self.combo_auto_download.pack_start(cellrenderer, True)
165 self.combo_auto_download.add_attribute(cellrenderer, 'text', NewEpisodeActionList.C_CAPTION)
166 self.combo_auto_download.set_active(self.auto_download_model.get_index())
168 if self._config.auto_remove_played_episodes:
169 adjustment_expiration = self.hscale_expiration.get_adjustment()
170 if self._config.episode_old_age > adjustment_expiration.get_upper():
171 # Patch the adjustment to include the higher current value
172 adjustment_expiration.upper = self._config.episode_old_age
174 self.hscale_expiration.set_value(self._config.episode_old_age)
175 else:
176 self.hscale_expiration.set_value(0)
178 self._config.connect_gtk_togglebutton('auto_remove_unplayed_episodes', self.checkbutton_expiration_unplayed)
179 self._config.connect_gtk_togglebutton('auto_cleanup_downloads', self.checkbutton_auto_cleanup_downloads)
181 self.device_type_model = DeviceTypeActionList(self._config)
182 self.combobox_device_type.set_model(self.device_type_model)
183 cellrenderer = gtk.CellRendererText()
184 self.combobox_device_type.pack_start(cellrenderer, True)
185 self.combobox_device_type.add_attribute(cellrenderer, 'text', DeviceTypeActionList.C_CAPTION)
186 self.combobox_device_type.set_active(self.device_type_model.get_index())
188 self.on_sync_model = OnSyncActionList(self._config)
189 self.combobox_on_sync.set_model(self.on_sync_model)
190 cellrenderer = gtk.CellRendererText()
191 self.combobox_on_sync.pack_start(cellrenderer, True)
192 self.combobox_on_sync.add_attribute(cellrenderer, 'text', OnSyncActionList.C_CAPTION)
193 self.combobox_on_sync.set_active(self.on_sync_model.get_index())
195 self._config.connect_gtk_togglebutton('only_sync_not_played', self.checkbutton_only_sync_not_played)
197 # Have to do this before calling set_active on checkbutton_enable
198 self._enable_mygpo = self._config.mygpo_enabled
200 # Initialize the UI state with configuration settings
201 self.checkbutton_enable.set_active(self._config.mygpo_enabled)
202 self.entry_username.set_text(self._config.mygpo_username)
203 self.entry_password.set_text(self._config.mygpo_password)
204 self.entry_caption.set_text(self._config.mygpo_device_caption)
206 # Disable mygpo sync while the dialog is open
207 self._config.mygpo_enabled = False
209 def on_dialog_destroy(self, widget):
210 # Re-enable mygpo sync if the user has selected it
211 self._config.mygpo_enabled = self._enable_mygpo
212 # Make sure the device is successfully created/updated
213 self.mygpo_client.create_device()
214 # Flush settings for mygpo client now
215 self.mygpo_client.flush(now=True)
217 if self.callback_finished:
218 self.callback_finished()
220 def on_button_close_clicked(self, widget):
221 self.main_window.destroy()
223 def on_button_advanced_clicked(self, widget):
224 self.main_window.destroy()
225 gPodderConfigEditor(self.parent_window, _config=self._config)
227 def on_combo_audio_player_app_changed(self, widget):
228 index = self.combo_audio_player_app.get_active()
229 self._config.player = self.audio_player_model.get_command(index)
231 def on_combo_video_player_app_changed(self, widget):
232 index = self.combo_video_player_app.get_active()
233 self._config.videoplayer = self.video_player_model.get_command(index)
235 def on_button_audio_player_clicked(self, widget):
236 result = self.show_text_edit_dialog(_('Configure audio player'), \
237 _('Command:'), \
238 self._config.player)
240 if result:
241 self._config.player = result
242 index = self.audio_player_model.get_index(self._config.player)
243 self.combo_audio_player_app.set_active(index)
245 def on_button_video_player_clicked(self, widget):
246 result = self.show_text_edit_dialog(_('Configure video player'), \
247 _('Command:'), \
248 self._config.videoplayer)
250 if result:
251 self._config.videoplayer = result
252 index = self.video_player_model.get_index(self._config.videoplayer)
253 self.combo_video_player_app.set_active(index)
255 def format_update_interval_value(self, scale, value):
256 value = int(value)
257 if value == 0:
258 return _('manually')
259 elif value > 0 and len(self.update_interval_presets) > value:
260 return util.format_seconds_to_hour_min_sec(self.update_interval_presets[value]*60)
261 else:
262 return str(value)
264 def on_update_interval_value_changed(self, range):
265 value = int(range.get_value())
266 self._config.auto_update_feeds = (value > 0)
267 self._config.auto_update_frequency = self.update_interval_presets[value]
269 def on_combo_auto_download_changed(self, widget):
270 index = self.combo_auto_download.get_active()
271 self.auto_download_model.set_index(index)
273 def on_combobox_on_sync_changed(self, widget):
274 index = self.combobox_on_sync.get_active()
275 self.on_sync_model.set_index(index)
277 def on_combobox_device_type_changed(self, widget):
278 index = self.combobox_device_type.get_active()
279 self.device_type_model.set_index(index)
280 if index == 0:
281 self.btn_filesystemMountpoint.set_label('')
282 self.btn_filesystemMountpoint.set_sensitive(False)
283 if index == 1:
284 self.btn_filesystemMountpoint.set_label(self._config.ipod_mount)
285 self.btn_filesystemMountpoint.set_sensitive(True)
286 self._config.connect_gtk_togglebutton('ipod_purge_old_episodes', self.checkbutton_delete_played)
287 if index == 2:
288 self.btn_filesystemMountpoint.set_label(self._config.mp3_player_folder)
289 self.btn_filesystemMountpoint.set_sensitive(True)
290 self._config.connect_gtk_togglebutton('mp3_player_delete_played', self.checkbutton_delete_played)
291 if index == 3:
292 self.btn_filesystemMountpoint.set_label('')
293 self.btn_filesystemMountpoint.set_sensitive(False)
295 children = self.btn_filesystemMountpoint.get_children()
296 if children:
297 label = children.pop()
298 label.set_alignment(0., .5)
300 def on_btn_device_mountpoint_clicked(self, widget):
301 fs = gtk.FileChooserDialog( title = _('Select folder for mount point'), action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
302 fs.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
303 fs.add_button( gtk.STOCK_OPEN, gtk.RESPONSE_OK)
304 fs.set_current_folder(self.btn_filesystemMountpoint.get_label())
305 if fs.run() == gtk.RESPONSE_OK:
306 filename = fs.get_filename()
307 if self._config.device_type == 'filesystem':
308 self._config.mp3_player_folder = filename
309 elif self._config.device_type == 'ipod':
310 self._config.ipod_mount = filename
312 # Request an update of the mountpoint button
313 self.on_combobox_device_type_changed(None)
315 fs.destroy()
317 def format_expiration_value(self, scale, value):
318 value = int(value)
319 if value == 0:
320 return _('manually')
321 else:
322 return N_('after %(count)d day', 'after %(count)d days', value) % {'count':value}
324 def on_expiration_value_changed(self, range):
325 value = int(range.get_value())
327 if value == 0:
328 self.checkbutton_expiration_unplayed.set_active(False)
329 self._config.auto_remove_played_episodes = False
330 self._config.auto_remove_unplayed_episodes = False
331 else:
332 self._config.auto_remove_played_episodes = True
333 self._config.episode_old_age = value
335 self.checkbutton_expiration_unplayed.set_sensitive(value > 0)
337 def on_enabled_toggled(self, widget):
338 # Only update indirectly (see on_dialog_destroy)
339 self._enable_mygpo = widget.get_active()
341 def on_username_changed(self, widget):
342 self._config.mygpo_username = widget.get_text()
344 def on_password_changed(self, widget):
345 self._config.mygpo_password = widget.get_text()
347 def on_device_caption_changed(self, widget):
348 self._config.mygpo_device_caption = widget.get_text()
350 def on_button_overwrite_clicked(self, button):
351 title = _('Replace subscription list on server')
352 message = _('Remote podcasts that have not been added locally will be removed on the server. Continue?')
353 if self.show_confirmation(message, title):
354 def thread_proc():
355 self._config.mygpo_enabled = True
356 self.on_send_full_subscriptions()
357 self._config.mygpo_enabled = False
358 threading.Thread(target=thread_proc).start()