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