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/>.
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)
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))
51 if self
._config
.do_not_show_new_episodes_dialog
:
54 for index
, row
in enumerate(self
):
55 if row
[self
.C_HIDE_DIALOG
]:
58 if self
._config
.auto_download
== \
59 row
[self
.C_AUTO_DOWNLOAD
]:
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)
76 self
.append((_('None'), 'none'))
77 self
.append((_('iPod'), 'ipod'))
78 self
.append((_('Filesystem-based'), 'filesystem'))
79 self
.append((_('MTP'), 'mtp'))
82 for index
, row
in enumerate(self
):
83 if self
._config
.device_type
== row
[self
.C_DEVICE_TYPE
]:
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)
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))
102 for index
, row
in enumerate(self
):
103 if self
._config
.on_sync_delete
and row
[self
.C_ON_SYNC_DELETE
]:
105 if self
._config
.on_sync_mark_played
and row
[self
.C_ON_SYNC_MARK_PLAYED
] \
106 and not self
._config
.on_sync_delete
:
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
):
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
)
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
)
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 # Initialize the UI state with configuration settings
198 self
.checkbutton_enable
.set_active(self
._config
.mygpo_enabled
)
199 self
.entry_username
.set_text(self
._config
.mygpo_username
)
200 self
.entry_password
.set_text(self
._config
.mygpo_password
)
201 self
.entry_caption
.set_text(self
._config
.mygpo_device_caption
)
203 # Disable mygpo sync while the dialog is open
204 self
._enable
_mygpo
= self
._config
.mygpo_enabled
205 self
._config
.mygpo_enabled
= False
207 def on_dialog_destroy(self
, widget
):
208 # Re-enable mygpo sync if the user has selected it
209 self
._config
.mygpo_enabled
= self
._enable
_mygpo
210 # Make sure the device is successfully created/updated
211 self
.mygpo_client
.create_device()
212 # Flush settings for mygpo client now
213 self
.mygpo_client
.flush(now
=True)
215 if self
.callback_finished
:
216 self
.callback_finished()
218 def on_button_close_clicked(self
, widget
):
219 self
.main_window
.destroy()
221 def on_button_advanced_clicked(self
, widget
):
222 self
.main_window
.destroy()
223 gPodderConfigEditor(self
.parent_window
, _config
=self
._config
)
225 def on_combo_audio_player_app_changed(self
, widget
):
226 index
= self
.combo_audio_player_app
.get_active()
227 self
._config
.player
= self
.audio_player_model
.get_command(index
)
229 def on_combo_video_player_app_changed(self
, widget
):
230 index
= self
.combo_video_player_app
.get_active()
231 self
._config
.videoplayer
= self
.video_player_model
.get_command(index
)
233 def on_button_audio_player_clicked(self
, widget
):
234 result
= self
.show_text_edit_dialog(_('Configure audio player'), \
239 self
._config
.player
= result
240 index
= self
.audio_player_model
.get_index(self
._config
.player
)
241 self
.combo_audio_player_app
.set_active(index
)
243 def on_button_video_player_clicked(self
, widget
):
244 result
= self
.show_text_edit_dialog(_('Configure video player'), \
246 self
._config
.videoplayer
)
249 self
._config
.videoplayer
= result
250 index
= self
.video_player_model
.get_index(self
._config
.videoplayer
)
251 self
.combo_video_player_app
.set_active(index
)
253 def format_update_interval_value(self
, scale
, value
):
256 return _('manual only')
258 return util
.format_seconds_to_hour_min_sec(self
.update_interval_presets
[int(value
)]*60)
260 def on_update_interval_value_changed(self
, range):
261 value
= int(range.get_value())
262 self
._config
.auto_update_feeds
= (value
> 0)
263 self
._config
.auto_update_frequency
= self
.update_interval_presets
[value
]
265 def on_combo_auto_download_changed(self
, widget
):
266 index
= self
.combo_auto_download
.get_active()
267 self
.auto_download_model
.set_index(index
)
269 def on_combobox_on_sync_changed(self
, widget
):
270 index
= self
.combobox_on_sync
.get_active()
271 self
.on_sync_model
.set_index(index
)
273 def on_combobox_device_type_changed(self
, widget
):
274 index
= self
.combobox_device_type
.get_active()
275 self
.device_type_model
.set_index(index
)
277 self
.btn_filesystemMountpoint
.set_label('')
278 self
.btn_filesystemMountpoint
.set_sensitive(False)
280 self
.btn_filesystemMountpoint
.set_label(self
._config
.ipod_mount
)
281 self
.btn_filesystemMountpoint
.set_sensitive(True)
282 self
._config
.connect_gtk_togglebutton('ipod_purge_old_episodes', self
.checkbutton_delete_played
)
284 self
.btn_filesystemMountpoint
.set_label(self
._config
.mp3_player_folder
)
285 self
.btn_filesystemMountpoint
.set_sensitive(True)
286 self
._config
.connect_gtk_togglebutton('mp3_player_delete_played', self
.checkbutton_delete_played
)
288 self
.btn_filesystemMountpoint
.set_label('')
289 self
.btn_filesystemMountpoint
.set_sensitive(False)
291 children
= self
.btn_filesystemMountpoint
.get_children()
293 label
= children
.pop()
294 label
.set_alignment(0., .5)
296 def on_btn_device_mountpoint_clicked(self
, widget
):
297 fs
= gtk
.FileChooserDialog( title
= _('Select folder for mount point'), action
= gtk
.FILE_CHOOSER_ACTION_SELECT_FOLDER
)
298 fs
.add_button( gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
)
299 fs
.add_button( gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
)
300 fs
.set_current_folder(self
.btn_filesystemMountpoint
.get_label())
301 if fs
.run() == gtk
.RESPONSE_OK
:
302 filename
= fs
.get_filename()
303 if self
._config
.device_type
== 'filesystem':
304 self
._config
.mp3_player_folder
= filename
305 elif self
._config
.device_type
== 'ipod':
306 self
._config
.ipod_mount
= filename
308 # Request an update of the mountpoint button
309 self
.on_combobox_device_type_changed(None)
313 def format_expiration_value(self
, scale
, value
):
318 return N_('after %(count)d day', 'after %(count)d days', value
) % {'count':value
}
320 def on_expiration_value_changed(self
, range):
321 value
= int(range.get_value())
324 self
.checkbutton_expiration_unplayed
.set_active(False)
325 self
._config
.auto_remove_played_episodes
= False
326 self
._config
.auto_remove_unplayed_episodes
= False
328 self
._config
.auto_remove_played_episodes
= True
329 self
._config
.episode_old_age
= value
331 self
.checkbutton_expiration_unplayed
.set_sensitive(value
> 0)
333 def on_enabled_toggled(self
, widget
):
334 # Only update indirectly (see on_dialog_destroy)
335 self
._enable
_mygpo
= widget
.get_active()
337 def on_username_changed(self
, widget
):
338 self
._config
.mygpo_username
= widget
.get_text()
340 def on_password_changed(self
, widget
):
341 self
._config
.mygpo_password
= widget
.get_text()
343 def on_device_caption_changed(self
, widget
):
344 self
._config
.mygpo_device_caption
= widget
.get_text()
346 def on_button_overwrite_clicked(self
, button
):
347 title
= _('Replace subscription list on server')
348 message
= _('Remote podcasts that have not been added locally will be removed on the server. Continue?')
349 if self
.show_confirmation(message
, title
):
351 self
._config
.mygpo_enabled
= True
352 self
.on_send_full_subscriptions()
353 self
._config
.mygpo_enabled
= False
354 threading
.Thread(target
=thread_proc
).start()