2007-04-01 19:53:04 +02:00
# -*- coding: utf-8 -*-
2005-11-21 19:21:25 +01:00
#
2007-08-29 20:30:26 +02:00
# gPodder - A media aggregator and podcast client
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
# Copyright (c) 2005-2008 Thomas Perl and the gPodder Team
2006-04-07 22:22:30 +02:00
#
2007-08-29 20:30:26 +02:00
# gPodder is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
2006-04-07 22:22:30 +02:00
#
2007-08-29 20:30:26 +02:00
# gPodder is distributed in the hope that it will be useful,
2006-04-07 22:22:30 +02:00
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
2007-08-29 20:30:26 +02:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2005-11-21 19:21:25 +01:00
#
import os
import gtk
2007-01-28 10:21:39 +01:00
import gtk . gdk
2005-11-21 19:21:25 +01:00
import gobject
2006-11-20 12:51:20 +01:00
import pango
2005-11-21 19:21:25 +01:00
import sys
2007-04-23 17:18:31 +02:00
import shutil
2008-01-21 10:52:09 +01:00
import subprocess
2008-03-03 23:09:34 +01:00
import glob
2008-03-20 11:24:26 +01:00
import time
2008-08-04 16:54:18 +02:00
import urllib
2008-06-14 15:17:55 +02:00
import urllib2
2008-06-30 03:10:18 +02:00
import datetime
2006-03-04 21:45:01 +01:00
2007-07-05 23:07:16 +02:00
from xml . sax import saxutils
2005-11-21 19:21:25 +01:00
from threading import Event
2006-04-07 03:43:06 +02:00
from threading import Thread
2008-08-30 19:23:04 +02:00
from threading import Semaphore
2006-03-04 21:45:01 +01:00
from string import strip
2005-11-21 19:21:25 +01:00
2008-04-06 02:19:03 +02:00
import gpodder
2007-08-07 20:11:31 +02:00
from gpodder import util
2007-08-19 16:28:24 +02:00
from gpodder import opml
2007-08-24 16:49:41 +02:00
from gpodder import services
2008-01-28 12:38:53 +01:00
from gpodder import sync
2007-09-18 20:25:25 +02:00
from gpodder import download
2007-08-28 00:18:01 +02:00
from gpodder import SimpleGladeApp
2008-03-11 18:45:52 +01:00
from gpodder . liblogger import log
2008-06-30 03:10:18 +02:00
from gpodder . dbsqlite import db
2008-03-11 18:45:52 +01:00
try :
from gpodder import trayicon
have_trayicon = True
except Exception , exc :
2008-04-22 22:24:19 +02:00
log ( ' Warning: Could not import gpodder.trayicon. ' , traceback = True )
2008-03-11 18:45:52 +01:00
log ( ' Warning: This probably means your PyGTK installation is too old! ' )
have_trayicon = False
2005-11-21 19:21:25 +01:00
from libpodcasts import podcastChannel
2008-06-30 03:10:18 +02:00
from libpodcasts import LocalDBReader
from libpodcasts import podcastItem
2007-11-27 23:04:15 +01:00
from libpodcasts import channels_to_model
2007-08-20 15:45:46 +02:00
from libpodcasts import load_channels
2008-06-30 03:10:18 +02:00
from libpodcasts import update_channels
2007-08-20 15:45:46 +02:00
from libpodcasts import save_channels
2008-06-30 03:10:18 +02:00
from libpodcasts import can_restore_from_opml
2005-11-21 19:21:25 +01:00
2008-03-02 14:22:29 +01:00
from gpodder . libgpodder import gl
2005-11-21 19:21:25 +01:00
2006-03-30 00:07:27 +02:00
from libplayers import UserAppsReader
2007-03-17 13:11:13 +01:00
from libtagupdate import tagging_supported
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
WEB_BROWSER_ICON = ' web-browser '
elif gpodder . interface == gpodder . MAEMO :
import hildon
WEB_BROWSER_ICON = ' qgn_toolb_browser_web '
2005-11-21 19:21:25 +01:00
app_name = " gpodder "
app_version = " unknown " # will be set in main() call
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
app_authors = [
_ ( ' Current maintainer: ' ) , ' Thomas Perl <thpinfo.com> ' ,
' ' ,
_ ( ' Patches, bug reports and donations by: ' ) , ' Adrien Beaucreux ' ,
' Alain Tauch ' , ' Alistair Sutton ' , ' Anders Kvist ' , ' Andy Busch ' ,
2008-07-10 23:55:13 +02:00
' Antonio Roversi ' , ' Aravind Seshadri ' , ' Atte André Jensen ' , ' audioworld ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Bernd Schlapsi ' , ' Bill Barnard ' , ' Bjørn Rasmussen ' , ' Camille Moncelier ' ,
2008-06-17 14:50:27 +02:00
' Carlos Moffat ' , ' Chris ' , ' Chris Arnold ' , ' Clark Burbidge ' , ' Daniel Ramos ' ,
2008-06-05 18:11:25 +02:00
' David Spreen ' , ' Doug Hellmann ' , ' FFranci72 ' , ' Florian Richter ' , ' Frank Harper ' ,
2008-07-03 20:27:13 +02:00
' Franz Seidl ' , ' FriedBunny ' , ' Gerrit Sangel ' , ' Götz Waschk ' ,
2008-08-09 17:14:16 +02:00
' Haim Roitgrund ' , ' Heinz Erhard ' , ' Hex ' , ' Holger Bauer ' , ' Holger Leskien ' , ' Jens Thiele ' ,
2008-05-05 15:55:27 +02:00
' Jérôme Chabod ' , ' Jerry Moss ' ,
' Jessica Henline ' , ' João Trindade ' , ' Joel Calado ' , ' John Ferguson ' ,
2008-04-23 22:33:27 +02:00
' José Luis Fustel ' , ' Joseph Bleau ' , ' Julio Acuña ' , ' Junio C Hamano ' ,
2008-06-13 14:30:42 +02:00
' Jürgen Schinker ' , ' Justin Forest ' ,
2008-08-10 14:38:20 +02:00
' Konstantin Ryabitsev ' , ' Leonid Ponomarev ' , ' Marcos Hernández ' , ' Mark Alford ' , ' Markus Golser ' , ' Michael Salim ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Mika Leppinen ' , ' Mike Coulson ' , ' Mykola Nikishov ' , ' narf at inode.at ' ,
2008-03-30 10:50:34 +02:00
' Nick L. ' , ' Nicolas Quienot ' , ' Ondrej Vesely ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Ortwin Forster ' , ' Paul Elliot ' , ' Paul Rudkin ' ,
2008-07-06 23:11:55 +02:00
' Pavel Mlčoch ' , ' Peter Hoffmann ' , ' PhilF ' , ' Philippe Gouaillier ' , ' Pieter de Decker ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Preben Randhol ' , ' Rafael Proença ' , ' red26wings ' , ' Richard Voigt ' ,
2008-05-12 11:07:05 +02:00
' Robert Young ' , ' Roel Groeneveld ' ,
2008-06-15 14:28:24 +02:00
' Scott Wegner ' , ' Sebastian Krause ' , ' Seth Remington ' , ' Shane Donohoe ' , ' SPGoetze ' ,
2008-05-10 16:47:29 +02:00
' Stefan Lohmaier ' , ' Stephan Buys ' , ' Stylianos Papanastasiou ' , ' Teo Ramirez ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Thomas Matthijs ' , ' Thomas Mills Hinkle ' , ' Thomas Nilsson ' ,
2008-04-13 08:14:01 +02:00
' Tim Michelsen ' , ' Tim Preetz ' , ' Todd Zullinger ' , ' Tomas Matheson ' , ' VladDrac ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Vladimir Zemlyakov ' , ' Wilfred van Rooijen ' ,
' ' ,
' List may be incomplete - please contact me. '
]
app_copyright = ' © 2005-2008 Thomas Perl and the gPodder Team '
2008-03-03 20:38:22 +01:00
app_website = ' http://www.gpodder.org/ '
2005-11-21 19:21:25 +01:00
2007-03-18 17:11:34 +01:00
# these will be filled with pathnames in bin/gpodder
glade_dir = [ ' share ' , ' gpodder ' ]
icon_dir = [ ' share ' , ' pixmaps ' , ' gpodder.png ' ]
2007-03-25 21:55:28 +02:00
scalable_dir = [ ' share ' , ' icons ' , ' hicolor ' , ' scalable ' , ' apps ' , ' gpodder.svg ' ]
2005-11-21 19:21:25 +01:00
2006-03-29 13:33:56 +02:00
2007-08-28 00:18:01 +02:00
class GladeWidget ( SimpleGladeApp . SimpleGladeApp ) :
gpodder_main_window = None
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ]
2007-08-28 00:18:01 +02:00
def __init__ ( self , * * kwargs ) :
path = os . path . join ( glade_dir , ' %s .glade ' % app_name )
root = self . __class__ . __name__
domain = app_name
SimpleGladeApp . SimpleGladeApp . __init__ ( self , path , root , domain , * * kwargs )
2008-04-24 18:28:39 +02:00
# Set widgets to finger-friendly mode if on Maemo
for widget_name in self . finger_friendly_widgets :
self . set_finger_friendly ( getattr ( self , widget_name ) )
2007-08-28 00:18:01 +02:00
if root == ' gPodder ' :
GladeWidget . gpodder_main_window = self . gPodder
else :
# If we have a child window, set it transient for our main window
getattr ( self , root ) . set_transient_for ( GladeWidget . gpodder_main_window )
2007-11-08 20:11:57 +01:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
if hasattr ( self , ' center_on_widget ' ) :
( x , y ) = self . gpodder_main_window . get_position ( )
a = self . center_on_widget . allocation
( x , y ) = ( x + a . x , y + a . y )
( w , h ) = ( a . width , a . height )
( pw , ph ) = getattr ( self , root ) . get_size ( )
getattr ( self , root ) . move ( x + w / 2 - pw / 2 , y + h / 2 - ph / 2 )
else :
getattr ( self , root ) . set_position ( gtk . WIN_POS_CENTER_ON_PARENT )
2007-08-28 00:18:01 +02:00
2008-01-15 14:54:22 +01:00
def notification ( self , message , title = None ) :
util . idle_add ( self . show_message , message , title )
2007-11-05 13:55:36 +01:00
2007-08-28 00:18:01 +02:00
def show_message ( self , message , title = None ) :
2008-01-24 14:52:37 +01:00
if hasattr ( self , ' tray_icon ' ) and hasattr ( self , ' minimized ' ) and self . tray_icon and self . minimized :
2008-01-23 09:08:13 +01:00
if title is None :
title = ' gPodder '
2008-02-04 11:29:41 +01:00
self . tray_icon . send_notification ( message , title )
2008-01-23 09:08:13 +01:00
return
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
dlg = gtk . MessageDialog ( GladeWidget . gpodder_main_window , gtk . DIALOG_MODAL , gtk . MESSAGE_INFO , gtk . BUTTONS_OK )
if title :
dlg . set_title ( str ( title ) )
dlg . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
else :
dlg . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> ' % ( message ) )
elif gpodder . interface == gpodder . MAEMO :
dlg = hildon . Note ( ' information ' , ( GladeWidget . gpodder_main_window , message ) )
2007-08-28 00:18:01 +02:00
dlg . run ( )
dlg . destroy ( )
2008-04-24 18:28:39 +02:00
def set_finger_friendly ( self , widget ) :
"""
If we are on Maemo , we carry out the necessary
operations to turn a widget into a finger - friendly
one , depending on which type of widget it is ( i . e .
buttons will have more padding , TreeViews a thick
scrollbar , etc . . )
"""
if gpodder . interface == gpodder . MAEMO :
if isinstance ( widget , gtk . Misc ) :
widget . set_padding ( 0 , 5 )
elif isinstance ( widget , gtk . Button ) :
for child in widget . get_children ( ) :
if isinstance ( child , gtk . Alignment ) :
child . set_padding ( 10 , 10 , 5 , 5 )
else :
child . set_padding ( 10 , 10 )
elif isinstance ( widget , gtk . TreeView ) or isinstance ( widget , gtk . TextView ) :
parent = widget . get_parent ( )
if isinstance ( parent , gtk . ScrolledWindow ) :
hildon . hildon_helper_set_thumb_scrollbar ( parent , True )
elif isinstance ( widget , gtk . MenuItem ) :
for child in widget . get_children ( ) :
self . set_finger_friendly ( child )
else :
log ( ' Cannot set widget finger-friendly: %s ' , widget , sender = self )
return widget
2007-08-28 00:18:01 +02:00
def show_confirmation ( self , message , title = None ) :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
affirmative = gtk . RESPONSE_YES
dlg = gtk . MessageDialog ( GladeWidget . gpodder_main_window , gtk . DIALOG_MODAL , gtk . MESSAGE_QUESTION , gtk . BUTTONS_YES_NO )
if title :
dlg . set_title ( str ( title ) )
dlg . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
else :
dlg . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> ' % ( message ) )
elif gpodder . interface == gpodder . MAEMO :
affirmative = gtk . RESPONSE_OK
dlg = hildon . Note ( ' confirmation ' , ( GladeWidget . gpodder_main_window , message ) )
2007-08-28 00:18:01 +02:00
response = dlg . run ( )
dlg . destroy ( )
2008-04-06 02:19:03 +02:00
return response == affirmative
2007-08-28 00:18:01 +02:00
2007-10-28 15:30:11 +01:00
def show_copy_dialog ( self , src_filename , dst_filename = None , dst_directory = None , title = _ ( ' Select destination ' ) ) :
2008-02-26 16:49:59 +01:00
if dst_filename is None :
2007-10-28 15:30:11 +01:00
dst_filename = src_filename
2008-02-26 16:49:59 +01:00
if dst_directory is None :
2007-10-28 15:30:11 +01:00
dst_directory = os . path . expanduser ( ' ~ ' )
( base , extension ) = os . path . splitext ( src_filename )
if not dst_filename . endswith ( extension ) :
dst_filename + = extension
2008-05-02 17:36:43 +02:00
if gpodder . interface == gpodder . GUI :
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 )
2007-10-28 15:30:11 +01:00
2008-05-02 17:36:43 +02:00
dlg . set_do_overwrite_confirmation ( True )
2007-10-28 15:30:11 +01:00
dlg . set_current_name ( os . path . basename ( dst_filename ) )
dlg . set_current_folder ( dst_directory )
if dlg . run ( ) == gtk . RESPONSE_OK :
dst_filename = dlg . get_filename ( )
if not dst_filename . endswith ( extension ) :
dst_filename + = extension
log ( ' Copying %s => %s ' , src_filename , dst_filename , sender = self )
try :
shutil . copyfile ( src_filename , dst_filename )
except :
2008-04-22 22:24:19 +02:00
log ( ' Error copying file. ' , sender = self , traceback = True )
2007-10-28 15:30:11 +01:00
dlg . destroy ( )
2006-03-31 18:20:18 +02:00
2005-11-21 19:21:25 +01:00
2007-08-28 00:18:01 +02:00
class gPodder ( GladeWidget ) :
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ' btnUpdateFeeds ' , ' btnCancelFeedUpdate ' , ' treeAvailable ' , ' label2 ' , ' labelDownloads ' ]
2008-05-02 17:36:43 +02:00
ENTER_URL_TEXT = _ ( ' Enter podcast URL... ' )
2008-04-24 18:28:39 +02:00
2005-11-21 19:21:25 +01:00
def new ( self ) :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Maemo-specific changes to the UI
global scalable_dir
scalable_dir = scalable_dir . replace ( ' .svg ' , ' .png ' )
self . app = hildon . Program ( )
gtk . set_application_name ( ' gPodder ' )
self . window = hildon . Window ( )
self . window . connect ( ' delete-event ' , self . on_gPodder_delete_event )
self . window . connect ( ' window-state-event ' , self . window_state_event )
self . window . connect ( ' key-press-event ' , self . on_key_press )
2008-08-30 19:23:04 +02:00
self . itemUpdateChannel . show ( )
self . UpdateChannelSeparator . show ( )
2008-04-06 02:19:03 +02:00
# Give toolbar to the hildon window
self . toolbar . parent . remove ( self . toolbar )
self . toolbar . set_style ( gtk . TOOLBAR_ICONS )
self . window . add_toolbar ( self . toolbar )
self . app . add_window ( self . window )
self . vMain . reparent ( self . window )
self . gPodder = self . window
# Reparent the main menu
menu = gtk . Menu ( )
for child in self . mainMenu . get_children ( ) :
child . reparent ( menu )
2008-04-11 10:29:13 +02:00
self . itemQuit . reparent ( menu )
2008-04-06 02:19:03 +02:00
self . window . set_menu ( menu )
self . mainMenu . destroy ( )
2008-04-22 20:34:41 +02:00
self . window . show ( )
2008-04-06 02:19:03 +02:00
# do some widget hiding
self . toolbar . remove ( self . toolTransfer )
self . itemTransferSelected . hide_all ( )
2008-05-02 17:36:43 +02:00
self . item_show_url_entry . hide_all ( )
2008-05-02 17:45:45 +02:00
self . item_email_subscriptions . hide_all ( )
2008-04-22 20:34:41 +02:00
# Feed cache update button
2008-04-24 19:42:57 +02:00
self . label120 . set_text ( _ ( ' Update ' ) )
2008-04-24 18:28:39 +02:00
# get screen real estate
self . hboxContainer . set_border_width ( 0 )
2008-05-02 17:36:43 +02:00
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 )
2008-04-06 02:19:03 +02:00
2007-08-28 00:18:01 +02:00
self . uar = None
2008-03-20 11:24:26 +01:00
self . tray_icon = None
2007-08-28 00:18:01 +02:00
2008-04-06 02:19:03 +02:00
self . fullscreen = False
2008-01-09 23:46:17 +01:00
self . minimized = False
self . gPodder . connect ( ' window-state-event ' , self . window_state_event )
2008-02-04 11:29:41 +01:00
self . already_notified_new_episodes = [ ]
2008-04-15 20:59:22 +02:00
self . show_hide_tray_icon ( )
2008-05-02 17:36:43 +02:00
self . no_episode_selected . set_sensitive ( False )
2008-02-06 10:29:56 +01:00
self . itemShowToolbar . set_active ( gl . config . show_toolbar )
self . itemShowDescription . set_active ( gl . config . episode_list_descriptions )
2008-05-02 17:36:43 +02:00
self . item_show_url_entry . set_active ( gl . config . show_podcast_url_entry )
2008-01-09 23:46:17 +01:00
2007-11-02 17:37:14 +01:00
gl . config . connect_gtk_window ( self . gPodder )
gl . config . connect_gtk_paned ( ' paned_position ' , self . channelPaned )
2008-04-17 17:45:29 +02:00
gl . config . connect_gtk_spinbutton ( ' max_downloads ' , self . spinMaxDownloads )
gl . config . connect_gtk_togglebutton ( ' max_downloads_enabled ' , self . cbMaxDownloads )
gl . config . connect_gtk_spinbutton ( ' limit_rate_value ' , self . spinLimitDownloads )
gl . config . connect_gtk_togglebutton ( ' limit_rate ' , self . cbLimitDownloads )
# Make sure we free/close the download queue when we
# update the "max downloads" spin button
changed_cb = lambda spinbutton : services . download_status_manager . update_max_downloads ( )
self . spinMaxDownloads . connect ( ' value-changed ' , changed_cb )
2008-04-06 02:19:03 +02:00
self . default_title = None
2008-08-03 22:24:41 +02:00
if app_version . rfind ( ' git ' ) != - 1 :
2008-04-06 02:19:03 +02:00
self . set_title ( ' gPodder %s ' % app_version )
else :
2008-08-11 18:00:10 +02:00
title = self . gPodder . get_title ( )
if title is not None :
self . set_title ( title )
else :
self . set_title ( _ ( ' gPodder ' ) )
2007-09-02 15:02:28 +02:00
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
gtk . about_dialog_set_url_hook ( lambda dlg , link , data : util . open_website ( link ) , None )
2007-03-07 15:53:05 +01:00
2007-07-05 23:07:16 +02:00
# cell renderers for channel tree
2008-05-02 17:36:43 +02:00
namecolumn = gtk . TreeViewColumn ( _ ( ' Podcast ' ) )
2007-07-05 23:07:16 +02:00
iconcell = gtk . CellRendererPixbuf ( )
namecolumn . pack_start ( iconcell , False )
2007-11-27 23:04:15 +01:00
namecolumn . add_attribute ( iconcell , ' pixbuf ' , 5 )
2008-04-24 19:42:57 +02:00
self . cell_channel_icon = iconcell
2007-07-05 23:07:16 +02:00
namecell = gtk . CellRendererText ( )
2008-08-30 19:23:04 +02:00
namecell . set_property ( ' foreground-set ' , True )
2007-07-05 23:07:16 +02:00
namecell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
namecolumn . pack_start ( namecell , True )
2007-11-27 23:04:15 +01:00
namecolumn . add_attribute ( namecell , ' markup ' , 2 )
2008-08-30 19:23:04 +02:00
namecolumn . add_attribute ( namecell , ' foreground ' , 8 )
2007-07-05 23:07:16 +02:00
2007-11-27 23:04:15 +01:00
iconcell = gtk . CellRendererPixbuf ( )
2008-06-19 09:47:03 +02:00
iconcell . set_property ( ' xalign ' , 1.0 )
2007-11-27 23:04:15 +01:00
namecolumn . pack_start ( iconcell , False )
namecolumn . add_attribute ( iconcell , ' pixbuf ' , 3 )
2008-06-19 09:31:58 +02:00
namecolumn . add_attribute ( iconcell , ' visible ' , 7 )
2008-04-24 19:42:57 +02:00
self . cell_channel_pill = iconcell
2007-07-05 23:07:16 +02:00
self . treeChannels . append_column ( namecolumn )
2008-04-06 02:19:03 +02:00
self . treeChannels . set_headers_visible ( False )
2005-11-21 19:21:25 +01:00
2007-04-03 08:27:46 +02:00
# enable alternating colors hint
self . treeAvailable . set_rules_hint ( True )
2007-09-19 17:04:42 +02:00
self . treeChannels . set_rules_hint ( True )
2007-04-03 08:27:46 +02:00
2007-11-14 21:57:31 +01:00
# connect to tooltip signals
2007-11-17 18:43:23 +01:00
try :
self . treeChannels . set_property ( ' has-tooltip ' , True )
self . treeChannels . connect ( ' query-tooltip ' , self . treeview_channels_query_tooltip )
2008-06-15 14:28:24 +02:00
self . treeAvailable . set_property ( ' has-tooltip ' , True )
self . treeAvailable . connect ( ' query-tooltip ' , self . treeview_episodes_query_tooltip )
2007-11-17 18:43:23 +01:00
except :
2008-06-15 14:28:24 +02:00
log ( ' I cannot set has-tooltip/query-tooltip (need at least PyGTK 2.12) ' , sender = self )
2007-11-14 21:57:31 +01:00
self . last_tooltip_channel = None
2008-06-15 14:28:24 +02:00
self . last_tooltip_episode = None
self . podcast_list_can_tooltip = True
self . episode_list_can_tooltip = True
2007-11-14 21:57:31 +01:00
2007-08-26 17:20:46 +02:00
# Add our context menu to treeAvailable
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
self . treeAvailable . connect ( ' button-release-event ' , self . treeview_button_pressed )
else :
self . treeAvailable . connect ( ' button-press-event ' , self . treeview_button_pressed )
2007-09-15 16:29:37 +02:00
self . treeChannels . connect ( ' button-press-event ' , self . treeview_channels_button_pressed )
2007-08-26 17:20:46 +02:00
2007-04-03 08:27:46 +02:00
iconcell = gtk . CellRendererPixbuf ( )
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
status_column_label = ' '
else :
status_column_label = _ ( ' Status ' )
iconcolumn = gtk . TreeViewColumn ( status_column_label , iconcell , pixbuf = 4 )
2007-04-03 13:21:12 +02:00
2005-11-22 14:30:28 +01:00
namecell = gtk . CellRendererText ( )
2008-02-06 10:29:56 +01:00
namecell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
namecolumn = gtk . TreeViewColumn ( _ ( " Episode " ) , namecell , markup = 6 )
namecolumn . set_sizing ( gtk . TREE_VIEW_COLUMN_AUTOSIZE )
namecolumn . set_expand ( True )
2005-11-22 14:30:28 +01:00
sizecell = gtk . CellRendererText ( )
2006-03-31 15:00:54 +02:00
sizecolumn = gtk . TreeViewColumn ( _ ( " Size " ) , sizecell , text = 2 )
2006-04-10 18:46:50 +02:00
releasecell = gtk . CellRendererText ( )
releasecolumn = gtk . TreeViewColumn ( _ ( " Released " ) , releasecell , text = 5 )
2005-11-22 14:30:28 +01:00
2008-02-06 10:29:56 +01:00
for itemcolumn in ( iconcolumn , namecolumn , sizecolumn , releasecolumn ) :
itemcolumn . set_reorderable ( True )
self . treeAvailable . append_column ( itemcolumn )
2006-06-22 23:41:32 +02:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Due to screen space contraints, we
# hide these columns here by default
self . column_size = sizecolumn
self . column_released = releasecolumn
self . column_released . set_visible ( False )
self . column_size . set_visible ( False )
2007-04-03 08:27:46 +02:00
# enable search in treeavailable
self . treeAvailable . set_search_equal_func ( self . treeAvailable_search_equal )
2006-06-22 23:41:32 +02:00
# enable multiple selection support
self . treeAvailable . get_selection ( ) . set_mode ( gtk . SELECTION_MULTIPLE )
2006-07-06 20:56:43 +02:00
self . treeDownloads . get_selection ( ) . set_mode ( gtk . SELECTION_MULTIPLE )
2006-02-04 18:29:17 +01:00
2005-11-22 23:26:51 +01:00
# columns and renderers for "download progress" tab
2005-11-22 23:04:58 +01:00
episodecell = gtk . CellRendererText ( )
2008-04-06 02:19:03 +02:00
episodecell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
2006-03-31 15:00:54 +02:00
episodecolumn = gtk . TreeViewColumn ( _ ( " Episode " ) , episodecell , text = 0 )
2008-04-06 02:19:03 +02:00
episodecolumn . set_sizing ( gtk . TREE_VIEW_COLUMN_AUTOSIZE )
episodecolumn . set_expand ( True )
2005-11-22 23:26:51 +01:00
2005-11-22 23:04:58 +01:00
speedcell = gtk . CellRendererText ( )
2006-03-31 15:00:54 +02:00
speedcolumn = gtk . TreeViewColumn ( _ ( " Speed " ) , speedcell , text = 1 )
2005-11-22 23:26:51 +01:00
progresscell = gtk . CellRendererProgress ( )
2006-03-31 15:00:54 +02:00
progresscolumn = gtk . TreeViewColumn ( _ ( " Progress " ) , progresscell , value = 2 )
2008-04-06 02:19:03 +02:00
progresscolumn . set_expand ( True )
2005-11-22 23:26:51 +01:00
for itemcolumn in ( episodecolumn , speedcolumn , progresscolumn ) :
2005-11-22 23:04:58 +01:00
self . treeDownloads . append_column ( itemcolumn )
2007-08-24 16:49:41 +02:00
2008-05-10 13:35:06 +02:00
# After we've set up most of the window, show it :)
if not gpodder . interface == gpodder . MAEMO :
self . gPodder . show ( )
2008-06-08 20:10:09 +02:00
if self . tray_icon :
if gl . config . start_iconified :
self . iconify_main_window ( )
elif gl . config . minimize_to_tray :
self . tray_icon . set_visible ( False )
2007-08-24 16:49:41 +02:00
services . download_status_manager . register ( ' list-changed ' , self . download_status_updated )
services . download_status_manager . register ( ' progress-changed ' , self . download_progress_updated )
2008-06-14 13:43:53 +02:00
services . cover_downloader . register ( ' cover-available ' , self . cover_download_finished )
services . cover_downloader . register ( ' cover-removed ' , self . cover_file_removed )
self . cover_cache = { }
2007-08-24 16:49:41 +02:00
self . treeDownloads . set_model ( services . download_status_manager . tree_model )
2005-11-22 23:26:51 +01:00
2005-11-22 14:23:50 +01:00
#Add Drag and Drop Support
2007-09-19 17:04:42 +02:00
flags = gtk . DEST_DEFAULT_ALL
targets = [ ( ' text/plain ' , 0 , 2 ) , ( ' STRING ' , 0 , 3 ) , ( ' TEXT ' , 0 , 4 ) ]
actions = gtk . gdk . ACTION_DEFAULT | gtk . gdk . ACTION_COPY
self . treeChannels . drag_dest_set ( flags , targets , actions )
self . treeChannels . connect ( ' drag_data_received ' , self . drag_data_received )
2006-04-04 00:27:57 +02:00
2007-03-10 16:57:56 +01:00
# Subscribed channels
self . active_channel = None
2008-06-30 03:10:18 +02:00
self . channels = load_channels ( )
2008-07-11 19:13:04 +02:00
self . update_podcasts_tab ( )
2006-12-03 19:11:14 +01:00
2008-01-21 09:57:03 +01:00
# load list of user applications for audio playback
2008-03-20 11:24:26 +01:00
self . user_apps_reader = UserAppsReader ( [ ' audio ' , ' video ' ] )
Thread ( target = self . read_apps ) . start ( )
2006-12-09 03:04:32 +01:00
2006-12-03 19:11:14 +01:00
# Clean up old, orphaned download files
2007-02-03 11:48:32 +01:00
gl . clean_up_downloads ( delete_partial = True )
2007-05-25 12:06:43 +02:00
2007-11-08 20:11:57 +01:00
# Set the "Device" menu item for the first time
self . update_item_device ( )
2008-08-30 19:23:04 +02:00
# Set up default channel colors
self . channel_colors = {
' default ' : None ,
' updating ' : gl . config . color_updating_feeds ,
' parse_error ' : ' #ff0000 ' ,
}
2007-05-25 12:06:43 +02:00
# Now, update the feed cache, when everything's in place
2008-08-30 19:23:04 +02:00
self . btnUpdateFeeds . show_all ( )
self . updated_feeds = 0
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = False
2008-04-22 20:34:41 +02:00
self . feed_cache_update_cancelled = False
2008-01-09 23:46:17 +01:00
self . update_feed_cache ( force_update = gl . config . update_on_startup )
# Start the auto-update procedure
self . auto_update_procedure ( first_run = True )
2005-11-21 19:21:25 +01:00
2007-12-12 20:57:50 +01:00
# Delete old episodes if the user wishes to
if gl . config . auto_remove_old_episodes :
old_episodes = self . get_old_episodes ( )
if len ( old_episodes ) > 0 :
self . delete_episode_list ( old_episodes , confirm = False )
self . updateComboBox ( )
2008-05-02 17:36:43 +02:00
# 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 )
2008-04-24 19:42:57 +02:00
def on_tree_channels_resize ( self , widget , allocation ) :
2008-05-16 10:03:13 +02:00
if not gl . config . podcast_sidebar_save_space :
return
2008-04-24 19:42:57 +02:00
window_allocation = self . gPodder . get_allocation ( )
percentage = 100. * float ( allocation . width ) / float ( window_allocation . width )
2008-04-29 20:22:55 +02:00
if hasattr ( self , ' cell_channel_icon ' ) :
2008-05-02 17:36:43 +02:00
self . cell_channel_icon . set_property ( ' visible ' , bool ( percentage > 22. ) )
2008-04-29 20:22:55 +02:00
if hasattr ( self , ' cell_channel_pill ' ) :
2008-05-02 17:36:43 +02:00
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 ' :
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 ( )
2008-04-24 19:42:57 +02:00
2008-03-20 11:24:26 +01:00
def read_apps ( self ) :
time . sleep ( 3 ) # give other parts of gpodder a chance to start up
self . user_apps_reader . read ( )
util . idle_add ( self . user_apps_reader . get_applications_as_model , ' audio ' , False )
util . idle_add ( self . user_apps_reader . get_applications_as_model , ' video ' , False )
2007-12-12 20:57:50 +01:00
2008-06-15 14:28:24 +02:00
def treeview_episodes_query_tooltip ( self , treeview , x , y , keyboard_tooltip , tooltip ) :
# With get_bin_window, we get the window that contains the rows without
# the header. The Y coordinate of this window will be the height of the
# treeview header. This is the amount we have to subtract from the
# event's Y coordinate to get the coordinate to pass to get_path_at_pos
( x_bin , y_bin ) = treeview . get_bin_window ( ) . get_position ( )
y - = x_bin
y - = y_bin
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
if not self . episode_list_can_tooltip :
self . last_tooltip_episode = None
return False
if path is not None :
model = treeview . get_model ( )
iter = model . get_iter ( path )
url = model . get_value ( iter , 0 )
description = model . get_value ( iter , 7 )
if self . last_tooltip_episode is not None and self . last_tooltip_episode != url :
self . last_tooltip_episode = None
return False
self . last_tooltip_episode = url
tooltip . set_text ( description )
return True
self . last_tooltip_episode = None
return False
def podcast_list_allow_tooltips ( self ) :
self . podcast_list_can_tooltip = True
def episode_list_allow_tooltips ( self ) :
self . episode_list_can_tooltip = True
2007-11-14 21:57:31 +01:00
def treeview_channels_query_tooltip ( self , treeview , x , y , keyboard_tooltip , tooltip ) :
2008-04-06 17:25:17 +02:00
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
2007-11-14 21:57:31 +01:00
2008-06-15 14:28:24 +02:00
if not self . podcast_list_can_tooltip :
self . last_tooltip_channel = None
return False
2007-11-14 21:57:31 +01:00
if path is not None :
model = treeview . get_model ( )
iter = model . get_iter ( path )
url = model . get_value ( iter , 0 )
for channel in self . channels :
if channel . url == url :
if self . last_tooltip_channel is not None and self . last_tooltip_channel != channel :
self . last_tooltip_channel = None
return False
self . last_tooltip_channel = channel
2008-06-05 18:17:09 +02:00
channel . request_save_dir_size ( )
diskspace_str = gl . format_filesize ( channel . save_dir_size , 0 )
2008-03-29 16:33:18 +01:00
error_str = model . get_value ( iter , 6 )
if error_str :
error_str = _ ( ' Feedparser error: %s ' ) % saxutils . escape ( error_str . strip ( ) )
2008-06-05 18:17:09 +02:00
error_str = ' <span foreground= " #ff0000 " > %s </span> ' % error_str
table = gtk . Table ( rows = 3 , columns = 3 )
table . set_row_spacings ( 5 )
table . set_col_spacings ( 5 )
table . set_border_width ( 5 )
heading = gtk . Label ( )
heading . set_alignment ( 0 , 1 )
heading . set_markup ( ' <b><big> %s </big></b> \n <small> %s </small> ' % ( saxutils . escape ( channel . title ) , saxutils . escape ( channel . url ) ) )
table . attach ( heading , 0 , 1 , 0 , 1 )
size_info = gtk . Label ( )
size_info . set_alignment ( 1 , 1 )
size_info . set_justify ( gtk . JUSTIFY_RIGHT )
size_info . set_markup ( ' <b> %s </b> \n <small> %s </small> ' % ( diskspace_str , _ ( ' disk usage ' ) ) )
table . attach ( size_info , 2 , 3 , 0 , 1 )
table . attach ( gtk . HSeparator ( ) , 0 , 3 , 1 , 2 )
if len ( channel . description ) < 500 :
description = channel . description
else :
pos = channel . description . find ( ' \n \n ' )
if pos == - 1 or pos > 500 :
description = channel . description [ : 498 ] + ' [...] '
else :
description = channel . description [ : pos ]
description = gtk . Label ( description )
if error_str :
description . set_markup ( error_str )
description . set_alignment ( 0 , 0 )
description . set_line_wrap ( True )
table . attach ( description , 0 , 3 , 2 , 3 )
table . show_all ( )
tooltip . set_custom ( table )
2008-05-14 15:38:06 +02:00
2007-11-14 21:57:31 +01:00
return True
self . last_tooltip_channel = None
return False
2008-03-10 16:50:12 +01:00
def update_m3u_playlist_clicked ( self , widget ) :
self . active_channel . update_m3u_playlist ( )
self . show_message ( _ ( ' Updated M3U playlist in download folder. ' ) , _ ( ' Updated playlist ' ) )
2007-09-15 16:29:37 +02:00
def treeview_channels_button_pressed ( self , treeview , event ) :
2008-04-06 02:19:03 +02:00
global WEB_BROWSER_ICON
2007-09-15 16:29:37 +02:00
if event . button == 3 :
( x , y ) = ( int ( event . x ) , int ( event . y ) )
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
paths = [ ]
# Did the user right-click into a selection?
selection = treeview . get_selection ( )
if selection . count_selected_rows ( ) and path :
( model , paths ) = selection . get_selected_rows ( )
if path not in paths :
# We have right-clicked, but not into the
# selection, assume we don't want to operate
# on the selection
paths = [ ]
# No selection or right click not in selection:
# Select the single item where we clicked
if not len ( paths ) and path :
treeview . grab_focus ( )
treeview . set_cursor ( path , column , 0 )
( model , paths ) = ( treeview . get_model ( ) , [ path ] )
# We did not find a selection, and the user didn't
# click on an item to select -- don't show the menu
if not len ( paths ) :
return True
menu = gtk . Menu ( )
2007-11-02 07:53:30 +01:00
item = gtk . ImageMenuItem ( _ ( ' Open download folder ' ) )
item . set_image ( gtk . image_new_from_icon_name ( ' folder-open ' , gtk . ICON_SIZE_MENU ) )
2008-03-02 14:04:37 +01:00
item . connect ( ' activate ' , lambda x : util . gui_open ( self . active_channel . save_dir ) )
2007-11-02 07:53:30 +01:00
menu . append ( item )
2008-08-30 19:23:04 +02:00
item = gtk . ImageMenuItem ( _ ( ' Update Feed ' ) )
item . set_image ( gtk . image_new_from_icon_name ( ' gtk-refresh ' , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . on_itemUpdateChannel_activate )
item . set_sensitive ( not self . updating_feed_cache )
menu . append ( item )
2008-03-10 16:50:12 +01:00
if gl . config . create_m3u_playlists :
item = gtk . ImageMenuItem ( _ ( ' Update M3U playlist ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_REFRESH , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . update_m3u_playlist_clicked )
menu . append ( item )
2008-03-22 20:43:20 +01:00
if self . active_channel . link :
item = gtk . ImageMenuItem ( _ ( ' Visit website ' ) )
2008-04-06 02:19:03 +02:00
item . set_image ( gtk . image_new_from_icon_name ( WEB_BROWSER_ICON , gtk . ICON_SIZE_MENU ) )
2008-03-22 20:43:20 +01:00
item . connect ( ' activate ' , lambda w : util . open_website ( self . active_channel . link ) )
menu . append ( item )
2007-11-02 07:53:30 +01:00
menu . append ( gtk . SeparatorMenuItem ( ) )
2008-03-22 20:43:20 +01:00
item = gtk . ImageMenuItem ( gtk . STOCK_EDIT )
2007-09-15 16:29:37 +02:00
item . connect ( ' activate ' , self . on_itemEditChannel_activate )
menu . append ( item )
2008-03-22 20:43:20 +01:00
item = gtk . ImageMenuItem ( gtk . STOCK_DELETE )
2007-09-15 16:29:37 +02:00
item . connect ( ' activate ' , self . on_itemRemoveChannel_activate )
menu . append ( item )
menu . show_all ( )
2008-06-15 14:28:24 +02:00
# Disable tooltips while we are showing the menu, so
# the tooltip will not appear over the menu
self . podcast_list_can_tooltip = False
menu . connect ( ' deactivate ' , lambda menushell : self . podcast_list_allow_tooltips ( ) )
2007-09-15 16:29:37 +02:00
menu . popup ( None , None , None , event . button , event . time )
return True
2008-04-11 10:29:13 +02:00
def on_itemClose_activate ( self , widget ) :
if self . tray_icon is not None :
2008-05-02 17:36:43 +02:00
if gpodder . interface == gpodder . MAEMO :
self . gPodder . set_property ( ' visible ' , False )
else :
self . iconify_main_window ( )
2008-04-11 10:29:13 +02:00
else :
self . on_gPodder_delete_event ( widget )
2008-06-14 13:43:53 +02:00
def cover_file_removed ( self , channel_url ) :
"""
The Cover Downloader calls this when a previously -
available cover has been removed from the disk . We
have to update our cache to reflect this change .
"""
( COLUMN_URL , COLUMN_PIXBUF ) = ( 0 , 5 )
for row in self . treeChannels . get_model ( ) :
if row [ COLUMN_URL ] == channel_url :
row [ COLUMN_PIXBUF ] = None
2008-06-14 15:57:34 +02:00
key = ( channel_url , gl . config . podcast_list_icon_size , \
gl . config . podcast_list_icon_size )
if key in self . cover_cache :
del self . cover_cache [ key ]
2008-06-14 13:43:53 +02:00
def cover_download_finished ( self , channel_url , pixbuf ) :
"""
The Cover Downloader calls this when it has finished
downloading ( or registering , if already downloaded )
a new channel cover , which is ready for displaying .
"""
if pixbuf is not None :
( COLUMN_URL , COLUMN_PIXBUF ) = ( 0 , 5 )
for row in self . treeChannels . get_model ( ) :
if row [ COLUMN_URL ] == channel_url and row [ COLUMN_PIXBUF ] is None :
new_pixbuf = util . resize_pixbuf_keep_ratio ( pixbuf , gl . config . podcast_list_icon_size , gl . config . podcast_list_icon_size , channel_url , self . cover_cache )
row [ COLUMN_PIXBUF ] = new_pixbuf or pixbuf
2007-10-28 15:30:11 +01:00
def save_episode_as_file ( self , url , * args ) :
episode = self . active_channel . find_episode ( url )
self . show_copy_dialog ( src_filename = episode . local_filename ( ) , dst_filename = episode . sync_filename ( ) )
2008-01-21 10:52:09 +01:00
def copy_episode_bluetooth ( self , url , * args ) :
episode = self . active_channel . find_episode ( url )
filename = episode . local_filename ( )
if gl . config . bluetooth_ask_always :
device = None
else :
device = gl . config . bluetooth_device_address
2008-07-09 03:19:14 +02:00
destfile = os . path . join ( gl . tempdir , util . sanitize_filename ( episode . sync_filename ( ) ) )
2008-02-27 09:42:38 +01:00
( base , ext ) = os . path . splitext ( filename )
if not destfile . endswith ( ext ) :
destfile + = ext
2008-01-21 10:52:09 +01:00
2008-02-27 09:42:38 +01:00
if gl . config . bluetooth_use_converter :
2008-01-21 10:52:09 +01:00
title = _ ( ' Converting file ' )
message = _ ( ' Please wait while gPodder converts your media file for bluetooth file transfer. ' )
dlg = gtk . MessageDialog ( self . gPodder , gtk . DIALOG_MODAL , gtk . MESSAGE_INFO , gtk . BUTTONS_NONE )
dlg . set_title ( title )
dlg . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
dlg . show_all ( )
2008-02-27 09:42:38 +01:00
else :
dlg = None
2008-01-21 10:52:09 +01:00
2008-02-27 09:42:38 +01:00
def convert_and_send_thread ( filename , destfile , device , dialog , notify ) :
if gl . config . bluetooth_use_converter :
2008-01-21 10:52:09 +01:00
p = subprocess . Popen ( [ gl . config . bluetooth_converter , filename , destfile ] , stdout = sys . stdout , stderr = sys . stderr )
result = p . wait ( )
2008-02-27 09:42:38 +01:00
if dialog is not None :
dialog . destroy ( )
else :
try :
shutil . copyfile ( filename , destfile )
result = 0
except :
2008-03-19 15:43:22 +01:00
log ( ' Cannot copy " %s " to " %s " . ' , filename , destfile , sender = self )
2008-02-27 09:42:38 +01:00
result = 1
if result == 0 or not os . path . exists ( destfile ) :
util . bluetooth_send_file ( destfile , device )
else :
notify ( _ ( ' Error converting file. ' ) , _ ( ' Bluetooth file transfer ' ) )
util . delete_file ( destfile )
Thread ( target = convert_and_send_thread , args = [ filename , destfile , device , dlg , self . notification ] ) . start ( )
2008-01-21 10:52:09 +01:00
2007-08-26 17:20:46 +02:00
def treeview_button_pressed ( self , treeview , event ) :
2008-04-06 02:19:03 +02:00
global WEB_BROWSER_ICON
# Use right-click for the Desktop version and left-click for Maemo
if ( event . button == 1 and gpodder . interface == gpodder . MAEMO ) or \
( event . button == 3 and gpodder . interface == gpodder . GUI ) :
2007-08-26 17:20:46 +02:00
( x , y ) = ( int ( event . x ) , int ( event . y ) )
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
paths = [ ]
# Did the user right-click into a selection?
selection = self . treeAvailable . get_selection ( )
if selection . count_selected_rows ( ) and path :
( model , paths ) = selection . get_selected_rows ( )
if path not in paths :
# We have right-clicked, but not into the
# selection, assume we don't want to operate
# on the selection
paths = [ ]
# No selection or right click not in selection:
# Select the single item where we clicked
if not len ( paths ) and path :
treeview . grab_focus ( )
treeview . set_cursor ( path , column , 0 )
( model , paths ) = ( treeview . get_model ( ) , [ path ] )
# We did not find a selection, and the user didn't
# click on an item to select -- don't show the menu
if not len ( paths ) :
return True
2007-08-30 20:54:18 +02:00
first_url = model . get_value ( model . get_iter ( paths [ 0 ] ) , 0 )
2008-06-30 03:10:18 +02:00
episode = db . load_episode ( first_url )
2007-08-30 20:54:18 +02:00
2007-08-26 17:20:46 +02:00
menu = gtk . Menu ( )
2008-08-04 23:26:51 +02:00
( can_play , can_download , can_transfer , can_cancel , can_delete , open_instead_of_play ) = self . play_or_download ( )
2007-08-26 17:20:46 +02:00
if can_play :
2008-08-04 23:26:51 +02:00
if open_instead_of_play :
item = gtk . ImageMenuItem ( gtk . STOCK_OPEN )
else :
item = gtk . ImageMenuItem ( gtk . STOCK_MEDIA_PLAY )
2007-08-26 17:20:46 +02:00
item . connect ( ' activate ' , lambda w : self . on_treeAvailable_row_activated ( self . toolPlay ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-12-12 19:50:52 +01:00
2008-06-30 03:10:18 +02:00
if not episode [ ' is_locked ' ] and can_delete :
item = gtk . ImageMenuItem ( gtk . STOCK_DELETE )
item . connect ( ' activate ' , self . on_btnDownloadedDelete_clicked )
menu . append ( self . set_finger_friendly ( item ) )
2008-03-19 18:07:59 +01:00
if can_cancel :
item = gtk . ImageMenuItem ( _ ( ' Cancel download ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_STOP , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_treeDownloads_row_activated ( self . toolCancel ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-26 17:20:46 +02:00
if can_download :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Download ' ) )
2007-08-26 17:20:46 +02:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_GO_DOWN , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_treeAvailable_row_activated ( self . toolDownload ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-06-30 03:10:18 +02:00
if episode [ ' state ' ] == db . STATE_NORMAL and not episode [ ' is_played ' ] : # can_download:
item = gtk . ImageMenuItem ( _ ( ' Do not download ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DELETE , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . mark_selected_episodes_old ( ) )
menu . append ( self . set_finger_friendly ( item ) )
elif episode [ ' state ' ] != db . STATE_NORMAL and can_download :
item = gtk . ImageMenuItem ( _ ( ' Mark as new ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_ABOUT , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . mark_selected_episodes_new ( ) )
menu . append ( self . set_finger_friendly ( item ) )
2007-08-30 20:54:18 +02:00
2008-03-19 18:07:59 +01:00
if can_play :
menu . append ( gtk . SeparatorMenuItem ( ) )
item = gtk . ImageMenuItem ( _ ( ' Save to disk ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_SAVE_AS , gtk . ICON_SIZE_MENU ) )
2008-07-15 09:56:40 +02:00
item . connect ( ' activate ' , lambda w : self . for_each_selected_episode_url ( self . save_episode_as_file ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-03-19 18:07:59 +01:00
if gl . config . bluetooth_enabled :
item = gtk . ImageMenuItem ( _ ( ' Send via bluetooth ' ) )
item . set_image ( gtk . image_new_from_icon_name ( ' bluetooth ' , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . copy_episode_bluetooth ( episode_url ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-03-19 18:07:59 +01:00
if can_transfer :
item = gtk . ImageMenuItem ( _ ( ' Transfer to %s ' ) % gl . get_device_name ( ) )
item . set_image ( gtk . image_new_from_icon_name ( ' multimedia-player ' , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_treeAvailable_row_activated ( self . toolTransfer ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-26 17:20:46 +02:00
2007-08-30 20:54:18 +02:00
if can_play :
menu . append ( gtk . SeparatorMenuItem ( ) )
2008-06-30 03:10:18 +02:00
is_played = episode [ ' is_played ' ]
2007-08-30 20:54:18 +02:00
if is_played :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Mark as unplayed ' ) )
2007-08-30 20:54:18 +02:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_CANCEL , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_item_toggle_played_activate ( w , False , False ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-30 20:54:18 +02:00
else :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Mark as played ' ) )
2007-08-30 20:54:18 +02:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_APPLY , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_item_toggle_played_activate ( w , False , True ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-30 20:54:18 +02:00
2008-06-30 03:10:18 +02:00
is_locked = episode [ ' is_locked ' ]
2007-12-12 19:50:52 +01:00
if is_locked :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Allow deletion ' ) )
2007-12-12 19:50:52 +01:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . on_item_toggle_lock_activate )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-12-12 19:50:52 +01:00
else :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Prohibit deletion ' ) )
2007-12-12 19:50:52 +01:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . on_item_toggle_lock_activate )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-03-19 18:07:59 +01:00
if len ( paths ) == 1 :
menu . append ( gtk . SeparatorMenuItem ( ) )
# Single item, add episode information menu item
episode_url = model . get_value ( model . get_iter ( paths [ 0 ] ) , 0 )
item = gtk . ImageMenuItem ( _ ( ' Episode details ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_INFO , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_treeAvailable_row_activated ( self . treeAvailable ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-03-22 20:43:20 +01:00
episode = self . active_channel . find_episode ( episode_url )
# If we have it, also add episode website link
if episode and episode . link and episode . link != episode . url :
item = gtk . ImageMenuItem ( _ ( ' Visit website ' ) )
2008-04-06 02:19:03 +02:00
item . set_image ( gtk . image_new_from_icon_name ( WEB_BROWSER_ICON , gtk . ICON_SIZE_MENU ) )
2008-03-22 20:43:20 +01:00
item . connect ( ' activate ' , lambda w : util . open_website ( episode . link ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Because we open the popup on left-click for Maemo,
# we also include a non-action to close the menu
menu . append ( gtk . SeparatorMenuItem ( ) )
item = gtk . ImageMenuItem ( _ ( ' Close this menu ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_CLOSE , gtk . ICON_SIZE_MENU ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-26 17:20:46 +02:00
menu . show_all ( )
2008-06-15 14:28:24 +02:00
# Disable tooltips while we are showing the menu, so
# the tooltip will not appear over the menu
self . episode_list_can_tooltip = False
menu . connect ( ' deactivate ' , lambda menushell : self . episode_list_allow_tooltips ( ) )
2007-08-26 17:20:46 +02:00
menu . popup ( None , None , None , event . button , event . time )
return True
2008-04-06 02:19:03 +02:00
def set_title ( self , new_title ) :
self . default_title = new_title
self . gPodder . set_title ( new_title )
2007-08-24 16:49:41 +02:00
def download_progress_updated ( self , count , percentage ) :
title = [ self . default_title ]
2008-08-04 14:17:01 +02:00
total_speed = gl . format_filesize ( services . download_status_manager . total_speed ( ) )
2007-08-24 16:49:41 +02:00
if count == 1 :
title . append ( _ ( ' downloading one file ' ) )
elif count > 1 :
title . append ( _ ( ' downloading %d files ' ) % count )
if len ( title ) == 2 :
2008-08-04 14:17:01 +02:00
title [ 1 ] = ' ' . join ( [ title [ 1 ] , ' ( %d %% , %s /s) ' % ( percentage , total_speed ) ] )
2007-08-24 16:49:41 +02:00
self . gPodder . set_title ( ' - ' . join ( title ) )
2008-06-15 14:46:34 +02:00
# Have all the downloads completed?
# If so execute user command if defined, else do nothing
if count == 0 :
if len ( gl . config . cmd_all_downloads_complete ) > 0 :
Thread ( target = gl . ext_command_thread , args = ( self . notification , gl . config . cmd_all_downloads_complete ) ) . start ( )
2008-08-05 14:56:58 +02:00
def playback_episode ( self , episode ) :
( success , application ) = gl . playback_episode ( episode )
2007-08-13 23:53:26 +02:00
if not success :
self . show_message ( _ ( ' The selected player application cannot be found. Please check your media player settings in the preferences dialog. ' ) , _ ( ' Error opening player: %s ' ) % ( saxutils . escape ( application ) , ) )
2007-08-20 15:45:46 +02:00
self . download_status_updated ( )
2007-04-03 13:21:12 +02:00
2007-04-03 08:27:46 +02:00
def treeAvailable_search_equal ( self , model , column , key , iter , data = None ) :
2008-02-26 16:49:59 +01:00
if model is None :
2007-04-03 08:27:46 +02:00
return True
key = key . lower ( )
# columns, as defined in libpodcasts' get model method
# 1 = episode title, 7 = description
columns = ( 1 , 7 )
for column in columns :
value = model . get_value ( iter , column ) . lower ( )
if value . find ( key ) != - 1 :
return False
return True
2008-05-02 17:36:43 +02:00
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 )
2007-04-03 08:27:46 +02:00
2008-06-30 03:10:18 +02:00
def play_or_download ( self ) :
2007-03-31 04:00:30 +02:00
if self . wNotebook . get_current_page ( ) > 0 :
return
2008-06-30 03:10:18 +02:00
( can_play , can_download , can_transfer , can_cancel , can_delete ) = ( False , ) * 5
( is_played , is_locked ) = ( False , ) * 2
2006-12-13 00:11:34 +01:00
2008-08-04 23:26:51 +02:00
open_instead_of_play = False
2007-08-26 17:20:46 +02:00
selection = self . treeAvailable . get_selection ( )
if selection . count_selected_rows ( ) > 0 :
( model , paths ) = selection . get_selected_rows ( )
for path in paths :
url = model . get_value ( model . get_iter ( path ) , 0 )
local_filename = model . get_value ( model . get_iter ( path ) , 8 )
2006-12-13 00:11:34 +01:00
2008-06-30 03:10:18 +02:00
episode = podcastItem . load ( url , self . active_channel )
2008-08-04 23:26:51 +02:00
if episode . file_type ( ) not in ( ' audio ' , ' video ' ) :
open_instead_of_play = True
2008-06-30 03:10:18 +02:00
if episode . was_downloaded ( and_exists = True ) :
2007-08-26 17:20:46 +02:00
can_play = True
2008-06-30 03:10:18 +02:00
can_delete = True
is_played = episode . is_played
is_locked = episode . is_locked
2007-08-26 17:20:46 +02:00
else :
2008-06-30 03:10:18 +02:00
if services . download_status_manager . is_download_in_progress ( url ) :
2007-08-26 17:20:46 +02:00
can_cancel = True
else :
can_download = True
2006-12-13 00:11:34 +01:00
2008-08-04 23:26:51 +02:00
if episode . file_type ( ) == ' torrent ' :
2008-03-02 14:22:29 +01:00
can_download = can_download or gl . config . use_gnome_bittorrent
2007-08-26 17:20:46 +02:00
can_download = can_download and not can_cancel
can_play = can_play and not can_cancel and not can_download
2008-03-02 14:22:29 +01:00
can_transfer = can_play and gl . config . device_type != ' none '
2007-08-26 17:20:46 +02:00
2008-08-04 23:26:51 +02:00
if open_instead_of_play :
self . toolPlay . set_stock_id ( gtk . STOCK_OPEN )
can_transfer = False
else :
self . toolPlay . set_stock_id ( gtk . STOCK_MEDIA_PLAY )
2007-08-26 17:20:46 +02:00
self . toolPlay . set_sensitive ( can_play )
self . toolDownload . set_sensitive ( can_download )
self . toolTransfer . set_sensitive ( can_transfer )
self . toolCancel . set_sensitive ( can_cancel )
2008-05-02 17:36:43 +02:00
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 :
2008-08-04 23:26:51 +02:00
if open_instead_of_play :
self . itemOpenSelected . show_all ( )
else :
self . itemPlaySelected . show_all ( )
2008-05-02 17:36:43 +02:00
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 ( )
2008-08-04 23:26:51 +02:00
self . itemOpenSelected . hide_all ( )
2008-05-02 17:36:43 +02:00
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 ( )
2008-08-04 23:26:51 +02:00
return ( can_play , can_download , can_transfer , can_cancel , can_delete , open_instead_of_play )
2006-12-13 00:11:34 +01:00
2007-07-24 20:42:04 +02:00
def download_status_updated ( self ) :
2007-08-24 16:49:41 +02:00
count = services . download_status_manager . count ( )
2007-07-24 20:42:04 +02:00
if count :
self . labelDownloads . set_text ( _ ( ' Downloads ( %d ) ' ) % count )
else :
self . labelDownloads . set_text ( _ ( ' Downloads ' ) )
self . updateComboBox ( )
2008-04-17 17:45:29 +02:00
def on_cbMaxDownloads_toggled ( self , widget , * args ) :
self . spinMaxDownloads . set_sensitive ( self . cbMaxDownloads . get_active ( ) )
def on_cbLimitDownloads_toggled ( self , widget , * args ) :
self . spinLimitDownloads . set_sensitive ( self . cbLimitDownloads . get_active ( ) )
2008-05-14 15:38:06 +02:00
def updateComboBox ( self , selected_url = None ) :
( model , iter ) = self . treeChannels . get_selection ( ) . get_selected ( )
2007-09-19 17:04:42 +02:00
2008-05-14 15:38:06 +02:00
if model and iter and selected_url is None :
# Get the URL of the currently-selected podcast
selected_url = model . get_value ( iter , 0 )
2007-09-19 17:04:42 +02:00
rect = self . treeChannels . get_visible_rect ( )
2008-08-30 19:23:04 +02:00
self . treeChannels . set_model ( channels_to_model ( self . channels , self . channel_colors , self . cover_cache , gl . config . podcast_list_icon_size , gl . config . podcast_list_icon_size ) )
2008-05-14 15:38:06 +02:00
util . idle_add ( self . treeChannels . scroll_to_point , rect . x , rect . y )
2008-06-30 03:10:18 +02:00
2005-11-21 19:21:25 +01:00
try :
2008-05-14 15:38:06 +02:00
selected_path = ( 0 , )
# Find the previously-selected URL in the new
# model if we have an URL (else select first)
if selected_url is not None :
model = self . treeChannels . get_model ( )
pos = model . get_iter_first ( )
while pos is not None :
url = model . get_value ( pos , 0 )
if url == selected_url :
selected_path = model . get_path ( pos )
break
pos = model . iter_next ( pos )
self . treeChannels . get_selection ( ) . select_path ( selected_path )
2005-11-21 19:21:25 +01:00
except :
2007-09-19 17:04:42 +02:00
log ( ' Cannot set selection on treeChannels ' , sender = self )
2007-09-19 17:22:14 +02:00
self . on_treeChannels_cursor_changed ( self . treeChannels )
2005-11-21 19:21:25 +01:00
def updateTreeView ( self ) :
2008-03-04 12:06:55 +01:00
if self . channels and self . active_channel is not None :
self . treeAvailable . set_model ( self . active_channel . tree_model )
2007-04-01 19:53:04 +02:00
self . treeAvailable . columns_autosize ( )
2007-03-18 19:28:17 +01:00
self . play_or_download ( )
2006-12-06 21:25:26 +01:00
else :
if self . treeAvailable . get_model ( ) :
self . treeAvailable . get_model ( ) . clear ( )
2005-11-21 19:21:25 +01:00
2005-11-22 14:23:50 +01:00
def drag_data_received ( self , widget , context , x , y , sel , ttype , time ) :
2008-06-14 13:43:53 +02:00
( path , column , rx , ry ) = self . treeChannels . get_path_at_pos ( x , y ) or ( None , ) * 4
dnd_channel = None
if path is not None :
model = self . treeChannels . get_model ( )
iter = model . get_iter ( path )
url = model . get_value ( iter , 0 )
for channel in self . channels :
if channel . url == url :
dnd_channel = channel
break
2005-11-22 14:23:50 +01:00
result = sel . data
2008-06-14 13:43:53 +02:00
rl = result . strip ( ) . lower ( )
if ( rl . endswith ( ' .jpg ' ) or rl . endswith ( ' .png ' ) or rl . endswith ( ' .gif ' ) or rl . endswith ( ' .svg ' ) ) and dnd_channel is not None :
services . cover_downloader . replace_cover ( dnd_channel , result )
else :
self . add_new_channel ( result )
2006-03-31 18:20:18 +02:00
2008-06-30 03:10:18 +02:00
def add_new_channel ( self , result = None , ask_download_new = True , quiet = False ) :
2007-08-07 20:11:31 +02:00
result = util . normalize_feed_url ( result )
2007-07-19 14:44:12 +02:00
2008-09-23 03:48:59 +02:00
if not result :
title = _ ( ' URL scheme not supported ' )
message = _ ( ' gPodder currently only supports URLs starting with <b>http://</b>, <b>feed://</b> or <b>ftp://</b>. ' )
self . show_message ( message , title )
return
for old_channel in self . channels :
if old_channel . url == result :
log ( ' Channel already exists: %s ' , result )
# Select the existing channel in combo box
for i in range ( len ( self . channels ) ) :
if self . channels [ i ] == old_channel :
self . treeChannels . get_selection ( ) . select_path ( ( i , ) )
self . on_treeChannels_cursor_changed ( self . treeChannels )
break
self . show_message ( _ ( ' You have already subscribed to this podcast: %s ' ) % (
saxutils . escape ( old_channel . title ) , ) , _ ( ' Already added ' ) )
return
self . entryAddChannel . set_text ( _ ( ' Downloading feed... ' ) )
self . entryAddChannel . set_sensitive ( False )
self . btnAddChannel . set_sensitive ( False )
args = ( result , self . add_new_channel_finish , ask_download_new , quiet )
thread = Thread ( target = self . add_new_channel_proc , args = args )
thread . start ( )
def add_new_channel_proc ( self , url , callback , * callback_args ) :
log ( ' Adding new channel: %s ' , url )
try :
channel = podcastChannel . load ( url = url , create = True )
except Exception , e :
log ( ' Error in podcastChannel.load( %s ): %s ' , url , e , traceback = True , sender = self )
channel = None
util . idle_add ( callback , channel , url , * callback_args )
def add_new_channel_finish ( self , channel , url , ask_download_new , quiet ) :
if channel is not None :
self . channels . append ( channel )
save_channels ( self . channels )
if not quiet :
# download changed channels and select the new episode in the UI afterwards
self . update_feed_cache ( force_update = False , select_url_afterwards = channel . url )
( username , password ) = util . username_password_from_url ( url )
if username and self . show_confirmation ( _ ( ' You have supplied <b> %s </b> as username and a password for this feed. Would you like to use the same authentication data for downloading episodes? ' ) % ( saxutils . escape ( username ) , ) , _ ( ' Password authentication ' ) ) :
channel . username = username
channel . password = password
log ( ' Saving authentication data for episode downloads.. ' , sender = self )
channel . save ( )
if ask_download_new :
new_episodes = channel . get_new_episodes ( )
if len ( new_episodes ) :
self . new_episodes_show ( new_episodes )
else :
# Ok, the URL is not a channel, or there is some other
# error - let's see if it's a web page or OPML file...
2007-08-20 15:45:46 +02:00
try :
2008-09-23 03:48:59 +02:00
data = urllib2 . urlopen ( url ) . read ( ) . lower ( )
if ' </opml> ' in data :
# This looks like an OPML feed
self . on_item_import_from_file_activate ( None , url )
return
elif ' </html> ' in data :
# This looks like a web page
title = _ ( ' The URL is a website ' )
message = _ ( ' The URL you specified points to a web page. You need to find the " feed " URL of the podcast to add to gPodder. Do you want to visit this website now and look for the podcast feed URL? \n \n (Hint: Look for " XML feed " , " RSS feed " or " Podcast feed " if you are unsure for what to look. If there is only an iTunes URL, try adding this one.) ' )
if self . show_confirmation ( message , title ) :
util . open_website ( url )
return
2008-06-30 03:10:18 +02:00
except Exception , e :
2008-09-23 03:48:59 +02:00
log ( ' Error trying to handle the URL as OPML or web page: %s ' , e , sender = self )
title = _ ( ' Error adding podcast ' )
message = _ ( ' The podcast could not be added. Please check the spelling of the URL or try again later. ' )
self . show_message ( message , title )
self . entryAddChannel . set_text ( ' ' )
self . entryAddChannel . set_sensitive ( True )
self . btnAddChannel . set_sensitive ( True )
2008-08-30 19:23:04 +02:00
self . update_podcasts_tab ( )
2006-12-09 02:59:53 +01:00
2007-08-20 15:45:46 +02:00
2008-08-30 19:23:04 +02:00
def update_feed_cache_finish_callback ( self , channels = None ,
notify_no_new_episodes = False , select_url_afterwards = None ) :
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = False
2008-04-22 20:34:41 +02:00
self . hboxUpdateFeeds . hide_all ( )
self . btnUpdateFeeds . show_all ( )
2008-07-14 18:28:34 +02:00
self . itemUpdate . set_sensitive ( True )
2008-08-30 19:23:04 +02:00
self . itemUpdateChannel . set_sensitive ( True )
2008-01-09 23:46:17 +01:00
2008-05-14 15:38:06 +02:00
# If we want to select a specific podcast (via its URL)
# after the update, we give it to updateComboBox here to
# select exactly this podcast after updating the view
self . updateComboBox ( selected_url = select_url_afterwards )
2008-01-09 23:46:17 +01:00
if self . tray_icon :
self . tray_icon . set_status ( None )
2008-08-30 19:23:04 +02:00
if self . minimized :
2008-01-09 23:46:17 +01:00
new_episodes = [ ]
2008-02-04 11:29:41 +01:00
# look for new episodes to notify
2008-01-09 23:46:17 +01:00
for channel in self . channels :
for episode in channel . get_new_episodes ( ) :
2008-04-15 20:59:22 +02:00
if not episode in self . already_notified_new_episodes :
2008-02-04 11:29:41 +01:00
new_episodes . append ( episode )
2008-04-15 20:59:22 +02:00
self . already_notified_new_episodes . append ( episode )
2008-02-04 11:29:41 +01:00
# notify new episodes
2008-02-27 10:46:32 +01:00
if len ( new_episodes ) == 0 :
if notify_no_new_episodes and self . tray_icon is not None :
msg = _ ( ' No new episodes available for download ' )
self . tray_icon . send_notification ( msg )
return
elif len ( new_episodes ) == 1 :
title = _ ( ' gPodder has found %s ' ) % ( _ ( ' one new episode: ' ) , )
else :
title = _ ( ' gPodder has found %s ' ) % ( _ ( ' %i new episodes: ' ) % len ( new_episodes ) )
message = self . tray_icon . format_episode_list ( new_episodes )
#auto download new episodes
if gl . config . auto_download_when_minimized :
message + = ' \n <i>( %s ...)</i> ' % _ ( ' downloading ' )
self . download_episode_list ( new_episodes )
self . tray_icon . send_notification ( message , title )
2008-01-23 09:08:13 +01:00
return
2008-01-09 23:46:17 +01:00
2008-02-04 11:29:41 +01:00
# open the episodes selection dialog
2008-08-30 19:23:04 +02:00
self . channels = load_channels ( )
self . updateComboBox ( )
if not self . feed_cache_update_cancelled :
self . download_all_new ( channels = channels )
2006-07-14 22:09:00 +02:00
2008-08-30 19:23:04 +02:00
def update_feed_cache_callback ( self , progressbar , title , position , count ) :
progression = _ ( ' Updated %s ( %d / %d ) ' ) % ( title , position + 1 , count )
progressbar . set_text ( progression )
if self . tray_icon :
self . tray_icon . set_status (
self . tray_icon . STATUS_UPDATING_FEED_CACHE , progression )
if count > 0 :
progressbar . set_fraction ( float ( position ) / float ( count ) )
def update_feed_cache_proc ( self , channel , total_channels , semaphore ,
callback_proc , finish_proc ) :
2008-06-30 03:10:18 +02:00
2008-08-30 19:23:04 +02:00
semaphore . acquire ( )
if not self . feed_cache_update_cancelled :
try :
channel . update ( )
except :
log ( ' Darn SQLite LOCK! ' , sender = self , traceback = True )
# By the time we get here the update may have already been cancelled
if not self . feed_cache_update_cancelled :
callback_proc ( channel . title , self . updated_feeds , total_channels )
self . updated_feeds + = 1
self . treeview_channel_set_color ( channel , ' default ' )
channel . update_flag = False
semaphore . release ( )
if self . updated_feeds == total_channels :
2007-08-20 15:45:46 +02:00
finish_proc ( )
2007-03-10 16:57:56 +01:00
2008-04-22 20:34:41 +02:00
def on_btnCancelFeedUpdate_clicked ( self , widget ) :
self . pbFeedUpdate . set_text ( _ ( ' Cancelling... ' ) )
self . feed_cache_update_cancelled = True
2008-08-30 19:23:04 +02:00
def update_feed_cache ( self , channels = None , force_update = True ,
notify_no_new_episodes = False , select_url_afterwards = None ) :
2008-07-14 18:28:34 +02:00
if self . updating_feed_cache :
return
2008-08-30 19:23:04 +02:00
if not force_update :
self . channels = load_channels ( )
self . updateComboBox ( )
return
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = True
self . itemUpdate . set_sensitive ( False )
2008-08-30 19:23:04 +02:00
self . itemUpdateChannel . set_sensitive ( False )
2008-01-09 23:46:17 +01:00
if self . tray_icon :
self . tray_icon . set_status ( self . tray_icon . STATUS_UPDATING_FEED_CACHE )
2008-08-30 19:23:04 +02:00
if channels is None :
channels = self . channels
if len ( channels ) == 1 :
text = _ ( ' Updating %d feed. ' )
else :
text = _ ( ' Updating %d feeds. ' )
self . pbFeedUpdate . set_text ( text % len ( channels ) )
self . pbFeedUpdate . set_fraction ( 0 )
2008-01-09 23:46:17 +01:00
2006-07-14 22:09:00 +02:00
# let's get down to business..
2008-08-30 19:23:04 +02:00
callback_proc = lambda title , pos , count : util . idle_add (
self . update_feed_cache_callback , self . pbFeedUpdate , title , pos , count )
finish_proc = lambda : util . idle_add ( self . update_feed_cache_finish_callback ,
channels , notify_no_new_episodes , select_url_afterwards )
2008-04-22 20:34:41 +02:00
2008-08-30 19:23:04 +02:00
self . updated_feeds = 0
2008-04-22 20:34:41 +02:00
self . feed_cache_update_cancelled = False
self . btnUpdateFeeds . hide_all ( )
self . hboxUpdateFeeds . show_all ( )
2008-08-30 19:23:04 +02:00
semaphore = Semaphore ( gl . config . max_simulaneous_feeds_updating )
for channel in channels :
self . treeview_channel_set_color ( channel , ' updating ' )
channel . update_flag = True
args = ( channel , len ( channels ) , semaphore , callback_proc , finish_proc )
thread = Thread ( target = self . update_feed_cache_proc , args = args )
thread . start ( )
def treeview_channel_set_color ( self , channel , color ) :
2008-09-10 18:07:39 +02:00
if self . treeChannels . get_model ( ) :
if color in self . channel_colors :
self . treeChannels . get_model ( ) . set ( channel . iter , 8 , self . channel_colors [ color ] )
else :
self . treeChannels . get_model ( ) . set ( channel . iter , 8 , color )
2007-03-10 16:57:56 +01:00
2008-01-09 23:46:17 +01:00
def on_gPodder_delete_event ( self , widget , * args ) :
""" Called when the GUI wants to close the window
Displays a confirmation dialog ( and closes / hides gPodder )
"""
2005-11-21 19:21:25 +01:00
2008-01-09 23:46:17 +01:00
downloading = services . download_status_manager . has_items ( )
2008-03-30 10:50:34 +02:00
# Only iconify if we are using the window's "X" button,
# but not when we are using "Quit" in the menu or toolbar
2008-04-11 10:29:13 +02:00
if not gl . config . on_quit_ask and gl . config . on_quit_systray and self . tray_icon and widget . name not in ( ' toolQuit ' , ' itemQuit ' ) :
2008-02-06 10:45:40 +01:00
self . iconify_main_window ( )
elif gl . config . on_quit_ask or downloading :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
result = self . show_confirmation ( _ ( ' Do you really want to quit gPodder now? ' ) )
if result :
self . close_gpodder ( )
else :
return True
2008-01-09 23:46:17 +01:00
dialog = gtk . MessageDialog ( self . gPodder , gtk . DIALOG_MODAL , gtk . MESSAGE_QUESTION , gtk . BUTTONS_NONE )
dialog . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
dialog . add_button ( gtk . STOCK_QUIT , gtk . RESPONSE_CLOSE )
title = _ ( ' Quit gPodder ' )
if downloading :
message = _ ( ' You are downloading episodes. If you close gPodder now, the downloads will be aborted. ' )
else :
message = _ ( ' Do you really want to quit gPodder now? ' )
dialog . set_title ( title )
dialog . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
if not downloading :
cb_ask = gtk . CheckButton ( _ ( " Don ' t ask me again " ) )
dialog . vbox . pack_start ( cb_ask )
cb_ask . show_all ( )
result = dialog . run ( )
dialog . destroy ( )
if result == gtk . RESPONSE_CLOSE :
if not downloading and cb_ask . get_active ( ) == True :
gl . config . on_quit_ask = False
self . close_gpodder ( )
else :
self . close_gpodder ( )
return True
def close_gpodder ( self ) :
""" clean everything and exit properly
"""
2006-12-09 02:59:53 +01:00
if self . channels :
2007-11-25 11:55:12 +01:00
if not save_channels ( self . channels ) :
2008-05-02 17:36:43 +02:00
self . show_message ( _ ( ' Please check your permissions and free disk space. ' ) , _ ( ' Error saving podcast list ' ) )
2005-11-21 19:21:25 +01:00
2007-08-24 16:49:41 +02:00
services . download_status_manager . cancel_all ( )
2005-11-23 20:53:18 +01:00
2005-11-21 19:21:25 +01:00
self . gtk_main_quit ( )
2007-05-05 10:10:10 +02:00
sys . exit ( 0 )
2005-11-21 19:21:25 +01:00
2007-12-12 20:57:50 +01:00
def get_old_episodes ( self ) :
episodes = [ ]
for channel in self . channels :
2008-06-30 03:10:18 +02:00
for episode in channel . get_downloaded_episodes ( ) :
if episode . is_old ( ) and not episode . is_locked and episode . is_played :
2007-12-12 20:57:50 +01:00
episodes . append ( episode )
return episodes
2007-08-30 20:54:18 +02:00
def for_each_selected_episode_url ( self , callback ) :
( model , paths ) = self . treeAvailable . get_selection ( ) . get_selected_rows ( )
for path in paths :
url = model . get_value ( model . get_iter ( path ) , 0 )
try :
callback ( url )
2008-06-30 03:10:18 +02:00
except Exception , e :
log ( ' Warning: Error in for_each_selected_episode_url for URL %s : %s ' , url , e , sender = self )
2007-08-30 20:54:18 +02:00
self . updateComboBox ( )
2007-11-08 20:11:57 +01:00
def delete_episode_list ( self , episodes , confirm = True ) :
if len ( episodes ) == 0 :
return
if len ( episodes ) == 1 :
message = _ ( ' Do you really want to delete this episode? ' )
else :
message = _ ( ' Do you really want to delete %d episodes? ' ) % len ( episodes )
if confirm and self . show_confirmation ( message , _ ( ' Delete episodes ' ) ) == False :
return
for episode in episodes :
log ( ' Deleting episode: %s ' , episode . title , sender = self )
episode . delete_from_disk ( )
self . download_status_updated ( )
def on_itemRemoveOldEpisodes_activate ( self , widget ) :
columns = (
2008-08-13 03:47:01 +02:00
( ' title_and_description ' , None , None , _ ( ' Episode ' ) ) ,
( ' channel_prop ' , None , None , _ ( ' Podcast ' ) ) ,
( ' filesize_prop ' , ' length ' , gobject . TYPE_INT , _ ( ' Size ' ) ) ,
( ' pubdate_prop ' , ' pubDate ' , gobject . TYPE_INT , _ ( ' Released ' ) ) ,
( ' played_prop ' , None , None , _ ( ' Status ' ) ) ,
( ' age_prop ' , None , None , _ ( ' Downloaded ' ) ) ,
2007-11-08 20:11:57 +01:00
)
selection_buttons = {
2008-06-30 03:10:18 +02:00
_ ( ' Select played ' ) : lambda episode : episode . is_played ,
2007-12-10 09:41:17 +01:00
_ ( ' Select older than %d days ' ) % gl . config . episode_old_age : lambda episode : episode . is_old ( ) ,
2007-11-08 20:11:57 +01:00
}
instructions = _ ( ' Select the episodes you want to delete from your hard disk. ' )
episodes = [ ]
selected = [ ]
for channel in self . channels :
2008-06-30 03:10:18 +02:00
for episode in channel . get_downloaded_episodes ( ) :
if not episode . is_locked :
episodes . append ( episode )
selected . append ( episode . is_played )
2007-11-08 20:11:57 +01:00
gPodderEpisodeSelector ( title = _ ( ' Remove old episodes ' ) , instructions = instructions , \
episodes = episodes , selected = selected , columns = columns , \
stock_ok_button = gtk . STOCK_DELETE , callback = self . delete_episode_list , \
selection_buttons = selection_buttons )
2008-06-30 03:10:18 +02:00
def mark_selected_episodes_new ( self ) :
callback = lambda url : self . active_channel . find_episode ( url ) . mark_new ( )
self . for_each_selected_episode_url ( callback )
2007-08-30 20:54:18 +02:00
2008-06-30 03:10:18 +02:00
def mark_selected_episodes_old ( self ) :
callback = lambda url : self . active_channel . find_episode ( url ) . mark_old ( )
self . for_each_selected_episode_url ( callback )
2008-06-13 14:30:42 +02:00
2007-08-30 20:54:18 +02:00
def on_item_toggle_played_activate ( self , widget , toggle = True , new_value = False ) :
if toggle :
2008-06-30 03:10:18 +02:00
callback = lambda url : db . mark_episode ( url , is_played = True , toggle = True )
2007-08-30 20:54:18 +02:00
else :
2008-06-30 03:10:18 +02:00
callback = lambda url : db . mark_episode ( url , is_played = new_value )
2007-08-30 20:54:18 +02:00
2008-06-30 03:10:18 +02:00
self . for_each_selected_episode_url ( callback )
2007-08-30 20:54:18 +02:00
2007-12-12 19:50:52 +01:00
def on_item_toggle_lock_activate ( self , widget , toggle = True , new_value = False ) :
if toggle :
2008-06-30 03:10:18 +02:00
callback = lambda url : db . mark_episode ( url , is_locked = True , toggle = True )
2007-12-12 19:50:52 +01:00
else :
2008-06-30 03:10:18 +02:00
callback = lambda url : db . mark_episode ( url , is_locked = new_value )
2007-12-12 19:50:52 +01:00
self . for_each_selected_episode_url ( callback )
2008-05-02 17:36:43 +02:00
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 ( )
2008-08-30 19:23:04 +02:00
def on_itemUpdateChannel_activate ( self , widget = None ) :
self . update_feed_cache ( channels = [ self . active_channel , ] )
2008-02-27 10:46:32 +01:00
def on_itemUpdate_activate ( self , widget , notify_no_new_episodes = False ) :
2008-06-30 03:10:18 +02:00
restore_from = can_restore_from_opml ( )
2006-12-06 21:25:26 +01:00
if self . channels :
2008-02-27 10:46:32 +01:00
self . update_feed_cache ( notify_no_new_episodes = notify_no_new_episodes )
2008-06-30 03:10:18 +02:00
elif restore_from is not None :
title = _ ( ' Database upgrade required ' )
message = _ ( ' gPodder is now using a new (much faster) database backend and needs to convert your current data. This can take some time. Start the conversion now? ' )
if self . show_confirmation ( message , title ) :
add_callback = lambda url : self . add_new_channel ( url , False , True )
w = gtk . Dialog ( _ ( ' Migrating to SQLite ' ) , self . gPodder , 0 , ( gtk . STOCK_CLOSE , gtk . RESPONSE_ACCEPT ) )
w . set_has_separator ( False )
w . set_response_sensitive ( gtk . RESPONSE_ACCEPT , False )
w . set_default_size ( 500 , - 1 )
pb = gtk . ProgressBar ( )
l = gtk . Label ( )
l . set_padding ( 6 , 3 )
l . set_markup ( ' <b><big> %s </big></b> ' % _ ( ' SQLite migration ' ) )
l . set_alignment ( 0.0 , 0.5 )
w . vbox . pack_start ( l )
l = gtk . Label ( )
l . set_padding ( 6 , 3 )
l . set_alignment ( 0.0 , 0.5 )
l . set_text ( _ ( ' Please wait while your settings are converted. ' ) )
w . vbox . pack_start ( l )
w . vbox . pack_start ( pb )
lb = gtk . Label ( )
lb . set_ellipsize ( pango . ELLIPSIZE_END )
lb . set_alignment ( 0.0 , 0.5 )
lb . set_padding ( 6 , 6 )
w . vbox . pack_start ( lb )
def set_pb_status ( pb , lb , fraction , text ) :
pb . set_fraction ( float ( fraction ) / 100.0 )
pb . set_text ( ' %.0f %% ' % fraction )
lb . set_markup ( ' <i> %s </i> ' % saxutils . escape ( text ) )
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
status_callback = lambda fraction , text : set_pb_status ( pb , lb , fraction , text )
get_localdb = lambda channel : LocalDBReader ( channel . url ) . read ( channel . index_file )
w . show_all ( )
start = datetime . datetime . now ( )
gl . migrate_to_sqlite ( add_callback , status_callback , load_channels , get_localdb )
# Refresh the view with the updated episodes
self . updateComboBox ( )
time_taken = str ( datetime . datetime . now ( ) - start )
status_callback ( 100.0 , _ ( ' Migration finished in %s ' ) % time_taken )
w . set_response_sensitive ( gtk . RESPONSE_ACCEPT , True )
w . run ( )
w . destroy ( )
2006-12-06 21:25:26 +01:00
else :
2008-05-02 17:36:43 +02:00
title = _ ( ' Import podcasts from the web ' )
message = _ ( ' Your podcast list is empty. Do you want to see a list of example podcasts you can subscribe to? ' )
if self . show_confirmation ( message , title ) :
self . on_itemImportChannels_activate ( self , widget )
2005-11-21 19:21:25 +01:00
2007-11-08 20:11:57 +01:00
def download_episode_list ( self , episodes ) :
2008-05-14 15:38:06 +02:00
services . download_status_manager . start_batch_mode ( )
2007-11-08 20:11:57 +01:00
for episode in episodes :
log ( ' Downloading episode: %s ' , episode . title , sender = self )
filename = episode . local_filename ( )
if not os . path . exists ( filename ) and not services . download_status_manager . is_download_in_progress ( episode . url ) :
download . DownloadThread ( episode . channel , episode , self . notification ) . start ( )
2008-05-14 15:38:06 +02:00
services . download_status_manager . end_batch_mode ( )
2006-12-20 20:44:29 +01:00
2008-01-22 10:10:08 +01:00
def new_episodes_show ( self , episodes ) :
2007-11-08 20:11:57 +01:00
columns = (
2008-08-13 03:47:01 +02:00
( ' title_and_description ' , None , None , _ ( ' Episode ' ) ) ,
( ' channel_prop ' , None , None , _ ( ' Podcast ' ) ) ,
( ' filesize_prop ' , ' length ' , gobject . TYPE_INT , _ ( ' Size ' ) ) ,
( ' pubdate_prop ' , ' pubDate ' , gobject . TYPE_INT , _ ( ' Released ' ) ) ,
2007-11-08 20:11:57 +01:00
)
if len ( episodes ) > 0 :
instructions = _ ( ' Select the episodes you want to download now. ' )
2007-03-10 18:42:32 +01:00
2008-01-22 10:10:08 +01:00
gPodderEpisodeSelector ( title = _ ( ' New episodes available ' ) , instructions = instructions , \
episodes = episodes , columns = columns , selected_default = True , \
2008-06-14 18:53:16 +02:00
stock_ok_button = ' gpodder-download ' , \
2008-01-22 10:10:08 +01:00
callback = self . download_episode_list )
2006-12-20 20:44:29 +01:00
else :
2007-03-10 18:42:32 +01:00
title = _ ( ' No new episodes ' )
2008-04-06 02:19:03 +02:00
message = _ ( ' No new episodes to download. \n Please check for new episodes later. ' )
2008-01-22 10:10:08 +01:00
self . show_message ( message , title )
def on_itemDownloadAllNew_activate ( self , widget , * args ) :
2008-08-30 19:23:04 +02:00
self . download_all_new ( )
def download_all_new ( self , channels = None ) :
if channels is None :
channels = self . channels
2008-01-22 10:10:08 +01:00
episodes = [ ]
2008-08-30 19:23:04 +02:00
for channel in channels :
2008-01-22 10:10:08 +01:00
for episode in channel . get_new_episodes ( ) :
episodes . append ( episode )
self . new_episodes_show ( episodes )
2006-12-20 20:44:29 +01:00
2008-06-13 09:41:36 +02:00
def get_all_episodes ( self , exclude_nonsignificant = True ) :
""" ' exclude_nonsignificant ' will exclude non-downloaded episodes
and all episodes from channels that are set to skip when syncing """
episode_list = [ ]
for channel in self . channels :
if not channel . sync_to_devices and exclude_nonsignificant :
log ( ' Skipping channel: %s ' , channel . title , sender = self )
continue
for episode in channel . get_all_episodes ( ) :
2008-06-30 03:10:18 +02:00
if episode . was_downloaded ( and_exists = True ) or not exclude_nonsignificant :
2008-06-13 09:41:36 +02:00
episode_list . append ( episode )
return episode_list
def ipod_delete_played ( self , device ) :
all_episodes = self . get_all_episodes ( exclude_nonsignificant = False )
episodes_on_device = device . get_all_tracks ( )
for local_episode in all_episodes :
2008-07-11 20:17:48 +02:00
device_episode = device . episode_on_device ( local_episode )
if device_episode and ( local_episode . is_played and not local_episode . is_locked
or local_episode . state == db . STATE_DELETED ) :
2008-07-09 03:43:01 +02:00
log ( " mp3_player_delete_played: removing %s " % device_episode . title )
device . remove_track ( device_episode )
2008-06-13 09:41:36 +02:00
2008-01-28 12:38:53 +01:00
def on_sync_to_ipod_activate ( self , widget , episodes = None ) :
2008-07-04 02:30:28 +02:00
# make sure gpod is available before even trying to sync
if gl . config . device_type == ' ipod ' and not sync . gpod_available :
title = _ ( ' Cannot Sync To iPod ' )
message = _ ( ' Please install the libgpod python bindings (python-gpod) and restart gPodder to continue. ' )
self . notification ( message , title )
2008-07-20 04:55:30 +02:00
return
2008-09-01 21:26:35 +02:00
elif gl . config . device_type == ' mtp ' and not sync . pymtp_available :
title = _ ( ' Cannot sync to MTP device ' )
message = _ ( ' Please install the libmtp python bindings (python-pymtp) and restart gPodder to continue. ' )
self . notification ( message , title )
return
2008-07-20 04:55:30 +02:00
2008-01-28 12:38:53 +01:00
device = sync . open_device ( )
2008-07-20 04:55:30 +02:00
device . register ( ' post-done ' , self . sync_to_ipod_completed )
2008-01-28 12:38:53 +01:00
if device is None :
2007-03-10 18:42:32 +01:00
title = _ ( ' No device configured ' )
message = _ ( ' To use the synchronization feature, please configure your device in the preferences dialog first. ' )
2008-01-28 12:38:53 +01:00
self . notification ( message , title )
2007-03-18 19:28:17 +01:00
return
2008-01-28 12:38:53 +01:00
if not device . open ( ) :
title = _ ( ' Cannot open device ' )
message = _ ( ' There has been an error opening your device. ' )
self . notification ( message , title )
2008-01-09 23:56:03 +01:00
return
2007-03-18 19:28:17 +01:00
2008-07-20 04:55:30 +02:00
sync_all_episodes = not bool ( episodes )
2007-03-18 19:28:17 +01:00
2008-01-28 12:38:53 +01:00
if episodes is None :
2008-07-09 03:43:01 +02:00
episodes = self . get_all_episodes ( )
# make sure we have enough space on the device
total_size = 0
free_space = device . get_free_space ( )
for episode in episodes :
if not device . episode_on_device ( episode ) :
total_size + = util . calculate_size ( str ( episode . local_filename ( ) ) )
if total_size > free_space :
2008-07-20 04:55:30 +02:00
# can be negative because of the 10 MiB for reserved for the iTunesDB
free_space = max ( free_space , 0 )
2008-07-09 03:43:01 +02:00
log ( ' (gpodder.sync) Not enough free space. Transfer size = %d , Free space = %d ' , total_size , free_space )
title = _ ( ' Not enough space left on device. ' )
message = _ ( ' %s remaining on device. \n Please free up %s and try again. ' % (
util . format_filesize ( free_space ) , util . format_filesize ( total_size - free_space ) ) )
self . notification ( message , title )
2008-01-28 12:38:53 +01:00
else :
2008-07-09 03:43:01 +02:00
# start syncing!
gPodderSync ( device = device , gPodder = self )
2008-07-20 04:55:30 +02:00
Thread ( target = self . sync_to_ipod_thread , args = ( widget , device , sync_all_episodes , episodes ) ) . start ( )
2008-07-09 03:43:01 +02:00
if self . tray_icon :
self . tray_icon . set_synchronisation_device ( device )
2008-07-20 04:55:30 +02:00
def sync_to_ipod_completed ( self , device , successful_sync ) :
device . unregister ( ' post-done ' , self . sync_to_ipod_completed )
2008-07-09 03:43:01 +02:00
2008-07-20 04:55:30 +02:00
if self . tray_icon :
self . tray_icon . release_synchronisation_device ( )
2008-01-28 12:38:53 +01:00
2008-07-20 04:55:30 +02:00
if not successful_sync :
2008-01-28 12:38:53 +01:00
title = _ ( ' Error closing device ' )
message = _ ( ' There has been an error closing your device. ' )
self . notification ( message , title )
2007-03-18 19:28:17 +01:00
2008-01-28 12:38:53 +01:00
# update model for played state updates after sync
util . idle_add ( self . updateComboBox )
2008-07-20 04:55:30 +02:00
def sync_to_ipod_thread ( self , widget , device , sync_all_episodes , episodes = None ) :
if sync_all_episodes :
device . add_tracks ( episodes )
# 'only_sync_not_played' must be used or else all the played
# tracks will be copied then immediately deleted
if gl . config . mp3_player_delete_played and gl . config . only_sync_not_played :
self . ipod_delete_played ( device )
else :
device . add_tracks ( episodes , force_played = True )
device . close ( )
2008-01-28 12:38:53 +01:00
def ipod_cleanup_callback ( self , device , tracks ) :
title = _ ( ' Delete podcasts from device? ' )
2008-09-10 16:11:44 +02:00
message = _ ( ' The selected episodes will be removed from your device. This cannot be undone. Files in your gPodder library will be unaffected. Do you really want to delete these episodes from your device? ' )
2008-01-28 12:38:53 +01:00
if len ( tracks ) > 0 and self . show_confirmation ( message , title ) :
device . remove_tracks ( tracks )
if not device . close ( ) :
title = _ ( ' Error closing device ' )
message = _ ( ' There has been an error closing your device. ' )
self . show_message ( message , title )
return
2006-04-07 03:43:06 +02:00
2006-04-06 16:11:03 +02:00
def on_cleanup_ipod_activate ( self , widget , * args ) :
2008-01-28 12:38:53 +01:00
columns = (
2008-08-13 03:47:01 +02:00
( ' title ' , None , None , _ ( ' Episode ' ) ) ,
( ' podcast ' , None , None , _ ( ' Podcast ' ) ) ,
( ' filesize ' , None , None , _ ( ' Size ' ) ) ,
( ' modified ' , None , None , _ ( ' Copied ' ) ) ,
( ' playcount ' , None , None , _ ( ' Play count ' ) ) ,
( ' released ' , None , None , _ ( ' Released ' ) ) ,
2008-01-28 12:38:53 +01:00
)
device = sync . open_device ( )
if device is None :
2007-03-10 18:42:32 +01:00
title = _ ( ' No device configured ' )
message = _ ( ' To use the synchronization feature, please configure your device in the preferences dialog first. ' )
2008-01-28 12:38:53 +01:00
self . show_message ( message , title )
2007-03-18 19:28:17 +01:00
return
2008-01-28 12:38:53 +01:00
if not device . open ( ) :
title = _ ( ' Cannot open device ' )
message = _ ( ' There has been an error opening your device. ' )
self . show_message ( message , title )
2007-03-18 19:28:17 +01:00
return
2008-01-28 12:38:53 +01:00
2008-02-20 13:46:51 +01:00
gPodderSync ( device = device , gPodder = self )
2008-01-28 12:38:53 +01:00
tracks = device . get_all_tracks ( )
if len ( tracks ) > 0 :
remove_tracks_callback = lambda tracks : self . ipod_cleanup_callback ( device , tracks )
2008-04-19 19:01:09 +02:00
wanted_columns = [ ]
2008-08-13 03:47:01 +02:00
for key , sort_name , sort_type , caption in columns :
2008-04-19 19:01:09 +02:00
want_this_column = False
for track in tracks :
if getattr ( track , key ) is not None :
want_this_column = True
break
if want_this_column :
2008-08-13 03:47:01 +02:00
wanted_columns . append ( ( key , sort_name , sort_type , caption ) )
2008-01-28 12:38:53 +01:00
title = _ ( ' Remove podcasts from device ' )
instructions = _ ( ' Select the podcast episodes you want to remove from your device. ' )
2008-04-19 19:01:09 +02:00
gPodderEpisodeSelector ( title = title , instructions = instructions , episodes = tracks , columns = wanted_columns , \
2008-08-04 15:34:29 +02:00
stock_ok_button = gtk . STOCK_DELETE , callback = remove_tracks_callback , tooltip_attribute = None )
2008-01-28 12:38:53 +01:00
else :
title = _ ( ' No files on device ' )
message = _ ( ' The devices contains no files to be removed. ' )
self . show_message ( message , title )
2008-09-10 16:11:44 +02:00
device . close ( )
2006-04-06 16:11:03 +02:00
2008-01-09 23:46:17 +01:00
def show_hide_tray_icon ( self ) :
2008-03-11 18:45:52 +01:00
if gl . config . display_tray_icon and have_trayicon and self . tray_icon is None :
2008-01-09 23:46:17 +01:00
self . tray_icon = trayicon . GPodderStatusIcon ( self , scalable_dir )
elif not gl . config . display_tray_icon and self . tray_icon is not None :
self . tray_icon . set_visible ( False )
del self . tray_icon
self . tray_icon = None
if gl . config . minimize_to_tray and self . tray_icon :
self . tray_icon . set_visible ( self . minimized )
elif self . tray_icon :
self . tray_icon . set_visible ( True )
2008-02-06 10:29:56 +01:00
def on_itemShowToolbar_activate ( self , widget ) :
gl . config . show_toolbar = self . itemShowToolbar . get_active ( )
def on_itemShowDescription_activate ( self , widget ) :
gl . config . episode_list_descriptions = self . itemShowDescription . get_active ( )
2007-11-08 20:11:57 +01:00
def update_item_device ( self ) :
if gl . config . device_type != ' none ' :
self . itemDevice . show_all ( )
2008-05-02 17:36:43 +02:00
( label , ) = self . itemDevice . get_children ( )
label . set_text ( gl . get_device_name ( ) )
2007-11-08 20:11:57 +01:00
else :
self . itemDevice . hide_all ( )
def properties_closed ( self ) :
2008-01-09 23:46:17 +01:00
self . show_hide_tray_icon ( )
2007-11-08 20:11:57 +01:00
self . update_item_device ( )
self . updateComboBox ( )
2005-11-21 19:21:25 +01:00
def on_itemPreferences_activate ( self , widget , * args ) :
2008-04-24 19:42:57 +02:00
if gpodder . interface == gpodder . GUI :
gPodderProperties ( callback_finished = self . properties_closed , user_apps_reader = self . user_apps_reader )
else :
gPodderMaemoPreferences ( )
2005-11-21 19:21:25 +01:00
2008-08-04 16:54:18 +02:00
def on_add_new_google_search ( self , widget , * args ) :
def add_google_video_search ( query ) :
self . add_new_channel ( ' http://video.google.com/videofeed?type=search&q= ' + urllib . quote ( query ) + ' &so=1&num=250&output=rss ' )
gPodderAddPodcastDialog ( url_callback = add_google_video_search , custom_title = _ ( ' Add Google Video search ' ) , custom_label = _ ( ' Search for: ' ) )
2005-11-21 19:21:25 +01:00
def on_itemAddChannel_activate ( self , widget , * args ) :
2008-05-02 17:36:43 +02:00
if gpodder . interface == gpodder . MAEMO or not gl . config . show_podcast_url_entry :
2008-04-24 19:42:57 +02:00
gPodderAddPodcastDialog ( url_callback = self . add_new_channel )
else :
if self . channelPaned . get_position ( ) < 200 :
self . channelPaned . set_position ( 200 )
self . entryAddChannel . grab_focus ( )
2005-11-21 19:21:25 +01:00
def on_itemEditChannel_activate ( self , widget , * args ) :
2008-02-26 16:49:59 +01:00
if self . active_channel is None :
2008-05-02 17:36:43 +02:00
title = _ ( ' No podcast selected ' )
message = _ ( ' Please select a podcast in the podcasts list to edit. ' )
2007-03-10 18:42:32 +01:00
self . show_message ( message , title )
2006-04-06 16:11:03 +02:00
return
2007-07-11 20:28:09 +02:00
2008-03-03 23:09:34 +01:00
gPodderChannel ( channel = self . active_channel , callback_closed = self . updateComboBox , callback_change_url = self . change_channel_url )
def change_channel_url ( self , old_url , new_url ) :
channel = None
try :
2008-06-30 03:10:18 +02:00
channel = podcastChannel . load ( url = new_url , create = True )
2008-03-03 23:09:34 +01:00
except :
channel = None
if channel is None :
self . show_message ( _ ( ' The specified URL is invalid. The old URL has been used instead. ' ) , _ ( ' Invalid URL ' ) )
return
for channel in self . channels :
if channel . url == old_url :
log ( ' => change channel url from %s to %s ' , old_url , new_url )
old_save_dir = channel . save_dir
channel . url = new_url
new_save_dir = channel . save_dir
log ( ' old save dir= %s ' , old_save_dir , sender = self )
log ( ' new save dir= %s ' , new_save_dir , sender = self )
files = glob . glob ( os . path . join ( old_save_dir , ' * ' ) )
log ( ' moving %d files to %s ' , len ( files ) , new_save_dir , sender = self )
for file in files :
log ( ' moving %s ' , file , sender = self )
shutil . move ( file , new_save_dir )
try :
os . rmdir ( old_save_dir )
except :
log ( ' Warning: cannot delete %s ' , old_save_dir , sender = self )
save_channels ( self . channels )
2008-05-14 15:38:06 +02:00
# update feed cache and select the podcast with the new URL afterwards
self . update_feed_cache ( force_update = False , select_url_afterwards = new_url )
2007-07-11 13:05:02 +02:00
2005-11-21 19:21:25 +01:00
def on_itemRemoveChannel_activate ( self , widget , * args ) :
try :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
dialog = gtk . MessageDialog ( self . gPodder , gtk . DIALOG_MODAL , gtk . MESSAGE_QUESTION , gtk . BUTTONS_NONE )
dialog . add_button ( gtk . STOCK_NO , gtk . RESPONSE_NO )
dialog . add_button ( gtk . STOCK_YES , gtk . RESPONSE_YES )
2008-05-02 17:36:43 +02:00
title = _ ( ' Remove podcast and episodes? ' )
2008-04-06 02:19:03 +02:00
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_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
cb_ask = gtk . CheckButton ( _ ( ' Do not delete my downloaded episodes ' ) )
dialog . vbox . pack_start ( cb_ask )
cb_ask . show_all ( )
affirmative = gtk . RESPONSE_YES
elif gpodder . interface == gpodder . MAEMO :
cb_ask = gtk . CheckButton ( ' ' ) # dummy check button
2008-05-02 17:36:43 +02:00
dialog = hildon . Note ( ' confirmation ' , ( self . gPodder , _ ( ' Do you really want to remove this podcast and all downloaded episodes? ' ) ) )
2008-04-06 02:19:03 +02:00
affirmative = gtk . RESPONSE_OK
2008-01-14 20:06:26 +01:00
result = dialog . run ( )
dialog . destroy ( )
2008-04-06 02:19:03 +02:00
if result == affirmative :
2008-01-14 20:06:26 +01:00
# delete downloaded episodes only if checkbox is unchecked
if cb_ask . get_active ( ) == False :
self . active_channel . remove_downloaded ( )
else :
log ( ' Not removing downloaded episodes ' , sender = self )
2007-02-03 11:48:32 +01:00
# only delete partial files if we do not have any downloads in progress
2007-08-24 16:49:41 +02:00
delete_partial = not services . download_status_manager . has_items ( )
2008-03-02 14:22:29 +01:00
gl . clean_up_downloads ( delete_partial )
2008-07-22 21:09:48 +02:00
# cancel any active downloads from this channel
if not delete_partial :
for episode in self . active_channel . get_all_episodes ( ) :
2008-07-22 21:09:48 +02:00
services . download_status_manager . cancel_by_url ( episode . url )
2008-05-14 15:38:06 +02:00
# get the URL of the podcast we want to select next
position = self . channels . index ( self . active_channel )
if position == len ( self . channels ) - 1 :
# this is the last podcast, so select the URL
# of the item before this one (i.e. the "new last")
select_url = self . channels [ position - 1 ] . url
else :
# there is a podcast after the deleted one, so
# we simply select the one that comes after it
select_url = self . channels [ position + 1 ] . url
# Remove the channel
2008-06-30 03:10:18 +02:00
self . active_channel . delete ( )
2008-01-14 20:06:26 +01:00
self . channels . remove ( self . active_channel )
save_channels ( self . channels )
2008-05-14 15:38:06 +02:00
# Re-load the channels and select the desired new channel
self . update_feed_cache ( force_update = False , select_url_afterwards = select_url )
2005-11-21 19:21:25 +01:00
except :
2008-04-22 22:24:19 +02:00
log ( ' There has been an error removing the channel. ' , traceback = True , sender = self )
2008-07-11 19:13:04 +02:00
self . update_podcasts_tab ( )
2005-11-21 19:21:25 +01:00
2008-05-02 17:36:43 +02:00
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
2008-06-14 15:17:55 +02:00
def on_item_import_from_file_activate ( self , widget , filename = None ) :
if filename is None :
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 ( )
2008-05-02 17:36:43 +02:00
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 ) )
2005-11-21 19:21:25 +01:00
def on_itemExportChannels_activate ( self , widget , * args ) :
2006-12-09 02:59:53 +01:00
if not self . channels :
2007-03-10 18:42:32 +01:00
title = _ ( ' Nothing to export ' )
2008-05-02 17:36:43 +02:00
message = _ ( ' Your list of podcast subscriptions is empty. Please subscribe to some podcasts first before trying to export your subscription list. ' )
2007-03-10 18:42:32 +01:00
self . show_message ( message , title )
2006-12-09 02:59:53 +01:00
return
2008-05-02 17:36:43 +02:00
if gpodder . interface == gpodder . GUI :
dlg = gtk . FileChooserDialog ( title = _ ( ' Export to OPML ' ) , parent = self . gPodder , 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 ( self . gPodder , gtk . FILE_CHOOSER_ACTION_SAVE )
dlg . set_filter ( self . get_opml_filter ( ) )
2005-12-08 20:47:35 +01:00
response = dlg . run ( )
if response == gtk . RESPONSE_OK :
2007-08-19 16:28:24 +02:00
filename = dlg . get_filename ( )
2008-09-10 16:02:06 +02:00
dlg . destroy ( )
2007-08-19 16:28:24 +02:00
exporter = opml . Exporter ( filename )
2008-09-10 16:02:06 +02:00
if exporter . write ( self . channels ) :
if len ( self . channels ) == 1 :
title = _ ( ' One subscription exported ' )
else :
title = _ ( ' %d subscriptions exported ' ) % len ( self . channels )
self . show_message ( _ ( ' Your podcast list has been successfully exported. ' ) , title )
else :
2007-08-19 16:28:24 +02:00
self . show_message ( _ ( ' Could not export OPML to file. Please check your permissions. ' ) , _ ( ' OPML export failed ' ) )
2008-09-10 16:02:06 +02:00
else :
dlg . destroy ( )
2005-11-21 19:21:25 +01:00
2006-06-13 23:00:31 +02:00
def on_itemImportChannels_activate ( self , widget , * args ) :
2008-03-02 14:22:29 +01:00
gPodderOpmlLister ( ) . get_channels_from_url ( gl . config . opml_url , lambda url : self . add_new_channel ( url , False ) , lambda : self . on_itemDownloadAllNew_activate ( self . gPodder ) )
2006-06-13 23:00:31 +02:00
2006-12-04 14:06:42 +01:00
def on_homepage_activate ( self , widget , * args ) :
2008-03-03 20:38:22 +01:00
util . open_website ( app_website )
2006-12-04 14:06:42 +01:00
2007-12-13 08:05:25 +01:00
def on_wiki_activate ( self , widget , * args ) :
2008-03-03 20:38:22 +01:00
util . open_website ( ' http://wiki.gpodder.org/ ' )
2006-12-04 14:06:42 +01:00
2008-01-14 20:23:49 +01:00
def on_bug_tracker_activate ( self , widget , * args ) :
2008-03-03 20:38:22 +01:00
util . open_website ( ' http://bugs.gpodder.org/ ' )
2008-01-14 20:23:49 +01:00
2005-11-21 19:21:25 +01:00
def on_itemAbout_activate ( self , widget , * args ) :
dlg = gtk . AboutDialog ( )
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
dlg . set_name ( app_name . replace ( ' p ' , ' P ' ) ) # gpodder->gPodder
2005-11-21 19:21:25 +01:00
dlg . set_version ( app_version )
dlg . set_copyright ( app_copyright )
dlg . set_website ( app_website )
2006-04-06 16:11:03 +02:00
dlg . set_translator_credits ( _ ( ' translator-credits ' ) )
2007-08-28 00:18:01 +02:00
dlg . connect ( ' response ' , lambda dlg , response : dlg . destroy ( ) )
2006-12-04 14:06:42 +01:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
# For the "GUI" version, we add some more
# items to the about dialog (credits and logo)
dlg . set_authors ( app_authors )
try :
dlg . set_logo ( gtk . gdk . pixbuf_new_from_file_at_size ( scalable_dir , 200 , 200 ) )
except :
pass
2006-12-04 14:06:42 +01:00
2005-11-21 19:21:25 +01:00
dlg . run ( )
2006-11-29 00:23:16 +01:00
2006-02-04 18:29:17 +01:00
def on_wNotebook_switch_page ( self , widget , * args ) :
2007-03-31 04:00:30 +02:00
page_num = args [ 1 ]
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
page = self . wNotebook . get_nth_page ( page_num )
2008-04-24 18:28:39 +02:00
tab_label = self . wNotebook . get_tab_label ( page ) . get_text ( )
if page_num == 0 and self . active_channel is not None :
self . set_title ( self . active_channel . title )
else :
self . set_title ( tab_label )
2007-03-31 04:00:30 +02:00
if page_num == 0 :
self . play_or_download ( )
else :
self . toolDownload . set_sensitive ( False )
self . toolPlay . set_sensitive ( False )
self . toolTransfer . set_sensitive ( False )
2007-08-24 16:49:41 +02:00
self . toolCancel . set_sensitive ( services . download_status_manager . has_items ( ) )
2006-02-04 18:29:17 +01:00
2007-07-05 23:07:16 +02:00
def on_treeChannels_row_activated ( self , widget , * args ) :
self . on_itemEditChannel_activate ( self . treeChannels )
def on_treeChannels_cursor_changed ( self , widget , * args ) :
2007-09-19 17:04:42 +02:00
( model , iter ) = self . treeChannels . get_selection ( ) . get_selected ( )
2007-07-11 13:05:02 +02:00
2008-04-22 21:16:30 +02:00
if model is not None and iter != None :
2007-09-19 17:04:42 +02:00
id = model . get_path ( iter ) [ 0 ]
self . active_channel = self . channels [ id ]
2006-12-09 02:59:53 +01:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
self . set_title ( self . active_channel . title )
2006-12-16 18:44:13 +01:00
self . itemEditChannel . show_all ( )
self . itemRemoveChannel . show_all ( )
else :
2007-09-19 17:04:42 +02:00
self . active_channel = None
2006-12-16 18:44:13 +01:00
self . itemEditChannel . hide_all ( )
self . itemRemoveChannel . hide_all ( )
2005-11-21 19:21:25 +01:00
self . updateTreeView ( )
2007-09-19 17:04:42 +02:00
def on_entryAddChannel_changed ( self , widget , * args ) :
2008-05-02 17:36:43 +02:00
active = self . entryAddChannel . get_text ( ) not in ( ' ' , self . ENTER_URL_TEXT )
2007-09-19 17:04:42 +02:00
self . btnAddChannel . set_sensitive ( active )
def on_btnAddChannel_clicked ( self , widget , * args ) :
url = self . entryAddChannel . get_text ( )
self . entryAddChannel . set_text ( ' ' )
self . add_new_channel ( url )
2006-03-04 21:45:01 +01:00
def on_btnEditChannel_clicked ( self , widget , * args ) :
self . on_itemEditChannel_activate ( widget , args )
2008-08-09 13:34:38 +02:00
def on_treeAvailable_row_activated ( self , widget , path = None , view_column = None ) :
2008-08-05 14:56:58 +02:00
"""
What this function does depends on from which widget it is called .
It gets the selected episodes of the current podcast and runs one
of the following actions on them :
* Transfer ( to MP3 player , iPod , etc . . )
* Playback / open files
* Show the episode info dialog
* Download episodes
"""
2006-03-29 16:40:08 +02:00
try :
2006-06-22 23:41:32 +02:00
selection = self . treeAvailable . get_selection ( )
2008-08-05 14:56:58 +02:00
( model , paths ) = selection . get_selected_rows ( )
2007-03-18 19:28:17 +01:00
2008-08-05 14:56:58 +02:00
wname = widget . get_name ( )
do_transfer = ( wname in ( ' itemTransferSelected ' , ' toolTransfer ' ) )
do_playback = ( wname in ( ' itemPlaySelected ' , ' itemOpenSelected ' , ' toolPlay ' ) )
do_epdialog = ( wname in ( ' treeAvailable ' , ' item_episode_details ' ) )
2007-03-18 19:28:17 +01:00
2008-08-05 14:56:58 +02:00
episodes = [ ]
for path in paths :
it = model . get_iter ( path )
url = model . get_value ( it , 0 )
episode = self . active_channel . find_episode ( url )
episodes . append ( episode )
2007-03-18 19:28:17 +01:00
2008-08-05 14:56:58 +02:00
if len ( episodes ) == 0 :
log ( ' No episodes selected ' , sender = self )
if do_transfer :
self . on_sync_to_ipod_activate ( widget , episodes )
elif do_playback :
for episode in episodes :
# Make sure to mark the episode as downloaded
if os . path . exists ( episode . local_filename ( ) ) :
episode . channel . addDownloadedItem ( episode )
self . playback_episode ( episode )
elif do_epdialog :
play_callback = lambda : self . playback_episode ( episode )
download_callback = lambda : self . download_episode_list ( [ episode ] )
gPodderEpisode ( episode = episode , download_callback = download_callback , play_callback = play_callback )
else :
self . download_episode_list ( episodes )
2006-03-29 16:40:08 +02:00
except :
2008-08-05 14:56:58 +02:00
log ( ' Error in on_treeAvailable_row_activated ' , traceback = True , sender = self )
2005-11-21 19:21:25 +01:00
2006-12-13 00:11:34 +01:00
def on_treeAvailable_button_release_event ( self , widget , * args ) :
self . play_or_download ( )
2008-01-09 23:46:17 +01:00
def auto_update_procedure ( self , first_run = False ) :
log ( ' auto_update_procedure() got called ' , sender = self )
2008-02-02 11:38:34 +01:00
if not first_run and gl . config . auto_update_feeds and self . minimized :
2008-06-30 03:10:18 +02:00
self . update_feed_cache ( force_update = True )
2008-01-09 23:46:17 +01:00
next_update = 60 * 1000 * gl . config . auto_update_frequency
gobject . timeout_add ( next_update , self . auto_update_procedure )
2006-07-20 15:19:19 +02:00
2005-11-23 20:53:18 +01:00
def on_treeDownloads_row_activated ( self , widget , * args ) :
2007-08-25 18:05:03 +02:00
cancel_urls = [ ]
if self . wNotebook . get_current_page ( ) > 0 :
# Use the download list treeview + model
( tree , column ) = ( self . treeDownloads , 3 )
else :
# Use the available podcasts treeview + model
( tree , column ) = ( self . treeAvailable , 0 )
2006-07-14 22:09:00 +02:00
2007-08-26 17:20:46 +02:00
selection = tree . get_selection ( )
( model , paths ) = selection . get_selected_rows ( )
2007-08-25 18:05:03 +02:00
for path in paths :
url = model . get_value ( model . get_iter ( path ) , column )
cancel_urls . append ( url )
2007-03-10 18:42:32 +01:00
2007-08-25 18:05:03 +02:00
if len ( cancel_urls ) == 0 :
log ( ' Nothing selected. ' , sender = self )
return
if len ( cancel_urls ) == 1 :
2007-03-10 18:42:32 +01:00
title = _ ( ' Cancel download? ' )
message = _ ( " Cancelling this download will remove the partially downloaded file and stop the download. " )
2007-08-25 18:05:03 +02:00
else :
title = _ ( ' Cancel downloads? ' )
message = _ ( " Cancelling the download will stop the %d selected downloads and remove partially downloaded files. " ) % selection . count_selected_rows ( )
2007-03-10 18:42:32 +01:00
if self . show_confirmation ( message , title ) :
2008-05-14 15:38:06 +02:00
services . download_status_manager . start_batch_mode ( )
2007-08-25 18:05:03 +02:00
for url in cancel_urls :
services . download_status_manager . cancel_by_url ( url )
2008-05-14 15:38:06 +02:00
services . download_status_manager . end_batch_mode ( )
2005-11-23 20:53:18 +01:00
def on_btnCancelDownloadStatus_clicked ( self , widget , * args ) :
self . on_treeDownloads_row_activated ( widget , None )
2006-07-20 15:19:19 +02:00
def on_btnCancelAll_clicked ( self , widget , * args ) :
self . treeDownloads . get_selection ( ) . select_all ( )
2007-03-31 04:00:30 +02:00
self . on_treeDownloads_row_activated ( self . toolCancel , None )
2006-07-20 15:19:19 +02:00
self . treeDownloads . get_selection ( ) . unselect_all ( )
2006-03-24 20:08:59 +01:00
def on_btnDownloadedDelete_clicked ( self , widget , * args ) :
2008-02-26 16:49:59 +01:00
if self . active_channel is None :
2007-08-26 17:20:46 +02:00
return
2006-12-09 02:59:53 +01:00
channel_url = self . active_channel . url
selection = self . treeAvailable . get_selection ( )
2007-09-09 18:49:56 +02:00
( model , paths ) = selection . get_selected_rows ( )
2008-06-30 03:10:18 +02:00
2006-07-14 22:09:00 +02:00
if selection . count_selected_rows ( ) == 0 :
2006-11-17 15:26:10 +01:00
log ( ' Nothing selected - will not remove any downloaded episode. ' )
2006-07-14 22:09:00 +02:00
return
2006-07-14 21:10:47 +02:00
if selection . count_selected_rows ( ) == 1 :
2007-12-12 19:50:52 +01:00
episode_title = saxutils . escape ( model . get_value ( model . get_iter ( paths [ 0 ] ) , 1 ) )
2008-06-30 03:10:18 +02:00
episode = db . load_episode ( model . get_value ( model . get_iter ( paths [ 0 ] ) , 0 ) )
if episode [ ' is_locked ' ] :
2007-12-12 19:50:52 +01:00
title = _ ( ' %s is locked ' ) % episode_title
message = _ ( ' You cannot delete this locked episode. You must unlock it before you can delete it. ' )
self . notification ( message , title )
return
title = _ ( ' Remove %s ? ' ) % episode_title
2007-03-10 18:42:32 +01:00
message = _ ( " If you remove this episode, it will be deleted from your computer. If you want to listen to this episode again, you will have to re-download it. " )
2007-09-09 18:49:56 +02:00
else :
title = _ ( ' Remove %d episodes? ' ) % selection . count_selected_rows ( )
message = _ ( ' If you remove these episodes, they will be deleted from your computer. If you want to listen to any of these episodes again, you will have to re-download the episodes in question. ' )
2007-12-12 19:50:52 +01:00
locked_count = 0
for path in paths :
2008-06-30 03:10:18 +02:00
episode = db . load_episode ( model . get_value ( model . get_iter ( path ) , 0 ) )
if episode [ ' is_locked ' ] :
2007-12-12 19:50:52 +01:00
locked_count + = 1
if selection . count_selected_rows ( ) == locked_count :
title = _ ( ' Episodes are locked ' )
message = _ ( ' The selected episodes are locked. Please unlock the episodes that you want to delete before trying to delete them. ' )
self . notification ( message , title )
return
elif locked_count > 0 :
title = _ ( ' Remove %d out of %d episodes? ' ) % ( selection . count_selected_rows ( ) - locked_count , selection . count_selected_rows ( ) )
message = _ ( ' The selection contains locked episodes. These will not be deleted. If you want to listen to any of these episodes again, then you will have to re-download them. ' )
2006-07-14 21:10:47 +02:00
# if user confirms deletion, let's remove some stuff ;)
2007-03-10 18:42:32 +01:00
if self . show_confirmation ( message , title ) :
2007-02-03 11:48:32 +01:00
try :
2006-07-14 21:10:47 +02:00
# iterate over the selection, see also on_treeDownloads_row_activated
2007-09-09 18:49:56 +02:00
for path in paths :
url = model . get_value ( model . get_iter ( path ) , 0 )
2007-03-14 20:35:15 +01:00
self . active_channel . delete_episode_by_url ( url )
2006-07-14 21:10:47 +02:00
# now, clear local db cache so we can re-read it
2006-03-29 14:41:34 +02:00
self . updateComboBox ( )
2007-02-03 11:48:32 +01:00
except :
log ( ' Error while deleting (some) downloads. ' )
2007-09-09 18:49:56 +02:00
2007-02-03 11:48:32 +01:00
# only delete partial files if we do not have any downloads in progress
2007-08-24 16:49:41 +02:00
delete_partial = not services . download_status_manager . has_items ( )
2008-03-02 14:22:29 +01:00
gl . clean_up_downloads ( delete_partial )
2007-08-20 15:45:46 +02:00
self . updateTreeView ( )
2006-03-24 20:08:59 +01:00
2008-04-06 02:19:03 +02:00
def on_key_press ( self , widget , event ) :
# Currently, we only handle Maemo hardware keys here,
# so if we are not a Maemo app, we don't do anything!
if gpodder . interface != gpodder . MAEMO :
return
if event . keyval == gtk . keysyms . F6 :
if self . fullscreen :
self . window . unfullscreen ( )
else :
self . window . fullscreen ( )
if event . keyval == gtk . keysyms . Escape :
new_visibility = not self . vboxChannelNavigator . get_property ( ' visible ' )
self . vboxChannelNavigator . set_property ( ' visible ' , new_visibility )
self . column_size . set_visible ( not new_visibility )
self . column_released . set_visible ( not new_visibility )
diff = 0
if event . keyval == gtk . keysyms . F7 : #plus
diff = 1
elif event . keyval == gtk . keysyms . F8 : #minus
diff = - 1
if diff != 0 :
selection = self . treeChannels . get_selection ( )
( model , iter ) = selection . get_selected ( )
selection . select_path ( ( ( model . get_path ( iter ) [ 0 ] + diff ) % len ( model ) , ) )
self . on_treeChannels_cursor_changed ( self . treeChannels )
2008-01-09 23:46:17 +01:00
def window_state_event ( self , widget , event ) :
2008-04-06 02:19:03 +02:00
if event . new_window_state & gtk . gdk . WINDOW_STATE_FULLSCREEN :
self . fullscreen = True
else :
self . fullscreen = False
2008-01-09 23:46:17 +01:00
old_minimized = self . minimized
2008-09-01 20:42:00 +02:00
self . minimized = bool ( event . new_window_state & gtk . gdk . WINDOW_STATE_ICONIFIED )
if gpodder . interface == gpodder . MAEMO :
self . minimized = bool ( event . new_window_state & gtk . gdk . WINDOW_STATE_WITHDRAWN )
2007-07-11 13:05:02 +02:00
2008-01-09 23:46:17 +01:00
if old_minimized != self . minimized and self . tray_icon :
self . gPodder . set_skip_taskbar_hint ( self . minimized )
elif not self . tray_icon :
self . gPodder . set_skip_taskbar_hint ( False )
if gl . config . minimize_to_tray and self . tray_icon :
self . tray_icon . set_visible ( self . minimized )
def uniconify_main_window ( self ) :
if self . minimized :
self . gPodder . present ( )
def iconify_main_window ( self ) :
if not self . minimized :
2008-07-11 19:13:04 +02:00
self . gPodder . iconify ( )
def update_podcasts_tab ( self ) :
if len ( self . channels ) :
self . label2 . set_text ( _ ( ' Podcasts ( %d ) ' ) % len ( self . channels ) )
else :
self . label2 . set_text ( _ ( ' Podcasts ' ) )
2007-07-11 13:05:02 +02:00
2007-08-28 00:18:01 +02:00
class gPodderChannel ( GladeWidget ) :
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ' btn_website ' , ' btnOK ' , ' channel_description ' ]
2005-11-21 19:21:25 +01:00
def new ( self ) :
2008-04-06 02:19:03 +02:00
global WEB_BROWSER_ICON
2008-06-14 13:43:53 +02:00
self . changed = False
2008-04-06 02:19:03 +02:00
self . image3167 . set_property ( ' icon-name ' , WEB_BROWSER_ICON )
2007-08-28 00:18:01 +02:00
self . gPodderChannel . set_title ( self . channel . title )
self . entryTitle . set_text ( self . channel . title )
self . entryURL . set_text ( self . channel . url )
self . LabelDownloadTo . set_text ( self . channel . save_dir )
self . LabelWebsite . set_text ( self . channel . link )
self . cbNoSync . set_active ( not self . channel . sync_to_devices )
2008-01-28 12:38:53 +01:00
self . musicPlaylist . set_text ( self . channel . device_playlist_name )
2007-08-28 00:18:01 +02:00
if self . channel . username :
self . FeedUsername . set_text ( self . channel . username )
if self . channel . password :
self . FeedPassword . set_text ( self . channel . password )
2007-09-19 17:04:42 +02:00
2008-06-14 13:43:53 +02:00
services . cover_downloader . register ( ' cover-available ' , self . cover_download_finished )
services . cover_downloader . request_cover ( self . channel )
2008-03-03 20:54:42 +01:00
# Hide the website button if we don't have a valid URL
if not self . channel . link :
self . btn_website . hide_all ( )
2006-03-31 18:20:18 +02:00
2006-03-04 21:45:01 +01:00
b = gtk . TextBuffer ( )
2007-08-28 00:18:01 +02:00
b . set_text ( self . channel . description )
2006-03-04 21:45:01 +01:00
self . channel_description . set_buffer ( b )
2007-07-11 13:05:02 +02:00
2007-09-19 17:04:42 +02:00
#Add Drag and Drop Support
flags = gtk . DEST_DEFAULT_ALL
targets = [ ( ' text/uri-list ' , 0 , 2 ) , ( ' text/plain ' , 0 , 4 ) ]
actions = gtk . gdk . ACTION_DEFAULT | gtk . gdk . ACTION_COPY
self . vboxCoverEditor . drag_dest_set ( flags , targets , actions )
self . vboxCoverEditor . connect ( ' drag_data_received ' , self . drag_data_received )
2008-03-03 20:54:42 +01:00
def on_btn_website_clicked ( self , widget ) :
util . open_website ( self . channel . link )
2008-06-14 13:43:53 +02:00
def on_btnDownloadCover_clicked ( self , widget ) :
if gpodder . interface == gpodder . GUI :
dlg = gtk . FileChooserDialog ( title = _ ( ' Select new podcast cover artwork ' ) , parent = self . gPodderChannel , 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 . gPodderChannel , gtk . FILE_CHOOSER_ACTION_OPEN )
2007-09-19 17:04:42 +02:00
2008-06-14 13:43:53 +02:00
if dlg . run ( ) == gtk . RESPONSE_OK :
url = dlg . get_uri ( )
services . cover_downloader . replace_cover ( self . channel , url )
2007-09-19 17:04:42 +02:00
2008-06-14 13:43:53 +02:00
dlg . destroy ( )
2007-09-19 17:04:42 +02:00
2008-06-14 13:43:53 +02:00
def on_btnClearCover_clicked ( self , widget ) :
services . cover_downloader . replace_cover ( self . channel )
2007-09-19 17:04:42 +02:00
2008-06-14 13:43:53 +02:00
def cover_download_finished ( self , channel_url , pixbuf ) :
if pixbuf is not None :
self . imgCover . set_from_pixbuf ( pixbuf )
self . gPodderChannel . show ( )
2007-09-19 17:04:42 +02:00
def drag_data_received ( self , widget , content , x , y , sel , ttype , time ) :
files = sel . data . strip ( ) . split ( ' \n ' )
if len ( files ) != 1 :
self . show_message ( _ ( ' You can only drop a single image or URL here. ' ) , _ ( ' Drag and drop ' ) )
return
file = files [ 0 ]
2008-06-14 13:43:53 +02:00
if file . startswith ( ' file:// ' ) or file . startswith ( ' http:// ' ) :
services . cover_downloader . replace_cover ( self . channel , file )
2007-09-19 17:04:42 +02:00
return
self . show_message ( _ ( ' You can only drop local files and http:// URLs here. ' ) , _ ( ' Drag and drop ' ) )
2005-11-21 19:21:25 +01:00
def on_gPodderChannel_destroy ( self , widget , * args ) :
2008-06-30 03:10:18 +02:00
services . cover_downloader . unregister ( ' cover-available ' , self . cover_download_finished )
2005-11-21 19:21:25 +01:00
def on_btnOK_clicked ( self , widget , * args ) :
2008-03-03 23:09:34 +01:00
entered_url = self . entryURL . get_text ( )
channel_url = self . channel . url
if entered_url != channel_url :
2008-05-02 17:36:43 +02:00
if self . show_confirmation ( _ ( ' Do you really want to move this podcast to <b> %s </b>? ' ) % ( saxutils . escape ( entered_url ) , ) , _ ( ' Really change URL? ' ) ) :
2008-03-03 23:09:34 +01:00
if hasattr ( self , ' callback_change_url ' ) :
self . gPodderChannel . hide_all ( )
self . callback_change_url ( channel_url , entered_url )
2007-07-11 13:05:02 +02:00
self . channel . sync_to_devices = not self . cbNoSync . get_active ( )
self . channel . device_playlist_name = self . musicPlaylist . get_text ( )
self . channel . set_custom_title ( self . entryTitle . get_text ( ) )
2007-07-19 14:44:12 +02:00
self . channel . username = self . FeedUsername . get_text ( ) . strip ( )
self . channel . password = self . FeedPassword . get_text ( )
2008-06-30 03:10:18 +02:00
self . channel . save ( )
2005-11-21 19:21:25 +01:00
2007-07-11 13:05:02 +02:00
self . gPodderChannel . destroy ( )
2008-06-30 17:40:48 +02:00
self . callback_closed ( )
2005-11-21 19:21:25 +01:00
2008-04-24 19:42:57 +02:00
class gPodderAddPodcastDialog ( GladeWidget ) :
finger_friendly_widgets = [ ' btn_close ' , ' btn_add ' ]
def new ( self ) :
if not hasattr ( self , ' url_callback ' ) :
log ( ' No url callback set ' , sender = self )
self . url_callback = None
2008-08-04 16:54:18 +02:00
if hasattr ( self , ' custom_label ' ) :
self . label_add . set_text ( self . custom_label )
if hasattr ( self , ' custom_title ' ) :
self . gPodderAddPodcastDialog . set_title ( self . custom_title )
2008-04-24 19:42:57 +02:00
def on_btn_close_clicked ( self , widget ) :
self . gPodderAddPodcastDialog . destroy ( )
def on_entry_url_changed ( self , widget ) :
self . btn_add . set_sensitive ( self . entry_url . get_text ( ) . strip ( ) != ' ' )
def on_btn_add_clicked ( self , widget ) :
url = self . entry_url . get_text ( )
self . on_btn_close_clicked ( widget )
if self . url_callback is not None :
self . url_callback ( url )
class gPodderMaemoPreferences ( GladeWidget ) :
finger_friendly_widgets = [ ' btn_close ' , ' label128 ' , ' label129 ' , ' btn_advanced ' ]
def new ( self ) :
gl . config . connect_gtk_togglebutton ( ' update_on_startup ' , self . update_on_startup )
gl . config . connect_gtk_togglebutton ( ' display_tray_icon ' , self . show_tray_icon )
gl . config . connect_gtk_togglebutton ( ' enable_notifications ' , self . show_notifications )
2008-05-02 17:45:45 +02:00
gl . config . connect_gtk_togglebutton ( ' on_quit_ask ' , self . on_quit_ask )
2008-04-24 19:42:57 +02:00
self . restart_required = False
self . show_tray_icon . connect ( ' clicked ' , self . on_restart_required )
self . show_notifications . connect ( ' clicked ' , self . on_restart_required )
def on_restart_required ( self , widget ) :
self . restart_required = True
def on_btn_advanced_clicked ( self , widget ) :
self . gPodderMaemoPreferences . destroy ( )
gPodderConfigEditor ( )
def on_btn_close_clicked ( self , widget ) :
self . gPodderMaemoPreferences . destroy ( )
if self . restart_required :
self . show_message ( _ ( ' Please restart gPodder for the changes to take effect. ' ) )
2005-11-21 19:21:25 +01:00
2007-08-28 00:18:01 +02:00
class gPodderProperties ( GladeWidget ) :
2006-01-09 00:52:40 +01:00
def new ( self ) :
2007-11-08 20:11:57 +01:00
if not hasattr ( self , ' callback_finished ' ) :
self . callback_finished = None
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
self . table13 . hide_all ( ) # bluetooth
self . table5 . hide_all ( ) # player
self . table6 . hide_all ( ) # bittorrent
self . gPodderProperties . fullscreen ( )
2007-11-02 17:37:14 +01:00
gl . config . connect_gtk_editable ( ' http_proxy ' , self . httpProxy )
gl . config . connect_gtk_editable ( ' ftp_proxy ' , self . ftpProxy )
gl . config . connect_gtk_editable ( ' player ' , self . openApp )
2008-01-21 09:57:03 +01:00
gl . config . connect_gtk_editable ( ' videoplayer ' , self . openVideoApp )
2007-11-02 17:37:14 +01:00
gl . config . connect_gtk_editable ( ' custom_sync_name ' , self . entryCustomSyncName )
gl . config . connect_gtk_togglebutton ( ' custom_sync_name_enabled ' , self . cbCustomSyncName )
2008-02-04 11:29:41 +01:00
gl . config . connect_gtk_togglebutton ( ' auto_download_when_minimized ' , self . downloadnew )
2007-11-02 17:37:14 +01:00
gl . config . connect_gtk_togglebutton ( ' use_gnome_bittorrent ' , self . radio_gnome_bittorrent )
gl . config . connect_gtk_togglebutton ( ' update_on_startup ' , self . updateonstartup )
gl . config . connect_gtk_togglebutton ( ' only_sync_not_played ' , self . only_sync_not_played )
2007-12-03 21:26:18 +01:00
gl . config . connect_gtk_togglebutton ( ' fssync_channel_subfolders ' , self . cbChannelSubfolder )
gl . config . connect_gtk_togglebutton ( ' on_sync_mark_played ' , self . on_sync_mark_played )
2007-12-18 10:18:33 +01:00
gl . config . connect_gtk_togglebutton ( ' on_sync_delete ' , self . on_sync_delete )
2007-11-02 17:37:14 +01:00
gl . config . connect_gtk_togglebutton ( ' proxy_use_environment ' , self . cbEnvironmentVariables )
gl . config . connect_gtk_filechooser ( ' bittorrent_dir ' , self . chooserBitTorrentTo )
2007-12-12 20:57:50 +01:00
gl . config . connect_gtk_spinbutton ( ' episode_old_age ' , self . episode_old_age )
gl . config . connect_gtk_togglebutton ( ' auto_remove_old_episodes ' , self . auto_remove_old_episodes )
2008-01-09 23:46:17 +01:00
gl . config . connect_gtk_togglebutton ( ' auto_update_feeds ' , self . auto_update_feeds )
gl . config . connect_gtk_spinbutton ( ' auto_update_frequency ' , self . auto_update_frequency )
gl . config . connect_gtk_togglebutton ( ' display_tray_icon ' , self . display_tray_icon )
gl . config . connect_gtk_togglebutton ( ' minimize_to_tray ' , self . minimize_to_tray )
2008-02-04 11:29:41 +01:00
gl . config . connect_gtk_togglebutton ( ' enable_notifications ' , self . enable_notifications )
2008-01-09 23:46:17 +01:00
gl . config . connect_gtk_togglebutton ( ' start_iconified ' , self . start_iconified )
2008-01-21 10:52:09 +01:00
gl . config . connect_gtk_togglebutton ( ' bluetooth_enabled ' , self . bluetooth_enabled )
gl . config . connect_gtk_togglebutton ( ' bluetooth_ask_always ' , self . bluetooth_ask_always )
gl . config . connect_gtk_togglebutton ( ' bluetooth_ask_never ' , self . bluetooth_ask_never )
gl . config . connect_gtk_togglebutton ( ' bluetooth_use_converter ' , self . bluetooth_use_converter )
gl . config . connect_gtk_filechooser ( ' bluetooth_converter ' , self . bluetooth_converter , is_for_files = True )
2008-03-11 18:55:53 +01:00
gl . config . connect_gtk_togglebutton ( ' ipod_write_gtkpod_extended ' , self . ipod_write_gtkpod_extended )
2008-06-13 09:41:36 +02:00
gl . config . connect_gtk_togglebutton ( ' mp3_player_delete_played ' , self . delete_episodes_marked_played )
2008-01-09 23:46:17 +01:00
2008-02-04 11:29:41 +01:00
self . enable_notifications . set_sensitive ( self . display_tray_icon . get_active ( ) )
self . minimize_to_tray . set_sensitive ( self . display_tray_icon . get_active ( ) )
2007-09-08 16:49:54 +02:00
self . entryCustomSyncName . set_sensitive ( self . cbCustomSyncName . get_active ( ) )
2008-05-25 17:17:48 +02:00
self . radio_gnome_bittorrent . set_active ( gl . config . use_gnome_bittorrent )
self . radio_copy_torrents . set_active ( not gl . config . use_gnome_bittorrent )
2007-11-02 17:37:14 +01:00
self . iPodMountpoint . set_label ( gl . config . ipod_mount )
self . filesystemMountpoint . set_label ( gl . config . mp3_player_folder )
2008-01-21 10:52:09 +01:00
self . bluetooth_device_name . set_markup ( ' <b> %s </b> ' % gl . config . bluetooth_device_name )
2007-12-09 13:22:50 +01:00
self . chooserDownloadTo . set_current_folder ( gl . downloaddir )
2007-11-02 17:37:14 +01:00
2008-06-13 09:41:36 +02:00
self . on_sync_delete . set_sensitive ( not self . delete_episodes_marked_played . get_active ( ) )
self . on_sync_mark_played . set_sensitive ( not self . delete_episodes_marked_played . get_active ( ) )
2007-03-17 13:11:13 +01:00
if tagging_supported ( ) :
2007-11-02 17:37:14 +01:00
gl . config . connect_gtk_togglebutton ( ' update_tags ' , self . updatetags )
2007-03-17 13:11:13 +01:00
else :
self . updatetags . set_sensitive ( False )
new_label = ' %s ( %s ) ' % ( self . updatetags . get_label ( ) , _ ( ' needs python-eyed3 ' ) )
self . updatetags . set_label ( new_label )
2007-11-02 17:37:14 +01:00
2006-12-06 21:25:26 +01:00
# device type
self . comboboxDeviceType . set_active ( 0 )
2007-11-02 17:37:14 +01:00
if gl . config . device_type == ' ipod ' :
2006-12-06 21:25:26 +01:00
self . comboboxDeviceType . set_active ( 1 )
2007-11-02 17:37:14 +01:00
elif gl . config . device_type == ' filesystem ' :
2006-12-06 21:25:26 +01:00
self . comboboxDeviceType . set_active ( 2 )
2008-09-01 21:26:35 +02:00
elif gl . config . device_type == ' mtp ' :
self . comboboxDeviceType . set_active ( 3 )
2007-11-02 17:37:14 +01:00
2006-03-30 00:07:27 +02:00
# setup cell renderers
cellrenderer = gtk . CellRendererPixbuf ( )
2008-01-21 09:57:03 +01:00
self . comboAudioPlayerApp . pack_start ( cellrenderer , False )
self . comboAudioPlayerApp . add_attribute ( cellrenderer , ' pixbuf ' , 2 )
cellrenderer = gtk . CellRendererText ( )
self . comboAudioPlayerApp . pack_start ( cellrenderer , True )
self . comboAudioPlayerApp . add_attribute ( cellrenderer , ' markup ' , 0 )
cellrenderer = gtk . CellRendererPixbuf ( )
self . comboVideoPlayerApp . pack_start ( cellrenderer , False )
self . comboVideoPlayerApp . add_attribute ( cellrenderer , ' pixbuf ' , 2 )
2006-03-30 00:07:27 +02:00
cellrenderer = gtk . CellRendererText ( )
2008-01-21 09:57:03 +01:00
self . comboVideoPlayerApp . pack_start ( cellrenderer , True )
self . comboVideoPlayerApp . add_attribute ( cellrenderer , ' markup ' , 0 )
2006-03-31 18:20:18 +02:00
2008-03-20 11:24:26 +01:00
if not hasattr ( self , ' user_apps_reader ' ) :
self . user_apps_reader = UserAppsReader ( [ ' audio ' , ' video ' ] )
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
self . user_apps_reader . read ( )
2008-03-20 11:24:26 +01:00
self . comboAudioPlayerApp . set_model ( self . user_apps_reader . get_applications_as_model ( ' audio ' ) )
index = self . find_active_audio_app ( )
self . comboAudioPlayerApp . set_active ( index )
self . comboVideoPlayerApp . set_model ( self . user_apps_reader . get_applications_as_model ( ' video ' ) )
index = self . find_active_video_app ( )
self . comboVideoPlayerApp . set_active ( index )
2007-11-02 17:37:14 +01:00
self . ipodIcon . set_from_icon_name ( ' gnome-dev-ipod ' , gtk . ICON_SIZE_BUTTON )
2006-04-09 18:18:47 +02:00
def update_mountpoint ( self , ipod ) :
2008-02-26 16:49:59 +01:00
if ipod is None or ipod . mount_point is None :
2006-12-06 21:25:26 +01:00
self . iPodMountpoint . set_label ( ' ' )
2006-04-09 18:18:47 +02:00
else :
2006-12-06 21:25:26 +01:00
self . iPodMountpoint . set_label ( ipod . mount_point )
2008-01-21 10:52:09 +01:00
def on_bluetooth_select_device_clicked ( self , widget ) :
2008-03-22 21:08:54 +01:00
# Stupid GTK doesn't provide us with a method to directly
# edit the text of a gtk.Button without "destroying" the
# image on it, so we dig into the button's widget tree and
# get the gtk.Image and gtk.Label and edit the label directly.
alignment = self . bluetooth_select_device . get_child ( )
hbox = alignment . get_child ( )
( image , label ) = hbox . get_children ( )
old_text = label . get_text ( )
label . set_text ( _ ( ' Searching... ' ) )
2008-01-21 10:52:09 +01:00
self . bluetooth_select_device . set_sensitive ( False )
2008-03-22 21:08:54 +01:00
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
# FIXME: Make bluetooth device discovery threaded, so
# the GUI doesn't freeze while we are searching for devices
2008-03-19 15:43:22 +01:00
found = False
2008-01-21 10:52:09 +01:00
for name , address in util . discover_bluetooth_devices ( ) :
if self . show_confirmation ( ' Use this device as your bluetooth device? ' , name ) :
gl . config . bluetooth_device_name = name
gl . config . bluetooth_device_address = address
self . bluetooth_device_name . set_markup ( ' <b> %s </b> ' % gl . config . bluetooth_device_name )
2008-03-19 15:43:22 +01:00
found = True
break
if not found :
self . show_message ( ' No more devices found ' , ' Scan finished ' )
2008-01-21 10:52:09 +01:00
self . bluetooth_select_device . set_sensitive ( True )
2008-03-22 21:08:54 +01:00
label . set_text ( old_text )
2006-04-09 18:18:47 +02:00
2008-01-21 09:57:03 +01:00
def find_active_audio_app ( self ) :
model = self . comboAudioPlayerApp . get_model ( )
2006-03-30 00:07:27 +02:00
iter = model . get_iter_first ( )
index = 0
2008-01-21 09:57:03 +01:00
while iter is not None :
command = model . get_value ( iter , 1 )
2006-03-30 00:07:27 +02:00
if command == self . openApp . get_text ( ) :
return index
2008-01-21 09:57:03 +01:00
iter = model . iter_next ( iter )
index + = 1
# return last item = custom command
return index - 1
def find_active_video_app ( self ) :
model = self . comboVideoPlayerApp . get_model ( )
iter = model . get_iter_first ( )
index = 0
while iter is not None :
command = model . get_value ( iter , 1 )
if command == self . openVideoApp . get_text ( ) :
return index
iter = model . iter_next ( iter )
index + = 1
2006-03-30 00:07:27 +02:00
# return last item = custom command
return index - 1
2006-12-20 17:38:36 +01:00
def set_download_dir ( self , new_download_dir , event = None ) :
gl . downloaddir = self . chooserDownloadTo . get_filename ( )
2007-08-28 00:18:01 +02:00
if gl . downloaddir != self . chooserDownloadTo . get_filename ( ) :
2008-01-15 14:54:22 +01:00
self . notification ( _ ( ' There has been an error moving your downloads to the specified location. The old download directory will be used instead. ' ) , _ ( ' Error moving downloads ' ) )
2006-12-20 17:38:36 +01:00
if event :
event . set ( )
2008-01-09 23:46:17 +01:00
def on_auto_update_feeds_toggled ( self , widget , * args ) :
self . auto_update_frequency . set_sensitive ( widget . get_active ( ) )
2008-02-04 11:29:41 +01:00
def on_display_tray_icon_toggled ( self , widget , * args ) :
self . enable_notifications . set_sensitive ( widget . get_active ( ) )
self . minimize_to_tray . set_sensitive ( widget . get_active ( ) )
2007-09-08 16:49:54 +02:00
def on_cbCustomSyncName_toggled ( self , widget , * args ) :
self . entryCustomSyncName . set_sensitive ( widget . get_active ( ) )
2008-06-13 09:41:36 +02:00
def on_only_sync_not_played_toggled ( self , widget , * args ) :
self . delete_episodes_marked_played . set_sensitive ( widget . get_active ( ) )
if not widget . get_active ( ) :
self . delete_episodes_marked_played . set_active ( False )
def on_delete_episodes_marked_played_toggled ( self , widget , * args ) :
if widget . get_active ( ) and self . only_sync_not_played . get_active ( ) :
self . on_sync_leave . set_active ( True )
self . on_sync_delete . set_sensitive ( not widget . get_active ( ) )
self . on_sync_mark_played . set_sensitive ( not widget . get_active ( ) )
2007-09-08 16:49:54 +02:00
def on_btnCustomSyncNameHelp_clicked ( self , widget ) :
examples = [
2007-09-21 00:18:57 +02:00
' <i> {episode.title} </i> -> <b>Interview with RMS</b> ' ,
' <i> {episode.basename} </i> -> <b>70908-interview-rms</b> ' ,
' <i> {episode.published} </i> -> <b>20070908</b> '
2007-09-08 16:49:54 +02:00
]
info = [
_ ( ' You can specify a custom format string for the file names on your MP3 player here. ' ) ,
_ ( ' The format string will be used to generate a file name on your device. The file extension (e.g. " .mp3 " ) will be added automatically. ' ) ,
' \n ' . join ( [ ' %s ' % s for s in examples ] )
]
self . show_message ( ' \n \n ' . join ( info ) , _ ( ' Custom format strings ' ) )
2006-01-09 00:52:40 +01:00
def on_gPodderProperties_destroy ( self , widget , * args ) :
2007-03-17 13:31:37 +01:00
self . on_btnOK_clicked ( widget , * args )
2006-01-09 00:52:40 +01:00
2008-02-07 22:19:03 +01:00
def on_btnConfigEditor_clicked ( self , widget , * args ) :
self . on_btnOK_clicked ( widget , * args )
gPodderConfigEditor ( )
2008-01-21 09:57:03 +01:00
def on_comboAudioPlayerApp_changed ( self , widget , * args ) :
2006-03-30 00:07:27 +02:00
# find out which one
2008-01-21 09:57:03 +01:00
iter = self . comboAudioPlayerApp . get_active_iter ( )
model = self . comboAudioPlayerApp . get_model ( )
2006-03-30 00:07:27 +02:00
command = model . get_value ( iter , 1 )
if command == ' ' :
self . openApp . set_sensitive ( True )
self . openApp . show ( )
self . labelCustomCommand . show ( )
else :
self . openApp . set_text ( command )
self . openApp . set_sensitive ( False )
self . openApp . hide ( )
self . labelCustomCommand . hide ( )
2008-01-21 09:57:03 +01:00
def on_comboVideoPlayerApp_changed ( self , widget , * args ) :
# find out which one
iter = self . comboVideoPlayerApp . get_active_iter ( )
model = self . comboVideoPlayerApp . get_model ( )
command = model . get_value ( iter , 1 )
if command == ' ' :
self . openVideoApp . set_sensitive ( True )
self . openVideoApp . show ( )
2008-04-17 17:45:29 +02:00
self . label115 . show ( )
2008-01-21 09:57:03 +01:00
else :
self . openVideoApp . set_text ( command )
self . openVideoApp . set_sensitive ( False )
self . openVideoApp . hide ( )
2008-04-17 17:45:29 +02:00
self . label115 . hide ( )
2007-05-05 10:10:10 +02:00
def on_cbEnvironmentVariables_toggled ( self , widget , * args ) :
sens = not self . cbEnvironmentVariables . get_active ( )
self . httpProxy . set_sensitive ( sens )
self . ftpProxy . set_sensitive ( sens )
2006-12-06 21:25:26 +01:00
def on_comboboxDeviceType_changed ( self , widget , * args ) :
active_item = self . comboboxDeviceType . get_active ( )
2007-11-08 12:12:06 +01:00
# None
sync_widgets = ( self . only_sync_not_played , self . labelSyncOptions ,
2007-12-03 21:26:18 +01:00
self . imageSyncOptions , self . separatorSyncOptions ,
2007-12-18 10:18:33 +01:00
self . on_sync_mark_played , self . on_sync_delete ,
2008-06-13 09:41:36 +02:00
self . on_sync_leave , self . label_after_sync , self . delete_episodes_marked_played )
2007-11-08 12:12:06 +01:00
for widget in sync_widgets :
if active_item == 0 :
widget . hide_all ( )
else :
widget . show_all ( )
2006-12-06 21:25:26 +01:00
# iPod
2008-03-11 18:55:53 +01:00
ipod_widgets = ( self . ipodLabel , self . btn_iPodMountpoint ,
self . ipod_write_gtkpod_extended )
2007-11-08 12:12:06 +01:00
for widget in ipod_widgets :
if active_item == 1 :
widget . show_all ( )
else :
widget . hide_all ( )
2006-12-06 21:25:26 +01:00
# filesystem-based MP3 player
2007-11-08 12:12:06 +01:00
fs_widgets = ( self . filesystemLabel , self . btn_filesystemMountpoint ,
self . cbChannelSubfolder , self . cbCustomSyncName ,
self . entryCustomSyncName , self . btnCustomSyncNameHelp )
for widget in fs_widgets :
if active_item == 2 :
widget . show_all ( )
else :
widget . hide_all ( )
2006-12-06 21:25:26 +01:00
def on_btn_iPodMountpoint_clicked ( self , widget , * args ) :
fs = gtk . FileChooserDialog ( title = _ ( ' Select iPod mountpoint ' ) , action = gtk . FILE_CHOOSER_ACTION_SELECT_FOLDER )
fs . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
fs . add_button ( gtk . STOCK_OPEN , gtk . RESPONSE_OK )
2007-12-09 13:22:50 +01:00
fs . set_current_folder ( self . iPodMountpoint . get_label ( ) )
2006-12-06 21:25:26 +01:00
if fs . run ( ) == gtk . RESPONSE_OK :
self . iPodMountpoint . set_label ( fs . get_filename ( ) )
fs . destroy ( )
def on_btn_FilesystemMountpoint_clicked ( self , widget , * args ) :
fs = gtk . FileChooserDialog ( title = _ ( ' Select folder for MP3 player ' ) , action = gtk . FILE_CHOOSER_ACTION_SELECT_FOLDER )
fs . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
fs . add_button ( gtk . STOCK_OPEN , gtk . RESPONSE_OK )
2007-12-09 13:22:50 +01:00
fs . set_current_folder ( self . filesystemMountpoint . get_label ( ) )
2006-12-06 21:25:26 +01:00
if fs . run ( ) == gtk . RESPONSE_OK :
self . filesystemMountpoint . set_label ( fs . get_filename ( ) )
fs . destroy ( )
2006-01-09 00:52:40 +01:00
def on_btnOK_clicked ( self , widget , * args ) :
2007-11-02 17:37:14 +01:00
gl . config . ipod_mount = self . iPodMountpoint . get_label ( )
gl . config . mp3_player_folder = self . filesystemMountpoint . get_label ( )
2006-12-20 17:38:36 +01:00
if gl . downloaddir != self . chooserDownloadTo . get_filename ( ) :
new_download_dir = self . chooserDownloadTo . get_filename ( )
2007-08-07 20:11:31 +02:00
download_dir_size = util . calculate_size ( gl . downloaddir )
2007-11-03 14:18:59 +01:00
download_dir_size_string = gl . format_filesize ( download_dir_size )
2006-12-20 17:38:36 +01:00
event = Event ( )
dlg = gtk . Dialog ( _ ( ' Moving downloads folder ' ) , self . gPodderProperties )
dlg . vbox . set_spacing ( 5 )
dlg . set_border_width ( 5 )
label = gtk . Label ( )
label . set_line_wrap ( True )
2007-10-31 15:25:16 +01:00
label . set_markup ( _ ( ' Moving downloads from <b> %s </b> to <b> %s </b>... ' ) % ( saxutils . escape ( gl . downloaddir ) , saxutils . escape ( new_download_dir ) , ) )
2006-12-20 17:38:36 +01:00
myprogressbar = gtk . ProgressBar ( )
# put it all together
dlg . vbox . pack_start ( label )
dlg . vbox . pack_end ( myprogressbar )
# switch windows
dlg . show_all ( )
self . gPodderProperties . hide_all ( )
# hide action area and separator line
dlg . action_area . hide ( )
dlg . set_has_separator ( False )
args = ( new_download_dir , event , )
thread = Thread ( target = self . set_download_dir , args = args )
thread . start ( )
while not event . isSet ( ) :
2008-03-03 23:09:34 +01:00
try :
new_download_dir_size = util . calculate_size ( new_download_dir )
except :
new_download_dir_size = 0
2007-09-25 22:06:48 +02:00
if download_dir_size > 0 :
fract = ( 1.00 * new_download_dir_size ) / ( 1.00 * download_dir_size )
else :
fract = 0.0
2006-12-20 17:38:36 +01:00
if fract < 0.99 :
2007-11-03 14:18:59 +01:00
myprogressbar . set_text ( _ ( ' %s of %s ' ) % ( gl . format_filesize ( new_download_dir_size ) , download_dir_size_string , ) )
2006-12-20 17:38:36 +01:00
else :
myprogressbar . set_text ( _ ( ' Finishing... please wait. ' ) )
2008-03-03 21:15:36 +01:00
myprogressbar . set_fraction ( max ( 0.0 , min ( 1.0 , fract ) ) )
2006-12-20 17:38:36 +01:00
event . wait ( 0.1 )
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
dlg . destroy ( )
2006-12-06 21:25:26 +01:00
device_type = self . comboboxDeviceType . get_active ( )
if device_type == 0 :
2007-11-02 17:37:14 +01:00
gl . config . device_type = ' none '
2006-12-06 21:25:26 +01:00
elif device_type == 1 :
2007-11-02 17:37:14 +01:00
gl . config . device_type = ' ipod '
2006-12-06 21:25:26 +01:00
elif device_type == 2 :
2007-11-02 17:37:14 +01:00
gl . config . device_type = ' filesystem '
2008-09-01 21:26:35 +02:00
elif device_type == 3 :
gl . config . device_type = ' mtp '
2006-01-09 00:52:40 +01:00
self . gPodderProperties . destroy ( )
2007-01-28 10:21:39 +01:00
if self . callback_finished :
self . callback_finished ( )
2007-08-28 00:18:01 +02:00
class gPodderEpisode ( GladeWidget ) :
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ' episode_description ' , ' btnCloseWindow ' , ' btnDownload ' ,
' btnCancel ' , ' btnSaveFile ' , ' btnPlay ' , ' btn_website ' ]
2006-03-31 18:20:18 +02:00
def new ( self ) :
2008-04-06 02:19:03 +02:00
global WEB_BROWSER_ICON
self . image3166 . set_property ( ' icon-name ' , WEB_BROWSER_ICON )
2007-08-28 00:18:01 +02:00
services . download_status_manager . register ( ' list-changed ' , self . on_download_status_changed )
services . download_status_manager . register ( ' progress-detail ' , self . on_download_status_progress )
self . episode_title . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> ' % saxutils . escape ( self . episode . title ) )
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Hide the advanced prefs expander
self . expander1 . hide_all ( )
2007-08-28 00:18:01 +02:00
b = gtk . TextBuffer ( )
b . set_text ( strip ( self . episode . description ) )
self . episode_description . set_buffer ( b )
self . gPodderEpisode . set_title ( self . episode . title )
self . LabelDownloadLink . set_text ( self . episode . url )
self . LabelWebsiteLink . set_text ( self . episode . link )
2008-06-14 18:53:16 +02:00
self . labelPubDate . set_text ( self . episode . cute_pubdate ( ) )
2007-08-28 00:18:01 +02:00
2008-03-03 20:54:42 +01:00
# Hide the "Go to website" button if we don't have a valid URL
if self . episode . link == self . episode . url or not self . episode . link :
self . btn_website . hide_all ( )
2008-08-05 14:56:58 +02:00
self . channel_title . set_markup ( _ ( ' <i>from %s </i> ' ) % saxutils . escape ( self . episode . channel . title ) )
2007-08-28 00:18:01 +02:00
self . hide_show_widgets ( )
services . download_status_manager . request_progress_detail ( self . episode . url )
2006-03-04 21:45:01 +01:00
2007-08-27 22:28:00 +02:00
def on_btnCancel_clicked ( self , widget ) :
services . download_status_manager . cancel_by_url ( self . episode . url )
def on_gPodderEpisode_destroy ( self , widget ) :
services . download_status_manager . unregister ( ' list-changed ' , self . on_download_status_changed )
services . download_status_manager . unregister ( ' progress-detail ' , self . on_download_status_progress )
def on_download_status_changed ( self ) :
self . hide_show_widgets ( )
2008-03-03 20:54:42 +01:00
def on_btn_website_clicked ( self , widget ) :
util . open_website ( self . episode . link )
2007-08-27 22:28:00 +02:00
def on_download_status_progress ( self , url , progress , speed ) :
if url == self . episode . url :
2008-01-09 23:46:17 +01:00
progress = float ( min ( 100.0 , max ( 0.0 , progress ) ) )
self . progress_bar . set_fraction ( progress / 100.0 )
2007-08-27 22:28:00 +02:00
self . progress_bar . set_text ( ' Downloading: %d %% ( %s ) ' % ( progress , speed , ) )
def hide_show_widgets ( self ) :
is_downloading = services . download_status_manager . is_download_in_progress ( self . episode . url )
if is_downloading :
self . progress_bar . show_all ( )
self . btnCancel . show_all ( )
self . btnPlay . hide_all ( )
2007-08-28 00:18:01 +02:00
self . btnSaveFile . hide_all ( )
2007-08-27 22:28:00 +02:00
self . btnDownload . hide_all ( )
else :
self . progress_bar . hide_all ( )
self . btnCancel . hide_all ( )
if os . path . exists ( self . episode . local_filename ( ) ) :
2008-08-04 23:26:51 +02:00
if self . episode . file_type ( ) in ( ' audio ' , ' video ' ) :
self . btnPlay . set_label ( gtk . STOCK_MEDIA_PLAY )
else :
self . btnPlay . set_label ( gtk . STOCK_OPEN )
self . btnPlay . set_use_stock ( True )
2007-08-27 22:28:00 +02:00
self . btnPlay . show_all ( )
2007-08-28 00:18:01 +02:00
self . btnSaveFile . show_all ( )
self . btnDownload . hide_all ( )
2007-08-27 22:28:00 +02:00
else :
2007-08-28 00:18:01 +02:00
self . btnPlay . hide_all ( )
self . btnSaveFile . hide_all ( )
2007-08-27 22:28:00 +02:00
self . btnDownload . show_all ( )
2006-03-04 21:45:01 +01:00
def on_btnCloseWindow_clicked ( self , widget , * args ) :
self . gPodderEpisode . destroy ( )
2006-07-14 22:30:30 +02:00
def on_btnDownload_clicked ( self , widget , * args ) :
2006-12-09 02:59:53 +01:00
if self . download_callback :
2006-07-14 22:30:30 +02:00
self . download_callback ( )
2006-12-09 02:59:53 +01:00
def on_btnPlay_clicked ( self , widget , * args ) :
if self . play_callback :
self . play_callback ( )
self . gPodderEpisode . destroy ( )
2007-04-23 17:18:31 +02:00
def on_btnSaveFile_clicked ( self , widget , * args ) :
2007-10-28 15:30:11 +01:00
self . show_copy_dialog ( src_filename = self . episode . local_filename ( ) , dst_filename = self . episode . sync_filename ( ) )
2007-04-23 17:18:31 +02:00
2006-03-04 21:45:01 +01:00
2007-08-28 00:18:01 +02:00
class gPodderSync ( GladeWidget ) :
def new ( self ) :
2008-01-28 12:38:53 +01:00
util . idle_add ( self . imageSync . set_from_icon_name , ' gnome-dev-ipod ' , gtk . ICON_SIZE_DIALOG )
2006-04-07 03:43:06 +02:00
2008-01-28 12:38:53 +01:00
self . device . register ( ' progress ' , self . on_progress )
self . device . register ( ' sub-progress ' , self . on_sub_progress )
self . device . register ( ' status ' , self . on_status )
self . device . register ( ' done ' , self . on_done )
2008-09-01 21:26:35 +02:00
def on_progress ( self , pos , max , text = None ) :
if text is None :
text = _ ( ' %d of %d done ' ) % ( pos , max )
2008-01-28 12:38:53 +01:00
util . idle_add ( self . progressbar . set_fraction , float ( pos ) / float ( max ) )
2008-09-01 21:26:35 +02:00
util . idle_add ( self . progressbar . set_text , text )
2006-04-07 03:43:06 +02:00
2008-01-28 12:38:53 +01:00
def on_sub_progress ( self , percentage ) :
util . idle_add ( self . progressbar . set_text , _ ( ' Processing ( %d %% ) ' ) % ( percentage ) )
2007-03-22 17:35:51 +01:00
2008-01-28 12:38:53 +01:00
def on_status ( self , status ) :
util . idle_add ( self . status_label . set_markup , ' <i> %s </i> ' % saxutils . escape ( status ) )
2007-03-22 17:35:51 +01:00
2008-01-28 12:38:53 +01:00
def on_done ( self ) :
util . idle_add ( self . gPodderSync . destroy )
2008-02-20 13:46:51 +01:00
if not self . gPodder . minimized :
util . idle_add ( self . notification , _ ( ' Your device has been updated by gPodder. ' ) , _ ( ' Operation finished ' ) )
2006-04-07 03:43:06 +02:00
def on_gPodderSync_destroy ( self , widget , * args ) :
2008-01-28 12:38:53 +01:00
self . device . unregister ( ' progress ' , self . on_progress )
self . device . unregister ( ' sub-progress ' , self . on_sub_progress )
self . device . unregister ( ' status ' , self . on_status )
self . device . unregister ( ' done ' , self . on_done )
self . device . cancel ( )
2006-04-07 03:43:06 +02:00
2007-03-22 17:35:51 +01:00
def on_cancel_button_clicked ( self , widget , * args ) :
2008-01-28 12:38:53 +01:00
self . device . cancel ( )
2007-03-22 17:35:51 +01:00
2006-04-07 03:43:06 +02:00
2007-08-28 00:18:01 +02:00
class gPodderOpmlLister ( GladeWidget ) :
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ' btnDownloadOpml ' , ' btnCancel ' , ' btnOK ' , ' treeviewChannelChooser ' ]
2006-06-13 23:00:31 +02:00
def new ( self ) :
# initiate channels list
self . channels = [ ]
self . callback_for_channel = None
2007-07-11 20:12:02 +02:00
self . callback_finished = None
2006-06-13 23:00:31 +02:00
2008-05-02 17:36:43 +02:00
if hasattr ( self , ' custom_title ' ) :
self . gPodderOpmlLister . set_title ( self . custom_title )
if hasattr ( self , ' hide_url_entry ' ) :
self . hbox25 . hide_all ( )
2006-06-13 23:00:31 +02:00
togglecell = gtk . CellRendererToggle ( )
togglecell . set_property ( ' activatable ' , True )
togglecell . connect ( ' toggled ' , self . callback_edited )
2007-07-11 20:12:02 +02:00
togglecolumn = gtk . TreeViewColumn ( ' ' , togglecell , active = 0 )
2006-06-13 23:00:31 +02:00
titlecell = gtk . CellRendererText ( )
2008-05-02 17:45:45 +02:00
titlecell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
2008-05-02 17:36:43 +02:00
titlecolumn = gtk . TreeViewColumn ( _ ( ' Podcast ' ) , titlecell , markup = 1 )
2006-06-13 23:00:31 +02:00
for itemcolumn in ( togglecolumn , titlecolumn ) :
self . treeviewChannelChooser . append_column ( itemcolumn )
def callback_edited ( self , cell , path ) :
model = self . treeviewChannelChooser . get_model ( )
2007-07-11 20:12:02 +02:00
url = model [ path ] [ 2 ]
2006-06-13 23:00:31 +02:00
2007-07-11 20:12:02 +02:00
model [ path ] [ 0 ] = not model [ path ] [ 0 ]
if model [ path ] [ 0 ] :
2006-06-13 23:00:31 +02:00
self . channels . append ( url )
else :
self . channels . remove ( url )
2007-07-11 20:12:02 +02:00
self . btnOK . set_sensitive ( bool ( len ( self . channels ) ) )
2008-01-15 14:54:22 +01:00
def thread_finished ( self , model ) :
self . treeviewChannelChooser . set_model ( model )
self . btnDownloadOpml . set_sensitive ( True )
self . entryURL . set_sensitive ( True )
self . treeviewChannelChooser . set_sensitive ( True )
self . channels = [ ]
def thread_func ( self ) :
2007-08-19 16:28:24 +02:00
url = self . entryURL . get_text ( )
2008-01-15 14:54:22 +01:00
importer = opml . Importer ( url )
2007-08-19 16:28:24 +02:00
model = importer . get_model ( )
2008-02-07 20:23:12 +01:00
if len ( model ) == 0 :
self . notification ( _ ( ' The specified URL does not provide any valid OPML podcast items. ' ) , _ ( ' No feeds found ' ) )
2008-01-15 14:54:22 +01:00
util . idle_add ( self . thread_finished , model )
2007-07-11 20:12:02 +02:00
def get_channels_from_url ( self , url , callback_for_channel = None , callback_finished = None ) :
if callback_for_channel :
self . callback_for_channel = callback_for_channel
if callback_finished :
self . callback_finished = callback_finished
self . entryURL . set_text ( url )
self . btnDownloadOpml . set_sensitive ( False )
self . entryURL . set_sensitive ( False )
self . btnOK . set_sensitive ( False )
self . treeviewChannelChooser . set_sensitive ( False )
Thread ( target = self . thread_func ) . start ( )
2006-06-13 23:00:31 +02:00
2008-06-18 01:49:47 +02:00
def select_all ( self , value ) :
self . channels = [ ]
for row in self . treeviewChannelChooser . get_model ( ) :
row [ 0 ] = value
if value :
self . channels . append ( row [ 2 ] )
2008-06-19 10:51:37 +02:00
self . btnOK . set_sensitive ( bool ( len ( self . channels ) ) )
2008-06-18 01:49:47 +02:00
2006-06-13 23:00:31 +02:00
def on_gPodderOpmlLister_destroy ( self , widget , * args ) :
2006-11-17 15:26:10 +01:00
pass
2006-06-13 23:00:31 +02:00
2007-07-11 20:12:02 +02:00
def on_btnDownloadOpml_clicked ( self , widget , * args ) :
self . get_channels_from_url ( self . entryURL . get_text ( ) )
2008-06-18 01:49:47 +02:00
def on_btnSelectAll_clicked ( self , widget , * args ) :
self . select_all ( True )
def on_btnSelectNone_clicked ( self , widget , * args ) :
self . select_all ( False )
2006-06-13 23:00:31 +02:00
def on_btnOK_clicked ( self , widget , * args ) :
self . gPodderOpmlLister . destroy ( )
2007-07-11 20:12:02 +02:00
2006-06-13 23:00:31 +02:00
# add channels that have been selected
for url in self . channels :
2007-07-11 20:12:02 +02:00
if self . callback_for_channel :
self . callback_for_channel ( url )
if self . callback_finished :
self . callback_finished ( )
2006-06-13 23:00:31 +02:00
def on_btnCancel_clicked ( self , widget , * args ) :
self . gPodderOpmlLister . destroy ( )
2005-11-21 19:21:25 +01:00
2007-11-08 20:11:57 +01:00
class gPodderEpisodeSelector ( GladeWidget ) :
""" Episode selection dialog
Optional keyword arguments that modify the behaviour of this dialog :
- callback : Function that takes 1 parameter which is a list of
the selected episodes ( or empty list when none selected )
- episodes : List of episodes that are presented for selection
- selected : ( optional ) List of boolean variables that define the
default checked state for the given episodes
- selected_default : ( optional ) The default boolean value for the
checked state if no other value is set
( default is False )
2008-08-13 03:47:01 +02:00
- columns : List of ( name , sort_name , sort_type , caption ) pairs for the
columns , the name is the attribute name of the episode to be
read from each episode object . The sort name is the
attribute name of the episode to be used to sort this column .
If the sort_name is None it will use the attribute name for
sorting . The sort type is the type of the sort column .
The caption attribute is the text that appear as column caption
( default is [ ( ' title_and_description ' , None , None , ' Episode ' ) , ] )
2007-11-08 20:11:57 +01:00
- title : ( optional ) The title of the window + heading
- instructions : ( optional ) A one - line text describing what the
user should select / what the selection is for
- stock_ok_button : ( optional ) Will replace the " OK " button with
another GTK + stock item to be used for the
affirmative button of the dialog ( e . g . can
be gtk . STOCK_DELETE when the episodes to be
selected will be deleted after closing the
dialog )
- selection_buttons : ( optional ) A dictionary with labels as
keys and callbacks as values ; for each
key a button will be generated , and when
the button is clicked , the callback will
be called for each episode and the return
value of the callback ( True or False ) will
be the new selected state of the episode
- size_attribute : ( optional ) The name of an attribute of the
supplied episode objects that can be used to
calculate the size of an episode ; set this to
None if no total size calculation should be
done ( in cases where total size is useless )
( default is ' length ' )
2008-08-04 15:34:29 +02:00
- tooltip_attribute : ( optional ) The name of an attribute of
the supplied episode objects that holds
the text for the tooltips when hovering
over an episode ( default is ' description ' )
2007-11-08 20:11:57 +01:00
"""
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ' btnCancel ' , ' btnOK ' , ' btnCheckAll ' , ' btnCheckNone ' , ' treeviewEpisodes ' ]
2008-07-20 10:51:43 +02:00
COLUMN_INDEX = 0
2008-08-04 15:34:29 +02:00
COLUMN_TOOLTIP = 1
COLUMN_TOGGLE = 2
COLUMN_ADDITIONAL = 3
2007-11-08 20:11:57 +01:00
def new ( self ) :
if not hasattr ( self , ' callback ' ) :
self . callback = None
if not hasattr ( self , ' episodes ' ) :
self . episodes = [ ]
if not hasattr ( self , ' size_attribute ' ) :
self . size_attribute = ' length '
2008-08-04 15:34:29 +02:00
if not hasattr ( self , ' tooltip_attribute ' ) :
self . tooltip_attribute = ' description '
2007-11-08 20:11:57 +01:00
if not hasattr ( self , ' selection_buttons ' ) :
self . selection_buttons = { }
if not hasattr ( self , ' selected_default ' ) :
self . selected_default = False
if not hasattr ( self , ' selected ' ) :
self . selected = [ self . selected_default ] * len ( self . episodes )
if len ( self . selected ) < len ( self . episodes ) :
self . selected + = [ self . selected_default ] * ( len ( self . episodes ) - len ( self . selected ) )
if not hasattr ( self , ' columns ' ) :
2008-08-13 03:47:01 +02:00
self . columns = ( ( ' title_and_description ' , None , None , _ ( ' Episode ' ) ) , )
2007-11-08 20:11:57 +01:00
if hasattr ( self , ' title ' ) :
self . gPodderEpisodeSelector . set_title ( self . title )
self . labelHeading . set_markup ( ' <b><big> %s </big></b> ' % saxutils . escape ( self . title ) )
2008-04-24 18:28:39 +02:00
if gpodder . interface == gpodder . MAEMO :
self . labelHeading . hide ( )
2007-11-08 20:11:57 +01:00
if hasattr ( self , ' instructions ' ) :
self . labelInstructions . set_text ( self . instructions )
self . labelInstructions . show_all ( )
2008-06-14 18:53:16 +02:00
if hasattr ( self , ' stock_ok_button ' ) :
if self . stock_ok_button == ' gpodder-download ' :
self . btnOK . set_image ( gtk . image_new_from_stock ( gtk . STOCK_GO_DOWN , gtk . ICON_SIZE_BUTTON ) )
self . btnOK . set_label ( _ ( ' Download ' ) )
else :
self . btnOK . set_label ( self . stock_ok_button )
self . btnOK . set_use_stock ( True )
2007-11-08 20:11:57 +01:00
2008-07-20 10:51:43 +02:00
# check/uncheck column
2007-11-08 20:11:57 +01:00
toggle_cell = gtk . CellRendererToggle ( )
toggle_cell . connect ( ' toggled ' , self . toggle_cell_handler )
self . treeviewEpisodes . append_column ( gtk . TreeViewColumn ( ' ' , toggle_cell , active = self . COLUMN_TOGGLE ) )
2008-07-20 10:51:43 +02:00
2007-11-08 20:11:57 +01:00
next_column = self . COLUMN_ADDITIONAL
2008-08-13 03:47:01 +02:00
for name , sort_name , sort_type , caption in self . columns :
2007-11-08 20:11:57 +01:00
renderer = gtk . CellRendererText ( )
2008-04-19 19:01:09 +02:00
renderer . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
2008-08-04 15:34:29 +02:00
column = gtk . TreeViewColumn ( caption , renderer , markup = next_column )
2007-11-08 20:11:57 +01:00
column . set_resizable ( True )
2008-04-19 19:01:09 +02:00
# Only set "expand" on the first column (so more text is displayed there)
column . set_expand ( next_column == self . COLUMN_ADDITIONAL )
2008-08-13 03:47:01 +02:00
if sort_name is not None :
column . set_sort_column_id ( next_column + 1 )
else :
column . set_sort_column_id ( next_column )
2007-11-08 20:11:57 +01:00
self . treeviewEpisodes . append_column ( column )
next_column + = 1
2008-08-13 03:47:01 +02:00
if sort_name is not None :
# add the sort column
column = gtk . TreeViewColumn ( )
column . set_visible ( False )
self . treeviewEpisodes . append_column ( column )
next_column + = 1
column_types = [ gobject . TYPE_INT , gobject . TYPE_STRING , gobject . TYPE_BOOLEAN ]
# add string column type plus sort column type if it exists
for name , sort_name , sort_type , caption in self . columns :
column_types . append ( gobject . TYPE_STRING )
if sort_name is not None :
column_types . append ( sort_type )
2007-11-08 20:11:57 +01:00
self . model = gtk . ListStore ( * column_types )
2008-08-04 15:34:29 +02:00
tooltip = None
2007-11-08 20:11:57 +01:00
for index , episode in enumerate ( self . episodes ) :
2008-08-04 15:34:29 +02:00
if self . tooltip_attribute is not None :
try :
tooltip = getattr ( episode , self . tooltip_attribute )
except :
log ( ' Episode object %s does not have tooltip attribute: " %s " ' , episode , self . tooltip_attribute , sender = self )
tooltip = None
row = [ index , tooltip , self . selected [ index ] ]
2008-08-13 03:47:01 +02:00
for name , sort_name , sort_type , caption in self . columns :
2008-04-13 08:04:51 +02:00
if not hasattr ( episode , name ) :
log ( ' Warning: Missing attribute " %s " ' , name , sender = self )
row . append ( None )
else :
row . append ( getattr ( episode , name ) )
2008-08-13 03:47:01 +02:00
if sort_name is not None :
if not hasattr ( episode , sort_name ) :
log ( ' Warning: Missing attribute " %s " ' , sort_name , sender = self )
row . append ( None )
else :
row . append ( getattr ( episode , sort_name ) )
2007-11-08 20:11:57 +01:00
self . model . append ( row )
2008-08-04 15:34:29 +02:00
# connect to tooltip signals
if self . tooltip_attribute is not None :
try :
self . treeviewEpisodes . set_property ( ' has-tooltip ' , True )
self . treeviewEpisodes . connect ( ' query-tooltip ' , self . treeview_episodes_query_tooltip )
except :
log ( ' I cannot set has-tooltip/query-tooltip (need at least PyGTK 2.12) ' , sender = self )
self . last_tooltip_episode = None
self . episode_list_can_tooltip = True
2008-08-04 12:16:44 +02:00
self . treeviewEpisodes . connect ( ' button-press-event ' , self . treeview_episodes_button_pressed )
2007-11-08 20:11:57 +01:00
self . treeviewEpisodes . set_rules_hint ( True )
self . treeviewEpisodes . set_model ( self . model )
self . treeviewEpisodes . columns_autosize ( )
self . calculate_total_size ( )
2008-08-04 15:34:29 +02:00
def treeview_episodes_query_tooltip ( self , treeview , x , y , keyboard_tooltip , tooltip ) :
# With get_bin_window, we get the window that contains the rows without
# the header. The Y coordinate of this window will be the height of the
# treeview header. This is the amount we have to subtract from the
# event's Y coordinate to get the coordinate to pass to get_path_at_pos
( x_bin , y_bin ) = treeview . get_bin_window ( ) . get_position ( )
y - = x_bin
y - = y_bin
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
if not self . episode_list_can_tooltip :
self . last_tooltip_episode = None
return False
if path is not None :
model = treeview . get_model ( )
iter = model . get_iter ( path )
index = model . get_value ( iter , self . COLUMN_INDEX )
description = model . get_value ( iter , self . COLUMN_TOOLTIP )
if self . last_tooltip_episode is not None and self . last_tooltip_episode != index :
self . last_tooltip_episode = None
return False
self . last_tooltip_episode = index
if description is not None :
tooltip . set_text ( description )
return True
else :
return False
self . last_tooltip_episode = None
return False
2008-08-04 12:16:44 +02:00
def treeview_episodes_button_pressed ( self , treeview , event ) :
if event . button == 3 :
menu = gtk . Menu ( )
if len ( self . selection_buttons ) :
for label in self . selection_buttons :
item = gtk . MenuItem ( label )
item . connect ( ' activate ' , self . custom_selection_button_clicked , label )
menu . append ( item )
menu . append ( gtk . SeparatorMenuItem ( ) )
item = gtk . MenuItem ( _ ( ' Select all ' ) )
item . connect ( ' activate ' , self . on_btnCheckAll_clicked )
menu . append ( item )
item = gtk . MenuItem ( _ ( ' Select none ' ) )
item . connect ( ' activate ' , self . on_btnCheckNone_clicked )
menu . append ( item )
menu . show_all ( )
2008-08-04 15:34:29 +02:00
# Disable tooltips while we are showing the menu, so
# the tooltip will not appear over the menu
self . episode_list_can_tooltip = False
menu . connect ( ' deactivate ' , lambda menushell : self . episode_list_allow_tooltips ( ) )
2008-08-04 12:16:44 +02:00
menu . popup ( None , None , None , event . button , event . time )
return True
2008-08-04 15:34:29 +02:00
def episode_list_allow_tooltips ( self ) :
self . episode_list_can_tooltip = True
2007-11-08 20:11:57 +01:00
def calculate_total_size ( self ) :
if self . size_attribute is not None :
2008-06-19 10:51:37 +02:00
( total_size , count ) = ( 0 , 0 )
2008-07-20 10:51:43 +02:00
for episode in self . get_selected_episodes ( ) :
try :
total_size + = int ( getattr ( episode , self . size_attribute ) )
count + = 1
except :
log ( ' Cannot get size for %s ' , episode . title , sender = self )
text = [ ]
if count == 0 :
text . append ( _ ( ' Nothing selected ' ) )
elif count == 1 :
text . append ( _ ( ' One episode selected ' ) )
2007-11-08 20:11:57 +01:00
else :
2008-07-20 10:51:43 +02:00
text . append ( _ ( ' %d episodes selected ' ) % count )
if total_size > 0 :
text . append ( _ ( ' total size: %s ' ) % gl . format_filesize ( total_size ) )
self . labelTotalSize . set_text ( ' , ' . join ( text ) )
self . btnOK . set_sensitive ( count > 0 )
2007-11-08 20:11:57 +01:00
else :
2008-06-19 10:51:37 +02:00
self . btnOK . set_sensitive ( False )
for index , row in enumerate ( self . model ) :
if self . model . get_value ( row . iter , self . COLUMN_TOGGLE ) == True :
self . btnOK . set_sensitive ( True )
break
self . labelTotalSize . set_text ( ' ' )
2007-11-08 20:11:57 +01:00
def toggle_cell_handler ( self , cell , path ) :
model = self . treeviewEpisodes . get_model ( )
model [ path ] [ self . COLUMN_TOGGLE ] = not model [ path ] [ self . COLUMN_TOGGLE ]
2008-06-19 10:51:37 +02:00
self . calculate_total_size ( )
2007-11-08 20:11:57 +01:00
2008-03-04 12:17:29 +01:00
def custom_selection_button_clicked ( self , button , label ) :
2007-11-08 20:11:57 +01:00
callback = self . selection_buttons [ label ]
for index , row in enumerate ( self . model ) :
new_value = callback ( self . episodes [ index ] )
self . model . set_value ( row . iter , self . COLUMN_TOGGLE , new_value )
self . calculate_total_size ( )
def on_btnCheckAll_clicked ( self , widget ) :
for row in self . model :
self . model . set_value ( row . iter , self . COLUMN_TOGGLE , True )
self . calculate_total_size ( )
def on_btnCheckNone_clicked ( self , widget ) :
for row in self . model :
self . model . set_value ( row . iter , self . COLUMN_TOGGLE , False )
self . calculate_total_size ( )
def get_selected_episodes ( self ) :
selected_episodes = [ ]
for index , row in enumerate ( self . model ) :
if self . model . get_value ( row . iter , self . COLUMN_TOGGLE ) == True :
2008-07-20 10:51:43 +02:00
selected_episodes . append ( self . episodes [ self . model . get_value ( row . iter , self . COLUMN_INDEX ) ] )
2007-11-08 20:11:57 +01:00
return selected_episodes
def on_btnOK_clicked ( self , widget ) :
self . gPodderEpisodeSelector . destroy ( )
if self . callback is not None :
self . callback ( self . get_selected_episodes ( ) )
def on_btnCancel_clicked ( self , widget ) :
self . gPodderEpisodeSelector . destroy ( )
2007-12-10 09:41:17 +01:00
if self . callback is not None :
self . callback ( [ ] )
2008-02-07 22:19:03 +01:00
class gPodderConfigEditor ( GladeWidget ) :
2008-04-24 18:28:39 +02:00
finger_friendly_widgets = [ ' btnShowAll ' , ' btnClose ' , ' configeditor ' ]
2008-02-07 22:19:03 +01:00
def new ( self ) :
2008-07-09 12:21:49 +02:00
name_column = gtk . TreeViewColumn ( _ ( ' Setting ' ) )
2008-02-07 22:19:03 +01:00
name_renderer = gtk . CellRendererText ( )
name_column . pack_start ( name_renderer )
name_column . add_attribute ( name_renderer , ' text ' , 0 )
2008-07-09 12:21:49 +02:00
name_column . add_attribute ( name_renderer , ' style ' , 5 )
2008-02-07 22:19:03 +01:00
self . configeditor . append_column ( name_column )
2008-07-09 12:21:49 +02:00
value_column = gtk . TreeViewColumn ( _ ( ' Set to ' ) )
value_check_renderer = gtk . CellRendererToggle ( )
value_column . pack_start ( value_check_renderer , expand = False )
value_column . add_attribute ( value_check_renderer , ' active ' , 7 )
value_column . add_attribute ( value_check_renderer , ' visible ' , 6 )
value_column . add_attribute ( value_check_renderer , ' activatable ' , 6 )
value_check_renderer . connect ( ' toggled ' , self . value_toggled )
2008-02-07 22:19:03 +01:00
value_renderer = gtk . CellRendererText ( )
value_column . pack_start ( value_renderer )
value_column . add_attribute ( value_renderer , ' text ' , 2 )
2008-07-09 12:21:49 +02:00
value_column . add_attribute ( value_renderer , ' visible ' , 4 )
2008-02-07 22:19:03 +01:00
value_column . add_attribute ( value_renderer , ' editable ' , 4 )
2008-07-09 12:21:49 +02:00
value_column . add_attribute ( value_renderer , ' style ' , 5 )
2008-02-07 22:19:03 +01:00
value_renderer . connect ( ' edited ' , self . value_edited )
self . configeditor . append_column ( value_column )
self . model = gl . config . model ( )
self . filter = self . model . filter_new ( )
self . filter . set_visible_func ( self . visible_func )
self . configeditor . set_model ( self . filter )
self . configeditor . set_rules_hint ( True )
def visible_func ( self , model , iter , user_data = None ) :
text = self . entryFilter . get_text ( ) . lower ( )
if text == ' ' :
return True
else :
# either the variable name or its value
return ( text in model . get_value ( iter , 0 ) . lower ( ) or
text in model . get_value ( iter , 2 ) . lower ( ) )
def value_edited ( self , renderer , path , new_text ) :
model = self . configeditor . get_model ( )
iter = model . get_iter ( path )
name = model . get_value ( iter , 0 )
type_cute = model . get_value ( iter , 1 )
if not gl . config . update_field ( name , new_text ) :
self . notification ( _ ( ' Cannot set value of <b> %s </b> to <i> %s </i>. \n \n Needed data type: %s ' ) % ( saxutils . escape ( name ) , saxutils . escape ( new_text ) , saxutils . escape ( type_cute ) ) , _ ( ' Error updating %s ' ) % saxutils . escape ( name ) )
2008-07-09 12:21:49 +02:00
def value_toggled ( self , renderer , path ) :
model = self . configeditor . get_model ( )
iter = model . get_iter ( path )
field_name = model . get_value ( iter , 0 )
field_type = model . get_value ( iter , 3 )
# Flip the boolean config flag
if field_type == bool :
gl . config . toggle_flag ( field_name )
2008-02-07 22:19:03 +01:00
def on_entryFilter_changed ( self , widget ) :
self . filter . refilter ( )
def on_btnShowAll_clicked ( self , widget ) :
self . entryFilter . set_text ( ' ' )
self . entryFilter . grab_focus ( )
def on_btnClose_clicked ( self , widget ) :
self . gPodderConfigEditor . destroy ( )
2007-11-08 20:11:57 +01:00
2007-08-28 00:18:01 +02:00
def main ( ) :
2006-04-03 22:59:10 +02:00
gobject . threads_init ( )
2007-03-25 21:55:28 +02:00
gtk . window_set_default_icon_name ( ' gpodder ' )
2005-11-21 19:21:25 +01:00
2007-08-28 00:18:01 +02:00
gPodder ( ) . run ( )
2005-11-21 19:21:25 +01:00
2008-02-20 13:46:51 +01:00