Fixed handling None in pango_escape function.
[straw.git] / straw / helpers.py
blob1bb0dbc787a19ecf358fa3cbeb8adade43cb93e1
1 """ dialogs.py
3 Module for displaying dialogs related to errors, warnings, notifications,
4 etc...
5 """
6 __copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
7 __license__ = """
8 Straw is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
11 version.
13 Straw is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA. """
21 from gettext import gettext as _
22 from straw import opml, error, Config
23 import codecs
24 import gnome
25 import gobject
26 import gtk
27 import htmlentitydefs
28 import os, os.path, re, locale, time, calendar
29 import straw
30 import urllib, string, urlparse
31 import xml
33 class UIFactory(object):
34 _shared_state = {}
35 manager = None
37 actions = [
38 ("refresh", gtk.STOCK_REFRESH, _("_Refresh"), None, _("Update this feed"), None),
39 ("add_child", gtk.STOCK_ADD, _("Add child"), None, _("Update this feed"), None),
40 ("mark_as_read", gtk.STOCK_CLEAR, _("_Mark As Read"),
41 None, _("Mark all items in this feed as read"), None),
42 ("stop_refresh", None, _("_Stop Refresh"), None, _("Stop updating this feed"), None),
43 ("remove", gtk.STOCK_REMOVE, _("Remo_ve"), None, _("Remove"), None),
44 ("properties", gtk.STOCK_INFO, _("_Information"), None, _("Feed-specific properties"), None),
45 ("text_copy", gtk.STOCK_COPY, "_Copy", None, None, None),
46 ("link_copy", None, "_Copy Link Location", None, None, None),
47 ("link_open", None, "_Open Link", None, None, None),
48 ("subscribe", None, "_Subscribe", None, None, None),
49 ("zoom_in", gtk.STOCK_ZOOM_IN, "Zoom _In", None, None, None),
50 ("zoom_out", gtk.STOCK_ZOOM_OUT, "Zoom _Out", None, None, None),
51 ("zoom_100", gtk.STOCK_ZOOM_100, "_Normal Size", None, None, None),
52 ("mark_as_unread", gtk.STOCK_CONVERT, "Mark as _Unread", None, "Mark this item as unread", None)
55 def __new__(cls, *a, **k):
56 obj = object.__new__(cls, *a, **k)
57 obj.__dict__ = cls._shared_state
58 return obj
60 def __init__(self, name):
61 if not self.manager:
62 print 'initializing ui manager'
63 self.action_groups = {}
64 self.manager = gtk.UIManager()
65 action_group = gtk.ActionGroup(name)
66 action_group.add_actions(UIFactory.actions)
67 num_action_groups = len(self.manager.get_action_groups())
68 self.action_groups[name] = num_action_groups
69 self.manager.insert_action_group(action_group, num_action_groups)
70 ui = os.path.join(straw.defs.STRAW_DATA_DIR, 'ui.xml')
71 self.manager.add_ui_from_file(ui)
73 def get_popup(self, path):
74 return self.manager.get_widget(path)
76 def get_action(self, path):
77 return self.manager.get_action(path)
79 def ensure_update(self):
80 self.manager.ensure_update()
83 def report_error(primary, secondary, parent=None):
84 dialog = gtk.MessageDialog(parent,
85 type=gtk.MESSAGE_ERROR,
86 buttons=gtk.BUTTONS_OK,
87 message_format=primary)
88 dialog.format_secondary_text(secondary)
89 response = dialog.run()
90 dialog.destroy()
91 return response
93 def report_offline_status(parent=None):
94 dialog = gtk.MessageDialog(parent,
95 type=gtk.MESSAGE_WARNING,
96 buttons=gtk.BUTTONS_OK_CANCEL,
97 message_format="You Are Offline!")
98 dialog.format_secondary_markup(_("You are currently reading offline. Would you like to go online now?"))
99 response = dialog.run()
100 dialog.destroy()
101 return response
103 def credits():
104 people = u'''Author:
105 Juri Pakaste <juri@iki.fi>
107 Contributors:
108 Jan Alonzo <jmalonzo@unpluggable.com>
109 Ryan P. Skadberg <skadz@stigmata.org>
110 Leandro Lameiro <lameiro@gmail.com>
111 Tuukka Hastrup <tuukka@iki.fi>
113 Past Contributors:
114 Iain McCoy <iain@mccoy.id.au>
115 Lucas Nussbaum <lucas@lucas-nussbaum.net>
116 Olivier Crete <tester@tester.ca>
117 Scott Douglas-Watson <sdouglaswatson@yahoo.co.uk>
118 Terje R\xf8sten (distutils)
120 Special Thanks:
121 Mark Pilgrim (feedparser and feedfinder)
122 Joe Gregorio (httplib2)
124 iconfile = os.path.join(straw.defs.STRAW_DATA_DIR,"straw.png")
125 logo = gtk.gdk.pixbuf_new_from_file(iconfile)
126 description = _("A Desktop Feed Reader")
127 straw_copyright = u"""
128 Copyright \xa9 2005-2007 Straw Contributors
129 Copyright \xa9 2002-2004 Juri Pakaste"""
130 artists = [
131 u'Jakub \'jimmac\' Steiner',
132 u'Juri Pakaste'
135 translators = [
136 u"GNOME i18n Project and Translators<http://developer.gnome.org/projects/gtp/>",
137 u"Juri Pakaste <juri@iki.fi>",
138 u"Martin Steldinger <tribble@hanfplantage.de>",
139 u"David Rousseau <boiteaflood@wanadoo.fr>",
140 u"Sergei Vavinov <svv@cmc.msu.ru>",
141 u"Terje R\xf8sten <terjeros@phys.ntnu.no>",
142 u"Francisco J. Fernandez <franciscojavier.fernandez.serrador@hispalinux.es>",
143 u"Elros Cyriatan (Dutch Translation)"
146 translator_credits = 'translator_credits'
147 about = gtk.AboutDialog()
148 about.set_name(straw.defs.PACKAGE)
149 about.set_version(straw.defs.VERSION)
150 about.set_copyright(straw_copyright)
151 about.set_comments(description)
152 about.set_authors(people.splitlines())
153 about.set_artists(artists)
154 about.set_logo(logo)
155 about.set_translator_credits(translator_credits)
156 about.set_license(__license__)
157 gtk.about_dialog_set_url_hook(lambda about, url: url_show(url))
158 about.set_website(straw.defs.STRAW_HOME)
159 about.set_website_label(straw.defs.STRAW_HOME)
160 about.connect('response', lambda widget, response: widget.destroy())
161 return about
163 def listdiff(l1, l2, test=None):
164 if test is not None:
165 return _listdifftest(l1, l2, test)
166 common = []
167 inl1 = []
168 inl2 = []
169 for e in l1:
170 if e in l2:
171 common.append(e)
172 else:
173 inl1.append(e)
174 for e in l2:
175 if e in l1:
176 if e not in common:
177 common.append(e)
178 else:
179 inl2.append(e)
180 return (common, inl1, inl2)
182 def url_show(url):
183 config = Config.get_instance()
184 if config.browser_cmd:
185 try:
186 cmdbin, args = string.splitfields(str(config.browser_cmd), maxsplit=1)
187 link = args % url
188 import subprocess
189 pid = subprocess.Popen([cmdbin, link]).pid
190 return pid
191 except ValueError, ve:
192 raise UrlOpenException("Problem opening link %s" % ve)
193 else:
194 import gnomevfs
195 return gnomevfs.url_show(url)
197 def set_clipboard_text(text):
198 clipboard = gtk.clipboard_get(selection="CLIPBOARD")
199 clipboard.set_text(text)
202 class UrlOpenException(Exception):
203 pass
205 entity = re.compile(r'\&.\w*?\;')
206 def convert_entities(text):
207 def conv(ents):
208 entities = htmlentitydefs.entitydefs
209 ents = ents.group(0)
210 ent_code = entities.get(ents[1:-1], None)
211 if ent_code is not None:
212 try:
213 ents = unicode(ent_code, get_locale_encoding())
214 except UnicodeDecodeError:
215 ents = unicode(ent_code, 'latin-1')
216 except Exception, ex:
217 error.log("error occurred while converting entity %s: %s" % (ents, ex))
219 # check if it still needs conversion
220 if entity.search(ents) is None:
221 return ents
223 if ents[1] == '#':
224 code = ents[2:-1]
225 base = 10
226 if code[0] == 'x':
227 code = code[1:]
228 base = 16
229 return unichr(int(code, base))
230 else:
231 return
233 in_entity = entity.search(text)
234 if in_entity is None:
235 return text
236 else:
237 ctext = in_entity.re.sub(conv, text)
238 return ctext
240 def complete_url(url, feed_location):
241 #print "---- COMPLETING %s WITH %s" % (url, feed_location)
242 url = urllib.quote(url, safe=string.punctuation)
243 if urlparse.urlparse(url)[0] == '':
244 return urlparse.urljoin(feed_location, url)
245 else:
246 return url
248 def get_url_location(url):
249 url = urllib.quote(url, safe=string.punctuation)
250 parsed_url = urlparse.urlsplit(url)
251 return urlparse.urlunsplit((parsed_url[0], parsed_url[1], '','',''))
253 def get_locale_encoding():
254 try:
255 encoding = locale.getpreferredencoding()
256 except locale.Error:
257 encoding = sys.getdefaultencoding()
258 return encoding
260 def format_date(date, format=None, encoding=None):
261 return date
262 if format is None:
263 format = get_date_format()
264 if encoding is None:
265 encoding = get_locale_encoding()
266 timestr = time.strftime(format, time.localtime(calendar.timegm(date)))
267 return unicode(timestr, encoding)
269 def get_date_format():
270 # this is here just to make xgettext happy: it should be defined in
271 # only one place, and a good one would be MainWindow.py module level.
272 # however, we can't access _ there.
273 # The format: %A is the full weekday name
274 # %B is the full month name
275 # %e is the day of the month as a decimal number,
276 # without leading zero
277 # This should be translated to be suitable for the locale in
278 # question, feel free to alter the order and the parameters (they
279 # are strftime(3) parameters, the whole string is passed to the
280 # function, Straw does no additional interpretation) if you feel
281 # it's necessary in the translation file.
282 return _('%A %B %e %H:%M')
284 def pango_escape(text):
285 if text:
286 return xml.sax.saxutils.escape(text)
288 def html_replace(exc):
289 """ Python Cookbook 2ed, Section 1.23
291 if isinstance(exc, (UnicodeDecodeError, UnicodeTranslateError)):
292 s = [ u'&%s;' % codepoint2name[ord(c)] for c in exc.object[exc.start:exc.end]]
293 return ''.join(s), exc.end
294 else:
295 raise TypeError("can't handle %s" % exc.__name__)
297 codecs.register_error('html_replace', html_replace)