mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
d18c934e32
@ -114,6 +114,19 @@ class Cache(object):
|
|||||||
if self.dirtied_cache:
|
if self.dirtied_cache:
|
||||||
self.dirtied_sequence = max(self.dirtied_cache.itervalues())+1
|
self.dirtied_sequence = max(self.dirtied_cache.itervalues())+1
|
||||||
|
|
||||||
|
@write_api
|
||||||
|
def initialize_template_cache(self):
|
||||||
|
self.formatter_template_cache = {}
|
||||||
|
|
||||||
|
@write_api
|
||||||
|
def refresh(self):
|
||||||
|
self._initialize_template_cache()
|
||||||
|
for field in self.fields.itervalues():
|
||||||
|
if hasattr(field, 'clear_cache'):
|
||||||
|
field.clear_cache() # Clear the composite cache
|
||||||
|
if hasattr(field, 'table'):
|
||||||
|
field.table.read(self.backend) # Reread data from metadata.db
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def field_metadata(self):
|
def field_metadata(self):
|
||||||
return self.backend.field_metadata
|
return self.backend.field_metadata
|
||||||
|
@ -6,12 +6,13 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os
|
import os, traceback
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from calibre.db.backend import DB
|
from calibre.db.backend import DB
|
||||||
from calibre.db.cache import Cache
|
from calibre.db.cache import Cache
|
||||||
from calibre.db.view import View
|
from calibre.db.view import View
|
||||||
|
from calibre.utils.date import utcnow
|
||||||
|
|
||||||
class LibraryDatabase(object):
|
class LibraryDatabase(object):
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ class LibraryDatabase(object):
|
|||||||
progress_callback=lambda x, y:True, restore_all_prefs=False):
|
progress_callback=lambda x, y:True, restore_all_prefs=False):
|
||||||
|
|
||||||
self.is_second_db = is_second_db # TODO: Use is_second_db
|
self.is_second_db = is_second_db # TODO: Use is_second_db
|
||||||
|
self.listeners = set([])
|
||||||
|
|
||||||
backend = self.backend = DB(library_path, default_prefs=default_prefs,
|
backend = self.backend = DB(library_path, default_prefs=default_prefs,
|
||||||
read_only=read_only, restore_all_prefs=restore_all_prefs,
|
read_only=read_only, restore_all_prefs=restore_all_prefs,
|
||||||
@ -50,6 +52,8 @@ class LibraryDatabase(object):
|
|||||||
setattr(self, prop, partial(self.get_property,
|
setattr(self, prop, partial(self.get_property,
|
||||||
loc=self.FIELD_MAP[fm]))
|
loc=self.FIELD_MAP[fm]))
|
||||||
|
|
||||||
|
self.last_update_check = self.last_modified()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.backend.close()
|
self.backend.close()
|
||||||
|
|
||||||
@ -71,9 +75,22 @@ class LibraryDatabase(object):
|
|||||||
def library_id(self):
|
def library_id(self):
|
||||||
return self.backend.library_id
|
return self.backend.library_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def library_path(self):
|
||||||
|
return self.backend.library_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dbpath(self):
|
||||||
|
return self.backend.dbpath
|
||||||
|
|
||||||
def last_modified(self):
|
def last_modified(self):
|
||||||
return self.backend.last_modified()
|
return self.backend.last_modified()
|
||||||
|
|
||||||
|
def check_if_modified(self):
|
||||||
|
if self.last_modified() > self.last_update_check:
|
||||||
|
self.refresh()
|
||||||
|
self.last_update_check = utcnow()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def custom_column_num_map(self):
|
def custom_column_num_map(self):
|
||||||
return self.backend.custom_column_num_map
|
return self.backend.custom_column_num_map
|
||||||
@ -86,9 +103,48 @@ class LibraryDatabase(object):
|
|||||||
def FIELD_MAP(self):
|
def FIELD_MAP(self):
|
||||||
return self.backend.FIELD_MAP
|
return self.backend.FIELD_MAP
|
||||||
|
|
||||||
|
@property
|
||||||
|
def formatter_template_cache(self):
|
||||||
|
return self.data.cache.formatter_template_cache
|
||||||
|
|
||||||
|
def initialize_template_cache(self):
|
||||||
|
self.data.cache.initialize_template_cache()
|
||||||
|
|
||||||
def all_ids(self):
|
def all_ids(self):
|
||||||
for book_id in self.data.cache.all_book_ids():
|
for book_id in self.data.cache.all_book_ids():
|
||||||
yield book_id
|
yield book_id
|
||||||
|
|
||||||
|
def refresh(self, field=None, ascending=True):
|
||||||
|
self.data.cache.refresh()
|
||||||
|
self.data.refresh(field=field, ascending=ascending)
|
||||||
|
|
||||||
|
def add_listener(self, listener):
|
||||||
|
'''
|
||||||
|
Add a listener. Will be called on change events with two arguments.
|
||||||
|
Event name and list of affected ids.
|
||||||
|
'''
|
||||||
|
self.listeners.add(listener)
|
||||||
|
|
||||||
|
def notify(self, event, ids=[]):
|
||||||
|
'Notify all listeners'
|
||||||
|
for listener in self.listeners:
|
||||||
|
try:
|
||||||
|
listener(event, ids)
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
continue
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
def path(self, index, index_is_id=False):
|
||||||
|
'Return the relative path to the directory containing this books files as a unicode string.'
|
||||||
|
book_id = index if index_is_id else self.data.index_to_id(index)
|
||||||
|
return self.data.cache.field_for('path', book_id).replace('/', os.sep)
|
||||||
|
|
||||||
|
def abspath(self, index, index_is_id=False, create_dirs=True):
|
||||||
|
'Return the absolute path to the directory containing this books files as a unicode string.'
|
||||||
|
path = os.path.join(self.library_path, self.path(index, index_is_id=index_is_id))
|
||||||
|
if create_dirs and not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class LegacyTest(BaseTest):
|
|||||||
'Test library wide properties'
|
'Test library wide properties'
|
||||||
def get_props(db):
|
def get_props(db):
|
||||||
props = ('user_version', 'is_second_db', 'library_id', 'field_metadata',
|
props = ('user_version', 'is_second_db', 'library_id', 'field_metadata',
|
||||||
'custom_column_label_map', 'custom_column_num_map')
|
'custom_column_label_map', 'custom_column_num_map', 'library_path', 'dbpath')
|
||||||
fprops = ('last_modified', )
|
fprops = ('last_modified', )
|
||||||
ans = {x:getattr(db, x) for x in props}
|
ans = {x:getattr(db, x) for x in props}
|
||||||
ans.update({x:getattr(db, x)() for x in fprops})
|
ans.update({x:getattr(db, x)() for x in fprops})
|
||||||
@ -51,6 +51,11 @@ class LegacyTest(BaseTest):
|
|||||||
if label in {'tags', 'formats'}:
|
if label in {'tags', 'formats'}:
|
||||||
# Order is random in the old db for these
|
# Order is random in the old db for these
|
||||||
ans[label] = tuple(set(x.split(',')) if x else x for x in ans[label])
|
ans[label] = tuple(set(x.split(',')) if x else x for x in ans[label])
|
||||||
|
if label == 'series_sort':
|
||||||
|
# The old db code did not take book language into account
|
||||||
|
# when generating series_sort values (the first book has
|
||||||
|
# lang=deu)
|
||||||
|
ans[label] = ans[label][1:]
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
old = self.init_old()
|
old = self.init_old()
|
||||||
@ -64,3 +69,31 @@ class LegacyTest(BaseTest):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
def test_refresh(self): # {{{
|
||||||
|
' Test refreshing the view after a change to metadata.db '
|
||||||
|
db = self.init_legacy()
|
||||||
|
db2 = self.init_legacy()
|
||||||
|
self.assertEqual(db2.data.cache.set_field('title', {1:'xxx'}), set([1]))
|
||||||
|
db2.close()
|
||||||
|
del db2
|
||||||
|
self.assertNotEqual(db.title(1, index_is_id=True), 'xxx')
|
||||||
|
db.check_if_modified()
|
||||||
|
self.assertEqual(db.title(1, index_is_id=True), 'xxx')
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def test_legacy_getters(self): # {{{
|
||||||
|
old = self.init_old()
|
||||||
|
getters = ('path', 'abspath', 'title', 'authors', 'series',
|
||||||
|
'publisher', 'author_sort', 'authors', 'comments',
|
||||||
|
'comment', 'publisher', 'rating', 'series_index', 'tags',
|
||||||
|
'timestamp', 'uuid', 'pubdate', 'ondevice',
|
||||||
|
'metadata_last_modified', 'languages')
|
||||||
|
oldvals = {g:tuple(getattr(old, g)(x) for x in xrange(3)) + tuple(getattr(old, g)(x, True) for x in (1,2,3)) for g in getters}
|
||||||
|
old.close()
|
||||||
|
db = self.init_legacy()
|
||||||
|
newvals = {g:tuple(getattr(db, g)(x) for x in xrange(3)) + tuple(getattr(db, g)(x, True) for x in (1,2,3)) for g in getters}
|
||||||
|
for x in (oldvals, newvals):
|
||||||
|
x['tags'] = tuple(set(y.split(',')) if y else y for y in x['tags'])
|
||||||
|
self.assertEqual(oldvals, newvals)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -294,3 +294,11 @@ class View(object):
|
|||||||
self.marked_ids = dict(izip(id_dict.iterkeys(), imap(unicode,
|
self.marked_ids = dict(izip(id_dict.iterkeys(), imap(unicode,
|
||||||
id_dict.itervalues())))
|
id_dict.itervalues())))
|
||||||
|
|
||||||
|
def refresh(self, field=None, ascending=True):
|
||||||
|
self._map = tuple(self.cache.all_book_ids())
|
||||||
|
self._map_filtered = tuple(self._map)
|
||||||
|
if field is not None:
|
||||||
|
self.sort(field, ascending)
|
||||||
|
if self.search_restriction or self.base_restriction:
|
||||||
|
self.search('', return_matches=False)
|
||||||
|
|
||||||
|
@ -1199,9 +1199,9 @@ class KOBO(USBMS):
|
|||||||
|
|
||||||
class KOBOTOUCH(KOBO):
|
class KOBOTOUCH(KOBO):
|
||||||
name = 'KoboTouch'
|
name = 'KoboTouch'
|
||||||
gui_name = 'Kobo Touch'
|
gui_name = 'Kobo Touch/Glo/Mini/Aura HD'
|
||||||
author = 'David Forrester'
|
author = 'David Forrester'
|
||||||
description = 'Communicate with the Kobo Touch, Glo and Mini firmware. Based on the existing Kobo driver by %s.' % (KOBO.author)
|
description = 'Communicate with the Kobo Touch, Glo, Mini and Aura HD ereaders. Based on the existing Kobo driver by %s.' % (KOBO.author)
|
||||||
# icon = I('devices/kobotouch.jpg')
|
# icon = I('devices/kobotouch.jpg')
|
||||||
|
|
||||||
supported_dbversion = 80
|
supported_dbversion = 80
|
||||||
@ -1297,10 +1297,11 @@ class KOBOTOUCH(KOBO):
|
|||||||
|
|
||||||
TIMESTAMP_STRING = "%Y-%m-%dT%H:%M:%SZ"
|
TIMESTAMP_STRING = "%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
|
||||||
|
AURA_HD_PRODUCT_ID = [0x4193]
|
||||||
GLO_PRODUCT_ID = [0x4173]
|
GLO_PRODUCT_ID = [0x4173]
|
||||||
MINI_PRODUCT_ID = [0x4183]
|
MINI_PRODUCT_ID = [0x4183]
|
||||||
TOUCH_PRODUCT_ID = [0x4163]
|
TOUCH_PRODUCT_ID = [0x4163]
|
||||||
PRODUCT_ID = GLO_PRODUCT_ID + MINI_PRODUCT_ID + TOUCH_PRODUCT_ID
|
PRODUCT_ID = AURA_HD_PRODUCT_ID + GLO_PRODUCT_ID + MINI_PRODUCT_ID + TOUCH_PRODUCT_ID
|
||||||
|
|
||||||
BCD = [0x0110, 0x0326]
|
BCD = [0x0110, 0x0326]
|
||||||
|
|
||||||
@ -1319,6 +1320,11 @@ class KOBOTOUCH(KOBO):
|
|||||||
# ' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,],
|
# ' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,],
|
||||||
# ' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,],
|
# ' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,],
|
||||||
}
|
}
|
||||||
|
AURA_HD_COVER_FILE_ENDINGS = {
|
||||||
|
' - N3_FULL.parsed': [(1080,1440), 0, 99,True,], # Used for screensaver, home screen
|
||||||
|
' - N3_LIBRARY_FULL.parsed':[(355, 471), 0, 99,False,], # Used for Details screen
|
||||||
|
' - N3_LIBRARY_GRID.parsed':[(149, 198), 0, 99,False,], # Used for library lists
|
||||||
|
}
|
||||||
#Following are the sizes used with pre2.1.4 firmware
|
#Following are the sizes used with pre2.1.4 firmware
|
||||||
# COVER_FILE_ENDINGS = {
|
# COVER_FILE_ENDINGS = {
|
||||||
# ' - N3_LIBRARY_FULL.parsed':[(355,530),0, 99,], # Used for Details screen
|
# ' - N3_LIBRARY_FULL.parsed':[(355,530),0, 99,], # Used for Details screen
|
||||||
@ -1334,6 +1340,10 @@ class KOBOTOUCH(KOBO):
|
|||||||
super(KOBOTOUCH, self).initialize()
|
super(KOBOTOUCH, self).initialize()
|
||||||
self.bookshelvelist = []
|
self.bookshelvelist = []
|
||||||
|
|
||||||
|
def get_device_information(self, end_session=True):
|
||||||
|
self.set_device_name()
|
||||||
|
return super(KOBOTOUCH, self).get_device_information(end_session)
|
||||||
|
|
||||||
def books(self, oncard=None, end_session=True):
|
def books(self, oncard=None, end_session=True):
|
||||||
debug_print("KoboTouch:books - oncard='%s'"%oncard)
|
debug_print("KoboTouch:books - oncard='%s'"%oncard)
|
||||||
from calibre.ebooks.metadata.meta import path_to_ext
|
from calibre.ebooks.metadata.meta import path_to_ext
|
||||||
@ -1366,7 +1376,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
except:
|
except:
|
||||||
self.fwversion = (0,0,0)
|
self.fwversion = (0,0,0)
|
||||||
|
|
||||||
|
debug_print('Kobo device: %s' % self.gui_name)
|
||||||
debug_print('Version of driver:', self.version, 'Has kepubs:', self.has_kepubs)
|
debug_print('Version of driver:', self.version, 'Has kepubs:', self.has_kepubs)
|
||||||
debug_print('Version of firmware:', self.fwversion, 'Has kepubs:', self.has_kepubs)
|
debug_print('Version of firmware:', self.fwversion, 'Has kepubs:', self.has_kepubs)
|
||||||
|
|
||||||
@ -1379,7 +1389,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print(opts.extra_customization)
|
debug_print(opts.extra_customization)
|
||||||
if opts.extra_customization:
|
if opts.extra_customization:
|
||||||
debugging_title = opts.extra_customization[self.OPT_DEBUGGING_TITLE]
|
debugging_title = opts.extra_customization[self.OPT_DEBUGGING_TITLE]
|
||||||
debug_print("KoboTouch:books - set_debugging_title to", debugging_title )
|
debug_print("KoboTouch:books - set_debugging_title to '%s'" % debugging_title )
|
||||||
bl.set_debugging_title(debugging_title)
|
bl.set_debugging_title(debugging_title)
|
||||||
debug_print("KoboTouch:books - length bl=%d"%len(bl))
|
debug_print("KoboTouch:books - length bl=%d"%len(bl))
|
||||||
need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE)
|
need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE)
|
||||||
@ -1930,7 +1940,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
delete_empty_shelves = opts.extra_customization[self.OPT_DELETE_BOOKSHELVES] and self.supports_bookshelves()
|
delete_empty_shelves = opts.extra_customization[self.OPT_DELETE_BOOKSHELVES] and self.supports_bookshelves()
|
||||||
update_series_details = opts.extra_customization[self.OPT_UPDATE_SERIES_DETAILS] and self.supports_series()
|
update_series_details = opts.extra_customization[self.OPT_UPDATE_SERIES_DETAILS] and self.supports_series()
|
||||||
debugging_title = opts.extra_customization[self.OPT_DEBUGGING_TITLE]
|
debugging_title = opts.extra_customization[self.OPT_DEBUGGING_TITLE]
|
||||||
debug_print("KoboTouch:update_device_database_collections - set_debugging_title to", debugging_title )
|
debug_print("KoboTouch:update_device_database_collections - set_debugging_title to '%s'" % debugging_title )
|
||||||
booklists.set_debugging_title(debugging_title)
|
booklists.set_debugging_title(debugging_title)
|
||||||
else:
|
else:
|
||||||
delete_empty_shelves = False
|
delete_empty_shelves = False
|
||||||
@ -2516,6 +2526,8 @@ class KOBOTOUCH(KOBO):
|
|||||||
return opts
|
return opts
|
||||||
|
|
||||||
|
|
||||||
|
def isAuraHD(self):
|
||||||
|
return self.detected_device.idProduct in self.AURA_HD_PRODUCT_ID
|
||||||
def isGlo(self):
|
def isGlo(self):
|
||||||
return self.detected_device.idProduct in self.GLO_PRODUCT_ID
|
return self.detected_device.idProduct in self.GLO_PRODUCT_ID
|
||||||
def isMini(self):
|
def isMini(self):
|
||||||
@ -2524,7 +2536,21 @@ class KOBOTOUCH(KOBO):
|
|||||||
return self.detected_device.idProduct in self.TOUCH_PRODUCT_ID
|
return self.detected_device.idProduct in self.TOUCH_PRODUCT_ID
|
||||||
|
|
||||||
def cover_file_endings(self):
|
def cover_file_endings(self):
|
||||||
return self.GLO_COVER_FILE_ENDINGS if self.isGlo() else self.COVER_FILE_ENDINGS
|
return self.GLO_COVER_FILE_ENDINGS if self.isGlo() else self.AURA_HD_COVER_FILE_ENDINGS if self.isAuraHD() else self.COVER_FILE_ENDINGS
|
||||||
|
|
||||||
|
def set_device_name(self):
|
||||||
|
device_name = self.gui_name
|
||||||
|
if self.isAuraHD():
|
||||||
|
device_name = 'Kobo Aura HD'
|
||||||
|
elif self.isGlo():
|
||||||
|
device_name = 'Kobo Glo'
|
||||||
|
elif self.isMini():
|
||||||
|
device_name = 'Kobo Mini'
|
||||||
|
elif self.isTouch():
|
||||||
|
device_name = 'Kobo Touch'
|
||||||
|
self.__class__.gui_name = device_name
|
||||||
|
return device_name
|
||||||
|
|
||||||
|
|
||||||
def copying_covers(self):
|
def copying_covers(self):
|
||||||
opts = self.settings()
|
opts = self.settings()
|
||||||
@ -2582,14 +2608,6 @@ class KOBOTOUCH(KOBO):
|
|||||||
# Supported database version
|
# Supported database version
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# @classmethod
|
|
||||||
# def get_gui_name(cls):
|
|
||||||
# if hasattr(cls, 'gui_name'):
|
|
||||||
# return cls.gui_name
|
|
||||||
# if hasattr(cls, '__name__'):
|
|
||||||
# return cls.__name__
|
|
||||||
# return cls.name
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_debugging_title(cls, title):
|
def is_debugging_title(cls, title):
|
||||||
|
@ -95,7 +95,6 @@ class PDNOVEL(USBMS):
|
|||||||
SUPPORTS_SUB_DIRS = False
|
SUPPORTS_SUB_DIRS = False
|
||||||
DELETE_EXTS = ['.jpg', '.jpeg', '.png']
|
DELETE_EXTS = ['.jpg', '.jpeg', '.png']
|
||||||
|
|
||||||
|
|
||||||
def upload_cover(self, path, filename, metadata, filepath):
|
def upload_cover(self, path, filename, metadata, filepath):
|
||||||
coverdata = getattr(metadata, 'thumbnail', None)
|
coverdata = getattr(metadata, 'thumbnail', None)
|
||||||
if coverdata and coverdata[2]:
|
if coverdata and coverdata[2]:
|
||||||
@ -427,8 +426,8 @@ class WAYTEQ(USBMS):
|
|||||||
EBOOK_DIR_MAIN = 'Documents'
|
EBOOK_DIR_MAIN = 'Documents'
|
||||||
SCAN_FROM_ROOT = True
|
SCAN_FROM_ROOT = True
|
||||||
|
|
||||||
VENDOR_NAME = 'ROCKCHIP'
|
VENDOR_NAME = ['ROCKCHIP', 'CBR']
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'RK28_SDK_DEMO'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['RK28_SDK_DEMO', 'EINK_EBOOK_READE']
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
def get_gui_name(self):
|
def get_gui_name(self):
|
||||||
@ -445,7 +444,8 @@ class WAYTEQ(USBMS):
|
|||||||
return self.EBOOK_DIR_CARD_A
|
return self.EBOOK_DIR_CARD_A
|
||||||
|
|
||||||
def windows_sort_drives(self, drives):
|
def windows_sort_drives(self, drives):
|
||||||
if len(drives) < 2: return drives
|
if len(drives) < 2:
|
||||||
|
return drives
|
||||||
main = drives.get('main', None)
|
main = drives.get('main', None)
|
||||||
carda = drives.get('carda', None)
|
carda = drives.get('carda', None)
|
||||||
if main and carda:
|
if main and carda:
|
||||||
@ -455,7 +455,8 @@ class WAYTEQ(USBMS):
|
|||||||
|
|
||||||
def linux_swap_drives(self, drives):
|
def linux_swap_drives(self, drives):
|
||||||
# See https://bugs.launchpad.net/bugs/1151901
|
# See https://bugs.launchpad.net/bugs/1151901
|
||||||
if len(drives) < 2 or not drives[1] or not drives[2]: return drives
|
if len(drives) < 2 or not drives[1] or not drives[2]:
|
||||||
|
return drives
|
||||||
drives = list(drives)
|
drives = list(drives)
|
||||||
t = drives[0]
|
t = drives[0]
|
||||||
drives[0] = drives[1]
|
drives[0] = drives[1]
|
||||||
@ -463,7 +464,8 @@ class WAYTEQ(USBMS):
|
|||||||
return tuple(drives)
|
return tuple(drives)
|
||||||
|
|
||||||
def osx_sort_names(self, names):
|
def osx_sort_names(self, names):
|
||||||
if len(names) < 2: return names
|
if len(names) < 2:
|
||||||
|
return names
|
||||||
main = names.get('main', None)
|
main = names.get('main', None)
|
||||||
card = names.get('carda', None)
|
card = names.get('carda', None)
|
||||||
|
|
||||||
|
@ -58,8 +58,8 @@ class PICO(NEWSMY):
|
|||||||
gui_name = 'Pico'
|
gui_name = 'Pico'
|
||||||
description = _('Communicate with the Pico reader.')
|
description = _('Communicate with the Pico reader.')
|
||||||
|
|
||||||
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'LASER-', '']
|
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'LASER-', 'LASER', '']
|
||||||
WINDOWS_MAIN_MEM = ['USBDISK__USER', 'EB720']
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['USBDISK__USER', 'EB720', 'EBOOK-EB720']
|
||||||
EBOOK_DIR_MAIN = 'Books'
|
EBOOK_DIR_MAIN = 'Books'
|
||||||
FORMATS = ['EPUB', 'FB2', 'TXT', 'LRC', 'PDB', 'PDF', 'HTML', 'WTXT']
|
FORMATS = ['EPUB', 'FB2', 'TXT', 'LRC', 'PDB', 'PDF', 'HTML', 'WTXT']
|
||||||
SCAN_FROM_ROOT = True
|
SCAN_FROM_ROOT = True
|
||||||
|
@ -188,7 +188,6 @@ class EPUBInput(InputFormatPlugin):
|
|||||||
raise DRMError(os.path.basename(path))
|
raise DRMError(os.path.basename(path))
|
||||||
self.encrypted_fonts = self._encrypted_font_uris
|
self.encrypted_fonts = self._encrypted_font_uris
|
||||||
|
|
||||||
|
|
||||||
if len(parts) > 1 and parts[0]:
|
if len(parts) > 1 and parts[0]:
|
||||||
delta = '/'.join(parts[:-1])+'/'
|
delta = '/'.join(parts[:-1])+'/'
|
||||||
for elem in opf.itermanifest():
|
for elem in opf.itermanifest():
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
'''
|
'''
|
||||||
Basic support for manipulating OEB 1.x/2.0 content and metadata.
|
Basic support for manipulating OEB 1.x/2.0 content and metadata.
|
||||||
'''
|
'''
|
||||||
from __future__ import with_statement
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||||
@ -11,7 +10,7 @@ import os, re, uuid, logging
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import count
|
from itertools import count
|
||||||
from urlparse import urldefrag, urlparse, urlunparse, urljoin
|
from urlparse import urldefrag, urlparse, urlunparse, urljoin
|
||||||
from urllib import unquote as urlunquote
|
from urllib import unquote
|
||||||
|
|
||||||
from lxml import etree, html
|
from lxml import etree, html
|
||||||
from calibre.constants import filesystem_encoding, __version__
|
from calibre.constants import filesystem_encoding, __version__
|
||||||
@ -40,11 +39,11 @@ CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata'
|
|||||||
RE_NS = 'http://exslt.org/regular-expressions'
|
RE_NS = 'http://exslt.org/regular-expressions'
|
||||||
MBP_NS = 'http://www.mobipocket.com'
|
MBP_NS = 'http://www.mobipocket.com'
|
||||||
|
|
||||||
XPNSMAP = {'h' : XHTML_NS, 'o1' : OPF1_NS, 'o2' : OPF2_NS,
|
XPNSMAP = {'h': XHTML_NS, 'o1': OPF1_NS, 'o2': OPF2_NS,
|
||||||
'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS,
|
'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS,
|
||||||
'xsi': XSI_NS, 'dt' : DCTERMS_NS, 'ncx': NCX_NS,
|
'xsi': XSI_NS, 'dt': DCTERMS_NS, 'ncx': NCX_NS,
|
||||||
'svg': SVG_NS, 'xl' : XLINK_NS, 're': RE_NS,
|
'svg': SVG_NS, 'xl': XLINK_NS, 're': RE_NS,
|
||||||
'mbp': MBP_NS, 'calibre': CALIBRE_NS }
|
'mbp': MBP_NS, 'calibre': CALIBRE_NS}
|
||||||
|
|
||||||
OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS}
|
OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS}
|
||||||
OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS,
|
OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS,
|
||||||
@ -142,7 +141,6 @@ def iterlinks(root, find_links_in_css=True):
|
|||||||
if attr in link_attrs:
|
if attr in link_attrs:
|
||||||
yield (el, attr, attribs[attr], 0)
|
yield (el, attr, attribs[attr], 0)
|
||||||
|
|
||||||
|
|
||||||
if not find_links_in_css:
|
if not find_links_in_css:
|
||||||
continue
|
continue
|
||||||
if tag == XHTML('style') and el.text:
|
if tag == XHTML('style') and el.text:
|
||||||
@ -363,7 +361,9 @@ URL_SAFE = set('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|||||||
URL_UNSAFE = [ASCII_CHARS - URL_SAFE, UNIBYTE_CHARS - URL_SAFE]
|
URL_UNSAFE = [ASCII_CHARS - URL_SAFE, UNIBYTE_CHARS - URL_SAFE]
|
||||||
|
|
||||||
def urlquote(href):
|
def urlquote(href):
|
||||||
"""Quote URL-unsafe characters, allowing IRI-safe characters."""
|
""" Quote URL-unsafe characters, allowing IRI-safe characters.
|
||||||
|
That is, this function returns valid IRIs not valid URIs. In particular,
|
||||||
|
IRIs can contain non-ascii characters. """
|
||||||
result = []
|
result = []
|
||||||
unsafe = 0 if isinstance(href, unicode) else 1
|
unsafe = 0 if isinstance(href, unicode) else 1
|
||||||
unsafe = URL_UNSAFE[unsafe]
|
unsafe = URL_UNSAFE[unsafe]
|
||||||
@ -373,6 +373,19 @@ def urlquote(href):
|
|||||||
result.append(char)
|
result.append(char)
|
||||||
return ''.join(result)
|
return ''.join(result)
|
||||||
|
|
||||||
|
def urlunquote(href):
|
||||||
|
# unquote must run on a bytestring and will return a bytestring
|
||||||
|
# If it runs on a unicode object, it returns a double encoded unicode
|
||||||
|
# string: unquote(u'%C3%A4') != unquote(b'%C3%A4').decode('utf-8')
|
||||||
|
# and the latter is correct
|
||||||
|
want_unicode = isinstance(href, unicode)
|
||||||
|
if want_unicode:
|
||||||
|
href = href.encode('utf-8')
|
||||||
|
href = unquote(href)
|
||||||
|
if want_unicode:
|
||||||
|
href = href.decode('utf-8')
|
||||||
|
return href
|
||||||
|
|
||||||
def urlnormalize(href):
|
def urlnormalize(href):
|
||||||
"""Convert a URL into normalized form, with all and only URL-unsafe
|
"""Convert a URL into normalized form, with all and only URL-unsafe
|
||||||
characters URL quoted.
|
characters URL quoted.
|
||||||
@ -469,7 +482,7 @@ class DirContainer(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _unquote(self, path):
|
def _unquote(self, path):
|
||||||
# urlunquote must run on a bytestring and will return a bytestring
|
# unquote must run on a bytestring and will return a bytestring
|
||||||
# If it runs on a unicode object, it returns a double encoded unicode
|
# If it runs on a unicode object, it returns a double encoded unicode
|
||||||
# string: unquote(u'%C3%A4') != unquote(b'%C3%A4').decode('utf-8')
|
# string: unquote(u'%C3%A4') != unquote(b'%C3%A4').decode('utf-8')
|
||||||
# and the latter is correct
|
# and the latter is correct
|
||||||
@ -497,7 +510,7 @@ class DirContainer(object):
|
|||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
path = os.path.join(self.rootdir, self._unquote(path))
|
path = os.path.join(self.rootdir, self._unquote(path))
|
||||||
except ValueError: #Happens if path contains quoted special chars
|
except ValueError: # Happens if path contains quoted special chars
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
return os.path.isfile(path)
|
return os.path.isfile(path)
|
||||||
@ -577,12 +590,13 @@ class Metadata(object):
|
|||||||
allowed = self.allowed
|
allowed = self.allowed
|
||||||
if allowed is not None and term not in allowed:
|
if allowed is not None and term not in allowed:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
'attribute %r not valid for metadata term %r' \
|
'attribute %r not valid for metadata term %r'
|
||||||
% (self.attr(term), barename(obj.term)))
|
% (self.attr(term), barename(obj.term)))
|
||||||
return self.attr(term)
|
return self.attr(term)
|
||||||
|
|
||||||
def __get__(self, obj, cls):
|
def __get__(self, obj, cls):
|
||||||
if obj is None: return None
|
if obj is None:
|
||||||
|
return None
|
||||||
return obj.attrib.get(self.term_attr(obj), '')
|
return obj.attrib.get(self.term_attr(obj), '')
|
||||||
|
|
||||||
def __set__(self, obj, value):
|
def __set__(self, obj, value):
|
||||||
@ -628,7 +642,7 @@ class Metadata(object):
|
|||||||
self.value = value
|
self.value = value
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
scheme = Attribute(lambda term: 'scheme' if \
|
scheme = Attribute(lambda term: 'scheme' if
|
||||||
term == OPF('meta') else OPF('scheme'),
|
term == OPF('meta') else OPF('scheme'),
|
||||||
[DC('identifier'), OPF('meta')])
|
[DC('identifier'), OPF('meta')])
|
||||||
file_as = Attribute(OPF('file-as'), [DC('creator'), DC('contributor'),
|
file_as = Attribute(OPF('file-as'), [DC('creator'), DC('contributor'),
|
||||||
@ -882,7 +896,6 @@ class Manifest(object):
|
|||||||
|
|
||||||
return self._parse_xhtml(convert_markdown(data, title=title))
|
return self._parse_xhtml(convert_markdown(data, title=title))
|
||||||
|
|
||||||
|
|
||||||
def _parse_css(self, data):
|
def _parse_css(self, data):
|
||||||
from cssutils import CSSParser, log, resolveImports
|
from cssutils import CSSParser, log, resolveImports
|
||||||
log.setLevel(logging.WARN)
|
log.setLevel(logging.WARN)
|
||||||
@ -1022,7 +1035,8 @@ class Manifest(object):
|
|||||||
target, frag = urldefrag(href)
|
target, frag = urldefrag(href)
|
||||||
target = target.split('/')
|
target = target.split('/')
|
||||||
for index in xrange(min(len(base), len(target))):
|
for index in xrange(min(len(base), len(target))):
|
||||||
if base[index] != target[index]: break
|
if base[index] != target[index]:
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
index += 1
|
index += 1
|
||||||
relhref = (['..'] * (len(base) - index)) + target[index:]
|
relhref = (['..'] * (len(base) - index)) + target[index:]
|
||||||
|
@ -148,7 +148,6 @@ class OEBReader(object):
|
|||||||
if not has_aut:
|
if not has_aut:
|
||||||
m.add('creator', self.oeb.translate(__('Unknown')), role='aut')
|
m.add('creator', self.oeb.translate(__('Unknown')), role='aut')
|
||||||
|
|
||||||
|
|
||||||
def _manifest_prune_invalid(self):
|
def _manifest_prune_invalid(self):
|
||||||
'''
|
'''
|
||||||
Remove items from manifest that contain invalid data. This prevents
|
Remove items from manifest that contain invalid data. This prevents
|
||||||
@ -197,6 +196,8 @@ class OEBReader(object):
|
|||||||
item.media_type[-4:] in ('/xml', '+xml')):
|
item.media_type[-4:] in ('/xml', '+xml')):
|
||||||
hrefs = [r[2] for r in iterlinks(data)]
|
hrefs = [r[2] for r in iterlinks(data)]
|
||||||
for href in hrefs:
|
for href in hrefs:
|
||||||
|
if isinstance(href, bytes):
|
||||||
|
href = href.decode('utf-8')
|
||||||
href, _ = urldefrag(href)
|
href, _ = urldefrag(href)
|
||||||
if not href:
|
if not href:
|
||||||
continue
|
continue
|
||||||
@ -394,9 +395,9 @@ class OEBReader(object):
|
|||||||
|
|
||||||
authorElement = xpath(child,
|
authorElement = xpath(child,
|
||||||
'descendant::calibre:meta[@name = "author"]')
|
'descendant::calibre:meta[@name = "author"]')
|
||||||
if authorElement :
|
if authorElement:
|
||||||
author = authorElement[0].text
|
author = authorElement[0].text
|
||||||
else :
|
else:
|
||||||
author = None
|
author = None
|
||||||
|
|
||||||
descriptionElement = xpath(child,
|
descriptionElement = xpath(child,
|
||||||
@ -406,7 +407,7 @@ class OEBReader(object):
|
|||||||
method='text', encoding=unicode).strip()
|
method='text', encoding=unicode).strip()
|
||||||
if not description:
|
if not description:
|
||||||
description = None
|
description = None
|
||||||
else :
|
else:
|
||||||
description = None
|
description = None
|
||||||
|
|
||||||
index_image = xpath(child,
|
index_image = xpath(child,
|
||||||
@ -497,7 +498,8 @@ class OEBReader(object):
|
|||||||
titles = []
|
titles = []
|
||||||
headers = []
|
headers = []
|
||||||
for item in self.oeb.spine:
|
for item in self.oeb.spine:
|
||||||
if not item.linear: continue
|
if not item.linear:
|
||||||
|
continue
|
||||||
html = item.data
|
html = item.data
|
||||||
title = ''.join(xpath(html, '/h:html/h:head/h:title/text()'))
|
title = ''.join(xpath(html, '/h:html/h:head/h:title/text()'))
|
||||||
title = COLLAPSE_RE.sub(' ', title.strip())
|
title = COLLAPSE_RE.sub(' ', title.strip())
|
||||||
@ -515,17 +517,21 @@ class OEBReader(object):
|
|||||||
if len(titles) > len(set(titles)):
|
if len(titles) > len(set(titles)):
|
||||||
use = headers
|
use = headers
|
||||||
for title, item in izip(use, self.oeb.spine):
|
for title, item in izip(use, self.oeb.spine):
|
||||||
if not item.linear: continue
|
if not item.linear:
|
||||||
|
continue
|
||||||
toc.add(title, item.href)
|
toc.add(title, item.href)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _toc_from_opf(self, opf, item):
|
def _toc_from_opf(self, opf, item):
|
||||||
self.oeb.auto_generated_toc = False
|
self.oeb.auto_generated_toc = False
|
||||||
if self._toc_from_ncx(item): return
|
if self._toc_from_ncx(item):
|
||||||
|
return
|
||||||
# Prefer HTML to tour based TOC, since several LIT files
|
# Prefer HTML to tour based TOC, since several LIT files
|
||||||
# have good HTML TOCs but bad tour based TOCs
|
# have good HTML TOCs but bad tour based TOCs
|
||||||
if self._toc_from_html(opf): return
|
if self._toc_from_html(opf):
|
||||||
if self._toc_from_tour(opf): return
|
return
|
||||||
|
if self._toc_from_tour(opf):
|
||||||
|
return
|
||||||
self._toc_from_spine(opf)
|
self._toc_from_spine(opf)
|
||||||
self.oeb.auto_generated_toc = True
|
self.oeb.auto_generated_toc = True
|
||||||
|
|
||||||
@ -589,8 +595,10 @@ class OEBReader(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _pages_from_opf(self, opf, item):
|
def _pages_from_opf(self, opf, item):
|
||||||
if self._pages_from_ncx(opf, item): return
|
if self._pages_from_ncx(opf, item):
|
||||||
if self._pages_from_page_map(opf): return
|
return
|
||||||
|
if self._pages_from_page_map(opf):
|
||||||
|
return
|
||||||
return
|
return
|
||||||
|
|
||||||
def _cover_from_html(self, hcover):
|
def _cover_from_html(self, hcover):
|
||||||
|
@ -47,6 +47,8 @@ class ManifestTrimmer(object):
|
|||||||
item.data is not None:
|
item.data is not None:
|
||||||
hrefs = [r[2] for r in iterlinks(item.data)]
|
hrefs = [r[2] for r in iterlinks(item.data)]
|
||||||
for href in hrefs:
|
for href in hrefs:
|
||||||
|
if isinstance(href, bytes):
|
||||||
|
href = href.decode('utf-8')
|
||||||
try:
|
try:
|
||||||
href = item.abshref(urlnormalize(href))
|
href = item.abshref(urlnormalize(href))
|
||||||
except:
|
except:
|
||||||
|
@ -21,7 +21,7 @@ from PyQt4.Qt import (
|
|||||||
QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStyle, QStackedWidget,
|
QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStyle, QStackedWidget,
|
||||||
QWidget, QTableView, QGridLayout, QFontInfo, QPalette, QTimer, pyqtSignal,
|
QWidget, QTableView, QGridLayout, QFontInfo, QPalette, QTimer, pyqtSignal,
|
||||||
QAbstractTableModel, QVariant, QSize, QListView, QPixmap, QModelIndex,
|
QAbstractTableModel, QVariant, QSize, QListView, QPixmap, QModelIndex,
|
||||||
QAbstractListModel, QColor, QRect, QTextBrowser, QStringListModel)
|
QAbstractListModel, QColor, QRect, QTextBrowser, QStringListModel, QMenu, QCursor)
|
||||||
from PyQt4.QtWebKit import QWebView
|
from PyQt4.QtWebKit import QWebView
|
||||||
|
|
||||||
from calibre.customize.ui import metadata_plugins
|
from calibre.customize.ui import metadata_plugins
|
||||||
@ -182,7 +182,6 @@ class ResultsModel(QAbstractTableModel): # {{{
|
|||||||
p = book.publisher if book.publisher else ''
|
p = book.publisher if book.publisher else ''
|
||||||
return '<b>%s</b><br><i>%s</i>' % (d, p)
|
return '<b>%s</b><br><i>%s</i>' % (d, p)
|
||||||
|
|
||||||
|
|
||||||
def data(self, index, role):
|
def data(self, index, role):
|
||||||
row, col = index.row(), index.column()
|
row, col = index.row(), index.column()
|
||||||
try:
|
try:
|
||||||
@ -552,7 +551,6 @@ class IdentifyWidget(QWidget): # {{{
|
|||||||
self.results_view.show_results(self.worker.results)
|
self.results_view.show_results(self.worker.results)
|
||||||
self.results_found.emit()
|
self.results_found.emit()
|
||||||
|
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.abort.set()
|
self.abort.set()
|
||||||
# }}}
|
# }}}
|
||||||
@ -609,7 +607,8 @@ class CoverWorker(Thread): # {{{
|
|||||||
|
|
||||||
def scan_once(self, tdir, seen):
|
def scan_once(self, tdir, seen):
|
||||||
for x in list(os.listdir(tdir)):
|
for x in list(os.listdir(tdir)):
|
||||||
if x in seen: continue
|
if x in seen:
|
||||||
|
continue
|
||||||
if x.endswith('.cover') and os.path.exists(os.path.join(tdir,
|
if x.endswith('.cover') and os.path.exists(os.path.join(tdir,
|
||||||
x+'.done')):
|
x+'.done')):
|
||||||
name = x.rpartition('.')[0]
|
name = x.rpartition('.')[0]
|
||||||
@ -793,6 +792,8 @@ class CoversView(QListView): # {{{
|
|||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
|
|
||||||
self.doubleClicked.connect(self.chosen, type=Qt.QueuedConnection)
|
self.doubleClicked.connect(self.chosen, type=Qt.QueuedConnection)
|
||||||
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
self.customContextMenuRequested.connect(self.show_context_menu)
|
||||||
|
|
||||||
def select(self, num):
|
def select(self, num):
|
||||||
current = self.model().index(num)
|
current = self.model().index(num)
|
||||||
@ -814,6 +815,21 @@ class CoversView(QListView): # {{{
|
|||||||
else:
|
else:
|
||||||
self.select(self.m.index_from_pointer(pointer).row())
|
self.select(self.m.index_from_pointer(pointer).row())
|
||||||
|
|
||||||
|
def show_context_menu(self, point):
|
||||||
|
idx = self.currentIndex()
|
||||||
|
if idx and idx.isValid() and not idx.data(Qt.UserRole).toPyObject():
|
||||||
|
m = QMenu()
|
||||||
|
m.addAction(QIcon(I('view.png')), _('View this cover at full size'), self.show_cover)
|
||||||
|
m.exec_(QCursor.pos())
|
||||||
|
|
||||||
|
def show_cover(self):
|
||||||
|
idx = self.currentIndex()
|
||||||
|
pmap = self.model().cover_pixmap(idx)
|
||||||
|
if pmap is not None:
|
||||||
|
from calibre.gui2.viewer.image_popup import ImageView
|
||||||
|
d = ImageView(self, pmap, unicode(idx.data(Qt.DisplayRole).toString()), geom_name='metadata_download_cover_popup_geom')
|
||||||
|
d(use_exec=True)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class CoversWidget(QWidget): # {{{
|
class CoversWidget(QWidget): # {{{
|
||||||
|
@ -129,7 +129,7 @@
|
|||||||
<item row="6" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_16">
|
<widget class="QLabel" name="label_16">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Max. OPDS &ungrouped items:</string>
|
<string>Max. &ungrouped items:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>opt_max_opds_ungrouped_items</cstring>
|
<cstring>opt_max_opds_ungrouped_items</cstring>
|
||||||
|
@ -15,16 +15,17 @@ from calibre.gui2 import choose_save_file, gprefs
|
|||||||
|
|
||||||
class ImageView(QDialog):
|
class ImageView(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent, current_img, current_url):
|
def __init__(self, parent, current_img, current_url, geom_name='viewer_image_popup_geometry'):
|
||||||
QDialog.__init__(self)
|
QDialog.__init__(self)
|
||||||
dw = QApplication.instance().desktop()
|
dw = QApplication.instance().desktop()
|
||||||
self.avail_geom = dw.availableGeometry(parent)
|
self.avail_geom = dw.availableGeometry(parent)
|
||||||
self.current_img = current_img
|
self.current_img = current_img
|
||||||
self.current_url = current_url
|
self.current_url = current_url
|
||||||
self.factor = 1.0
|
self.factor = 1.0
|
||||||
|
self.geom_name = geom_name
|
||||||
|
|
||||||
self.label = l = QLabel()
|
self.label = l = QLabel()
|
||||||
l.setBackgroundRole(QPalette.Base);
|
l.setBackgroundRole(QPalette.Base)
|
||||||
l.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
l.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
||||||
l.setScaledContents(True)
|
l.setScaledContents(True)
|
||||||
|
|
||||||
@ -88,21 +89,27 @@ class ImageView(QDialog):
|
|||||||
self.label.setPixmap(pm)
|
self.label.setPixmap(pm)
|
||||||
self.label.adjustSize()
|
self.label.adjustSize()
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self, use_exec=False):
|
||||||
geom = self.avail_geom
|
geom = self.avail_geom
|
||||||
self.label.setPixmap(self.current_img)
|
self.label.setPixmap(self.current_img)
|
||||||
self.label.adjustSize()
|
self.label.adjustSize()
|
||||||
self.resize(QSize(int(geom.width()/2.5), geom.height()-50))
|
self.resize(QSize(int(geom.width()/2.5), geom.height()-50))
|
||||||
geom = gprefs.get('viewer_image_popup_geometry', None)
|
geom = gprefs.get(self.geom_name, None)
|
||||||
if geom is not None:
|
if geom is not None:
|
||||||
self.restoreGeometry(geom)
|
self.restoreGeometry(geom)
|
||||||
|
try:
|
||||||
self.current_image_name = unicode(self.current_url.toString()).rpartition('/')[-1]
|
self.current_image_name = unicode(self.current_url.toString()).rpartition('/')[-1]
|
||||||
|
except AttributeError:
|
||||||
|
self.current_image_name = self.current_url
|
||||||
title = _('View Image: %s')%self.current_image_name
|
title = _('View Image: %s')%self.current_image_name
|
||||||
self.setWindowTitle(title)
|
self.setWindowTitle(title)
|
||||||
|
if use_exec:
|
||||||
|
self.exec_()
|
||||||
|
else:
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def done(self, e):
|
def done(self, e):
|
||||||
gprefs['viewer_image_popup_geometry'] = bytearray(self.saveGeometry())
|
gprefs[self.geom_name] = bytearray(self.saveGeometry())
|
||||||
return QDialog.done(self, e)
|
return QDialog.done(self, e)
|
||||||
|
|
||||||
def wheelEvent(self, event):
|
def wheelEvent(self, event):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user