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.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-08-31  |  37.2 KB  |  1,067 lines

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