Skip to content
This repository has been archived by the owner on Sep 16, 2019. It is now read-only.

Adds option to override the theme's icons through monochrome icons and fixes string encoding issues #25

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions libqnotero/_themes/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, qnotero):
self.setWindowProperties()
self.setScrollBars()

def icon(self, iconName):
def icon(self, iconName, overrideIconExt=None):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other themes should be updated as well. Right now this breaks the chameleon theme.


"""
Retrieves an icon from the theme
Expand All @@ -53,8 +53,8 @@ def icon(self, iconName):
A QIcon
"""

return QIcon(os.path.join(self._themeFolder, iconName) \
+ self._iconExt)
iconExt = self._iconExt if overrideIconExt is None else overrideIconExt
return QIcon(os.path.join(self._themeFolder, iconName) + iconExt)

def iconExt(self):

Expand Down
1 change: 1 addition & 0 deletions libqnotero/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
u"noteProvider" : u"gnote",
u"pdfReader" : u"xdg-open",
u"theme" : u"Default",
u"iconOverride" : u"",
u"updateUrl" : \
u"http://files.cogsci.nl/software/qnotero/MOST_RECENT_VERSION.TXT",
u"pos" : u"Top right",
Expand Down
5 changes: 5 additions & 0 deletions libqnotero/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from libqnotero.uiloader import UiLoader
from libzotero.libzotero import valid_location

icons = [u'', u'qnotero-monochrome-white.svg', u'qnotero-monochrome-black.svg']

class Preferences(QDialog, UiLoader):

"""Qnotero preferences dialog"""
Expand Down Expand Up @@ -67,6 +69,8 @@ def __init__(self, qnotero, firstRun=False):
if theme == getConfig(u"theme").lower():
self.ui.comboBoxTheme.setCurrentIndex(i)
i += 1
icon = getConfig(u"iconOverride")
self.ui.comboBoxIcon.setCurrentIndex(icons.index(icon) if icon in icons else 0)
self.setStyleSheet(self.qnotero.styleSheet())
self.adjustSize()

Expand All @@ -83,6 +87,7 @@ def accept(self):
self.ui.checkBoxAutoUpdateCheck.isChecked())
setConfig(u"zoteroPath", self.ui.lineEditZoteroPath.text())
setConfig(u"theme", self.ui.comboBoxTheme.currentText().capitalize())
setConfig(u"iconOverride", icons[self.ui.comboBoxIcon.currentIndex()])
self.qnotero.saveState()
self.qnotero.reInit()
QDialog.accept(self)
Expand Down
11 changes: 9 additions & 2 deletions libqnotero/qnotero.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Qnotero(QMainWindow, UiLoader):

"""The main class of the Qnotero GUI"""

version = '2.0.0'
version = '2.1.1'

def __init__(self, systray=True, debug=False, reset=False, parent=None):

Expand Down Expand Up @@ -209,7 +209,7 @@ def reInit(self):
self.noteProvider = GnoteProvider(self)
self.zotero = LibZotero(getConfig(u"zoteroPath"), self.noteProvider)
if hasattr(self, u"sysTray"):
self.sysTray.setIcon(self.theme.icon(u"qnotero"))
self.sysTray.updateIcon()

def restoreState(self):

Expand Down Expand Up @@ -294,6 +294,13 @@ def setTheme(self):
"""Load a theme"""

theme = getConfig(u'theme')
iconOverride = getConfig(u'iconOverride')
if iconOverride:
self.isThemeIcon = False
self.iconName = u'../' + iconOverride
else:
self.isThemeIcon = True
self.iconName = u'qnotero'
mod = __import__(u'libqnotero._themes.%s' % theme.lower(), fromlist= \
[u'dummy'])
cls = getattr(mod, theme.capitalize())
Expand Down
15 changes: 12 additions & 3 deletions libqnotero/sysTray.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self, qnotero):

QSystemTrayIcon.__init__(self, qnotero)
self.qnotero = qnotero
self.setIcon(self.qnotero.theme.icon("qnotero"))
self.updateIcon()
self.menu = QMenu()
self.menu.addAction(self.qnotero.theme.icon("qnotero"), "Show",
self.qnotero.popUp)
Expand All @@ -47,7 +47,17 @@ def __init__(self, qnotero):
self.setContextMenu(self.menu)
self.activated.connect(self.activate)
self.listenerActivated.connect(self.activate)


def updateIcon(self):

"""
Reloads the systray icon according to the current settings
"""

iconName = self.qnotero.iconName
overrideIconExt = None if self.qnotero.isThemeIcon else ''
self.setIcon(self.qnotero.theme.icon(iconName, overrideIconExt))

def activate(self, reason=None):

"""
Expand All @@ -56,7 +66,6 @@ def activate(self, reason=None):
Keyword arguments:
reason -- the reason for activation (default=None)
"""


if reason == QSystemTrayIcon.Context:
return
Expand Down
30 changes: 28 additions & 2 deletions libqnotero/ui/preferences.ui
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditZoteroPath"/>
</item>
<item row="7" column="0" colspan="4">
<item row="8" column="0" colspan="4">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
Expand Down Expand Up @@ -90,7 +90,33 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="6" column="0" colspan="4">
<item row="6" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Icon</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="3">
<widget class="QComboBox" name="comboBoxIcon">
<item>
<property name="text">
<string>Theme default</string>
</property>
</item>
<item>
<property name="text">
<string>Monochrome white</string>
</property>
</item>
<item>
<property name="text">
<string>Monochrome black</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="QCheckBox" name="checkBoxAutoUpdateCheck">
<property name="text">
<string>Automatically check for updates on start-up</string>
Expand Down
28 changes: 28 additions & 0 deletions libzotero/libzotero.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,33 @@
import time
from libzotero.zotero_item import zoteroItem as zotero_item

def decode_zotero_str(raw, max_iters=3):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, this completely breaks the PDF attachments. This appears to be due to my own weird way of trying to fix the encoding. If I comment the line below out (from LibZotero.update()), it works again on my system.

item_attachment = item_attachment.encode('latin-1').decode('utf-8')

However, the fact that a patch that for you seems to fix things, breaks things for me makes me a bit worried. On what operating system do you work? It's not unthinkable that the encoding differs between operating systems.

Copy link
Contributor Author

@kostrykin kostrykin Oct 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on Linux. I have that fork running on 3 different systems,

  • two of which are Ubuntu 14.04 based Elementary OS Freya
  • and one is Ubuntu 16.04 based Elementary OS Loki.

What operating system are you on?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What operating system are you on?

I'm on Ubuntu 16.04.

It has been observerd that Zotero encodes its strings sometimes twice recursively. The modifications of this commit try to cope with that by decoding each string up to three times.

What makes you think this? Is this a conclusion based on the fact the decoding multiple times resolves the encoding issues, at least in some cases? Or is it a known bug in the Zotero code?

I'll think about this. The combination of my odd hack (clearly dating from before understood how encoding really works) and your fix is nonsensical; but it may be that your fix on its own is correct. But I'd like to do some more tests before I push another fix that actually doesn't fix things.

(As you may have noticed, I don't have that much time to work Qnotero anymore. But I'll get to it!)

Copy link
Contributor Author

@kostrykin kostrykin Oct 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds very reasonable!

To answer your question: My fix was based on the observation that Zotero encoded 'Öf' (a two-letters string, where the first letter is "Ö", that is a non-ASCII) as the five-bytes sequence \xc3\x83\xc2\x96\x66. This is strange, because UTF-8 encodes ASCII letters as single bytes and non-ASCII characters as a sequence of two bytes. Empirically, I ruled out the possibility of that being a UTF-16 encoding. So why are two letters encoded as five bytes?

In [14]: '\xc3\x83\xc2\x96\x66'.decode('utf-8')
Out[14]: u'\xc3\x96f'

Turns out, that \xc3\x83\xc2\x96\x66 is the UTF-8 encoding for the three-characters-string \xC3\x96f (note the f at the end). The first two bytes \xC3\x96 are in turn the UTF-8 representation of Ö. So I suspect that this is an odd behaviour from Zotero.


Update: In an earlier version of this, I miscounted the number of bytes in the exemplary sequence and wrote that it were 4, although it are 5.


"""
Decodes a string from the Zotero database.

There seems to be an issue with the way, Zotero encodes
strings in its database: For some reason, Zotero encodes
them *twice* with UTF-8, at least sometimes. This
functions aims to decode such strings, even if they were
encoded more than twice.

For example, Zotero was observed to encode

u'Öf' as b'\xc3\x83\xc2\x96\x66',

which is the UTF-8 encoding for u'\xC3\x96f', where
u'\xC3\x96' is *in turn* an UTF-8 representation of u'Ö'.
"""

result = str(raw, 'utf-8')
for iter in range(max_iters):
try:
result = str(bytes([ord(c) for c in result]), 'utf-8')
except:
break
return result

class LibZotero(object):

"""
Expand Down Expand Up @@ -170,6 +197,7 @@ def update(self, force=False):
# Copy the zotero database to the gnotero copy
shutil.copyfile(self.zotero_database, self.gnotero_database)
self.conn = sqlite3.connect(self.gnotero_database)
self.conn.text_factory = decode_zotero_str
self.cur = self.conn.cursor()
# First create a list of deleted items, so we can ignore those later
deleted = []
Expand Down
82 changes: 82 additions & 0 deletions resources/qnotero-monochrome-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions resources/qnotero-monochrome-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions setup-windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def flush(self):
],
}],
data_files = [
('resources', glob.glob('resources/*.svg')),
('resources/default', glob.glob('resources/default/*')),
('resources/elementary', glob.glob('resources/elementary/*')),
('resources/tango', glob.glob('resources/tango/*')),
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
data_files=[
("/usr/share/qnotero", ["COPYING"]),
("/usr/share/applications", ["data/qnotero.desktop"]),
("/usr/share/qnotero/resources", glob.glob("resources/*.svg")),
("/usr/share/qnotero/resources/default",
glob.glob("resources/default/*")),
("/usr/share/qnotero/resources/elementary",
Expand Down