Cleanup
[rox-menu.git] / infowin.py
blob4b5bcfb459dcdad723c06ba35b0ec1547ad9bdb9
1 # infowin.py
3 __all__ = ['infowin', 'InfoWin']
5 import os, re
7 import rox
8 from rox import g, AppInfo
10 _url_re = re.compile(r'(URL:)?(\w+?://\S+)')
12 def _collect_about(ai):
13 """Extract about information from AppInfo object in the current language."""
15 info = []
16 for key in ai.about[''].keys():
17 k, v = ai.getAbout(key)
18 if v == '':
19 v = ai.getAbout(key, [''])[1]
20 info.append((k, v.strip()))
21 return info
23 def infowin(pname, info=None):
24 """Open info window for this program.
26 info is a source of an AppInfo.xml file, if None then
27 APP_DIR/AppInfo.xml is loaded instead.
28 """
30 if info is None:
31 info = os.path.join(rox.app_dir, 'AppInfo.xml')
32 try:
33 ai = AppInfo.AppInfo(info)
34 except:
35 rox.report_exception()
36 return
38 info = _collect_about(ai)
39 if os.path.isdir(os.path.join(rox.app_dir, 'Help')):
40 help = True
41 else:
42 help = False
43 try:
44 iw = InfoWin(pname, fields=info, help=help)
45 iw.set_title(_("About"))
46 iw.show()
47 return iw
48 except:
49 rox.report_exception()
51 class CommonMixIn:
53 def key_event(self, window, kev):
54 if kev.keyval == g.keysyms.Escape:
55 self.close()
56 return True
57 elif kev.keyval in [g.keysyms.Return, g.keysyms.KP_Enter] \
58 and hasattr(self, '_ok_cb'):
59 self._ok_cb()
60 return True
61 return False
63 def show(self):
64 rox.Dialog.show(self)
65 if self.standalone:
66 rox.mainloop()
68 def close(self, widget=None, *args):
69 self.hide()
70 if self.standalone:
71 self.destroy()
72 return True
74 class InfoWin(CommonMixIn, rox.Dialog):
75 """A dialog showing information such as author, version, etc."""
77 def __init__(self, pname, fields=None, icon=None, help=False, standalone=False):
78 rox.Dialog.__init__(self)
80 if not fields:
81 raise ValueError, \
82 "'fields' keyword must have at least one label, value pair."
83 self.urls = {}
84 self.icon_size = 64
85 self.standalone = standalone
87 self.set_position(g.WIN_POS_CENTER)
88 self.set_default_size(350,-1)
89 self.connect("delete_event", self.close)
90 self.connect('key-press-event', self.key_event)
91 self.set_title(_("Info"))
92 tooltips = g.Tooltips()
94 hbox = g.HBox()
95 self.vbox.pack_start(hbox, False, False, 5)
96 hbox.show()
98 try:
99 if not icon:
100 icon = os.path.join(rox.app_dir, '.DirIcon')
101 pixbuf = g.gdk.pixbuf_new_from_file(icon)
102 scaled_pixbuf = pixbuf.scale_simple(self.icon_size,
103 self.icon_size, g.gdk.INTERP_BILINEAR)
104 ic = g.Image()
105 ic.set_from_pixbuf(scaled_pixbuf)
106 hbox.pack_start(ic)
107 ic.show()
108 except:
109 pass
111 # program name label
112 name = g.Label()
113 name.set_markup('<span size="xx-large" weight="bold">%s</span>' % pname)
114 hbox.pack_end(name)
116 # use table to pack labels and descriptions
117 table = g.Table(len(fields), 2)
118 self.vbox.pack_start(table, padding=3)
120 for i, (fieldname, fieldtext) in enumerate(fields):
121 label = g.Label("%s:" % fieldname)
122 label.set_alignment(1,0)
123 table.attach(label, 0, 1, i, i+1, xpadding=2)
125 # search for URLs or email addresses
126 if fieldtext.find('@') > 0:
127 self.urls[fieldname] = []
128 from parseaddrlist import parseaddrlist
129 for name, address in parseaddrlist(fieldtext):
130 if not address: continue
131 if not name:
132 name = address
133 self.urls[fieldname].append((name, 'mailto:%s' % address))
134 else:
135 ul = _url_re.findall(fieldtext)
136 if ul:
137 self.urls[fieldname] = [(fieldtext, x[1]) for x in ul]
139 frame = g.Frame()
140 frame.set_shadow_type(g.SHADOW_IN)
141 table.attach(frame, 1, 2, i, i+1, xpadding=0)
142 urls = self.urls.get(fieldname)
143 if urls:
144 hbox = g.VBox()
145 frame.add(hbox)
146 for j, (text, url) in enumerate(urls):
147 label = g.Label('')
148 label.set_markup(
149 '<span foreground="blue"><u>%s</u></span>' % text)
150 evb = g.EventBox()
151 evb.add(label)
152 evb.set_events(g.gdk.BUTTON_PRESS_MASK)
153 evb.connect("button_press_event", self.open_url,
154 fieldname, j)
155 hbox.pack_start(evb)
156 evb.realize()
157 evb.window.set_cursor(g.gdk.Cursor(g.gdk.HAND1))
158 tooltips.set_tip(evb,
159 _("Open URL <%s> with the default URL handler.") % url,
160 tip_private=None)
161 else:
162 label = g.Label('')
163 label.set_text(fieldtext)
164 label.set_selectable(True)
165 frame.add(label)
166 label.set_alignment(0.5,0)
167 label.set_justify(g.JUSTIFY_CENTER)
168 label.set_line_wrap(True)
170 self.vbox.show_all()
172 if help:
173 self.add_button(g.STOCK_HELP, g.RESPONSE_HELP)
174 self.add_button(g.STOCK_CLOSE, g.RESPONSE_CLOSE)
175 self.connect('response', self.response_cb)
176 self.set_default_response(g.RESPONSE_CLOSE)
178 def open_url(self, widget, event, name, idx):
179 """Open the url associated with a label with ROX url handler."""
181 try:
182 url = self.urls[name][idx][1]
183 except:
184 return
185 from rox import basedir, filer
186 app = basedir.load_first_config('rox.sourceforge.net',
187 'MIME-types', 'text_x-uri')
188 try:
189 if os.path.islink(app):
190 app = os.readlink(app)
191 if os.path.isdir(app):
192 app = os.path.join(app, 'AppRun')
193 filer._spawn((app, url))
194 except (OSError, IOError):
195 import threading, webbrowser
196 t = threading.Thread(target=webbrowser.open, args=(url, 1))
197 t.setDaemon(1)
198 t.start()
199 except:
200 rox.report_exception()
202 def response_cb(self, widget, id, *args):
203 """Callback for dialog buttons."""
205 if id == g.RESPONSE_CLOSE:
206 self.close()
207 elif id == g.RESPONSE_HELP:
208 self.open_help()
210 def open_help(self, widget=None, *args):
211 """Open the Help directory with the filer."""
213 from rox import filer
214 helpdir = os.path.join(rox.app_dir, 'Help')
215 if os.path.isdir(helpdir):
216 filer.open_dir(helpdir)