From 2ae0cc048d489204161f29e825485920c0604662 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 27 Jun 2011 17:18:51 +0100
Subject: [PATCH 01/18] Make get_categories respect a search restriction that
finds no books. This is an incompatible change, as [] used to mean no
restriction and now it means no books. Tested with tag browser and content
server.
---
src/calibre/library/database2.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 8b4ad47284..9229d44cac 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1442,7 +1442,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
raise ValueError('sort ' + sort + ' not a valid value')
self.books_list_filter.change([] if not ids else ids)
- id_filter = None if not ids else frozenset(ids)
+ id_filter = None if ids is None else frozenset(ids)
tb_cats = self.field_metadata
tcategories = {}
@@ -1520,7 +1520,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
rating_dex = self.FIELD_MAP['rating']
tag_class = LibraryDatabase2.TCat_Tag
for book in self.data.iterall():
- if id_filter and book[id_dex] not in id_filter:
+ if id_filter is not None and book[id_dex] not in id_filter:
continue
rating = book[rating_dex]
# We kept track of all possible category field_map positions above
From 1441bce41c71f87ee9a9c2bf818a7322e79c5ffd Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 27 Jun 2011 17:19:31 +0100
Subject: [PATCH 02/18] Remove spacer that shouldn't be there. Add 2-criteria
sorting for series.
---
src/calibre/gui2/dialogs/quickview.py | 21 +++++++++++++++++----
src/calibre/gui2/dialogs/quickview.ui | 13 -------------
2 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py
index 3a69368730..ec8e6b6bc7 100644
--- a/src/calibre/gui2/dialogs/quickview.py
+++ b/src/calibre/gui2/dialogs/quickview.py
@@ -18,16 +18,29 @@ class TableItem(QTableWidgetItem):
A QTableWidgetItem that sorts on a separate string and uses ICU rules
'''
- def __init__(self, val, sort):
+ def __init__(self, val, sort, idx=0):
self.sort = sort
+ self.sort_idx = idx
QTableWidgetItem.__init__(self, val)
self.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
def __ge__(self, other):
- return sort_key(self.sort) >= sort_key(other.sort)
+ l = sort_key(self.sort)
+ r = sort_key(other.sort)
+ if l > r:
+ return 1
+ if l == r:
+ return self.sort_idx >= other.sort_idx
+ return 0
def __lt__(self, other):
- return sort_key(self.sort) < sort_key(other.sort)
+ l = sort_key(self.sort)
+ r = sort_key(other.sort)
+ if l < r:
+ return 1
+ if l == r:
+ return self.sort_idx < other.sort_idx
+ return 0
class Quickview(QDialog, Ui_Quickview):
@@ -185,7 +198,7 @@ class Quickview(QDialog, Ui_Quickview):
series = mi.format_field('series')[1]
if series is None:
series = ''
- a = TableItem(series, series)
+ a = TableItem(series, mi.series, mi.series_index)
a.setToolTip(tt)
self.books_table.setItem(row, 2, a)
self.books_table.setRowHeight(row, self.books_table_row_height)
diff --git a/src/calibre/gui2/dialogs/quickview.ui b/src/calibre/gui2/dialogs/quickview.ui
index 2cdc7b7379..4b040e34d3 100644
--- a/src/calibre/gui2/dialogs/quickview.ui
+++ b/src/calibre/gui2/dialogs/quickview.ui
@@ -57,19 +57,6 @@
- -
-
-
- Qt::Vertical
-
-
-
- 0
- 0
-
-
-
-
-
-
From 2930304cd8eb549dfe6edb2a5983a461ba2c8393 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 27 Jun 2011 18:00:38 +0100
Subject: [PATCH 03/18] tag_view: Fix uses of py_name that should be
category_key.
---
src/calibre/gui2/tag_browser/model.py | 4 ++--
src/calibre/gui2/tag_browser/view.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py
index 5589a1bcb4..e759783d7b 100644
--- a/src/calibre/gui2/tag_browser/model.py
+++ b/src/calibre/gui2/tag_browser/model.py
@@ -514,7 +514,7 @@ class TagsModel(QAbstractItemModel): # {{{
# }}}
for category in self.category_nodes:
- process_one_node(category, state_map.get(category.py_name, {}))
+ process_one_node(category, state_map.get(category.category_key, {}))
# Drag'n Drop {{{
def mimeTypes(self):
@@ -851,7 +851,7 @@ class TagsModel(QAbstractItemModel): # {{{
def index_for_category(self, name):
for row, category in enumerate(self.category_nodes):
- if category.py_name == name:
+ if category.category_key == name:
return self.index(row, 0, QModelIndex())
def columnCount(self, parent):
diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py
index 1fad4eb9a3..c833f7fa43 100644
--- a/src/calibre/gui2/tag_browser/view.py
+++ b/src/calibre/gui2/tag_browser/view.py
@@ -129,10 +129,10 @@ class TagsView(QTreeView): # {{{
expanded_categories = []
for row, category in enumerate(self._model.category_nodes):
if self.isExpanded(self._model.index(row, 0, QModelIndex())):
- expanded_categories.append(category.py_name)
+ expanded_categories.append(category.category_key)
states = [c.tag.state for c in category.child_tags()]
names = [(c.tag.name, c.tag.category) for c in category.child_tags()]
- state_map[category.py_name] = dict(izip(names, states))
+ state_map[category.category_key] = dict(izip(names, states))
return expanded_categories, state_map
def reread_collapse_parameters(self):
From 7f1905b779ce141df4943cc954d0e01f6281a79b Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 27 Jun 2011 18:18:01 +0100
Subject: [PATCH 04/18] Make quickview survive changing the library.
---
src/calibre/gui2/actions/show_quickview.py | 3 +++
src/calibre/gui2/dialogs/quickview.py | 9 +++++++++
2 files changed, 12 insertions(+)
diff --git a/src/calibre/gui2/actions/show_quickview.py b/src/calibre/gui2/actions/show_quickview.py
index 78352e6da8..4f7bbc0473 100644
--- a/src/calibre/gui2/actions/show_quickview.py
+++ b/src/calibre/gui2/actions/show_quickview.py
@@ -38,3 +38,6 @@ class ShowQuickviewAction(InterfaceAction):
Quickview(self.gui, self.gui.library_view, index)
self.current_instance.show()
+ def library_changed(self, db):
+ if self.current_instance and not self.current_instance.is_closed:
+ self.current_instance.set_database(db)
diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py
index ec8e6b6bc7..30b68a7b7d 100644
--- a/src/calibre/gui2/dialogs/quickview.py
+++ b/src/calibre/gui2/dialogs/quickview.py
@@ -108,6 +108,15 @@ class Quickview(QDialog, Ui_Quickview):
self.search_button.clicked.connect(self.do_search)
view.model().new_bookdisplay_data.connect(self.book_was_changed)
+ def set_database(self, db):
+ self.db = db
+ self.items.blockSignals(True)
+ self.books_table.blockSignals(True)
+ self.items.clear()
+ self.books_table.setRowCount(0)
+ self.books_table.blockSignals(False)
+ self.items.blockSignals(False)
+
# search button
def do_search(self):
if self.last_search is not None:
From d1d5ee7a3e022331f775ddf812d6fb72bef49cd0 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 12:13:30 -0600
Subject: [PATCH 05/18] ...
---
src/calibre/gui2/preferences/main.py | 1 -
src/calibre/gui2/preferences/search.py | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py
index 85a5fc018c..774b7f8958 100644
--- a/src/calibre/gui2/preferences/main.py
+++ b/src/calibre/gui2/preferences/main.py
@@ -357,7 +357,6 @@ class Preferences(QMainWindow):
bytearray(self.saveGeometry()))
if self.committed:
self.gui.must_restart_before_config = self.must_restart
- self.gui.tags_view.set_new_model() # in case columns changed
self.gui.tags_view.recount()
self.gui.create_device_menu()
self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py
index 7bdb12ec55..c86de7f2a3 100644
--- a/src/calibre/gui2/preferences/search.py
+++ b/src/calibre/gui2/preferences/search.py
@@ -173,7 +173,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def refresh_gui(self, gui):
gui.set_highlight_only_button_icon()
if self.muc_changed:
- gui.tags_view.set_new_model()
+ gui.tags_view.recount()
gui.search.search_as_you_type(config['search_as_you_type'])
gui.search.do_search()
From ef13b74d1a6fb459688fe3de3f9f679889983b9a Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 12:48:01 -0600
Subject: [PATCH 06/18] Tag Browser: Do not allow methods to be called in a non
GUI thread
---
src/calibre/gui2/tag_browser/view.py | 30 +++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py
index c833f7fa43..ed1a597827 100644
--- a/src/calibre/gui2/tag_browser/view.py
+++ b/src/calibre/gui2/tag_browser/view.py
@@ -16,9 +16,10 @@ from PyQt4.Qt import (QItemDelegate, Qt, QTreeView, pyqtSignal, QSize, QIcon,
from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES,
TagsModel)
-from calibre.gui2 import config, gprefs
+from calibre.gui2 import config, gprefs, is_gui_thread
from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key
+from calibre.constants import DEBUG
class TagDelegate(QItemDelegate): # {{{
@@ -125,6 +126,8 @@ class TagsView(QTreeView): # {{{
self.recount()
def get_state(self):
+ if not is_gui_thread():
+ return self.debug_threading()
state_map = {}
expanded_categories = []
for row, category in enumerate(self._model.category_nodes):
@@ -136,9 +139,13 @@ class TagsView(QTreeView): # {{{
return expanded_categories, state_map
def reread_collapse_parameters(self):
+ if not is_gui_thread():
+ return self.debug_threading()
self._model.reread_collapse_parameters(self.get_state()[1])
def set_database(self, db, tag_match, sort_by):
+ if not is_gui_thread():
+ return self.debug_threading()
self._model.set_database(db)
self.pane_is_visible = True # because TagsModel.set_database did a recount
@@ -165,6 +172,8 @@ class TagsView(QTreeView): # {{{
self.expanded.connect(self.item_expanded)
def database_changed(self, event, ids):
+ if not is_gui_thread():
+ return self.debug_threading()
if self.refresh_signal_processed:
self.refresh_signal_processed = False
self.refresh_required.emit()
@@ -191,6 +200,8 @@ class TagsView(QTreeView): # {{{
pass
def set_search_restriction(self, s):
+ if not is_gui_thread():
+ return self.debug_threading()
s = s if s else None
self._model.set_search_restriction(s)
@@ -217,6 +228,8 @@ class TagsView(QTreeView): # {{{
set_to: if None, advance the state. Otherwise must be one of the values
in TAG_SEARCH_STATES
'''
+ if not is_gui_thread():
+ return self.debug_threading()
modifiers = int(QApplication.keyboardModifiers())
exclusive = modifiers not in (Qt.CTRL, Qt.SHIFT)
if self._model.toggle(index, exclusive, set_to=set_to):
@@ -529,11 +542,21 @@ class TagsView(QTreeView): # {{{
fm_src['display'].get('make_category', False)))):
self.setDropIndicatorShown(True)
+ def debug_threading(self):
+ if DEBUG:
+ import traceback
+ print ('Attempt to use Tab Browser in non GUI thread')
+ traceback.print_stack()
+
def clear(self):
+ if not is_gui_thread():
+ return self.debug_threading()
if self.model():
self.model().clear_state()
def is_visible(self, idx):
+ if not is_gui_thread():
+ return self.debug_threading()
item = idx.data(Qt.UserRole).toPyObject()
if getattr(item, 'type', None) == TagTreeItem.TAG:
idx = idx.parent()
@@ -544,6 +567,9 @@ class TagsView(QTreeView): # {{{
Rebuild the category tree, expand any categories that were expanded,
reset the search states, and reselect the current node.
'''
+ if not is_gui_thread():
+ return self.debug_threading()
+
if self.disable_recounting or not self.pane_is_visible:
return
self.refresh_signal_processed = True
@@ -570,6 +596,8 @@ class TagsView(QTreeView): # {{{
def show_item_at_index(self, idx, box=False,
position=QTreeView.PositionAtCenter):
+ if not is_gui_thread():
+ return self.debug_threading()
if idx.isValid() and idx.data(Qt.UserRole).toPyObject() is not self._model.root_item:
self.setCurrentIndex(idx)
self.scrollTo(idx, position)
From cc0aee27f5e23e29c7ba43afed026fb7373abb77 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 12:53:40 -0600
Subject: [PATCH 07/18] Revert thread check from Tag Browser since it doesn't
prevent the crash
---
src/calibre/gui2/tag_browser/view.py | 30 +---------------------------
1 file changed, 1 insertion(+), 29 deletions(-)
diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py
index ed1a597827..c833f7fa43 100644
--- a/src/calibre/gui2/tag_browser/view.py
+++ b/src/calibre/gui2/tag_browser/view.py
@@ -16,10 +16,9 @@ from PyQt4.Qt import (QItemDelegate, Qt, QTreeView, pyqtSignal, QSize, QIcon,
from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES,
TagsModel)
-from calibre.gui2 import config, gprefs, is_gui_thread
+from calibre.gui2 import config, gprefs
from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key
-from calibre.constants import DEBUG
class TagDelegate(QItemDelegate): # {{{
@@ -126,8 +125,6 @@ class TagsView(QTreeView): # {{{
self.recount()
def get_state(self):
- if not is_gui_thread():
- return self.debug_threading()
state_map = {}
expanded_categories = []
for row, category in enumerate(self._model.category_nodes):
@@ -139,13 +136,9 @@ class TagsView(QTreeView): # {{{
return expanded_categories, state_map
def reread_collapse_parameters(self):
- if not is_gui_thread():
- return self.debug_threading()
self._model.reread_collapse_parameters(self.get_state()[1])
def set_database(self, db, tag_match, sort_by):
- if not is_gui_thread():
- return self.debug_threading()
self._model.set_database(db)
self.pane_is_visible = True # because TagsModel.set_database did a recount
@@ -172,8 +165,6 @@ class TagsView(QTreeView): # {{{
self.expanded.connect(self.item_expanded)
def database_changed(self, event, ids):
- if not is_gui_thread():
- return self.debug_threading()
if self.refresh_signal_processed:
self.refresh_signal_processed = False
self.refresh_required.emit()
@@ -200,8 +191,6 @@ class TagsView(QTreeView): # {{{
pass
def set_search_restriction(self, s):
- if not is_gui_thread():
- return self.debug_threading()
s = s if s else None
self._model.set_search_restriction(s)
@@ -228,8 +217,6 @@ class TagsView(QTreeView): # {{{
set_to: if None, advance the state. Otherwise must be one of the values
in TAG_SEARCH_STATES
'''
- if not is_gui_thread():
- return self.debug_threading()
modifiers = int(QApplication.keyboardModifiers())
exclusive = modifiers not in (Qt.CTRL, Qt.SHIFT)
if self._model.toggle(index, exclusive, set_to=set_to):
@@ -542,21 +529,11 @@ class TagsView(QTreeView): # {{{
fm_src['display'].get('make_category', False)))):
self.setDropIndicatorShown(True)
- def debug_threading(self):
- if DEBUG:
- import traceback
- print ('Attempt to use Tab Browser in non GUI thread')
- traceback.print_stack()
-
def clear(self):
- if not is_gui_thread():
- return self.debug_threading()
if self.model():
self.model().clear_state()
def is_visible(self, idx):
- if not is_gui_thread():
- return self.debug_threading()
item = idx.data(Qt.UserRole).toPyObject()
if getattr(item, 'type', None) == TagTreeItem.TAG:
idx = idx.parent()
@@ -567,9 +544,6 @@ class TagsView(QTreeView): # {{{
Rebuild the category tree, expand any categories that were expanded,
reset the search states, and reselect the current node.
'''
- if not is_gui_thread():
- return self.debug_threading()
-
if self.disable_recounting or not self.pane_is_visible:
return
self.refresh_signal_processed = True
@@ -596,8 +570,6 @@ class TagsView(QTreeView): # {{{
def show_item_at_index(self, idx, box=False,
position=QTreeView.PositionAtCenter):
- if not is_gui_thread():
- return self.debug_threading()
if idx.isValid() and idx.data(Qt.UserRole).toPyObject() is not self._model.root_item:
self.setCurrentIndex(idx)
self.scrollTo(idx, position)
From 2395ef81120f19c3c82303d733d57c4829050a49 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 13:28:40 -0600
Subject: [PATCH 08/18] Fix another crash in the Tag Browser
---
src/calibre/gui2/tag_browser/view.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py
index c833f7fa43..2660d8f969 100644
--- a/src/calibre/gui2/tag_browser/view.py
+++ b/src/calibre/gui2/tag_browser/view.py
@@ -571,6 +571,9 @@ class TagsView(QTreeView): # {{{
def show_item_at_index(self, idx, box=False,
position=QTreeView.PositionAtCenter):
if idx.isValid() and idx.data(Qt.UserRole).toPyObject() is not self._model.root_item:
+ self.setExpanded(idx, True) # Needed otherwise Qt segfaults if the
+ # node is buried in a collapsed, off
+ # screen hierarchy
self.setCurrentIndex(idx)
self.scrollTo(idx, position)
self.setCurrentIndex(idx)
From ccfff7f456b3d347725977f73c34f9ba8bd71618 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 13:33:22 -0600
Subject: [PATCH 09/18] ...
---
src/calibre/gui2/tag_browser/view.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py
index 2660d8f969..853fc296b5 100644
--- a/src/calibre/gui2/tag_browser/view.py
+++ b/src/calibre/gui2/tag_browser/view.py
@@ -574,9 +574,7 @@ class TagsView(QTreeView): # {{{
self.setExpanded(idx, True) # Needed otherwise Qt segfaults if the
# node is buried in a collapsed, off
# screen hierarchy
- self.setCurrentIndex(idx)
self.scrollTo(idx, position)
- self.setCurrentIndex(idx)
if box:
self._model.set_boxed(idx)
From f87375c45ed1f0cda3bf9a0602a50119fff178e1 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 14:39:54 -0600
Subject: [PATCH 10/18] TB: When showing item at index, expand parent, not item
---
src/calibre/gui2/tag_browser/view.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py
index 853fc296b5..39fd19c130 100644
--- a/src/calibre/gui2/tag_browser/view.py
+++ b/src/calibre/gui2/tag_browser/view.py
@@ -571,9 +571,10 @@ class TagsView(QTreeView): # {{{
def show_item_at_index(self, idx, box=False,
position=QTreeView.PositionAtCenter):
if idx.isValid() and idx.data(Qt.UserRole).toPyObject() is not self._model.root_item:
- self.setExpanded(idx, True) # Needed otherwise Qt segfaults if the
- # node is buried in a collapsed, off
- # screen hierarchy
+ self.expand(self._model.parent(idx)) # Needed otherwise Qt sometimes segfaults if the
+ # node is buried in a collapsed, off
+ # screen hierarchy
+ self.setCurrentIndex(idx)
self.scrollTo(idx, position)
if box:
self._model.set_boxed(idx)
From fe1695b742e51eff5cb8ea84a81917d3c643efa2 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 14:59:32 -0600
Subject: [PATCH 11/18] Support for detecting and mounting reader devices on
FreeBSD. Fixes #802708 (Patches for Devices on FreeBSD)
---
src/calibre/devices/linux_mount_helper.c | 18 +++
src/calibre/devices/usbms/device.py | 160 ++++++++++++++++++++++-
2 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/src/calibre/devices/linux_mount_helper.c b/src/calibre/devices/linux_mount_helper.c
index 2ced0f31fa..550510106e 100644
--- a/src/calibre/devices/linux_mount_helper.c
+++ b/src/calibre/devices/linux_mount_helper.c
@@ -64,14 +64,24 @@ int do_mount(const char *dev, const char *mp) {
snprintf(options, 1000, "rw,noexec,nosuid,sync,nodev");
snprintf(uids, 100, "%d", getuid());
snprintf(gids, 100, "%d", getgid());
+#else
+#ifdef __FreeBSD__
+ snprintf(options, 1000, "rw,noexec,nosuid,sync,-u=%d,-g=%d",getuid(),getgid());
#else
snprintf(options, 1000, "rw,noexec,nosuid,sync,nodev,quiet,shortname=mixed,uid=%d,gid=%d,umask=077,fmask=0177,dmask=0077,utf8,iocharset=iso8859-1", getuid(), getgid());
#endif
+#endif
+
ensure_root();
+
#ifdef __NetBSD__
execlp("mount_msdos", "mount_msdos", "-u", uids, "-g", gids, "-o", options, dev, mp, NULL);
+#else
+#ifdef __FreeBSD__
+ execlp("mount", "mount", "-t", "msdosfs", "-o", options, dev, mp, NULL);
#else
execlp("mount", "mount", "-t", "auto", "-o", options, dev, mp, NULL);
+#endif
#endif
errsv = errno;
fprintf(stderr, "Failed to mount with error: %s\n", strerror(errsv));
@@ -91,8 +101,12 @@ int call_eject(const char *dev, const char *mp) {
ensure_root();
#ifdef __NetBSD__
execlp("eject", "eject", dev, NULL);
+#else
+#ifdef __FreeBSD__
+ execlp("umount", "umount", dev, NULL);
#else
execlp("eject", "eject", "-s", dev, NULL);
+#endif
#endif
/* execlp failed */
errsv = errno;
@@ -121,7 +135,11 @@ int call_umount(const char *dev, const char *mp) {
if (pid == 0) { /* Child process */
ensure_root();
+#ifdef __FreeBSD__
+ execlp("umount", "umount", mp, NULL);
+#else
execlp("umount", "umount", "-l", mp, NULL);
+#endif
/* execlp failed */
errsv = errno;
fprintf(stderr, "Failed to umount with error: %s\n", strerror(errsv));
diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py
index 442f3701c4..bdbf5f44cf 100644
--- a/src/calibre/devices/usbms/device.py
+++ b/src/calibre/devices/usbms/device.py
@@ -17,7 +17,7 @@ from itertools import repeat
from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError, FreeSpaceError
from calibre.devices.usbms.deviceconfig import DeviceConfig
-from calibre.constants import iswindows, islinux, isosx, plugins
+from calibre.constants import iswindows, islinux, isosx, isfreebsd, plugins
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
if isosx:
@@ -701,7 +701,152 @@ class Device(DeviceConfig, DevicePlugin):
self._card_a_prefix = self._card_b_prefix
self._card_b_prefix = None
+# ------------------------------------------------------
+#
+# open for FreeBSD
+# find the device node or nodes that match the S/N we already have from the scanner
+# and attempt to mount each one
+# 1. get list of disk devices from sysctl
+# 2. compare that list with the one from camcontrol
+# 3. and see if it has a matching s/n
+# 6. find any partitions/slices associated with each node
+# 7. attempt to mount, using calibre-mount-helper, each one
+# 8. when finished, we have a list of mount points and associated device nodes
+#
+ def open_freebsd(self):
+ # this gives us access to the S/N, etc. of the reader that the scanner has found
+ # and the match routines for some of that data, like s/n, vendor ID, etc.
+ d=self.detected_device
+
+ if not d.serial:
+ raise DeviceError("Device has no S/N. Can't continue")
+ return False
+
+ devs={}
+ di=0
+ ndevs=4 # number of possible devices per reader (main, carda, cardb, launcher)
+
+ #get list of disk devices
+ p=subprocess.Popen(["sysctl", "kern.disks"], stdout=subprocess.PIPE)
+ kdsks=subprocess.Popen(["sed", "s/kern.disks: //"], stdin=p.stdout, stdout=subprocess.PIPE).communicate()[0]
+ p.stdout.close()
+ #print kdsks
+ for dvc in kdsks.split():
+ # for each one that's also in the list of cam devices ...
+ p=subprocess.Popen(["camcontrol", "devlist"], stdout=subprocess.PIPE)
+ devmatch=subprocess.Popen(["grep", dvc], stdin=p.stdout, stdout=subprocess.PIPE).communicate()[0]
+ p.stdout.close()
+ if devmatch:
+ #print "Checking ", devmatch
+ # ... see if we can get a S/N from the actual device node
+ sn=subprocess.Popen(["camcontrol", "inquiry", dvc, "-S"], stdout=subprocess.PIPE).communicate()[0]
+ sn=sn[0:-1] # drop the trailing newline
+ #print "S/N = ", sn
+ if sn and d.match_serial(sn):
+ # we have a matching s/n, record this device node
+ #print "match found: ", dvc
+ devs[di]=dvc
+ di += 1
+
+ # sort the list of devices
+ for i in range(1,ndevs+1):
+ for j in reversed(range(1,i)):
+ if devs[j-1] > devs[j]:
+ x=devs[j-1]
+ devs[j-1]=devs[j]
+ devs[j]=x
+ #print devs
+
+ # now we need to see if any of these have slices/partitions
+ mtd=0
+ label="READER" # could use something more unique, like S/N or productID...
+ cmd = '/usr/local/bin/calibre-mount-helper'
+ cmd = [cmd, 'mount']
+ for i in range(0,ndevs):
+ cmd2="ls /dev/"+devs[i]+"*"
+ p=subprocess.Popen(cmd2, shell=True, stdout=subprocess.PIPE)
+ devs[i]=subprocess.Popen(["cut", "-d", "/", "-f" "3"], stdin=p.stdout, stdout=subprocess.PIPE).communicate()[0]
+ p.stdout.close()
+
+ # try all the nodes to see what we can mount
+ for dev in devs[i].split():
+ mp='/media/'+label+'-'+dev
+ #print "trying ", dev, "on", mp
+ try:
+ p = subprocess.Popen(cmd + ["/dev/"+dev, mp])
+ except OSError:
+ raise DeviceError(_('Could not find mount helper: %s.')%cmd[0])
+ while p.poll() is None:
+ time.sleep(0.1)
+
+ if p.returncode == 0:
+ #print " mounted", dev
+ if i == 0:
+ self._main_prefix = mp
+ self._main_dev = "/dev/"+dev
+ #print "main = ", self._main_dev, self._main_prefix
+ if i == 1:
+ self._card_a_prefix = mp
+ self._card_a_dev = "/dev/"+dev
+ #print "card a = ", self._card_a_dev, self._card_a_prefix
+ if i == 2:
+ self._card_b_prefix = mp
+ self._card_b_dev = "/dev/"+dev
+ #print "card b = ", self._card_b_dev, self._card_b_prefix
+
+ mtd += 1
+ break
+
+ if mtd > 0:
+ return True
+ else :
+ return False
+#
+# ------------------------------------------------------
+#
+# this one is pretty simple:
+# just umount each of the previously
+# mounted filesystems, using the mount helper
+#
+ def eject_freebsd(self):
+ cmd = '/usr/local/bin/calibre-mount-helper'
+ cmd = [cmd, 'eject']
+
+ if self._main_prefix:
+ #print "umount main:", cmd, self._main_dev, self._main_prefix
+ try:
+ p = subprocess.Popen(cmd + [self._main_dev, self._main_prefix])
+ except OSError:
+ raise DeviceError(
+ _('Could not find mount helper: %s.')%cmd[0])
+ while p.poll() is None:
+ time.sleep(0.1)
+
+ if self._card_a_prefix:
+ #print "umount card a:", cmd, self._card_a_dev, self._card_a_prefix
+ try:
+ p = subprocess.Popen(cmd + [self._card_a_dev, self._card_a_prefix])
+ except OSError:
+ raise DeviceError(
+ _('Could not find mount helper: %s.')%cmd[0])
+ while p.poll() is None:
+ time.sleep(0.1)
+
+ if self._card_b_prefix:
+ #print "umount card b:", cmd, self._card_b_dev, self._card_b_prefix
+ try:
+ p = subprocess.Popen(cmd + [self._card_b_dev, self._card_b_prefix])
+ except OSError:
+ raise DeviceError(
+ _('Could not find mount helper: %s.')%cmd[0])
+ while p.poll() is None:
+ time.sleep(0.1)
+
+ self._main_prefix = None
+ self._card_a_prefix = None
+ self._card_b_prefix = None
+# ------------------------------------------------------
def open(self, library_uuid):
time.sleep(5)
@@ -712,6 +857,14 @@ class Device(DeviceConfig, DevicePlugin):
except DeviceError:
time.sleep(7)
self.open_linux()
+ if isfreebsd:
+ self._main_dev = self._card_a_dev = self._card_b_dev = None
+ try:
+ self.open_freebsd()
+ except DeviceError:
+ subprocess.Popen(["camcontrol", "rescan", "all"])
+ time.sleep(2)
+ self.open_freebsd()
if iswindows:
try:
self.open_windows()
@@ -800,6 +953,11 @@ class Device(DeviceConfig, DevicePlugin):
self.eject_linux()
except:
pass
+ if isfreebsd:
+ try:
+ self.eject_freebsd()
+ except:
+ pass
if iswindows:
try:
self.eject_windows()
From 64426fb0ea16901296424ddce43d02986454f7e0 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 15:34:52 -0600
Subject: [PATCH 12/18] Fix handling of filenames that have an even number of
periods before the file extension. Fixes #801939 (If html name ends with 3
periods, calibre will crash on conversion)
---
src/calibre/__init__.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py
index 33e80982d1..cf4d09770c 100644
--- a/src/calibre/__init__.py
+++ b/src/calibre/__init__.py
@@ -106,10 +106,12 @@ def sanitize_file_name(name, substitute='_', as_unicode=False):
name = name.encode(filesystem_encoding, 'ignore')
one = _filename_sanitize.sub(substitute, name)
one = re.sub(r'\s', ' ', one).strip()
- one = re.sub(r'^\.+$', '_', one)
+ bname, ext = os.path.splitext(one)
+ one = re.sub(r'^\.+$', '_', bname)
if as_unicode:
one = one.decode(filesystem_encoding)
one = one.replace('..', substitute)
+ one += ext
# Windows doesn't like path components that end with a period
if one and one[-1] in ('.', ' '):
one = one[:-1]+'_'
@@ -132,8 +134,10 @@ def sanitize_file_name_unicode(name, substitute='_'):
name]
one = u''.join(chars)
one = re.sub(r'\s', ' ', one).strip()
- one = re.sub(r'^\.+$', '_', one)
+ bname, ext = os.path.splitext(one)
+ one = re.sub(r'^\.+$', '_', bname)
one = one.replace('..', substitute)
+ one += ext
# Windows doesn't like path components that end with a period or space
if one and one[-1] in ('.', ' '):
one = one[:-1]+'_'
From 7d5fd85c53e2ca4043f828436054796a12aed2a1 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 15:45:00 -0600
Subject: [PATCH 13/18] Fix a regression in 0.8.7 that broke reading metadata
from MOBI files in the Edit metadata dialog. Fixes #801981 (Private bug)
---
src/calibre/ebooks/mobi/reader.py | 5 ++++-
src/calibre/library/database2.py | 1 +
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py
index 46505de4bd..1173b84266 100644
--- a/src/calibre/ebooks/mobi/reader.py
+++ b/src/calibre/ebooks/mobi/reader.py
@@ -957,7 +957,10 @@ def get_metadata(stream):
return get_metadata(stream)
from calibre.utils.logging import Log
log = Log()
- mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')])
+ try:
+ mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')])
+ except:
+ mi = MetaInformation(_('Unknown'), [_('Unknown')])
mh = MetadataHeader(stream, log)
if mh.title and mh.title != _('Unknown'):
mi.title = mh.title
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 9229d44cac..c8fd660e1a 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1245,6 +1245,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
ret = tempfile.SpooledTemporaryFile(max_size=SPOOL_SIZE)
shutil.copyfileobj(f, ret)
ret.seek(0)
+ ret.name = f.name
else:
ret = f.read()
return ret
From d7b68a12d32b4389c7c637254c20266d6e3b695d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 27 Jun 2011 15:53:16 -0600
Subject: [PATCH 14/18] ...
---
src/calibre/ebooks/compression/palmdoc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/ebooks/compression/palmdoc.c b/src/calibre/ebooks/compression/palmdoc.c
index 6b07bb9cd5..922b63fe1b 100644
--- a/src/calibre/ebooks/compression/palmdoc.c
+++ b/src/calibre/ebooks/compression/palmdoc.c
@@ -54,7 +54,7 @@ cpalmdoc_decompress(PyObject *self, PyObject *args) {
// Map chars to bytes
for (j = 0; j < input_len; j++)
input[j] = (_input[j] < 0) ? _input[j]+256 : _input[j];
- output = (char *)PyMem_Malloc(sizeof(char)*(MAX(BUFFER, 5*input_len)));
+ output = (char *)PyMem_Malloc(sizeof(char)*(MAX(BUFFER, 8*input_len)));
if (output == NULL) return PyErr_NoMemory();
while (i < input_len) {
From 448196eff35a4336b1e93ac46c955bb8ca3023da Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Tue, 28 Jun 2011 10:11:54 -0600
Subject: [PATCH 15/18] Add proxy info to FAQ
---
src/calibre/manual/faq.rst | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst
index 97551b403f..c67d44b7d5 100644
--- a/src/calibre/manual/faq.rst
+++ b/src/calibre/manual/faq.rst
@@ -558,11 +558,16 @@ Most readers do not support this. You should complain to the manufacturer about
Another alternative is to create a catalog in ebook form containing a listing of all the books in your calibre library, with their metadata. Click the arrow next to the convert button to access the catalog creation tool. And before you ask, no you cannot have the catalog "link directly to" books on your reader.
+How do I get |app| to use my HTTP proxy?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, |app| uses whatever proxy settings are set in your OS. Sometimes these are incorrect, for example, on windows if you don't use Internet Explorer then the proxy settings may not be up to date. You can tell |app| to use a particular proxy server by setting the http_proxy environment variable. The format of the variable is: http://username:password@servername you should ask your network admin to give you the correct value for this variable. Note that |app| only supports HTTP proxies not SOCKS proxies. You can see the current proxies used by |app| in Preferences->Miscellaneous.
+
I want some feature added to |app|. What can I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You have two choices:
1. Create a patch by hacking on |app| and send it to me for review and inclusion. See `Development `_.
- 2. `Open a ticket `_ (you have to register and login first). Remember that |app| development is done by volunteers, so if you get no response to your feature request, it means no one feels like implementing it.
+ 2. `Open a bug requesting the feature `_ . Remember that |app| development is done by volunteers, so if you get no response to your feature request, it means no one feels like implementing it.
Why doesn't |app| have an automatic update?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
From c417377ba7135682aaf9bdd13d3140da7e6985f3 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Tue, 28 Jun 2011 10:17:22 -0600
Subject: [PATCH 16/18] ...
---
recipes/ming_pao.recipe | 18 +++++++++---------
recipes/ming_pao_toronto.recipe | 18 +++++++++---------
recipes/ming_pao_vancouver.recipe | 18 +++++++++---------
3 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/recipes/ming_pao.recipe b/recipes/ming_pao.recipe
index 3566fca667..947d85692f 100644
--- a/recipes/ming_pao.recipe
+++ b/recipes/ming_pao.recipe
@@ -179,17 +179,17 @@ class MPRecipe(BasicNewsRecipe):
def get_dtlocal(self):
dt_utc = datetime.datetime.utcnow()
if __Region__ == 'Hong Kong':
- # convert UTC to local hk time - at HKT 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(8.0/24) - datetime.timedelta(4.5/24)
- # dt_local = dt_utc.astimezone(pytz.timezone('Asia/Hong_Kong')) - datetime.timedelta(4.5/24)
+ # convert UTC to local hk time - at HKT 5.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(8.0/24) - datetime.timedelta(5.5/24)
+ # dt_local = dt_utc.astimezone(pytz.timezone('Asia/Hong_Kong')) - datetime.timedelta(5.5/24)
elif __Region__ == 'Vancouver':
- # convert UTC to local Vancouver time - at PST time 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(-8.0/24) - datetime.timedelta(4.5/24)
- #dt_local = dt_utc.astimezone(pytz.timezone('America/Vancouver')) - datetime.timedelta(4.5/24)
+ # convert UTC to local Vancouver time - at PST time 5.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(-8.0/24) - datetime.timedelta(5.5/24)
+ #dt_local = dt_utc.astimezone(pytz.timezone('America/Vancouver')) - datetime.timedelta(5.5/24)
elif __Region__ == 'Toronto':
- # convert UTC to local Toronto time - at EST time 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(-5.0/24) - datetime.timedelta(4.5/24)
- #dt_local = dt_utc.astimezone(pytz.timezone('America/Toronto')) - datetime.timedelta(4.5/24)
+ # convert UTC to local Toronto time - at EST time 8.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(-5.0/24) - datetime.timedelta(8.5/24)
+ #dt_local = dt_utc.astimezone(pytz.timezone('America/Toronto')) - datetime.timedelta(8.5/24)
return dt_local
def get_fetchdate(self):
diff --git a/recipes/ming_pao_toronto.recipe b/recipes/ming_pao_toronto.recipe
index 677a8272b0..9f3d7f510c 100644
--- a/recipes/ming_pao_toronto.recipe
+++ b/recipes/ming_pao_toronto.recipe
@@ -179,17 +179,17 @@ class MPRecipe(BasicNewsRecipe):
def get_dtlocal(self):
dt_utc = datetime.datetime.utcnow()
if __Region__ == 'Hong Kong':
- # convert UTC to local hk time - at HKT 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(8.0/24) - datetime.timedelta(4.5/24)
- # dt_local = dt_utc.astimezone(pytz.timezone('Asia/Hong_Kong')) - datetime.timedelta(4.5/24)
+ # convert UTC to local hk time - at HKT 5.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(8.0/24) - datetime.timedelta(5.5/24)
+ # dt_local = dt_utc.astimezone(pytz.timezone('Asia/Hong_Kong')) - datetime.timedelta(5.5/24)
elif __Region__ == 'Vancouver':
- # convert UTC to local Vancouver time - at PST time 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(-8.0/24) - datetime.timedelta(4.5/24)
- #dt_local = dt_utc.astimezone(pytz.timezone('America/Vancouver')) - datetime.timedelta(4.5/24)
+ # convert UTC to local Vancouver time - at PST time 5.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(-8.0/24) - datetime.timedelta(5.5/24)
+ #dt_local = dt_utc.astimezone(pytz.timezone('America/Vancouver')) - datetime.timedelta(5.5/24)
elif __Region__ == 'Toronto':
- # convert UTC to local Toronto time - at EST time 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(-5.0/24) - datetime.timedelta(4.5/24)
- #dt_local = dt_utc.astimezone(pytz.timezone('America/Toronto')) - datetime.timedelta(4.5/24)
+ # convert UTC to local Toronto time - at EST time 8.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(-5.0/24) - datetime.timedelta(8.5/24)
+ #dt_local = dt_utc.astimezone(pytz.timezone('America/Toronto')) - datetime.timedelta(8.5/24)
return dt_local
def get_fetchdate(self):
diff --git a/recipes/ming_pao_vancouver.recipe b/recipes/ming_pao_vancouver.recipe
index 3312c8f7b8..3b13211d01 100644
--- a/recipes/ming_pao_vancouver.recipe
+++ b/recipes/ming_pao_vancouver.recipe
@@ -179,17 +179,17 @@ class MPRecipe(BasicNewsRecipe):
def get_dtlocal(self):
dt_utc = datetime.datetime.utcnow()
if __Region__ == 'Hong Kong':
- # convert UTC to local hk time - at HKT 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(8.0/24) - datetime.timedelta(4.5/24)
- # dt_local = dt_utc.astimezone(pytz.timezone('Asia/Hong_Kong')) - datetime.timedelta(4.5/24)
+ # convert UTC to local hk time - at HKT 5.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(8.0/24) - datetime.timedelta(5.5/24)
+ # dt_local = dt_utc.astimezone(pytz.timezone('Asia/Hong_Kong')) - datetime.timedelta(5.5/24)
elif __Region__ == 'Vancouver':
- # convert UTC to local Vancouver time - at PST time 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(-8.0/24) - datetime.timedelta(4.5/24)
- #dt_local = dt_utc.astimezone(pytz.timezone('America/Vancouver')) - datetime.timedelta(4.5/24)
+ # convert UTC to local Vancouver time - at PST time 5.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(-8.0/24) - datetime.timedelta(5.5/24)
+ #dt_local = dt_utc.astimezone(pytz.timezone('America/Vancouver')) - datetime.timedelta(5.5/24)
elif __Region__ == 'Toronto':
- # convert UTC to local Toronto time - at EST time 4.30am, all news are available
- dt_local = dt_utc + datetime.timedelta(-5.0/24) - datetime.timedelta(4.5/24)
- #dt_local = dt_utc.astimezone(pytz.timezone('America/Toronto')) - datetime.timedelta(4.5/24)
+ # convert UTC to local Toronto time - at EST time 8.30am, all news are available
+ dt_local = dt_utc + datetime.timedelta(-5.0/24) - datetime.timedelta(8.5/24)
+ #dt_local = dt_utc.astimezone(pytz.timezone('America/Toronto')) - datetime.timedelta(8.5/24)
return dt_local
def get_fetchdate(self):
From fc02b538f5558a54aeb7a608ecbd2b17772265e1 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Tue, 28 Jun 2011 11:05:44 -0600
Subject: [PATCH 17/18] ...
---
src/calibre/library/database2.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index c8fd660e1a..ef709cd85e 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1245,6 +1245,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
ret = tempfile.SpooledTemporaryFile(max_size=SPOOL_SIZE)
shutil.copyfileobj(f, ret)
ret.seek(0)
+ # Various bits of code try to use the name as the default
+ # title when reading metadata, so set it
ret.name = f.name
else:
ret = f.read()
From d3a93c500b50f7a36aec753de4ea4e324b122b7e Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Tue, 28 Jun 2011 12:06:24 -0600
Subject: [PATCH 18/18] Fix #802288 (ISBN ID not recognized when not lower
case)
---
src/calibre/gui2/metadata/basic_widgets.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py
index 2d6c79d0e3..227a2257bc 100644
--- a/src/calibre/gui2/metadata/basic_widgets.py
+++ b/src/calibre/gui2/metadata/basic_widgets.py
@@ -1092,11 +1092,12 @@ class IdentifiersEdit(QLineEdit): # {{{
for x in parts:
c = x.split(':')
if len(c) > 1:
- if c[0] == 'isbn':
+ itype = c[0].lower()
+ if itype == 'isbn':
v = check_isbn(c[1])
if v is not None:
c[1] = v
- ans[c[0]] = c[1]
+ ans[itype] = c[1]
return ans
def fset(self, val):
if not val:
@@ -1112,7 +1113,7 @@ class IdentifiersEdit(QLineEdit): # {{{
if v is not None:
val[k] = v
ids = sorted(val.iteritems(), key=keygen)
- txt = ', '.join(['%s:%s'%(k, v) for k, v in ids])
+ txt = ', '.join(['%s:%s'%(k.lower(), v) for k, v in ids])
self.setText(txt.strip())
self.setCursorPosition(0)
return property(fget=fget, fset=fset)