Revert 32-bit building since it errors on travis-ci
[appimagekit/gsi.git] / AppImageAssistant.AppDir / dialogs.py
blob1e68561d3c383de688be87f21718b7d747e5fd89
2 # Kiwi: a Framework and Enhanced Widgets for Python
4 # Copyright (C) 2005-2007 Async Open Source
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 # Author(s): Johan Dahlin <jdahlin@async.com.br>
23 import os
24 import gettext
26 import atk
27 import gtk
29 __all__ = ['error', 'info', 'messagedialog', 'warning', 'yesno', 'save',
30 'open', 'HIGAlertDialog', 'BaseDialog']
32 _ = lambda m: gettext.dgettext('kiwi', m)
34 _IMAGE_TYPES = {
35 gtk.MESSAGE_INFO: gtk.STOCK_DIALOG_INFO,
36 gtk.MESSAGE_WARNING : gtk.STOCK_DIALOG_WARNING,
37 gtk.MESSAGE_QUESTION : gtk.STOCK_DIALOG_QUESTION,
38 gtk.MESSAGE_ERROR : gtk.STOCK_DIALOG_ERROR,
41 _BUTTON_TYPES = {
42 gtk.BUTTONS_NONE: (),
43 gtk.BUTTONS_OK: (gtk.STOCK_OK, gtk.RESPONSE_OK,),
44 gtk.BUTTONS_CLOSE: (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,),
45 gtk.BUTTONS_CANCEL: (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,),
46 gtk.BUTTONS_YES_NO: (gtk.STOCK_NO, gtk.RESPONSE_NO,
47 gtk.STOCK_YES, gtk.RESPONSE_YES),
48 gtk.BUTTONS_OK_CANCEL: (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
49 gtk.STOCK_OK, gtk.RESPONSE_OK)
52 class HIGAlertDialog(gtk.Dialog):
53 def __init__(self, parent, flags,
54 type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_NONE):
55 if not type in _IMAGE_TYPES:
56 raise TypeError(
57 "type must be one of: %s", ', '.join(_IMAGE_TYPES.keys()))
58 if not buttons in _BUTTON_TYPES:
59 raise TypeError(
60 "buttons be one of: %s", ', '.join(_BUTTON_TYPES.keys()))
62 gtk.Dialog.__init__(self, '', parent, flags)
63 self.set_border_width(5)
64 self.set_resizable(False)
65 self.set_has_separator(False)
66 # Some window managers (ION) displays a default title (???) if
67 # the specified one is empty, workaround this by setting it
68 # to a single space instead
69 self.set_title(" ")
70 self.set_skip_taskbar_hint(True)
71 self.vbox.set_spacing(14)
73 # It seems like get_accessible is not available on windows, go figure
74 if hasattr(self, 'get_accessible'):
75 self.get_accessible().set_role(atk.ROLE_ALERT)
77 self._primary_label = gtk.Label()
78 self._secondary_label = gtk.Label()
79 self._details_label = gtk.Label()
80 self._image = gtk.image_new_from_stock(_IMAGE_TYPES[type],
81 gtk.ICON_SIZE_DIALOG)
82 self._image.set_alignment(0.5, 0.0)
84 self._primary_label.set_use_markup(True)
85 for label in (self._primary_label, self._secondary_label,
86 self._details_label):
87 label.set_line_wrap(True)
88 label.set_selectable(True)
89 label.set_alignment(0.0, 0.5)
91 hbox = gtk.HBox(False, 12)
92 hbox.set_border_width(5)
93 hbox.pack_start(self._image, False, False)
95 vbox = gtk.VBox(False, 0)
96 hbox.pack_start(vbox, False, False)
97 vbox.pack_start(self._primary_label, False, False)
98 vbox.pack_start(self._secondary_label, False, False)
100 self._expander = gtk.expander_new_with_mnemonic(
101 _("Show more _details"))
102 self._expander.set_spacing(6)
103 self._expander.add(self._details_label)
104 vbox.pack_start(self._expander, False, False)
105 self.vbox.pack_start(hbox, False, False)
106 hbox.show_all()
107 self._expander.hide()
108 self.add_buttons(*_BUTTON_TYPES[buttons])
109 self.label_vbox = vbox
111 def set_primary(self, text):
112 self._primary_label.set_markup(
113 "<span weight=\"bold\" size=\"larger\">%s</span>" % text)
115 def set_secondary(self, text):
116 self._secondary_label.set_markup(text)
118 def set_details(self, text):
119 self._details_label.set_text(text)
120 self._expander.show()
122 def set_details_widget(self, widget):
123 self._expander.remove(self._details_label)
124 self._expander.add(widget)
125 widget.show()
126 self._expander.show()
128 class BaseDialog(gtk.Dialog):
129 def __init__(self, parent=None, title='', flags=0, buttons=()):
130 if parent and not isinstance(parent, gtk.Window):
131 raise TypeError("parent needs to be None or a gtk.Window subclass")
133 if not flags and parent:
134 flags &= (gtk.DIALOG_MODAL |
135 gtk.DIALOG_DESTROY_WITH_PARENT)
137 gtk.Dialog.__init__(self, title=title, parent=parent,
138 flags=flags, buttons=buttons)
139 self.set_border_width(6)
140 self.set_has_separator(False)
141 self.vbox.set_spacing(6)
143 def messagedialog(dialog_type, short, long=None, parent=None,
144 buttons=gtk.BUTTONS_OK, default=-1):
145 """Create and show a MessageDialog.
147 @param dialog_type: one of constants
148 - gtk.MESSAGE_INFO
149 - gtk.MESSAGE_WARNING
150 - gtk.MESSAGE_QUESTION
151 - gtk.MESSAGE_ERROR
152 @param short: A header text to be inserted in the dialog.
153 @param long: A long description of message.
154 @param parent: The parent widget of this dialog
155 @type parent: a gtk.Window subclass
156 @param buttons: The button type that the dialog will be display,
157 one of the constants:
158 - gtk.BUTTONS_NONE
159 - gtk.BUTTONS_OK
160 - gtk.BUTTONS_CLOSE
161 - gtk.BUTTONS_CANCEL
162 - gtk.BUTTONS_YES_NO
163 - gtk.BUTTONS_OK_CANCEL
164 or a tuple or 2-sized tuples representing label and response. If label
165 is a stock-id a stock icon will be displayed.
166 @param default: optional default response id
168 if buttons in (gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE,
169 gtk.BUTTONS_CANCEL, gtk.BUTTONS_YES_NO,
170 gtk.BUTTONS_OK_CANCEL):
171 dialog_buttons = buttons
172 buttons = []
173 else:
174 if buttons is not None and type(buttons) != tuple:
175 raise TypeError(
176 "buttons must be a GtkButtonsTypes constant or a tuple")
177 dialog_buttons = gtk.BUTTONS_NONE
179 if parent and not isinstance(parent, gtk.Window):
180 raise TypeError("parent must be a gtk.Window subclass")
182 d = HIGAlertDialog(parent=parent, flags=gtk.DIALOG_MODAL,
183 type=dialog_type, buttons=dialog_buttons)
184 if buttons:
185 for text, response in buttons:
186 d.add_buttons(text, response)
188 d.set_primary(short)
190 if long:
191 if isinstance(long, gtk.Widget):
192 d.set_details_widget(long)
193 elif isinstance(long, basestring):
194 d.set_details(long)
195 else:
196 raise TypeError(
197 "long must be a gtk.Widget or a string, not %r" % long)
199 if default != -1:
200 d.set_default_response(default)
202 if parent:
203 d.set_transient_for(parent)
204 d.set_modal(True)
206 response = d.run()
207 d.destroy()
208 return response
210 def _simple(type, short, long=None, parent=None, buttons=gtk.BUTTONS_OK,
211 default=-1):
212 if buttons == gtk.BUTTONS_OK:
213 default = gtk.RESPONSE_OK
214 return messagedialog(type, short, long,
215 parent=parent, buttons=buttons,
216 default=default)
218 def error(short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1):
219 return _simple(gtk.MESSAGE_ERROR, short, long, parent=parent,
220 buttons=buttons, default=default)
222 def info(short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1):
223 return _simple(gtk.MESSAGE_INFO, short, long, parent=parent,
224 buttons=buttons, default=default)
226 def warning(short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1):
227 return _simple(gtk.MESSAGE_WARNING, short, long, parent=parent,
228 buttons=buttons, default=default)
230 def yesno(text, parent=None, default=gtk.RESPONSE_YES,
231 buttons=gtk.BUTTONS_YES_NO):
232 return messagedialog(gtk.MESSAGE_WARNING, text, None, parent,
233 buttons=buttons, default=default)
235 def open(title='', parent=None, patterns=None, folder=None, filter=None):
236 """Displays an open dialog.
237 @param title: the title of the folder, defaults to 'Select folder'
238 @param parent: parent gtk.Window or None
239 @param patterns: a list of pattern strings ['*.py', '*.pl'] or None
240 @param folder: initial folder or None
241 @param filter: a filter to use or None, is incompatible with patterns
244 ffilter = filter
245 if patterns and ffilter:
246 raise TypeError("Can't use patterns and filter at the same time")
248 filechooser = gtk.FileChooserDialog(title or _('Open'),
249 parent,
250 gtk.FILE_CHOOSER_ACTION_OPEN,
251 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
252 gtk.STOCK_OPEN, gtk.RESPONSE_OK))
254 if patterns or ffilter:
255 if not ffilter:
256 ffilter = gtk.FileFilter()
257 for pattern in patterns:
258 ffilter.add_pattern(pattern)
259 filechooser.set_filter(ffilter)
260 filechooser.set_default_response(gtk.RESPONSE_OK)
262 if folder:
263 filechooser.set_current_folder(folder)
265 response = filechooser.run()
266 if response != gtk.RESPONSE_OK:
267 filechooser.destroy()
268 return
270 path = filechooser.get_filename()
271 if path and os.access(path, os.R_OK):
272 filechooser.destroy()
273 return path
275 abspath = os.path.abspath(path)
277 error(_('Could not open file "%s"') % abspath,
278 _('The file "%s" could not be opened. '
279 'Permission denied.') % abspath)
281 filechooser.destroy()
282 return
284 def selectfolder(title='', parent=None, folder=None):
285 """Displays a select folder dialog.
286 @param title: the title of the folder, defaults to 'Select folder'
287 @param parent: parent gtk.Window or None
288 @param folder: initial folder or None
291 filechooser = gtk.FileChooserDialog(
292 title or _('Select folder'),
293 parent,
294 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
295 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
296 gtk.STOCK_OK, gtk.RESPONSE_OK))
298 if folder:
299 filechooser.set_current_folder(folder)
301 filechooser.set_default_response(gtk.RESPONSE_OK)
303 response = filechooser.run()
304 if response != gtk.RESPONSE_OK:
305 filechooser.destroy()
306 return
308 path = filechooser.get_filename()
309 if path and os.access(path, os.R_OK | os.X_OK):
310 filechooser.destroy()
311 return path
313 abspath = os.path.abspath(path)
315 error(_('Could not select folder "%s"') % abspath,
316 _('The folder "%s" could not be selected. '
317 'Permission denied.') % abspath)
319 filechooser.destroy()
320 return
322 def ask_overwrite(filename, parent=None):
323 submsg1 = _('A file named "%s" already exists') % os.path.abspath(filename)
324 submsg2 = _('Do you wish to replace it with the current one?')
325 text = ('<span weight="bold" size="larger">%s</span>\n\n%s\n'
326 % (submsg1, submsg2))
327 result = messagedialog(gtk.MESSAGE_ERROR, text, parent=parent,
328 buttons=((gtk.STOCK_CANCEL,
329 gtk.RESPONSE_CANCEL),
330 (_("Replace"),
331 gtk.RESPONSE_YES)))
332 return result == gtk.RESPONSE_YES
334 def save(title='', parent=None, current_name='', folder=None):
335 """Displays a save dialog."""
336 filechooser = gtk.FileChooserDialog(title or _('Save'),
337 parent,
338 gtk.FILE_CHOOSER_ACTION_SAVE,
339 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
340 gtk.STOCK_SAVE, gtk.RESPONSE_OK))
341 if current_name:
342 filechooser.set_current_name(current_name)
343 filechooser.set_default_response(gtk.RESPONSE_OK)
345 if folder:
346 filechooser.set_current_folder(folder)
348 path = None
349 while True:
350 response = filechooser.run()
351 if response != gtk.RESPONSE_OK:
352 path = None
353 break
355 path = filechooser.get_filename()
356 if not os.path.exists(path):
357 break
359 if ask_overwrite(path, parent):
360 break
361 filechooser.destroy()
362 return path
364 def password(primary='', secondary='', parent=None):
366 Shows a password dialog and returns the password entered in the dialog
367 @param primary: primary text
368 @param secondary: secondary text
369 @param parent: a gtk.Window subclass or None
370 @returns: the password or None if none specified
371 @rtype: string or None
373 if not primary:
374 raise ValueError("primary cannot be empty")
376 d = HIGAlertDialog(parent=parent, flags=gtk.DIALOG_MODAL,
377 type=gtk.MESSAGE_QUESTION,
378 buttons=gtk.BUTTONS_OK_CANCEL)
379 d.set_default_response(gtk.RESPONSE_OK)
381 d.set_primary(primary + '\n')
382 if secondary:
383 secondary += '\n'
384 d.set_secondary(secondary)
386 hbox = gtk.HBox()
387 hbox.set_border_width(6)
388 hbox.show()
389 d.label_vbox.pack_start(hbox)
391 label = gtk.Label(_('Password:'))
392 label.show()
393 hbox.pack_start(label, False, False)
395 entry = gtk.Entry()
396 entry.set_invisible_char(u'\u2022')
397 entry.set_visibility(False)
398 entry.show()
400 d.add_action_widget(entry, gtk.RESPONSE_OK)
401 # FIXME: Is there another way of connecting widget::activate to a response?
402 d.action_area.remove(entry)
403 hbox.pack_start(entry, True, True, 12)
405 response = d.run()
407 if response == gtk.RESPONSE_OK:
408 password = entry.get_text()
409 else:
410 password = None
411 d.destroy()
412 return password
414 def _test():
415 yesno('Kill?', default=gtk.RESPONSE_NO)
417 info('Some information displayed not too long\nbut not too short',
418 long=('foobar ba asdjaiosjd oiadjoisjaoi aksjdasdasd kajsdhakjsdh\n'
419 'askdjhaskjdha skjdhasdasdjkasldj alksdjalksjda lksdjalksdj\n'
420 'asdjaslkdj alksdj lkasjdlkjasldkj alksjdlkasjd jklsdjakls\n'
421 'ask;ldjaklsjdlkasjd alksdj laksjdlkasjd lkajs kjaslk jkl\n'),
422 default=gtk.RESPONSE_OK,
425 error('An error occurred', gtk.Button('Woho'))
426 error('Unable to mount the selected volume.',
427 'mount: can\'t find /media/cdrom0 in /etc/fstab or /etc/mtab')
428 print open(title='Open a file', patterns=['*.py'])
429 print save(title='Save a file', current_name='foobar.py')
431 print password('Administrator password',
432 'To be able to continue the wizard you need to enter the '
433 'administrator password for the database on host anthem')
434 print selectfolder()
436 if __name__ == '__main__':
437 _test()