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