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