Example #1
0
class AdditionListSlave(SearchSlave):
    """A slave that offers a simple list and its management.

    This slave also has the option to display a small message right next to the
    buttons
    """

    domain = 'stoq'
    toplevel_name = gladefile = 'AdditionListSlave'
    widgets = ('add_button', 'delete_button', 'klist', 'list_vbox',
               'edit_button')
    gsignal('on-edit-item', object)
    gsignal('on-add-item', object)
    gsignal('before-delete-items', object)
    gsignal('after-delete-items')

    def __init__(self,
                 store,
                 columns=None,
                 editor_class=None,
                 klist_objects=None,
                 visual_mode=False,
                 restore_name=None,
                 tree=False):
        """ Creates a new AdditionListSlave object

        :param store:         a store
        :param columns:       column definitions
        :type columns:        sequence of :class:`kiwi.ui.objectlist.Columns`
        :param editor_class:  the window that is going to be open when user
                              clicks on add_button or edit_button.
        :type: editor_class:  a :class:`stoqlib.gui.editors.BaseEditor` subclass
        :param klist_objects: initial objects to insert into the list
        :param visual_mode:   if we are working on visual mode, that means,
                              not possible to edit the model on this object
        type visual_mode:     bool
        :param restore_name:  the name used to save and restore the columns
                              on a cache system (e.g. pickle)
        :type restore_name:   basestring
        :param tree:          Indication of which kind of list we are adding.
                              If `True` ObjectTree otherwise ObjectList will be
                              added
        """
        columns = columns or self.get_columns()
        SearchSlave.__init__(self,
                             columns=columns,
                             restore_name=restore_name,
                             store=store)
        self.tree = tree
        self.klist = ObjectTree() if tree else ObjectList()
        self.list_vbox.add(self.klist)
        self.list_vbox.show_all()

        if not self.columns:
            raise StoqlibError("columns must be specified")
        self.visual_mode = visual_mode
        self.store = store
        self.set_editor(editor_class)
        self._can_edit = True
        self._callback_id = None
        if self.visual_mode:
            self.hide_add_button()
            self.hide_edit_button()
            self.hide_del_button()
        items = klist_objects or self.get_items()
        self._setup_klist(items)
        self._update_sensitivity()

    def _setup_klist(self, klist_objects):
        self.klist.set_columns(self.columns)
        self.klist.set_selection_mode(gtk.SELECTION_MULTIPLE)
        if self.tree:
            (self.klist.append(obj.parent_item, obj) for obj in klist_objects)
        else:
            self.klist.add_list(klist_objects)
        if self.visual_mode:
            self.klist.set_sensitive(False)

    def _update_sensitivity(self, *args):
        if self.visual_mode:
            return
        can_delete = _can_edit = True
        objs = self.get_selection()
        if not objs:
            _can_edit = can_delete = False
        elif len(objs) > 1:
            _can_edit = False

        self.add_button.set_sensitive(True)
        self.edit_button.set_sensitive(_can_edit)
        self.delete_button.set_sensitive(can_delete)

    def _edit_model(self, model=None, parent=None):
        edit_mode = model
        result = self.run_editor(model)

        if not result:
            return

        if edit_mode:
            self.emit('on-edit-item', result)
            self.klist.update(result)
        else:
            if self.tree:
                self.klist.append(parent, result)
            else:
                self.klist.append(result)
            # Emit the signal after we added the item to the list to be able to
            # check the length of the list in our validation callbacks.
            self.emit('on-add-item', result)

        # As we have a selection extended mode for kiwi list, we
        # need to unselect everything before select the new instance.
        self.klist.unselect_all()
        self.klist.select(result)
        self._update_sensitivity()

    def _edit(self):
        if not self._can_edit:
            return
        objs = self.get_selection()
        qty = len(objs)
        if qty != 1:
            raise SelectionError(
                ("Please select only one item before choosing Edit."
                 "\nThere are currently %d items selected") % qty)
        self._edit_model(objs[0])

    def _clear(self):
        objs = self.get_selection()
        qty = len(objs)
        if qty < 1:
            raise SelectionError('There are no objects selected')

        msg = stoqlib_ngettext(_('Delete this item?'),
                               _('Delete these %d items?') % qty, qty)
        delete_label = stoqlib_ngettext(_("Delete item"), _("Delete items"),
                                        qty)

        keep_label = stoqlib_ngettext(_("Keep it"), _("Keep them"), qty)
        if not yesno(msg, gtk.RESPONSE_NO, delete_label, keep_label):
            return
        self.emit('before-delete-items', objs)
        if qty == len(self.klist):
            self.klist.clear()
        else:
            for obj in objs:
                self.klist.remove(obj)
        self.klist.unselect_all()
        self._update_sensitivity()
        self.emit('after-delete-items')

    #
    # Hooks
    #

    def get_items(self):
        return []

    def get_columns(self):
        raise NotImplementedError("get_columns must be implemented in "
                                  "subclasses")

    def run_editor(self, model):
        """This can be overriden to provide a custom run_dialog line,
        or a conversion function for the model
        """
        if self._editor_class is None:
            raise TypeError(
                "%s cannot create or edit items without the editor_class "
                "argument set" % (self.__class__.__name__))

        self.store.savepoint('before_run_editor_addition')
        retval = run_dialog(self._editor_class,
                            None,
                            store=self.store,
                            model=model)
        if not retval:
            self.store.rollback_to_savepoint('before_run_editor_addition')
        return retval

    def delete_model(self, model):
        """Deletes a model, can be overridden in subclass
        :param model: model to delete
        """
        model.__class__.delete(model.id, store=self.store)

    #
    # Public API
    #

    def add_extra_button(self, label=None, stock=None):
        """Add an extra button on the this slave

        The extra button will be appended at the end of the button box,
        the one containing the add/edit/delete buttons

        :param label: label of the button, can be ``None`` if stock is passed
        :param stock: stock label of the button, can be ``None`` if label
            is passed
        :param returns: the button added
        :rtype: gtk.Button
        """
        if label is None and stock is None:
            raise TypeError("You need to provide a label or a stock argument")

        button = gtk.Button(label=label, stock=stock)
        button.set_property('can_focus', True)
        self.button_box.pack_end(button, False, False)
        button.show()

        return button

    def set_message(self, message, details_callback=None):
        """Display a simple message on a label, next to the add, edit, delete buttons
        :param message: a message with properly escaped markup
        """
        self.message_hbox.set_visible(True)
        self.message_details_button.set_visible(bool(details_callback))
        if details_callback:
            if self._callback_id:
                self.message_details_button.disconnect(self._callback_id)
            self._callback_id = self.message_details_button.connect(
                'clicked', details_callback)

        self.message_label.set_markup(message)

    def clear_message(self):
        self.message_hbox.set_visible(False)

    def get_selection(self):
        # XXX: add get_selected_rows and raise exceptions if not in the
        #      right mode
        if self.klist.get_selection_mode() == gtk.SELECTION_MULTIPLE:
            return self.klist.get_selected_rows()
        selection = self.klist.get_selected()
        if not selection:
            return []
        return [selection]

    def hide_add_button(self):
        self.add_button.hide()

    def hide_edit_button(self):
        self._can_edit = False
        self.edit_button.hide()

    def hide_del_button(self):
        self.delete_button.hide()

    def set_editor(self, editor_class):
        if editor_class and not issubclass(editor_class,
                                           (BaseEditor, BaseWizard)):
            raise TypeError("editor_class must be a BaseEditor subclass")
        self._editor_class = editor_class

    #
    # Signal handlers
    #

    def on_klist__row_activated(self, *args):
        self._edit()

    def on_klist__selection_changed(self, *args):
        self._update_sensitivity()

    def on_add_button__clicked(self, button):
        self._edit_model()

    def on_edit_button__clicked(self, button):
        self._edit()

    def on_delete_button__clicked(self, button):
        self._clear()
Example #2
0
class AdditionListSlave(SearchSlave):
    """A slave that offers a simple list and its management.

    This slave also has the option to display a small message right next to the
    buttons
    """

    domain = 'stoq'
    toplevel_name = gladefile = 'AdditionListSlave'
    widgets = ('add_button',
               'delete_button',
               'klist',
               'list_vbox',
               'edit_button')
    gsignal('before-edit-item', object, retval=object)
    gsignal('on-edit-item', object)
    gsignal('on-add-item', object)
    gsignal('before-delete-items', object)
    gsignal('after-delete-items')

    def __init__(self, store, columns=None, editor_class=None,
                 klist_objects=None, visual_mode=False, restore_name=None,
                 tree=False):
        """ Creates a new AdditionListSlave object

        :param store:         a store
        :param columns:       column definitions
        :type columns:        sequence of :class:`kiwi.ui.objectlist.Columns`
        :param editor_class:  the window that is going to be open when user
                              clicks on add_button or edit_button.
        :type: editor_class:  a :class:`stoqlib.gui.editors.BaseEditor` subclass
        :param klist_objects: initial objects to insert into the list
        :param visual_mode:   if we are working on visual mode, that means,
                              not possible to edit the model on this object
        type visual_mode:     bool
        :param restore_name:  the name used to save and restore the columns
                              on a cache system (e.g. pickle)
        :type restore_name:   basestring
        :param tree:          Indication of which kind of list we are adding.
                              If `True` ObjectTree otherwise ObjectList will be
                              added
        """
        columns = columns or self.get_columns()
        SearchSlave.__init__(self, columns=columns,
                             restore_name=restore_name,
                             store=store)
        self.tree = tree
        self.klist = ObjectTree() if tree else ObjectList()
        self.list_vbox.add(self.klist)
        self.list_vbox.show_all()

        if not self.columns:
            raise StoqlibError("columns must be specified")
        self.visual_mode = visual_mode
        self.store = store
        self.set_editor(editor_class)
        self._can_edit = True
        self._callback_id = None
        if self.visual_mode:
            self.hide_add_button()
            self.hide_edit_button()
            self.hide_del_button()
        items = klist_objects or self.get_items()
        self._setup_klist(items)
        self._update_sensitivity()

    def _setup_klist(self, klist_objects):
        self.klist.set_columns(self.columns)
        self.klist.set_selection_mode(gtk.SELECTION_MULTIPLE)
        if self.tree:
            (self.klist.append(obj.parent_item, obj) for obj in klist_objects)
        else:
            self.klist.add_list(klist_objects)
        if self.visual_mode:
            self.klist.set_sensitive(False)

    def _update_sensitivity(self, *args):
        if self.visual_mode:
            return
        can_delete = _can_edit = True
        objs = self.get_selection()
        if not objs:
            _can_edit = can_delete = False
        elif len(objs) > 1:
            _can_edit = False

        self.add_button.set_sensitive(True)
        self.edit_button.set_sensitive(_can_edit)
        self.delete_button.set_sensitive(can_delete)

    def _edit_model(self, model=None, parent=None):
        edit_mode = model
        result = self.emit('before-edit-item', model)
        if result is None:
            result = self.run_editor(model)

        if not result:
            return

        if edit_mode:
            self.emit('on-edit-item', result)
            self.klist.update(result)
        else:
            if self.tree:
                self.klist.append(parent, result)
            else:
                self.klist.append(result)
            # Emit the signal after we added the item to the list to be able to
            # check the length of the list in our validation callbacks.
            self.emit('on-add-item', result)

        # As we have a selection extended mode for kiwi list, we
        # need to unselect everything before select the new instance.
        self.klist.unselect_all()
        self.klist.select(result)
        self._update_sensitivity()

    def _edit(self):
        if not self._can_edit:
            return
        objs = self.get_selection()
        qty = len(objs)
        if qty != 1:
            raise SelectionError(
                ("Please select only one item before choosing Edit."
                 "\nThere are currently %d items selected") % qty)
        self._edit_model(objs[0])

    def _clear(self):
        objs = self.get_selection()
        qty = len(objs)
        if qty < 1:
            raise SelectionError('There are no objects selected')

        msg = stoqlib_ngettext(
            _('Delete this item?'),
            _('Delete these %d items?') % qty,
            qty)
        delete_label = stoqlib_ngettext(
            _("Delete item"),
            _("Delete items"),
            qty)

        keep_label = stoqlib_ngettext(
            _("Keep it"),
            _("Keep them"),
            qty)
        if not yesno(msg, gtk.RESPONSE_NO, delete_label, keep_label):
            return
        self.emit('before-delete-items', objs)
        if qty == len(self.klist):
            self.klist.clear()
        else:
            for obj in objs:
                self.klist.remove(obj)
        self.klist.unselect_all()
        self._update_sensitivity()
        self.emit('after-delete-items')

    #
    # Hooks
    #

    def get_items(self):
        return []

    def get_columns(self):
        raise NotImplementedError("get_columns must be implemented in "
                                  "subclasses")

    def run_editor(self, model):
        """This can be overriden to provide a custom run_dialog line,
        or a conversion function for the model
        """
        if self._editor_class is None:
            raise TypeError(
                "%s cannot create or edit items without the editor_class "
                "argument set" % (self.__class__.__name__))

        self.store.savepoint('before_run_editor_addition')
        retval = run_dialog(self._editor_class, None, store=self.store,
                            model=model)
        if not retval:
            self.store.rollback_to_savepoint('before_run_editor_addition')
        return retval

    def delete_model(self, model):
        """Deletes a model, can be overridden in subclass
        :param model: model to delete
        """
        model.__class__.delete(model.id, store=self.store)

    #
    # Public API
    #

    def add_extra_button(self, label=None, stock=None):
        """Add an extra button on the this slave

        The extra button will be appended at the end of the button box,
        the one containing the add/edit/delete buttons

        :param label: label of the button, can be ``None`` if stock is passed
        :param stock: stock label of the button, can be ``None`` if label
            is passed
        :param returns: the button added
        :rtype: gtk.Button
        """
        if label is None and stock is None:
            raise TypeError("You need to provide a label or a stock argument")

        button = gtk.Button(label=label, stock=stock)
        button.set_property('can_focus', True)
        self.button_box.pack_end(button, False, False)
        button.show()

        return button

    def set_message(self, message, details_callback=None):
        """Display a simple message on a label, next to the add, edit, delete buttons
        :param message: a message with properly escaped markup
        """
        self.message_hbox.set_visible(True)
        self.message_details_button.set_visible(bool(details_callback))
        if details_callback:
            if self._callback_id:
                self.message_details_button.disconnect(self._callback_id)
            self._callback_id = self.message_details_button.connect(
                'clicked', details_callback)

        self.message_label.set_markup(message)

    def clear_message(self):
        self.message_hbox.set_visible(False)

    def get_selection(self):
        # XXX: add get_selected_rows and raise exceptions if not in the
        #      right mode
        if self.klist.get_selection_mode() == gtk.SELECTION_MULTIPLE:
            return self.klist.get_selected_rows()
        selection = self.klist.get_selected()
        if not selection:
            return []
        return [selection]

    def hide_add_button(self):
        self.add_button.hide()

    def hide_edit_button(self):
        self._can_edit = False
        self.edit_button.hide()

    def hide_del_button(self):
        self.delete_button.hide()

    def set_editor(self, editor_class):
        if editor_class and not issubclass(editor_class,
                                           (BaseEditor, BaseWizard)):
            raise TypeError("editor_class must be a BaseEditor subclass")
        self._editor_class = editor_class

    #
    # Signal handlers
    #

    def on_klist__row_activated(self, *args):
        self._edit()

    def on_klist__selection_changed(self, *args):
        self._update_sensitivity()

    def on_add_button__clicked(self, button):
        self._edit_model()

    def on_edit_button__clicked(self, button):
        self._edit()

    def on_delete_button__clicked(self, button):
        self._clear()