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