home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / python2.4 / site-packages / serpentine / gtkutil.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-08-31  |  37.6 KB  |  1,091 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. import gtk
  5. import gobject
  6. import datetime
  7. from gettext import gettext as _
  8. from gettext import ngettext as N_
  9.  
  10. class NotFoundError(KeyError):
  11.     '''This is raised when an element is not found'''
  12.     pass
  13.  
  14.  
  15. class DictModelIterator:
  16.     
  17.     def __init__(self, parent):
  18.         self.parent = parent
  19.         self.index = 0
  20.  
  21.     
  22.     def __iter__(self):
  23.         return self
  24.  
  25.     
  26.     def next(self):
  27.         if len(self.parent) <= self.index:
  28.             raise StopIteration
  29.         
  30.         ret = self.parent.get(self.index)
  31.         self.index += 1
  32.         return ret
  33.  
  34.  
  35.  
  36. class DictStore(gtk.ListStore):
  37.     
  38.     def __init__(self, *cols):
  39.         self.indexes = { }
  40.         self.cols = []
  41.         spec = []
  42.         index = 0
  43.         for col in cols:
  44.             self.indexes[col['name']] = index
  45.             spec.append(col['type'])
  46.             self.cols.append(col['name'])
  47.             index += 1
  48.         
  49.         gtk.ListStore.__init__(self, *tuple(spec))
  50.  
  51.     
  52.     def __dict_to_list(self, row):
  53.         values = []
  54.         for key in self.cols:
  55.             values.append(row[key])
  56.         
  57.         return values
  58.  
  59.     
  60.     def append(self, row):
  61.         gtk.ListStore.append(self, self._DictStore__dict_to_list(row))
  62.  
  63.     
  64.     def insert_before(self, iter, row):
  65.         gtk.ListStore.insert_before(self, iter, self._DictStore__dict_to_list(row))
  66.  
  67.     
  68.     def insert_after(self, iter, row):
  69.         gtk.ListStore.insert_after(self, iter, self._DictStore__dict_to_list(row))
  70.  
  71.     
  72.     def index_of(self, key):
  73.         return self.indexes[key]
  74.  
  75.     
  76.     def get(self, index):
  77.         return DictModelRow(self, self[index])
  78.  
  79.     
  80.     def __iter__(self):
  81.         return DictModelIterator(self)
  82.  
  83.  
  84. gobject.type_register(DictStore)
  85.  
  86. class DictModelRow(object):
  87.     
  88.     def __init__(self, parent, row):
  89.         self.parent = parent
  90.         self.row = row
  91.  
  92.     
  93.     def __getitem__(self, key):
  94.         return self.row[self.parent.indexes[key]]
  95.  
  96.     
  97.     def __setitem__(self, key, value):
  98.         self.row[self.parent.indexes[key]] = value
  99.  
  100.     
  101.     def __delitem__(self, key):
  102.         del self.row[self.parent.indexes[key]]
  103.  
  104.     
  105.     def keys(self):
  106.         return self.parent.cols
  107.  
  108.     
  109.     def get(self, key, default):
  110.         if self.has_key(key):
  111.             return self[key]
  112.         
  113.         return default
  114.  
  115.     
  116.     def has_key(self, key):
  117.         return key in self.keys()
  118.  
  119.     __contains__ = has_key
  120.  
  121.  
  122. class SimpleListWrapper:
  123.     
  124.     def __init__(self, store):
  125.         self.store = store
  126.  
  127.     
  128.     def __getitem__(self, index):
  129.         return self.store[index][0]
  130.  
  131.     
  132.     def append(self, item):
  133.         self.store.append([
  134.             item])
  135.  
  136.  
  137.  
  138. class SimpleList(gtk.TreeView):
  139.     
  140.     def __init__(self, column_title, editable = True):
  141.         gtk.TreeView.__init__(self)
  142.         store = gtk.ListStore(str)
  143.         self.set_model(store)
  144.         rend = gtk.CellRendererText()
  145.         if editable:
  146.             rend.set_property('editable', True)
  147.             rend.connect('edited', self._SimpleList__on_text_edited)
  148.         
  149.         if not column_title:
  150.             self.set_headers_visible(False)
  151.             column_title = ''
  152.         
  153.         col = gtk.TreeViewColumn(column_title, rend, text = 0)
  154.         self.append_column(col)
  155.         self.wrapper = SimpleListWrapper(store)
  156.  
  157.     
  158.     def get_simple_store(self):
  159.         return self.wrapper
  160.  
  161.     
  162.     def __on_text_edited(self, cell, path, new_text, user_data = None):
  163.         self.get_model()[path][0] = new_text
  164.  
  165.  
  166. gobject.type_register(SimpleList)
  167.  
  168. dialog_error = lambda primary_text, secondary_text, **kwargs: hig_alert(primary_text, secondary_text, stock = gtk.STOCK_DIALOG_ERROR, buttons = (gtk.STOCK_CLOSE, gtk.RESPONSE_OK), **kwargs)
  169.  
  170. dialog_warn = lambda primary_text, secondary_text, **kwargs: hig_alert(primary_text, secondary_text, stock = gtk.STOCK_DIALOG_WARNING, buttons = (gtk.STOCK_CLOSE, gtk.RESPONSE_OK), **kwargs)
  171.  
  172. dialog_ok_cancel = lambda primary_text, secondary_text, ok_button = gtk.STOCK_OK, **kwargs: hig_alert(primary_text, secondary_text, stock = gtk.STOCK_DIALOG_WARNING, buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, ok_button, gtk.RESPONSE_OK), **kwargs)
  173.  
  174. dialog_info = lambda primary_text, secondary_text, **kwargs: hig_alert(primary_text, secondary_text, stock = gtk.STOCK_DIALOG_INFO, buttons = (gtk.STOCK_CLOSE, gtk.RESPONSE_OK), **kwargs)
  175.  
  176. class WidgetCostumizer:
  177.     '''
  178.     The WidgetCostumizer is a class template for defining chaining of asseblies
  179.     of interfaces. For example you can create a dialog with this simple lines of
  180.     code::
  181.     
  182.         creator.bind(SetupDialog()).bind(SetupAlert(primary_text, secondary_text, **kwargs))
  183.         dlg = creator()
  184.     
  185.     The costumization of a widget is like a pipeline of filters that act on a
  186.     certain widget and on a toplevel container.
  187.     
  188.     '''
  189.     _to_attrs = True
  190.     _defaults = { }
  191.     _next_values = None
  192.     _next_iter = None
  193.     
  194.     def __init__(self, *args, **kwargs):
  195.         self._args = args
  196.         self._kwargs = dict(self._defaults)
  197.         self._kwargs.update(kwargs)
  198.         self._next_values = []
  199.  
  200.     
  201.     def _get_next(self):
  202.         return self._next_iter.next()
  203.  
  204.     
  205.     def update(self, **kwargs):
  206.         self._kwargs.update(kwargs)
  207.  
  208.     
  209.     def _run(self, *args, **kwargs):
  210.         pass
  211.  
  212.     
  213.     def bind(self, *others):
  214.         for other in others:
  215.             if not isinstance(other, WidgetCostumizer):
  216.                 raise TypeError(type(other))
  217.             
  218.             self._next_values.append(other)
  219.         
  220.         return self
  221.  
  222.     
  223.     def _call(self, widget, container):
  224.         if self._to_attrs:
  225.             for key, value in self._kwargs.iteritems():
  226.                 setattr(self, key, value)
  227.             
  228.         
  229.         (widget, container) = self._run(widget, container)
  230.         for costum in self._next_values:
  231.             (widget, container) = costum._call(widget, container)
  232.         
  233.         for key in self._kwargs:
  234.             delattr(self, key)
  235.         
  236.         return (widget, container)
  237.  
  238.     
  239.     def __call__(self, widget = None, container = None):
  240.         '''This method is only called once'''
  241.         return self._call(widget, container)[0]
  242.  
  243.  
  244.  
  245. class SetupScrolledWindow(WidgetCostumizer):
  246.     
  247.     def _run(self, scrolled, container):
  248.         if not container is None:
  249.             raise AssertionError
  250.         if scrolled is None:
  251.             scrolled = gtk.ScrolledWindow()
  252.         
  253.         scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  254.         return (scrolled, None)
  255.  
  256.  
  257.  
  258. class SetupLabel(WidgetCostumizer):
  259.     '''
  260.     Usage::
  261.  
  262.         lbl = SetupLabel("<b>foo</b>")(gtk.Label())
  263.         lbl.show()
  264.         
  265.     Or::
  266.     
  267.         lbl = SetupLabel("<b>foo</b>")()
  268.         lbl.show()
  269.     
  270.     '''
  271.     
  272.     def _run(self, lbl, container):
  273.         if not container is None:
  274.             raise AssertionError
  275.         if not len(self._args) == 0 and len(self._args) == 1:
  276.             raise AssertionError
  277.         if lbl is None:
  278.             lbl = gtk.Label()
  279.         
  280.         lbl.set_alignment(0, 0)
  281.         if len(self._args) == 1:
  282.             lbl.set_markup(self._args[0])
  283.         
  284.         lbl.set_selectable(True)
  285.         lbl.set_line_wrap(True)
  286.         return (lbl, container)
  287.  
  288.  
  289.  
  290. def dialog_decorator(func):
  291.     
  292.     def wrapper(self, dialog, container):
  293.         if container is None:
  294.             container = dialog.get_child()
  295.         
  296.         return func(self, dialog, container)
  297.  
  298.     return wrapper
  299.  
  300.  
  301. class SetupDialog(WidgetCostumizer):
  302.     
  303.     def _run(self, dialog, container):
  304.         dialog.set_border_width(4)
  305.         dialog.set_has_separator(False)
  306.         dialog.set_title('')
  307.         dialog.set_resizable(False)
  308.         align = gtk.Alignment()
  309.         align.set_padding(padding_top = 0, padding_bottom = 7, padding_left = 0, padding_right = 0)
  310.         align.set_border_width(5)
  311.         align.show()
  312.         container.add(align)
  313.         return (dialog, align)
  314.  
  315.     _run = dialog_decorator(_run)
  316.  
  317.  
  318. class SetupAlert(WidgetCostumizer):
  319.     
  320.     class _PrimaryTextDecorator:
  321.         
  322.         def __init__(self, label):
  323.             self.label = label
  324.  
  325.         
  326.         def __call__(self, primary_text):
  327.             self.label.set_markup('<span weight="bold" size="larger">' + primary_text + '</span>')
  328.  
  329.  
  330.     _defaults = {
  331.         'title': '',
  332.         'stock': gtk.STOCK_DIALOG_INFO }
  333.     
  334.     def _before_text(self, dialog, vbox):
  335.         pass
  336.  
  337.     
  338.     def _after_text(self, dialog, vbox):
  339.         pass
  340.  
  341.     
  342.     def _run(self, dialog, container):
  343.         (primary_text, secondary_text) = self._args
  344.         dialog.set_title(self.title)
  345.         hbox = gtk.HBox(spacing = 12)
  346.         hbox.set_border_width(0)
  347.         hbox.show()
  348.         container.add(hbox)
  349.         img = gtk.Image()
  350.         img.set_from_stock(self.stock, gtk.ICON_SIZE_DIALOG)
  351.         img.set_alignment(img.get_alignment()[0], 0.0)
  352.         img.show()
  353.         hbox.pack_start(img, False, False)
  354.         vbox = gtk.VBox(spacing = 6)
  355.         vbox.show()
  356.         hbox.pack_start(vbox)
  357.         lbl = SetupLabel('<span weight="bold" size="larger">' + primary_text + '</span>')()
  358.         lbl.show()
  359.         dialog.set_primary_text = self._PrimaryTextDecorator(lbl)
  360.         vbox.pack_start(lbl, False, False)
  361.         lbl = SetupLabel(secondary_text)()
  362.         lbl.show()
  363.         dialog.set_secondary_text = lbl.set_text
  364.         
  365.         def on_destroy(widget):
  366.             delattr(widget, 'set_secondary_text')
  367.             delattr(widget, 'set_primary_text')
  368.  
  369.         dialog.connect('destroy', on_destroy)
  370.         self._before_text(dialog, vbox)
  371.         vbox.pack_start(lbl, False, False)
  372.         self._after_text(dialog, vbox)
  373.         return (dialog, vbox)
  374.  
  375.     _run = dialog_decorator(_run)
  376.  
  377.  
  378. class SetupRadioChoiceList(SetupAlert):
  379.     
  380.     def _after_text(self, dialog, container):
  381.         vbox = gtk.VBox(spacing = 6)
  382.         vbox.show()
  383.         vbox.set_name('items')
  384.         container.pack_start(vbox)
  385.         group = None
  386.         for item in self.items:
  387.             radio = gtk.RadioButton(group, item)
  388.             radio.show()
  389.             if group is None:
  390.                 group = radio
  391.             
  392.             vbox.pack_start(radio, False, False)
  393.         
  394.  
  395.  
  396.  
  397. class SetupListAlertTemplate(SetupAlert):
  398.     
  399.     def get_list_title(self):
  400.         raise NotImplementedError
  401.  
  402.     
  403.     def configure_widgets(self, dialog, tree):
  404.         raise NotImplementedError
  405.  
  406.     
  407.     def create_store(self):
  408.         raise NotImplementedError
  409.  
  410.     
  411.     def _before_text(self, dialog, vbox):
  412.         store = self.create_store()
  413.         title = self.get_list_title()
  414.         if title is not None:
  415.             lbl = SetupLabel(title)()
  416.             lbl.show()
  417.             vbox.pack_start(lbl, False, False)
  418.         
  419.         tree = gtk.TreeView()
  420.         tree.set_name('list_view')
  421.         tree.set_model(store)
  422.         tree.set_headers_visible(False)
  423.         tree.show()
  424.         scroll = SetupScrolledWindow()()
  425.         scroll.add(tree)
  426.         scroll.show()
  427.         vbox.add(scroll)
  428.         self.configure_widgets(dialog, tree)
  429.         return (dialog, vbox)
  430.  
  431.  
  432.  
  433. class SetupMultipleChoiceList(SetupListAlertTemplate):
  434.     
  435.     def get_list_title(self):
  436.         return self.list_title
  437.  
  438.     
  439.     def configure_widgets(self, dialog, tree):
  440.         store = tree.get_model()
  441.         
  442.         def on_toggle(render, path, args):
  443.             (dialog, model, min_sel, max_sel) = args
  444.             tree_iter = model.get_iter(path)
  445.             row = model[tree_iter]
  446.             row[0] = not row[0]
  447.             if model.enabled_rows == 0:
  448.                 is_sensitive = False
  449.             elif max_sel >= 0:
  450.                 is_sensitive = None if model.enabled_rows <= model.enabled_rows else model.enabled_rows <= max_sel
  451.             else:
  452.                 is_sensitive = min_sel <= model.enabled_rows
  453.             dialog.set_response_sensitive(gtk.RESPONSE_OK, is_sensitive)
  454.  
  455.         args = (dialog, store, self.min_select, self.max_select)
  456.         rend = gtk.CellRendererToggle()
  457.         rend.connect('toggled', on_toggle, args)
  458.         col = gtk.TreeViewColumn('', rend, active = 0)
  459.         tree.append_column(col)
  460.         rend = gtk.CellRendererText()
  461.         col = gtk.TreeViewColumn('', rend, text = 1)
  462.         tree.append_column(col)
  463.         dialog.set_response_sensitive(gtk.RESPONSE_OK, False)
  464.  
  465.     
  466.     def create_store(self):
  467.         store = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING)
  468.         store.enabled_rows = 0
  469.         for item in self.items:
  470.             store.append((False, item))
  471.         
  472.         return store
  473.  
  474.  
  475.  
  476. class SetupListAlert(SetupListAlertTemplate):
  477.     _defaults = {
  478.         'list_title': None }
  479.     _defaults.update(SetupAlert._defaults)
  480.     
  481.     def get_list_title(self):
  482.         return self.list_title
  483.  
  484.     
  485.     def configure_widgets(self, dialog, tree):
  486.         rend = gtk.CellRendererText()
  487.         col = gtk.TreeViewColumn('', rend, text = 0)
  488.         tree.append_column(col)
  489.         tree.get_selection().set_mode(gtk.SELECTION_NONE)
  490.  
  491.     
  492.     def create_store(self):
  493.         store = gtk.ListStore(gobject.TYPE_STRING)
  494.         for item in self.items:
  495.             store.append((item,))
  496.         
  497.         return store
  498.  
  499.  
  500.  
  501. class SetupSingleChoiceList(SetupListAlert):
  502.     _defaults = {
  503.         'min_select': 1 }
  504.     _defaults.update(SetupListAlert._defaults)
  505.     
  506.     def configure_widgets(self, dialog, tree):
  507.         if not self.min_select in (0, 1):
  508.             raise AssertionError
  509.         SetupListAlert.configure_widgets(self, dialog, tree)
  510.         selection = tree.get_selection()
  511.         if self.min_select == 0:
  512.             selection_mode = gtk.SELECTION_SINGLE
  513.             
  514.             def on_selection_changed(selection, dialog):
  515.                 is_sensitive = selection.count_selected_rows() > 0
  516.                 dialog.set_response_sensitive(gtk.RESPONSE_OK, is_sensitive)
  517.  
  518.             selection.connect('changed', on_selection_changed, dialog)
  519.         else:
  520.             selection_mode = gtk.SELECTION_BROWSE
  521.         selection.set_mode(selection_mode)
  522.  
  523.  
  524.  
  525. class RunDialog(WidgetCostumizer):
  526.     """
  527.     This is a terminal costumizer because it swaps the gtk.Dialog recieved by
  528.     argument for its `gtk.Dialog.run`'s result.
  529.     """
  530.     
  531.     def _run(self, dialog, container):
  532.         response = dialog.run()
  533.         dialog.destroy()
  534.         return (response, None)
  535.  
  536.  
  537.  
  538. def hig_alert(primary_text, secondary_text, parent = None, flags = 0, buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK), run = True, _setup_alert = SetupAlert, **kwargs):
  539.     if parent is None and 'title' not in kwargs:
  540.         raise TypeError("When you don't define a parent you must define a title")
  541.     
  542.     dlg = gtk.Dialog(parent = parent, flags = flags, buttons = buttons)
  543.     costumizer = SetupDialog()
  544.     costumizer.bind(_setup_alert(primary_text, secondary_text, **kwargs))
  545.     if run:
  546.         costumizer.bind(RunDialog())
  547.     
  548.     return costumizer(dlg)
  549.  
  550.  
  551. def list_dialog(primary_text, secondary_text, parent = None, items = (), **kwargs):
  552.     '''
  553.     @param list_title: A label will be placed above the list of items describing
  554.         what\'s the content of the list. Optional.
  555.     
  556.     Every other argument that L{hig_alert} function does.
  557.     
  558.     Example::
  559.         primary_text = "Listing cool stuff"
  560.         secondary_text = "To select more of that stuff eat alot of cheese!"
  561.         list_title = "Your cool stuff:"
  562.         # Some random 20 elements
  563.         items = ["foo", "bar"] * 10
  564.         window_title = "Rat Demo"
  565.         list_dialog(
  566.             primary_text,
  567.             secondary_text,
  568.             items=items,
  569.             title=window_title,
  570.             list_title=list_title
  571.         )
  572.     '''
  573.     return hig_alert(primary_text, secondary_text, parent = parent, _setup_alert = SetupListAlert, items = items, **kwargs)
  574.  
  575.  
  576. class _OneStrategy:
  577.     
  578.     accepts = lambda self, choices, min_select, max_select: choices == 1
  579.     
  580.     def before(self, kwargs):
  581.         pass
  582.  
  583.     
  584.     def get_items(self, data):
  585.         return (0,)
  586.  
  587.  
  588.  
  589. class _BaseStrategy:
  590.     
  591.     def before(self, kwargs):
  592.         kwargs['_setup_alert'] = self.setup_factory
  593.  
  594.  
  595.  
  596. class _MultipleStrategy(_BaseStrategy):
  597.     
  598.     accepts = lambda self, choices, min_select, max_select: if not max_select == -1:
  599. passmax_select > 1
  600.     setup_factory = SetupMultipleChoiceList
  601.     
  602.     def get_items(self, dlg):
  603.         store = find_child_widget(dlg, 'list_view').get_model()
  604.         return tuple((lambda [outmost-iterable]: for row in [outmost-iterable]:
  605. if row[0]:
  606. row.path[0]continue)(store))
  607.  
  608.  
  609.  
  610. class _RadioStrategy(_BaseStrategy):
  611.     
  612.     accepts = lambda self, choices, min_select, max_select: choices < 5
  613.     setup_factory = SetupRadioChoiceList
  614.     
  615.     def get_items(self, dlg):
  616.         vbox = find_child_widget(dlg, 'items')
  617.         counter = 0
  618.         for radio in vbox.get_children():
  619.             if radio.get_active():
  620.                 break
  621.             
  622.             counter += 1
  623.         
  624.         if not radio.get_active():
  625.             raise AssertionError
  626.         return (counter,)
  627.  
  628.  
  629.  
  630. class _SingleListStrategy(_BaseStrategy):
  631.     
  632.     accepts = lambda self, a, b, c: True
  633.     setup_factory = SetupSingleChoiceList
  634.     
  635.     def get_items(self, dlg):
  636.         list_view = find_child_widget(dlg, 'list_view')
  637.         rows = list_view.get_selection().get_selected_rows()[1]
  638.         
  639.         get_element = lambda row: row[0]
  640.         items = tuple(map(get_element, rows))
  641.  
  642.  
  643. _STRATEGIES = (_OneStrategy, _MultipleStrategy, _RadioStrategy, _SingleListStrategy)
  644. _STRATEGIES = tuple((lambda [outmost-iterable]: for factory in [outmost-iterable]:
  645. factory())(_STRATEGIES))
  646.  
  647. def choice_dialog(primary_text, secondary_text, parent = None, allow_cancel = True, **kwargs):
  648.     """
  649.     @param items: the items you want to choose from
  650.     @param list_title: the title of the list. Optional.
  651.     @param allow_cancel: If the user can cancel/close the dialog.
  652.     @param min_select: The minimum number of elements to be selected.
  653.     @param max_select: The maximum number of elements to be selected.
  654.         -1 Means no limit.
  655.     
  656.     @param dialog_callback: This is a callback function that is going to be
  657.         called when the dialog is created. The argument is the dialog object.
  658.     @param one_item_text: when specified and if the number of `items` is one
  659.         this text will be the primary text. This string must contain a '%s'
  660.         which will be replaced by the item value.
  661.         Optional.
  662.     """
  663.     if 'run' in kwargs:
  664.         del kwargs['run']
  665.     
  666.     choices = len(kwargs['items'])
  667.     min_select = kwargs.get('min_select', 1)
  668.     max_select = kwargs.get('max_select', -1)
  669.     if not choices > 0:
  670.         raise AssertionError
  671.     if not None ^ max_select == -1 if max_select <= max_select else max_select <= choices:
  672.         raise AssertionError
  673.     if min_select <= min_select:
  674.         pass
  675.     elif not min_select <= choices:
  676.         raise AssertionError
  677.     buttons = (kwargs.get('ok_button', gtk.STOCK_OK), gtk.RESPONSE_OK)
  678.     if allow_cancel:
  679.         buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) + buttons
  680.     
  681.     if min_select == 0:
  682.         txt = N_("Don't select it", "Don't select any items", choices)
  683.         txt = kwargs.get('skip_button', txt)
  684.         buttons = (txt, gtk.RESPONSE_CLOSE) + buttons
  685.     
  686.     for strategy in _STRATEGIES:
  687.         if strategy.accepts(choices, min_select, max_select):
  688.             break
  689.             continue
  690.     
  691.     if not strategy.accepts(choices, min_select, max_select):
  692.         raise AssertionError
  693.     if choices == 1:
  694.         if 'one_item_text' in kwargs:
  695.             primary_text = kwargs['one_item_text'] % kwargs['items'][0]
  696.         
  697.     
  698.     data = strategy.before(kwargs)
  699.     if data is not None:
  700.         primary_text = data
  701.     
  702.     dlg = hig_alert(primary_text, secondary_text, parent = parent, run = False, buttons = buttons, **kwargs)
  703.     kwargs.get('dialog_callback', (lambda foo: pass))(dlg)
  704.     response = dlg.run()
  705.     if response != gtk.RESPONSE_OK:
  706.         dlg.destroy()
  707.         return ((), response)
  708.     
  709.     items = strategy.get_items(dlg)
  710.     dlg.destroy()
  711.     return (items, response)
  712.  
  713. MIN_FRACTION = 60
  714. HOUR_FRACTION = 60 * MIN_FRACTION
  715. DAY_FRACTION = 24 * HOUR_FRACTION
  716.  
  717. def humanize_seconds(elapsed_seconds, use_hours = True, use_days = True):
  718.     """
  719.     Turns a number of seconds into to a human readable string, example
  720.     125 seconds is: '2 minutes and 5 seconds'.
  721.     
  722.     @param elapsed_seconds: number of seconds you want to humanize
  723.     @param use_hours: wether or not to render the hours(if hours > 0)
  724.     @param use_days: wether or not to render the days(if days > 0)
  725.     """
  726.     text = []
  727.     duration = elapsed_seconds
  728.     if duration == 0:
  729.         return _('0 seconds')
  730.     
  731.     days = duration / DAY_FRACTION
  732.     if use_days and days > 0:
  733.         text.append(N_('%d day', '%d days', days) % days)
  734.         duration %= DAY_FRACTION
  735.     
  736.     hours = duration / HOUR_FRACTION
  737.     if use_hours and hours > 0:
  738.         text.append(N_('%d hour', '%d hours', hours) % hours)
  739.         duration %= HOUR_FRACTION
  740.     
  741.     minutes = duration / MIN_FRACTION
  742.     if minutes > 0:
  743.         text.append(N_('%d minute', '%d minutes', minutes) % minutes)
  744.         duration %= MIN_FRACTION
  745.     
  746.     seconds = duration % 60
  747.     if seconds > 0:
  748.         text.append(N_('%d second', '%d seconds', seconds) % seconds)
  749.     
  750.     if len(text) > 2:
  751.         return _(', ').join(text[:-1]) + _(' and ') + text[-1]
  752.     else:
  753.         return _(' and ').join(text)
  754.  
  755.  
  756. class _TimeUpdater:
  757.     
  758.     def __init__(self, initial_time):
  759.         self.initial_time = initial_time
  760.  
  761.     
  762.     def set_dialog(self, dialog):
  763.         self.dialog = dialog
  764.         self.dialog.connect('response', self.on_response)
  765.         self.source = gobject.timeout_add(500, self.on_tick)
  766.  
  767.     
  768.     def on_response(self, *args):
  769.         gobject.source_remove(self.source)
  770.  
  771.     
  772.     def get_text(self):
  773.         last_changes = datetime.datetime.now() - self.initial_time
  774.         secondary_text = _("If you don't save, changes from the last %s will be permanently lost.")
  775.         return secondary_text % humanize_seconds(last_changes.seconds)
  776.  
  777.     
  778.     def on_tick(self):
  779.         self.dialog.set_secondary_text(self.get_text())
  780.         return True
  781.  
  782.  
  783.  
  784. def save_changes(files, last_save = None, parent = None, **kwargs):
  785.     '''
  786.     Shows up a Save changes dialog to a certain list of documents and returns
  787.     a tuple with two values, the first is a list of files that are to be saved
  788.     the second is the value of the response, which can be one of:
  789.       - gtk.RESPONSE_OK - the user wants to save
  790.       - gtk.RESPONSE_CANCEL - the user canceled the dialog
  791.       - gtk.RESPONSE_CLOSE - the user wants to close without saving
  792.       - gtk.RESPONSE_DELETE_EVENT - the user closed the window
  793.     
  794.     So if you want to check if the user canceled just check if the response is
  795.     equal to gtk.RESPONSE_CANCEL or gtk.RESPONSE_DELETE_EVENT
  796.     
  797.     When the `elapsed_time` argument is not `None` it should be a list of the
  798.     elapsed time since each was modified. It must be in the same order of
  799.     the `files` argument.
  800.     
  801.     This function also accepts every argument that a hig_alert function accepts,
  802.     which means it accepts `title`, etc. Note that this function overrides
  803.     the `run` argument and sets it to True, because it\'s not possible for a user
  804.     to know which files were saved since the dialog changes is structure
  805.     depending on the arguments.
  806.     
  807.     Simple usage example::
  808.         files_to_save, response = save_changes(["foo.bar"])
  809.  
  810.     @param files: a list of filenames to be saved
  811.     @param last_save: when you only want to save one file you can optionally
  812.         send the date of when the user saved the file most recently.
  813.         
  814.     @type last_save: datetime.datetime
  815.     @param parent: the window that will be parent of this window.
  816.     @param primary_text: optional, see hig_alert.
  817.     @param secondary_text: optional, see hig_alert.
  818.     @param one_item_text: optional, see choice_alert.
  819.     @param list_title: optional, see choice_alert.
  820.     @param kwargs: the remaining keyword arguments are the same as used on the function
  821.         hig_alert.
  822.     @return: a tuple with a list of entries the user chose to save and a gtk.RESPONSE_*
  823.         from the dialog
  824.     '''
  825.     primary_text = N_('There is %d file with unsaved changes. Save changes before closing?', 'There are %d files with unsaved changes. Save changes before closing?', len(files))
  826.     primary_text %= len(files)
  827.     primary_text = kwargs.get('primary_text', primary_text)
  828.     secondary_text = _("If you don't save, all your changes will be permanently lost.")
  829.     secondary_text = kwargs.get('secondary_text', secondary_text)
  830.     one_item_text = _('Save the changes to <i>%s</i> before closing?')
  831.     one_item_text = kwargs.get('one_item_text', one_item_text)
  832.     list_title = _('Select the files you want to save:')
  833.     list_title = kwargs.get('list_title', list_title)
  834.     if len(files) == 1 and last_save is not None:
  835.         updater = _TimeUpdater(last_save)
  836.         secondary_text = updater.get_text()
  837.         kwargs['dialog_callback'] = updater.set_dialog
  838.     
  839.     (indexes, response) = choice_dialog(primary_text, secondary_text, min_select = 0, max_select = -1, skip_button = _('Close without saving'), ok_button = gtk.STOCK_SAVE, list_title = list_title, items = files, one_item_text = one_item_text, **kwargs)
  840.     return (map(files.__getitem__, indexes), response)
  841.  
  842.  
  843. def _simple_iterate_widget_children(widget):
  844.     '''This function iterates all over the widget children.
  845.     '''
  846.     get_children = getattr(widget, 'get_children', None)
  847.     if get_children is None:
  848.         return None
  849.     
  850.     for child in get_children():
  851.         yield child
  852.     
  853.     get_submenu = getattr(widget, 'get_submenu', None)
  854.     if get_submenu is None:
  855.         return None
  856.     
  857.     sub_menu = get_submenu()
  858.     if sub_menu is not None:
  859.         yield sub_menu
  860.     
  861.  
  862.  
  863. class _IterateWidgetChildren:
  864.     '''This iterator class is used to recurse to child widgets, it uses
  865.     the _simple_iterate_widget_children function
  866.     
  867.     '''
  868.     
  869.     def __init__(self, widget):
  870.         self.widget = widget
  871.         self.children_widgets = iter(_simple_iterate_widget_children(self.widget))
  872.         self.next_iter = None
  873.  
  874.     
  875.     def next(self):
  876.         if self.next_iter is None:
  877.             widget = self.children_widgets.next()
  878.             self.next_iter = _IterateWidgetChildren(widget)
  879.             return widget
  880.         else:
  881.             
  882.             try:
  883.                 return self.next_iter.next()
  884.             except StopIteration:
  885.                 self.next_iter = None
  886.                 return self.next()
  887.  
  888.  
  889.     
  890.     def __iter__(self):
  891.         return self
  892.  
  893.  
  894.  
  895. def iterate_widget_children(widget, recurse_children = False):
  896.     """
  897.     This function is used to iterate over the children of a given widget.
  898.     You can recurse to all the widgets contained in a certain widget.
  899.     
  900.     @param widget: The base widget of iteration
  901.     @param recurse_children: Wether or not to iterate recursively, by iterating
  902.         over the children's children.
  903.     
  904.     @return: an iterator
  905.     @rtype: C{GeneratorType}
  906.     """
  907.     if recurse_children:
  908.         return _IterateWidgetChildren(widget)
  909.     else:
  910.         return iter(_simple_iterate_widget_children(widget))
  911.  
  912.  
  913. def iterate_widget_parents(widget):
  914.     """Iterate over the widget's parents.
  915.  
  916.     @param widget: The base widget of iteration
  917.     @return: an iterator
  918.     @rtype: C{GeneratorType}
  919.     """
  920.     widget = widget.get_parent()
  921.     while widget is not None:
  922.         yield widget
  923.         widget = widget.get_parent()
  924.  
  925.  
  926. def find_parent_widget(widget, name, find_self = True):
  927.     """
  928.     Finds a widget by name upwards the tree, by searching self and its parents
  929.     
  930.     @return: C{None} when it didn't find it, otherwise a C{gtk.Container}
  931.     @rtype: C{gtk.Container}
  932.     @param find_self: Set this to C{False} if you want to only find on the parents
  933.     @param name: The name of the widget
  934.     @param widget: The widget where this function will start searching
  935.     """
  936.     if not widget is not None:
  937.         raise AssertionError
  938.     if find_self and widget.get_name() == name:
  939.         return widget
  940.     
  941.     for w in iterate_widget_parents(widget):
  942.         if w.get_name() == name:
  943.             return w
  944.             continue
  945.     
  946.     raise NotFoundError(name)
  947.  
  948.  
  949. def find_child_widget(widget, name, find_self = True):
  950.     """
  951.     Finds the widget by name downwards the tree, by searching self and its
  952.     children.
  953.  
  954.     @return: C{None} when it didn't find it, otherwise a C{gtk.Widget}
  955.     @rtype: C{gtk.Widget}
  956.     @param find_self: Set this to L{False} if you want to only find on the children
  957.     @param name: The name of the widget
  958.     @param widget: The widget where this function will start searching
  959.     """
  960.     if not widget is not None:
  961.         raise AssertionError
  962.     if find_self and widget.get_name() == name:
  963.         return widget
  964.     
  965.     for w in iterate_widget_children(widget, True):
  966.         if name == w.get_name():
  967.             return w
  968.             continue
  969.     
  970.     raise NotFoundError(name)
  971.  
  972.  
  973. def get_root_parent(widget):
  974.     '''Returns the first widget of a tree. If this widget has no children
  975.     it will return C{None}
  976.     
  977.     @return: C{None} when there is no parent widget, otherwise a C{gtk.Container}
  978.     @rtype: C{gtk.Container} 
  979.     '''
  980.     parents = list(iterate_widget_parents(widget))
  981.     if len(parents) == 0:
  982.         return None
  983.     else:
  984.         return parents[-1]
  985.  
  986.  
  987. class HigProgress(gtk.Window):
  988.     '''
  989.     HigProgress returns a window that contains a number of properties to
  990.     access what a common Progress window should have.
  991.     '''
  992.     
  993.     def __init__(self):
  994.         gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
  995.         self.set_border_width(6)
  996.         self.set_resizable(False)
  997.         self.set_title('')
  998.         self.set_position(gtk.WIN_POS_CENTER)
  999.         self.connect('delete-event', self._on_close)
  1000.         main = gtk.VBox(spacing = 12)
  1001.         main.set_spacing(12)
  1002.         main.set_border_width(6)
  1003.         main.show()
  1004.         self.add(main)
  1005.         alg = gtk.Alignment()
  1006.         alg.set_padding(0, 6, 0, 0)
  1007.         alg.show()
  1008.         main.pack_start(alg, False, False)
  1009.         lbl = SetupLabel()()
  1010.         lbl.set_selectable(False)
  1011.         lbl.show()
  1012.         self._primary_label = lbl
  1013.         alg.add(lbl)
  1014.         lbl = SetupLabel()()
  1015.         lbl.set_selectable(False)
  1016.         lbl.show()
  1017.         main.pack_start(lbl, False, False)
  1018.         self._secondary_label = lbl
  1019.         vbox = gtk.VBox()
  1020.         vbox.show()
  1021.         main.pack_start(vbox, False, False)
  1022.         prog = gtk.ProgressBar()
  1023.         prog.show()
  1024.         self._progress_bar = prog
  1025.         vbox.pack_start(prog, expand = False)
  1026.         lbl = SetupLabel()()
  1027.         lbl.set_selectable(False)
  1028.         lbl.show()
  1029.         self._sub_progress_label = lbl
  1030.         vbox.pack_start(lbl, False, False)
  1031.         bbox = gtk.HButtonBox()
  1032.         bbox.set_layout(gtk.BUTTONBOX_END)
  1033.         bbox.show()
  1034.         cancel = gtk.Button(gtk.STOCK_CANCEL)
  1035.         cancel.set_use_stock(True)
  1036.         cancel.show()
  1037.         self._cancel = cancel
  1038.         bbox.add(cancel)
  1039.         main.add(bbox)
  1040.         close = gtk.Button(gtk.STOCK_CLOSE)
  1041.         close.set_use_stock(True)
  1042.         close.hide()
  1043.         bbox.add(close)
  1044.         self._close = close
  1045.  
  1046.     primary_label = property((lambda self: self._primary_label))
  1047.     secondary_label = property((lambda self: self._secondary_label))
  1048.     progress_bar = property((lambda self: self._progress_bar))
  1049.     sub_progress_label = property((lambda self: self._sub_progress_label))
  1050.     cancel_button = property((lambda self: self._cancel))
  1051.     close_button = property((lambda self: self._close))
  1052.     
  1053.     def set_primary_text(self, text):
  1054.         self.primary_label.set_markup('<span weight="bold" size="larger">' + text + '</span>')
  1055.         self.set_title(text)
  1056.  
  1057.     primary_text = property(fset = set_primary_text)
  1058.     
  1059.     def set_secondary_text(self, text):
  1060.         self.secondary_label.set_markup(text)
  1061.  
  1062.     secondary_text = property(fset = set_secondary_text)
  1063.     
  1064.     def set_progress_fraction(self, fraction):
  1065.         self.progress_bar.set_fraction(fraction)
  1066.  
  1067.     
  1068.     def get_progress_fraction(self):
  1069.         return self.progress_bar.get_fraction()
  1070.  
  1071.     progress_fraction = property(get_progress_fraction, set_progress_fraction)
  1072.     
  1073.     def set_progress_text(self, text):
  1074.         self.progress_bar.set_text(text)
  1075.  
  1076.     progress_text = property(fset = set_progress_text)
  1077.     
  1078.     def set_sub_progress_text(self, text):
  1079.         self.sub_progress_label.set_markup('<i>' + text + '</i>')
  1080.  
  1081.     sub_progress_text = property(fset = set_sub_progress_text)
  1082.     
  1083.     def _on_close(self, *args):
  1084.         if not self.cancel_button.get_property('sensitive'):
  1085.             return True
  1086.         
  1087.         self.cancel_button.clicked()
  1088.         return True
  1089.  
  1090.  
  1091.