1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
"""filechooser.py - Custom FileChooserDialog implementations."""
import os
import gtk
import pango
import encoding
import image
import labels
from preferences import prefs
import thumbnail
_main_filechooser_dialog = None
_library_filechooser_dialog = None
class _ComicFileChooserDialog(gtk.Dialog):
"""We roll our own FileChooserDialog because the one in GTK seems
buggy with the preview widget. The <action> argument dictates what type
of filechooser dialog we want (i.e. it is gtk.FILE_CHOOSER_ACTION_OPEN
or gtk.FILE_CHOOSER_ACTION_SAVE).
This is a base class for the _MainFileChooserDialog, the
_LibraryFileChooserDialog and the StandAloneFileChooserDialog.
Subclasses should implement a method files_chosen(paths) that will be
called once the filechooser has done its job and selected some files.
If the dialog was closed or Cancel was pressed, <paths> is the empty list.
"""
_last_activated_file = None
def __init__(self, action=gtk.FILE_CHOOSER_ACTION_OPEN):
self._action = action
if action == gtk.FILE_CHOOSER_ACTION_OPEN:
title = _('Open')
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK)
else:
title = _('Save')
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK)
gtk.Dialog.__init__(self, title, None, 0, buttons)
self.set_default_response(gtk.RESPONSE_OK)
self.set_has_separator(False)
self.filechooser = gtk.FileChooserWidget(action=action)
self.filechooser.set_size_request(680, 420)
self.vbox.pack_start(self.filechooser)
self.set_border_width(4)
self.filechooser.set_border_width(6)
self.connect('response', self._response)
self.filechooser.connect('file_activated', self._response,
gtk.RESPONSE_OK)
preview_box = gtk.VBox(False, 10)
preview_box.set_size_request(130, 0)
self._preview_image = gtk.Image()
self._preview_image.set_size_request(130, 130)
preview_box.pack_start(self._preview_image, False, False)
self.filechooser.set_preview_widget(preview_box)
self._namelabel = labels.FormattedLabel(weight=pango.WEIGHT_BOLD,
scale=pango.SCALE_SMALL)
self._namelabel.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
preview_box.pack_start(self._namelabel, False, False)
self._sizelabel = labels.FormattedLabel(scale=pango.SCALE_SMALL)
self._sizelabel.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
preview_box.pack_start(self._sizelabel, False, False)
self.filechooser.set_use_preview_label(False)
preview_box.show_all()
self.filechooser.connect('update-preview', self._update_preview)
ffilter = gtk.FileFilter()
ffilter.add_pattern('*')
ffilter.set_name(_('All files'))
self.filechooser.add_filter(ffilter)
self.add_filter(_('All Archives'), ('application/x-zip',
'application/zip', 'application/x-rar', 'application/x-tar',
'application/x-gzip', 'application/x-bzip2', 'application/x-cbz',
'application/x-cbr', 'application/x-cbt'))
self.add_filter(_('ZIP archives'),
('application/x-zip', 'application/zip', 'application/x-cbz'))
self.add_filter(_('RAR archives'),
('application/x-rar', 'application/x-cbr'))
self.add_filter(_('Tar archives'),
('application/x-tar', 'application/x-gzip',
'application/x-bzip2', 'application/x-cbt'))
try:
if (self.__class__._last_activated_file is not None
and os.path.isfile(self.__class__._last_activated_file)):
self.filechooser.set_filename(
self.__class__._last_activated_file)
elif os.path.isdir(prefs['path of last browsed in filechooser']):
self.filechooser.set_current_folder(
prefs['path of last browsed in filechooser'])
except Exception: # E.g. broken prefs values.
pass
self.show_all()
def add_filter(self, name, mimes):
"""Add a filter, called <name>, for each mime type in <mimes> to
the filechooser.
"""
ffilter = gtk.FileFilter()
for mime in mimes:
ffilter.add_mime_type(mime)
ffilter.set_name(name)
self.filechooser.add_filter(ffilter)
def set_save_name(self, name):
self.filechooser.set_current_name(name)
def set_current_directory(self, path):
self.filechooser.set_current_folder(path)
def _response(self, widget, response):
"""Return a list of the paths of the chosen files, or None if the
event only changed the current directory.
"""
if response == gtk.RESPONSE_OK:
paths = self.filechooser.get_filenames()
if len(paths) == 1 and os.path.isdir(paths[0]):
self.filechooser.set_current_folder(paths[0])
self.emit_stop_by_name('response')
return
if not paths:
return
# FileChooser.set_do_overwrite_confirmation() doesn't seem to
# work on our custom dialog, so we use a simple alternative.
if (self._action == gtk.FILE_CHOOSER_ACTION_SAVE
and os.path.exists(paths[0])):
overwrite_dialog = gtk.MessageDialog(None, 0,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,
_("A file named '%s' already exists. Do you want to replace it?") %
os.path.basename(paths[0]))
overwrite_dialog.format_secondary_text(
_('Replacing it will overwrite its contents.'))
response = overwrite_dialog.run()
overwrite_dialog.destroy()
if response != gtk.RESPONSE_OK:
self.emit_stop_by_name('response')
return
prefs['path of last browsed in filechooser'] = \
self.filechooser.get_current_folder()
self.__class__._last_activated_file = paths[0]
self.files_chosen(paths)
else:
self.files_chosen([])
def _update_preview(self, *args):
path = self.filechooser.get_preview_filename()
if path and os.path.isfile(path):
pixbuf = thumbnail.get_thumbnail(path, prefs['create thumbnails'])
if pixbuf is None:
self._preview_image.clear()
self._namelabel.set_text('')
self._sizelabel.set_text('')
else:
pixbuf = image.add_border(pixbuf, 1)
self._preview_image.set_from_pixbuf(pixbuf)
self._namelabel.set_text(encoding.to_unicode(
os.path.basename(path)))
self._sizelabel.set_text(
'%.1f KiB' % (os.stat(path).st_size / 1024.0))
else:
self._preview_image.clear()
self._namelabel.set_text('')
self._sizelabel.set_text('')
class _MainFileChooserDialog(_ComicFileChooserDialog):
"""The normal filechooser dialog used with the "Open" menu item."""
def __init__(self, window):
_ComicFileChooserDialog.__init__(self)
self._window = window
self.set_transient_for(window)
ffilter = gtk.FileFilter()
ffilter.add_pixbuf_formats()
ffilter.set_name(_('All images'))
self.filechooser.add_filter(ffilter)
self.add_filter(_('JPEG images'), ('image/jpeg',))
self.add_filter(_('PNG images'), ('image/png',))
filters = self.filechooser.list_filters()
try:
# When setting this to the first filter ("All files"), this
# fails on some GTK+ versions and sets the filter to "blank".
# The effect is the same though (i.e. display all files), and
# there is no solution that I know of, so we'll have to live
# with it. It only happens the second time a dialog is created
# though, which is very strange.
self.filechooser.set_filter(filters[
prefs['last filter in main filechooser']])
except:
self.filechooser.set_filter(filters[0])
def files_chosen(self, paths):
if paths:
try: # For some reason this fails sometimes (GTK+ bug?)
filter_index = self.filechooser.list_filters().index(
self.filechooser.get_filter())
prefs['last filter in main filechooser'] = filter_index
except:
pass
_close_main_filechooser_dialog()
self._window.file_handler.open_file(paths[0])
else:
_close_main_filechooser_dialog()
class _LibraryFileChooserDialog(_ComicFileChooserDialog):
"""The filechooser dialog used when adding books to the library."""
def __init__(self, library):
_ComicFileChooserDialog.__init__(self)
self._library = library
self.set_transient_for(library)
self.filechooser.set_select_multiple(True)
self.filechooser.connect('current_folder_changed',
self._set_collection_name)
self._collection_button = gtk.CheckButton(
'%s:' % _('Automatically add the books to this collection'),
False)
self._collection_button.set_active(
prefs['auto add books into collections'])
self._comboentry = gtk.combo_box_entry_new_text()
self._comboentry.child.set_activates_default(True)
for collection in self._library.backend.get_all_collections():
name = self._library.backend.get_collection_name(collection)
self._comboentry.append_text(name)
collection_box = gtk.HBox(False, 6)
collection_box.pack_start(self._collection_button, False, False)
collection_box.pack_start(self._comboentry, True, True)
collection_box.show_all()
self.filechooser.set_extra_widget(collection_box)
filters = self.filechooser.list_filters()
try:
# When setting this to the first filter ("All files"), this
# fails on some GTK+ versions and sets the filter to "blank".
# The effect is the same though (i.e. display all files), and
# there is no solution that I know of, so we'll have to live
# with it. It only happens the second time a dialog is created
# though, which is very strange.
self.filechooser.set_filter(filters[
prefs['last filter in library filechooser']])
except Exception:
self.filechooser.set_filter(filters[1])
def _set_collection_name(self, *args):
"""Set the text in the ComboBoxEntry to the name of the current
directory.
"""
name = os.path.basename(self.filechooser.get_current_folder())
self._comboentry.child.set_text(name)
def files_chosen(self, paths):
if paths:
if self._collection_button.get_active():
prefs['auto add books into collections'] = True
collection_name = self._comboentry.get_active_text()
if not collection_name: # No empty-string names.
collection_name = None
else:
prefs['auto add books into collections'] = False
collection_name = None
try: # For some reason this fails sometimes (GTK+ bug?)
filter_index = self.filechooser.list_filters().index(
self.filechooser.get_filter())
prefs['last filter in library filechooser'] = filter_index
except Exception:
pass
close_library_filechooser_dialog()
self._library.add_books(paths, collection_name)
else:
close_library_filechooser_dialog()
class StandAloneFileChooserDialog(_ComicFileChooserDialog):
"""A simple filechooser dialog that is designed to be used with the
gtk.Dialog.run() method. The <action> dictates what type of filechooser
dialog we want (i.e. save or open). If the type is an open-dialog, we
use multiple selection by default.
"""
def __init__(self, action=gtk.FILE_CHOOSER_ACTION_OPEN):
_ComicFileChooserDialog.__init__(self, action)
if action == gtk.FILE_CHOOSER_ACTION_OPEN:
self.filechooser.set_select_multiple(True)
self._paths = None
ffilter = gtk.FileFilter()
ffilter.add_pixbuf_formats()
ffilter.set_name(_('All images'))
self.filechooser.add_filter(ffilter)
self.add_filter(_('JPEG images'), ('image/jpeg',))
self.add_filter(_('PNG images'), ('image/png',))
def get_paths(self):
"""Return the selected paths. To be called after run() has returned
a response.
"""
return self._paths
def files_chosen(self, paths):
self._paths = paths
def open_main_filechooser_dialog(action, window):
"""Open the main filechooser dialog."""
global _main_filechooser_dialog
if _main_filechooser_dialog is None:
_main_filechooser_dialog = _MainFileChooserDialog(window)
else:
_main_filechooser_dialog.present()
def _close_main_filechooser_dialog(*args):
"""Close the main filechooser dialog."""
global _main_filechooser_dialog
if _main_filechooser_dialog is not None:
_main_filechooser_dialog.destroy()
_main_filechooser_dialog = None
def open_library_filechooser_dialog(library):
"""Open the library filechooser dialog."""
global _library_filechooser_dialog
if _library_filechooser_dialog is None:
_library_filechooser_dialog = _LibraryFileChooserDialog(library)
else:
_library_filechooser_dialog.present()
def close_library_filechooser_dialog(*args):
"""Close the library filechooser dialog."""
global _library_filechooser_dialog
if _library_filechooser_dialog is not None:
_library_filechooser_dialog.destroy()
_library_filechooser_dialog = None
|