Пример #1
0
class StockIconDialog(BaseDialog):
    def __init__(self, parent=None):
        BaseDialog.__init__(self, parent, title=_('Stock Icons'),
                            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                     gtk.STOCK_OK, gtk.RESPONSE_OK))
        self.set_size_request(260, 330)

        self._stockicons = ObjectList([Column('stock_id', use_stock=True),
                                       Column('name')])
        self._stockicons.set_headers_visible(False)
        self._stockicons.connect('row-activated',
                                 self._on_stockicons__row_activated)
        self._icons = {}
        for stock_label, stock_id in get_stock_icons():
            icon = Settable(stock_id=stock_id, name=stock_label)
            self._stockicons.append(icon)
            self._icons[stock_id] = icon

        self.vbox.pack_start(self._stockicons)
        self._stockicons.show()

    def select(self, value):
        icon = self._icons.get(value)
        if icon:
            self._stockicons.select(icon)

    def get_selected(self):
        icon = self._stockicons.get_selected()
        if icon:
            return icon.stock_id

    def _on_stockicons__row_activated(self, objectlist, icon):
        self.emit('response', gtk.RESPONSE_OK)
Пример #2
0
class Diary(ProxyDelegate):
    def __init__(self):
        self.entries = ObjectList([Column("title", width=120),
                                   Column("period", width=80),
                                   Column("text", expand=True)])

        ProxyDelegate.__init__(self, DiaryEntry(), ['title', 'period', 'text'],
                               gladefile="diary",
                               delete_handler=self.quit_if_last)
        self.hbox.pack_start(self.entries)
        self.entries.show()
        self.entries.grab_focus()

    def on_add__clicked(self, button):
        entry = DiaryEntry()
        entry.title = 'New title'

        self.set_model(entry)
        self.entries.append(entry)
        self.title.grab_focus()

    def on_remove__clicked(self, button):
        entry = self.entries.get_selected()
        if entry:
            self.entries.remove(entry)

    def on_entries__selection_changed(self, entries, instance):
        if instance:
            self.set_model(instance)
Пример #3
0
class Diary(ProxyDelegate):
    def __init__(self):
        self.entries = ObjectList([
            Column("title", width=120),
            Column("period", width=80),
            Column("text", expand=True)
        ])

        ProxyDelegate.__init__(self,
                               DiaryEntry(), ['title', 'period', 'text'],
                               gladefile="diary.ui",
                               delete_handler=self.quit_if_last)
        self.hbox.pack_start(self.entries, True, True, 0)
        self.entries.show()
        self.entries.grab_focus()

    def on_add__clicked(self, button):
        entry = DiaryEntry()
        entry.title = 'New title'

        self.set_model(entry)
        self.entries.append(entry)
        self.title.grab_focus()

    def on_remove__clicked(self, button):
        entry = self.entries.get_selected()
        if entry:
            self.entries.remove(entry)

    def on_entries__selection_changed(self, entries, instance):
        if instance:
            self.set_model(instance)
Пример #4
0
class SimpleListDialog(BasicDialog):
    size = (500, 400)

    def __init__(self,
                 columns,
                 objects,
                 hide_cancel_btn=True,
                 title='',
                 multiple=True,
                 header_text=""):
        """
        Create a new SimpleListDialog.
        :param columns:
        :param objects:
        :param hide_cancel_btn:
        :param title:
        :param multiple: if we're allowed to select multiple items
        :type multiple: boolean
        """

        BasicDialog.__init__(self,
                             size=self.size,
                             title=title,
                             header_text=header_text)
        if hide_cancel_btn:
            self.cancel_button.hide()

        if multiple:
            selection_mode = gtk.SELECTION_MULTIPLE
        else:
            selection_mode = gtk.SELECTION_BROWSE
        self.setup_slave(columns, objects, selection_mode)

    def setup_slave(self, columns, objects, selection_mode):
        self.main.remove(self.main_label)
        self._klist = ObjectList(columns, objects, selection_mode)
        self.main.add(self._klist)
        self._klist.show()

    def get_selection(self):
        mode = self._klist.get_selection_mode()
        if mode == gtk.SELECTION_MULTIPLE:
            return self._klist.get_selected_rows()
        selection = self._klist.get_selected()
        if not selection:
            return []
        return [selection]

    #
    #  BasicDialog
    #

    def confirm(self):
        super(SimpleListDialog, self).confirm()
        self.retval = self.retval and self.get_selection()
Пример #5
0
class SimpleListDialog(BasicDialog):
    size = (500, 400)

    def __init__(self, columns, objects, hide_cancel_btn=True,
                 title='', multiple=True, header_text=""):
        """
        Create a new SimpleListDialog.
        :param columns:
        :param objects:
        :param hide_cancel_btn:
        :param title:
        :param multiple: if we're allowed to select multiple items
        :type multiple: boolean
        """

        BasicDialog.__init__(self, size=self.size, title=title,
                             header_text=header_text)
        if hide_cancel_btn:
            self.cancel_button.hide()

        if multiple:
            selection_mode = gtk.SELECTION_MULTIPLE
        else:
            selection_mode = gtk.SELECTION_BROWSE
        self.setup_slave(columns, objects, selection_mode)

    def setup_slave(self, columns, objects, selection_mode):
        self.main.remove(self.main_label)
        self._klist = ObjectList(columns, objects, selection_mode)
        self.main.add(self._klist)
        self._klist.show()

    def get_selection(self):
        mode = self._klist.get_selection_mode()
        if mode == gtk.SELECTION_MULTIPLE:
            return self._klist.get_selected_rows()
        selection = self._klist.get_selected()
        if not selection:
            return []
        return [selection]

    #
    #  BasicDialog
    #

    def confirm(self):
        super(SimpleListDialog, self).confirm()
        self.retval = self.retval and self.get_selection()
Пример #6
0
class Diary(ProxyDelegate):
    def __init__(self):
        self.entries = ObjectList([
            Column("title", width=120, sorted=True),
            Column("period", width=80),
            Column("text", expand=True, visible=False)
        ])
        ProxyDelegate.__init__(self,
                               DiaryEntry(),
                               ['title', 'period', 'text', 'chars', 'words'],
                               gladefile="diary2.ui",
                               delete_handler=self.quit_if_last)
        self.hbox.pack_start(self.entries, True, True, 0)
        self.entries.show()
        self.entries.grab_focus()
        self.set_editable(False)

    def set_editable(self, editable):
        self.leftbox.set_sensitive(editable)
        self.remove.set_sensitive(editable)

    def proxy_updated(self, *args):
        self.entries.update(self.model)

    def on_add__clicked(self, button):
        entry = DiaryEntry()
        entry.title = 'Untitled'
        self.entries.append(entry, select=True)
        self.set_editable(True)

    def on_remove__clicked(self, button):
        entry = self.entries.get_selected()
        if entry:
            self.entries.remove(entry, select=True)

        self.set_editable(len(self.entries) >= 1)
        if not len(self.entries):
            self.set_model(None)

    def on_text__content_changed(self, text):
        self.proxy.update_many(("chars", "words"))
        self.entries.update(self.model)

    def on_entries__selection_changed(self, entries, instance):
        if instance:
            self.set_model(instance)
            self.title.grab_focus()
Пример #7
0
class Diary(ProxyDelegate):
    def __init__(self):
        self.entries = ObjectList([Column("title", width=120, sorted=True),
                                   Column("period", width=80),
                                   Column("text", expand=True, visible=False)])
        ProxyDelegate.__init__(self, DiaryEntry(),
                               ['title', 'period', 'text', 'chars', 'words'],
                               gladefile="diary2.ui",
                               delete_handler=self.quit_if_last)
        self.hbox.pack_start(self.entries, True, True, 0)
        self.entries.show()
        self.entries.grab_focus()
        self.set_editable(False)

    def set_editable(self, editable):
        self.leftbox.set_sensitive(editable)
        self.remove.set_sensitive(editable)

    def proxy_updated(self, *args):
        self.entries.update(self.model)

    def on_add__clicked(self, button):
        entry = DiaryEntry()
        entry.title = 'Untitled'
        self.entries.append(entry, select=True)
        self.set_editable(True)

    def on_remove__clicked(self, button):
        entry = self.entries.get_selected()
        if entry:
            self.entries.remove(entry, select=True)

        self.set_editable(len(self.entries) >= 1)
        if not len(self.entries):
            self.set_model(None)

    def on_text__content_changed(self, text):
        self.proxy.update_many(("chars", "words"))
        self.entries.update(self.model)

    def on_entries__selection_changed(self, entries, instance):
        if instance:
            self.set_model(instance)
            self.title.grab_focus()
Пример #8
0
class DetailsTab(gtk.VBox):
    details_dialog_class = None

    def __init__(self, model, parent):
        super(DetailsTab, self).__init__()

        self.model = model
        self._parent = parent

        self.set_spacing(6)
        self.set_border_width(6)

        self.klist = ObjectList(self.get_columns())
        self.klist.add_list(self.populate())

        self.pack_start(self.klist)
        self.klist.show()

        if len(self.klist) and self.get_details_dialog_class():
            self.button_box = gtk.HButtonBox()
            self.button_box.set_layout(gtk.BUTTONBOX_START)

            details_button = gtk.Button(self.details_lbl)
            self.button_box.pack_start(details_button)
            details_button.set_sensitive(bool(self.klist.get_selected()))
            details_button.show()

            self.pack_end(self.button_box, False, False)
            self.button_box.show()

            self.button_box.details_button = details_button
            details_button.connect('clicked', self._on_details_button__clicked)

            self.klist.connect('row-activated', self._on_klist__row_activated)
            self.klist.connect('selection-changed',
                               self._on_klist__selection_changed)

        self.setup_widgets()

    def refresh(self):
        """Refreshes the list of respective tab."""
        self.klist.clear()
        self.klist.add_list(self.populate())

    def get_columns(self):
        """Returns a list of columns this tab should show."""
        raise NotImplementedError

    def show_details(self):
        """Called when the details button is clicked. Displays the details of
        the selected object in the list."""
        model = self.get_details_model(self.klist.get_selected())
        run_dialog(self.get_details_dialog_class(),
                   parent=self._parent,
                   store=self._parent.store,
                   model=model,
                   visual_mode=True)

    def get_label(self):
        """Returns the name of the tab."""
        label = gtk.Label(self.labels[1])
        return label

    def get_details_model(self, model):
        """Subclassses can overwrite this method if the details dialog class
        needs a model different than the one on the list."""
        return model

    def get_details_dialog_class(self):
        """Subclasses must return the dialog that should be displayed for more
        information about the item on the list"""
        return self.details_dialog_class

    def setup_widgets(self):
        """Override this if tab needs to do some custom widget setup."""

    #
    # Callbacks
    #

    def _on_details_button__clicked(self, button):
        self.show_details()

    def _on_klist__row_activated(self, klist, item):
        self.show_details()

    def _on_klist__selection_changed(self, klist, data):
        self.button_box.details_button.set_sensitive(bool(data))
Пример #9
0
class DetailsTab(gtk.VBox):
    details_dialog_class = None

    def __init__(self, model, parent):
        super(DetailsTab, self).__init__()

        self.model = model
        self._parent = parent

        self.set_spacing(6)
        self.set_border_width(6)

        self.klist = ObjectList(self.get_columns())
        self.klist.add_list(self.populate())

        self.pack_start(self.klist)
        self.klist.show()

        if len(self.klist) and self.get_details_dialog_class():
            self.button_box = gtk.HButtonBox()
            self.button_box.set_layout(gtk.BUTTONBOX_START)

            details_button = gtk.Button(self.details_lbl)
            self.button_box.pack_start(details_button)
            details_button.set_sensitive(bool(self.klist.get_selected()))
            details_button.show()

            self.pack_end(self.button_box, False, False)
            self.button_box.show()

            self.button_box.details_button = details_button
            details_button.connect('clicked', self._on_details_button__clicked)

            self.klist.connect('row-activated', self._on_klist__row_activated)
            self.klist.connect('selection-changed',
                               self._on_klist__selection_changed)

        self.setup_widgets()

    def refresh(self):
        """Refreshes the list of respective tab."""
        self.klist.clear()
        self.klist.add_list(self.populate())

    def get_columns(self):
        """Returns a list of columns this tab should show."""
        raise NotImplementedError

    def show_details(self):
        """Called when the details button is clicked. Displays the details of
        the selected object in the list."""
        model = self.get_details_model(self.klist.get_selected())
        run_dialog(self.get_details_dialog_class(),
                   parent=self._parent,
                   store=self._parent.store,
                   model=model,
                   visual_mode=True)

    def get_label(self):
        """Returns the name of the tab."""
        label = gtk.Label(self.labels[1])
        return label

    def get_details_model(self, model):
        """Subclassses can overwrite this method if the details dialog class
        needs a model different than the one on the list."""
        return model

    def get_details_dialog_class(self):
        """Subclasses must return the dialog that should be displayed for more
        information about the item on the list"""
        return self.details_dialog_class

    def setup_widgets(self):
        """Override this if tab needs to do some custom widget setup."""

    #
    # Callbacks
    #

    def _on_details_button__clicked(self, button):
        self.show_details()

    def _on_klist__row_activated(self, klist, item):
        self.show_details()

    def _on_klist__selection_changed(self, klist, data):
        self.button_box.details_button.set_sensitive(bool(data))
Пример #10
0
class ListContainer(Gtk.Box):
    """A ListContainer is an :class:`ObjectList` with buttons to be able
    to modify the content of the list.
    Depending on the list_mode, @see :class:`set_list_mode` you will
    have add, remove and edit buttons.

    Signals
    =======
      - B{add-item} (returns item):
        - emitted when the add button is clicked, you're expected to
          return an object here
      - B{remove-item} (item, returns bool):
        - emitted when removing an item,
          you can block the removal from the list by returning False
      - B{edit-item} (item):
        - emitted when editing an item
          you can block the update afterwards by returning False

    :ivar add_button: add button
    :type add_button: :class:`Gtk.Button`
    :ivar remove_button: remove button
    :type remove_button: :class:`Gtk.Button`
    :ivar edit_button: edit button
    :type edit_button: :class:`Gtk.Button`
    """

    __gtype_name__ = 'ListContainer'
    gsignal('add-item', retval=object)
    gsignal('remove-item', object, retval=bool)
    gsignal('edit-item', object, retval=bool)
    gsignal('selection-changed', object)

    def __init__(self, columns, orientation=Gtk.Orientation.VERTICAL):
        """
        Create a new ListContainer object.
        :param columns: columns for the :class:`kiwi.ui.objectlist.ObjectList`
        :type columns: a list of :class:`kiwi.ui.objectlist.Columns`
        :param orientation: the position where the buttons will be
            placed: at the right (vertically) or at the bottom (horizontally)
            of the list. Defaults to the right of the list.
        :type: Gtk.Orientation.HORIZONTAL or Gtk.Orientation.VERTICAL
        """
        self._list_type = None

        super(ListContainer, self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self._orientation = orientation

        self._create_ui(columns)
        self.set_list_type(ListType.NORMAL)

    # Private API

    def _create_ui(self, columns):
        self.list = ObjectList(columns)
        self.list.connect('selection-changed',
                          self._on_list__selection_changed)
        self.list.connect('row-activated',
                          self._on_list__row_activated)

        self.add_button = Gtk.Button(stock=Gtk.STOCK_ADD)
        self.add_button.connect('clicked', self._on_add_button__clicked)

        self.remove_button = Gtk.Button(stock=Gtk.STOCK_REMOVE)
        self.remove_button.set_sensitive(False)
        self.remove_button.connect('clicked', self._on_remove_button__clicked)

        self.edit_button = Gtk.Button(stock=Gtk.STOCK_EDIT)
        self.edit_button.set_sensitive(False)
        self.edit_button.connect('clicked', self._on_edit_button__clicked)

        self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)

        if self._orientation == Gtk.Orientation.VERTICAL:
            self.pack_start(self.list, True, True, 0)
            self.list.show()
            self._add_buttons_to_box(self._vbox)
            self._pack_vbox()
        elif self._orientation == Gtk.Orientation.HORIZONTAL:
            self._vbox.pack_start(self.list, True, True, 0)
            self.list.show()
            hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
            self._add_buttons_to_box(hbox)
            self._vbox.pack_start(hbox, False, True, 0)
            hbox.show()
            self._pack_vbox()
        else:
            raise TypeError(
                "buttons_orientation must be Gtk.Orientation.VERTICAL "
                " or Gtk.Orientation.HORIZONTAL")

    def _add_buttons_to_box(self, box):
        box.pack_start(self.add_button, False, True, 0)
        box.pack_start(self.remove_button, False, True, 0)
        box.pack_start(self.edit_button, False, True, 0)

    def _pack_vbox(self):
        self.pack_start(self._vbox, False, True, 6)
        self._vbox.show()

    def _set_child_packing(self, padding):
        expand = self._orientation == Gtk.Orientation.HORIZONTAL

        self.set_child_packing(self._vbox, expand, True, padding,
                               Gtk.PackType.START)

    def _add_item(self):
        retval = self.emit('add-item')
        if retval is None:
            return
        elif isinstance(retval, NotImplementedError):
            raise retval

        self.list.append(retval)
        self.list.refresh()

    def _remove_item(self, item):
        retval = self.emit('remove-item', item)
        if retval:
            self.list.remove(item)

    def _edit_item(self, item):
        retval = self.emit('edit-item', item)
        if retval:
            self.list.update(item)

    # Public API

    def add_item(self, item):
        """Appends an item to the list
        :param item: item to append
        """
        self.list.append(item)

    def add_items(self, items):
        """Appends a list of items to the list
        :param items: items to add
        :type items: a sequence of items
        """
        self.list.extend(items)

    def remove_item(self, item):
        """Removes an item from the list
        :param item: item to remove
        """
        self.list.remove(item)

    def update_item(self, item):
        """Updates an item in the list.
        You should call this if you change the object
        :param item: item to update
        """
        self.list.update(item)

    def default_remove(self, item):
        """Asks the user confirmation for removal of an item.
        :param item: a description of the item that will be removed
        :returns: True if the user confirm the removal, False otherwise
        """
        response = yesno(_('Do you want to remove %s ?') %
                        (GLib.markup_escape_text(str(item)),),
                         parent=None,
                         default=Gtk.ResponseType.OK,
                         buttons=((Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL),
                                  (Gtk.STOCK_REMOVE, Gtk.ResponseType.OK)))
        return response == Gtk.ResponseType.OK

    def set_list_type(self, list_type):
        """Sets the kind of list type.
        :param list_type:
        """
        if not isinstance(list_type, ListType):
            raise TypeError("list_type must be a ListType enum")

        self.add_button.set_property(
            'visible',
            list_type not in [ListType.READONLY, ListType.REMOVEONLY,
                              ListType.UNADDABLE])
        self.remove_button.set_property(
            'visible',
            list_type not in [ListType.READONLY, ListType.ADDONLY,
                              ListType.UNREMOVABLE])
        self.edit_button.set_property(
            'visible',
            list_type not in [ListType.READONLY, ListType.ADDONLY,
                              ListType.UNEDITABLE, ListType.REMOVEONLY])
        if list_type in [ListType.READONLY, ListType.REMOVEONLY]:
            padding = 0
        else:
            padding = 6
        self._set_child_packing(padding)
        self._list_type = list_type

    def clear(self):
        """Removes all the items in the list"""
        self.list.clear()

    # Callbacks

    def _on_list__selection_changed(self, list, selection):
        object_selected = selection is not None
        self.remove_button.set_sensitive(object_selected)
        self.edit_button.set_sensitive(object_selected)
        self.emit('selection-changed', selection)

    def _on_list__row_activated(self, list, item):
        if self._list_type not in [ListType.READONLY, ListType.ADDONLY,
                                   ListType.UNEDITABLE]:
            self._edit_item(item)

    def _on_add_button__clicked(self, button):
        self._add_item()

    def _on_remove_button__clicked(self, button):
        self._remove_item(self.list.get_selected())

    def _on_edit_button__clicked(self, button):
        self._edit_item(self.list.get_selected())
Пример #11
0
class ListContainer(gtk.HBox):
    """A ListContainer is an L{ObjectList} with buttons to be able
    to modify the content of the list.
    Depending on the list_mode, @see L{set_list_mode} you will
    have add, remove and edit buttons.

    Signals
    =======
      - B{add-item} (returns item):
        - emitted when the add button is clicked, you're expected to
          return an object here
      - B{remove-item} (item, returns bool):
        - emitted when removing an item,
          you can block the removal from the list by returning False
      - B{edit-item} (item):
        - emitted when editing an item
          you can block the update afterwards by returning False

    @ivar add_button: add button
    @type add_button: L{gtk.Button}
    @ivar remove_button: remove button
    @type remove_button: L{gtk.Button}
    @ivar edit_button: edit button
    @type edit_button: L{gtk.Button}
    """

    gsignal('add-item', retval=object)
    gsignal('remove-item', object, retval=bool)
    gsignal('edit-item', object, retval=bool)
    gsignal('selection-changed', object)

    def __init__(self, columns, orientation=gtk.ORIENTATION_VERTICAL):
        """
        Create a new ListContainer object.
        @param columns: columns for the L{kiwi.ui.objectlist.ObjectList}
        @type columns: a list of L{kiwi.ui.objectlist.Columns}
        @param orientation: the position where the buttons will be
            placed: at the right (vertically) or at the bottom (horizontally)
            of the list. Defaults to the right of the list.
        @type: gtk.ORIENTATION_HORIZONTAL or gtk.ORIENTATION_VERTICAL
        """
        self._list_type = None

        gtk.HBox.__init__(self)

        self._orientation = orientation

        self._create_ui(columns)
        self.set_list_type(ListType.NORMAL)

    # Private API

    def _create_ui(self, columns):
        self.list = ObjectList(columns)
        self.list.connect('selection-changed',
                          self._on_list__selection_changed)
        self.list.connect('row-activated',
                          self._on_list__row_activated)

        self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
        self.add_button.connect('clicked', self._on_add_button__clicked)

        self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE)
        self.remove_button.set_sensitive(False)
        self.remove_button.connect('clicked', self._on_remove_button__clicked)

        self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT)
        self.edit_button.set_sensitive(False)
        self.edit_button.connect('clicked', self._on_edit_button__clicked)

        self._vbox = gtk.VBox(spacing=6)

        if self._orientation == gtk.ORIENTATION_VERTICAL:
            self.pack_start(self.list)
            self.list.show()
            self._add_buttons_to_box(self._vbox)
            self._pack_vbox()
        elif self._orientation == gtk.ORIENTATION_HORIZONTAL:
            self._vbox.pack_start(self.list)
            self.list.show()
            hbox = gtk.HBox(spacing=6)
            self._add_buttons_to_box(hbox)
            self._vbox.pack_start(hbox, expand=False)
            hbox.show()
            self._pack_vbox()
        else:
            raise TypeError(
                "buttons_orientation must be gtk.ORIENTATION_VERTICAL "
                " or gtk.ORIENTATION_HORIZONTAL")

    def _add_buttons_to_box(self, box):
        box.pack_start(self.add_button, expand=False)
        box.pack_start(self.remove_button, expand=False)
        box.pack_start(self.edit_button, expand=False)

    def _pack_vbox(self):
        self.pack_start(self._vbox, expand=False, padding=6)
        self._vbox.show()

    def _set_child_packing(self, padding):
        expand = self._orientation == gtk.ORIENTATION_HORIZONTAL

        self.set_child_packing(self._vbox, expand, True, padding,
                               gtk.PACK_START)

    def _add_item(self):
        retval = self.emit('add-item')
        if retval is None:
            return
        elif isinstance(retval, NotImplementedError):
            raise retval

        self.list.append(retval)

    def _remove_item(self, item):
        retval = self.emit('remove-item', item)
        if retval:
            self.list.remove(item)

    def _edit_item(self, item):
        retval = self.emit('edit-item', item)
        if retval:
            self.list.update(item)

    # Public API

    def add_item(self, item):
        """Appends an item to the list
        @param item: item to append
        """
        self.list.append(item)

    def add_items(self, items):
        """Appends a list of items to the list
        @param items: items to add
        @type items: a sequence of items
        """
        self.list.extend(items)

    def remove_item(self, item):
        """Removes an item from the list
        @param item: item to remove
        """
        self.list.remove(item)

    def update_item(self, item):
        """Updates an item in the list.
        You should call this if you change the object
        @param item: item to update
        """
        self.list.update(item)

    def default_remove(self, item):
        """Asks the user confirmation for removal of an item.
        @param item: a description of the item that will be removed
        @returns: True if the user confirm the removal, False otherwise
        """
        response = yesno(_('Do you want to remove %s ?') % (quote(str(item)),),
                         parent=None,
                         default=gtk.RESPONSE_OK,
                         buttons=((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
                                  (gtk.STOCK_REMOVE, gtk.RESPONSE_OK)))
        return response == gtk.RESPONSE_OK

    def set_list_type(self, list_type):
        """Sets the kind of list type.
        @param list_type:
        """
        if not isinstance(list_type, ListType):
            raise TypeError("list_type must be a ListType enum")

        self.add_button.set_property(
            'visible',
            (list_type != ListType.READONLY and
             list_type != ListType.REMOVEONLY and
             list_type != ListType.UNADDABLE))
        self.remove_button.set_property(
            'visible',
            (list_type != ListType.READONLY and
             list_type != ListType.UNREMOVABLE))
        self.edit_button.set_property(
            'visible',
            (list_type != ListType.READONLY and
             list_type != ListType.UNEDITABLE and
             list_type != ListType.REMOVEONLY))
        if list_type in [ListType.READONLY, ListType.REMOVEONLY]:
            padding = 0
        else:
            padding = 6
        self._set_child_packing(padding)
        self._list_type = list_type

    def clear(self):
        """Removes all the items in the list"""
        self.list.clear()

    # Callbacks

    def _on_list__selection_changed(self, list, selection):
        object_selected = selection is not None
        self.remove_button.set_sensitive(object_selected)
        self.edit_button.set_sensitive(object_selected)
        self.emit('selection-changed', selection)

    def _on_list__row_activated(self, list, item):
        if (self._list_type != ListType.READONLY and
            self._list_type != ListType.UNEDITABLE):
            self._edit_item(item)

    def _on_add_button__clicked(self, button):
        self._add_item()

    def _on_remove_button__clicked(self, button):
        self._remove_item(self.list.get_selected())

    def _on_edit_button__clicked(self, button):
        self._edit_item(self.list.get_selected())
Пример #12
0
class MethodTest(unittest.TestCase):
    def setUp(self):
        self.klist = ObjectList([Column('name', sorted=True)],
                                [Settable(name='first')])

    def testNonZero(self):
        self.assertEqual(self.klist.__nonzero__(), True)
        self.klist.remove(self.klist[0])
        self.assertEqual(self.klist.__nonzero__(), True)
        if not self.klist:
            raise AssertionError

    def testIter(self):
        for item1 in self.klist:
            pass
        for item2 in iter(self.klist):
            self.assertEqual(item1, item2)

    def testGetItem(self):
        self.klist.append(Settable(name='second'))
        model = self.klist.get_model()
        item1 = model[0][0]
        item2 = model[1][0]
        self.assertEqual(self.klist[0], item1)
        self.assertEqual(self.klist[:1], [item1])
        self.assertEqual(self.klist[-1:], [item2])
        self.assertRaises(TypeError, self.klist.__getitem__, None)

    def testSetItem(self):
        self.klist[0] = Settable(name='second')
        self.assertRaises(NotImplementedError, self.klist.__setitem__,
                          slice(0), None)
        self.assertRaises(TypeError, self.klist.__setitem__, None, None)

    def testIndex(self):
        self.assertRaises(NotImplementedError, self.klist.index, 0, start=0)
        self.assertRaises(NotImplementedError, self.klist.index, 0, stop=0)

        self.assertEqual(self.klist.index(self.klist[0]), 0)
        self.assertRaises(ValueError, self.klist.index, None)

    def testCount(self):
        item = self.klist[0]
        self.assertEqual(self.klist.count(item), 1)
        self.klist.append(item)
        self.assertEqual(self.klist.count(item), 2)
        self.klist.clear()
        self.assertEqual(self.klist.count(item), 0)

    def testPop(self):
        self.assertRaises(NotImplementedError, self.klist.pop, None)

    def testReverse(self):
        self.assertRaises(NotImplementedError, self.klist.reverse, 1, 2)

    def testSort(self):
        self.assertRaises(NotImplementedError, self.klist.sort, 1, 2)

    def testSelectPath(self):
        self.klist.get_treeview().get_selection().set_mode(
            Gtk.SelectionMode.NONE)
        self.assertRaises(TypeError, self.klist.select_paths, (0, ))
        self.klist.get_treeview().get_selection().set_mode(
            Gtk.SelectionMode.SINGLE)
        self.klist.select_paths((0, ))

    def testSelect(self):
        self.klist.get_treeview().get_selection().set_mode(
            Gtk.SelectionMode.NONE)
        self.assertRaises(TypeError, self.klist.select, None)
        self.klist.get_treeview().get_selection().set_mode(
            Gtk.SelectionMode.SINGLE)

    def testGetSelected(self):
        item = self.klist[0]
        self.klist.select(item)
        self.klist.get_treeview().get_selection().set_mode(
            Gtk.SelectionMode.SINGLE)
        self.assertEqual(self.klist.get_selected(), item)

    def testGetSelectedRows(self):
        self.klist.get_treeview().get_selection().set_mode(
            Gtk.SelectionMode.MULTIPLE)
        item = self.klist[0]
        self.klist.select(item)
        self.assertEqual(self.klist.get_selected_rows(), [item])

    def testGetNextAndPrevious(self):
        self.klist.append(Settable(name='second'))
        self.klist.append(Settable(name='third'))
        item1, item2, item3 = self.klist

        self.assertEqual(self.klist.get_next(item1), item2)
        self.assertEqual(self.klist.get_next(item2), item3)
        self.assertEqual(self.klist.get_next(item3), item1)
        self.assertRaises(ValueError, self.klist.get_next, None)

        self.assertEqual(self.klist.get_previous(item1), item3)
        self.assertEqual(self.klist.get_previous(item2), item1)
        self.assertEqual(self.klist.get_previous(item3), item2)
        self.assertRaises(ValueError, self.klist.get_previous, None)

    def testInsert(self):
        self.klist = ObjectList([Column('name')])
        self.assertEqual(list(self.klist), [])

        self.klist.insert(0, Settable(name='one'))
        self.assertEqual(self.klist[0].name, 'one')

        self.klist.insert(0, Settable(name='two'))
        self.assertEqual(self.klist[0].name, 'two')
        self.assertEqual(self.klist[1].name, 'one')

        self.klist.insert(1, Settable(name='three'))
        self.assertEqual(self.klist[0].name, 'two')
        self.assertEqual(self.klist[1].name, 'three')
        self.assertEqual(self.klist[2].name, 'one')
Пример #13
0
class PluginManagerDialog(BasicDialog):
    size = (500, 350)
    title = _(u'Plugin Manager')
    help_section = 'plugin'

    def __init__(self, store):
        header = _(u'Select the plugin you want to activate and click in '
                   'the apply button.')
        BasicDialog.__init__(self,
                             hide_footer=False,
                             size=PluginManagerDialog.size,
                             title=PluginManagerDialog.title,
                             header_text=header)
        self.store = store
        self._manager = get_plugin_manager()
        self._setup_widgets()

    def _update_widgets(self):
        selected = self.klist.get_selected()
        assert selected

        self.ok_button.set_sensitive(selected.can_activate())

    def _setup_widgets(self):
        self.set_ok_label(_(u'Activate'), gtk.STOCK_APPLY)
        self.ok_button.set_sensitive(False)
        plugins = []

        for name in sorted(self._manager.available_plugins_names):
            # FIXME: Remove when magento plugin is functional for end users
            if not is_developer_mode() and name == 'magento':
                continue
            if platform.system() == 'Windows':
                if name in ['ecf', 'tef']:
                    continue

            desc = self._manager.get_description_by_name(name)
            plugins.append(
                _PluginModel(name, name
                             in self._manager.installed_plugins_names, desc))

        self.klist = ObjectList(self._get_columns(), plugins,
                                gtk.SELECTION_BROWSE)
        self.klist.set_headers_visible(False)
        self.klist.connect("selection-changed",
                           self._on_klist__selection_changed)
        self.main.remove(self.main.get_child())
        self.main.add(self.klist)
        self.klist.show()

    def _get_columns(self):
        return [
            Column('is_active', title=_('Active'), width=20, data_type=bool),
            Column('icon',
                   data_type=str,
                   width=24,
                   use_stock=True,
                   icon_size=gtk.ICON_SIZE_BUTTON),
            Column('description', data_type=str, expand=True, use_markup=True)
        ]

    def _enable_plugin(self, plugin_model):
        plugin_name = plugin_model.name
        # This should not really be necessary, but there may be deadlocks when
        # activating the plugin. See bug 5272
        default_store = get_default_store()
        default_store.commit()
        self._manager.install_plugin(plugin_name)
        self._manager.activate_plugin(plugin_name)

    #
    # BasicDialog
    #

    def confirm(self):
        msg = _("Are you sure you want activate this plugin?\n"
                "Please note that, once activated you will not "
                "be able to disable it.")
        response = yesno(msg, gtk.RESPONSE_NO, _("Activate plugin"),
                         _("Not now"))

        if response:
            self._enable_plugin(self.klist.get_selected())
            self.close()

    #
    # Callbacks
    #

    def _on_klist__selection_changed(self, list, data):
        self._update_widgets()
Пример #14
0
class PluginManagerDialog(BasicDialog):
    size = (500, 350)
    title = _(u'Plugin Manager')
    help_section = 'plugin'

    def __init__(self, store):
        header = _(u'Select the plugin you want to activate and click in '
                    'the apply button.')
        BasicDialog.__init__(self, hide_footer=False,
                             size=PluginManagerDialog.size,
                             title=PluginManagerDialog.title,
                             header_text=header)
        self.store = store
        self._manager = get_plugin_manager()
        self._setup_widgets()

    def _update_widgets(self):
        selected = self.klist.get_selected()
        assert selected

        self.ok_button.set_sensitive(selected.can_activate())

    def _setup_widgets(self):
        self.set_ok_label(_(u'Activate'), gtk.STOCK_APPLY)
        self.ok_button.set_sensitive(False)
        plugins = []

        for name in sorted(self._manager.available_plugins_names):
            # FIXME: Remove when magento plugin is functional for end users
            if not is_developer_mode() and name == 'magento':
                continue
            if platform.system() == 'Windows':
                if name in ['ecf', 'tef']:
                    continue

            desc = self._manager.get_description_by_name(name)
            plugins.append(_PluginModel(name, name in
                                        self._manager.installed_plugins_names,
                                        desc))

        self.klist = ObjectList(self._get_columns(), plugins,
                                gtk.SELECTION_BROWSE)
        self.klist.set_headers_visible(False)
        self.klist.connect("selection-changed",
                           self._on_klist__selection_changed)
        self.main.remove(self.main.get_child())
        self.main.add(self.klist)
        self.klist.show()

    def _get_columns(self):
        return [Column('is_active', title=_('Active'), width=20, data_type=bool),
                Column('icon', data_type=str, width=24, use_stock=True,
                       icon_size=gtk.ICON_SIZE_BUTTON),
                Column('description', data_type=str, expand=True,
                       use_markup=True)]

    def _enable_plugin(self, plugin_model):
        plugin_name = plugin_model.name
        # This should not really be necessary, but there may be deadlocks when
        # activating the plugin. See bug 5272
        default_store = get_default_store()
        default_store.commit()
        self._manager.install_plugin(plugin_name)
        self._manager.activate_plugin(plugin_name)

    #
    # BasicDialog
    #

    def confirm(self):
        msg = _("Are you sure you want activate this plugin?\n"
                "Please note that, once activated you will not "
                "be able to disable it.")
        response = yesno(msg, gtk.RESPONSE_NO,
                         _("Activate plugin"), _("Not now"))

        if response:
            self._enable_plugin(self.klist.get_selected())
            self.close()

    #
    # Callbacks
    #

    def _on_klist__selection_changed(self, list, data):
        self._update_widgets()
Пример #15
0
class ShortcutsEditor(BasicDialog):
    size = (700, 400)
    title = _("Keyboard shortcuts")

    def __init__(self):
        BasicDialog.__init__(self, size=ShortcutsEditor.size,
                             title=ShortcutsEditor.title)
        self._create_ui()

    def _create_ui(self):
        self.cancel_button.hide()

        hbox = gtk.HBox(spacing=6)
        self.main.remove(self.main.get_child())
        self.main.add(hbox)
        hbox.show()

        self.categories = ObjectList(
            [Column('label', sorted=True, expand=True)],
            get_binding_categories(),
            gtk.SELECTION_BROWSE)
        self.categories.connect('selection-changed',
                                self._on_categories__selection_changed)
        self.categories.set_headers_visible(False)
        self.categories.set_size_request(200, -1)
        hbox.pack_start(self.categories, False, False)
        self.categories.show()

        box = gtk.VBox(spacing=6)
        hbox.pack_start(box)
        box.show()

        self.shortcuts = ObjectList(self._get_columns(), [],
                                    gtk.SELECTION_BROWSE)
        box.pack_start(self.shortcuts)
        self.shortcuts.show()

        self._label = gtk.Label(
            _("You need to restart Stoq for the changes to take effect"))
        box.pack_start(self._label, False, False, 6)

        box.show()

        defaults_button = gtk.Button(_("Reset defaults"))
        defaults_button.connect('clicked', self._on_defaults_button__clicked)
        self.action_area.pack_start(defaults_button, False, False, 6)
        self.action_area.reorder_child(defaults_button, 0)
        defaults_button.show()

    def _on_categories__selection_changed(self, categories, category):
        if not category:
            return
        self.shortcuts.add_list(get_bindings(category.name), clear=True)

    def _on_defaults_button__clicked(self, button):
        old = self.categories.get_selected()
        api.user_settings.remove('shortcuts')
        remove_user_bindings()
        self._label.show()
        self.categories.refresh()
        self.categories.select(old)

    def _get_columns(self):
        return [Column('description', _("Description"), data_type=str,
                       expand=True, sorted=True),
                ShortcutColumn('shortcut', _("Shortcut"), self)]

    def set_binding(self, binding):
        set_user_binding(binding.name, binding.shortcut)
        d = api.user_settings.get('shortcuts', {})
        d[binding.name] = binding.shortcut
        self._label.show()

    def remove_binding(self, binding):
        remove_user_binding(binding.name)
        d = api.user_settings.get('shortcuts', {})
        try:
            del d[binding.name]
        except KeyError:
            pass
        self._label.show()
Пример #16
0
class DataTests(unittest.TestCase):
    """In all this tests we use the same configuration for a list"""
    def setUp(self):
        self.win = gtk.Window()
        self.win.set_default_size(400, 400)
        self.list = ObjectList([Column('name'), Column('age')])
        self.win.add(self.list)
        refresh_gui()

    def tearDown(self):
        self.win.destroy()
        del self.win

    def testAddingOneInstance(self):
        # we should have two columns now
        self.assertEqual(2, len(self.list.get_columns()))

        person = Person('henrique', 21)
        self.list.append(person)

        refresh_gui()

        # usually you don't use the model directly, but tests are all about
        # breaking APIs, right?
        self.assertEqual(self.list[0], person)
        self.assertEqual(self.list[0].name, 'henrique')
        self.assertEqual(self.list[0].age, 21)

        # we still have to columns, right?
        self.assertEqual(2, len(self.list.get_columns()))

    def testAddingAObjectList(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        self.assertEqual(len(self.list), len(persons))

    def testAddingABunchOfInstances(self):
        global persons

        for person in persons:
            self.list.append(person)
            refresh_gui()

        self.assertEqual(len(self.list), len(persons))

    def testRemovingOneInstance(self):
        global  persons

        self.list.add_list(persons)
        refresh_gui()

        # we are going to remove Kiko
        person = persons[2]

        self.list.remove(person)

        self.assertEqual(len(self.list), len(persons) - 1)

        # now let's remove something that is not on the list
        #new_person = Person('Evandro', 24)
        #self.assertRaises(ValueError, self.list.remove, new_person)

        # note that even a new person with the same values as a person
        # in the list is not considered to be in the list
        #existing_person = Person('Gustavo', 25)
        #self.assertRaises(ValueError, self.list.remove,
        #                  existing_person)

    def testClearObjectList(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        self.list.clear()

        self.assertEqual(len(self.list), 0)


    def testUpdatingOneInstance(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        persons[0].age = 29
        self.list.update(persons[0])

        refresh_gui()

        # Do we have the same number of instances that we had before ?
        self.assertEqual(len(self.list), len(persons))

        # Trying to find our updated instance in the list
        self.assertEqual(self.list[0].age, 29)

        # let's be evil
        new_person = Person('Nando', 32)
        self.assertRaises(ValueError, self.list.update, new_person)


    def testContains(self):
        global persons

        self.list.add_list(persons)
        self.assertEqual(persons[0] in self.list, True)

        new_person = Person('Nando', 32)
        self.assertEqual(new_person in self.list, False)

    def testSelect(self):
        first = persons[0]
        self.list.add_list(persons)
        self.list.select(first)
        self.assertEqual(self.list.get_selected(), first)

        self.list.remove(first)
        self.assertRaises(ValueError, self.list.select, first)
Пример #17
0
class ListContainer(gtk.HBox):
    """A ListContainer is an L{ObjectList} with buttons to be able
    to modify the content of the list.
    Depending on the list_mode, @see L{set_list_mode} you will
    have add, remove and edit buttons.

    Signals
    =======
      - B{add-item} (returns item):
        - emitted when the add button is clicked, you're expected to
          return an object here
      - B{remove-item} (item, returns bool):
        - emitted when removing an item,
          you can block the removal from the list by returning False
      - B{edit-item} (item):
        - emitted when editing an item
          you can block the update afterwards by returning False

    @ivar add_button: add button
    @type add_button: L{gtk.Button}
    @ivar remove_button: remove button
    @type remove_button: L{gtk.Button}
    @ivar edit_button: edit button
    @type edit_button: L{gtk.Button}
    """

    gsignal('add-item', retval=object)
    gsignal('remove-item', object, retval=bool)
    gsignal('edit-item', object, retval=bool)
    gsignal('selection-changed', object)

    def __init__(self, columns, orientation=gtk.ORIENTATION_VERTICAL):
        """
        Create a new ListContainer object.
        @param columns: columns for the L{kiwi.ui.objectlist.ObjectList}
        @type columns: a list of L{kiwi.ui.objectlist.Columns}
        @param orientation: the position where the buttons will be
            placed: at the right (vertically) or at the bottom (horizontally)
            of the list. Defaults to the right of the list.
        @type: gtk.ORIENTATION_HORIZONTAL or gtk.ORIENTATION_VERTICAL
        """
        self._list_type = None

        gtk.HBox.__init__(self)

        self._orientation = orientation

        self._create_ui(columns)
        self.set_list_type(ListType.NORMAL)

    # Private API

    def _create_ui(self, columns):
        self.list = ObjectList(columns)
        self.list.connect('selection-changed',
                          self._on_list__selection_changed)
        self.list.connect('row-activated', self._on_list__row_activated)

        self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
        self.add_button.connect('clicked', self._on_add_button__clicked)

        self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE)
        self.remove_button.set_sensitive(False)
        self.remove_button.connect('clicked', self._on_remove_button__clicked)

        self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT)
        self.edit_button.set_sensitive(False)
        self.edit_button.connect('clicked', self._on_edit_button__clicked)

        self._vbox = gtk.VBox(spacing=6)

        if self._orientation == gtk.ORIENTATION_VERTICAL:
            self.pack_start(self.list)
            self.list.show()
            self._add_buttons_to_box(self._vbox)
            self._pack_vbox()
        elif self._orientation == gtk.ORIENTATION_HORIZONTAL:
            self._vbox.pack_start(self.list)
            self.list.show()
            hbox = gtk.HBox(spacing=6)
            self._add_buttons_to_box(hbox)
            self._vbox.pack_start(hbox, expand=False)
            hbox.show()
            self._pack_vbox()
        else:
            raise TypeError(
                "buttons_orientation must be gtk.ORIENTATION_VERTICAL "
                " or gtk.ORIENTATION_HORIZONTAL")

    def _add_buttons_to_box(self, box):
        box.pack_start(self.add_button, expand=False)
        box.pack_start(self.remove_button, expand=False)
        box.pack_start(self.edit_button, expand=False)

    def _pack_vbox(self):
        self.pack_start(self._vbox, expand=False, padding=6)
        self._vbox.show()

    def _set_child_packing(self, padding):
        expand = self._orientation == gtk.ORIENTATION_HORIZONTAL

        self.set_child_packing(self._vbox, expand, True, padding,
                               gtk.PACK_START)

    def _add_item(self):
        retval = self.emit('add-item')
        if retval is None:
            return
        elif isinstance(retval, NotImplementedError):
            raise retval

        self.list.append(retval)

    def _remove_item(self, item):
        retval = self.emit('remove-item', item)
        if retval:
            self.list.remove(item)

    def _edit_item(self, item):
        retval = self.emit('edit-item', item)
        if retval:
            self.list.update(item)

    # Public API

    def add_item(self, item):
        """Appends an item to the list
        @param item: item to append
        """
        self.list.append(item)

    def add_items(self, items):
        """Appends a list of items to the list
        @param items: items to add
        @type items: a sequence of items
        """
        self.list.extend(items)

    def remove_item(self, item):
        """Removes an item from the list
        @param item: item to remove
        """
        self.list.remove(item)

    def update_item(self, item):
        """Updates an item in the list.
        You should call this if you change the object
        @param item: item to update
        """
        self.list.update(item)

    def default_remove(self, item):
        """Asks the user confirmation for removal of an item.
        @param item: a description of the item that will be removed
        @returns: True if the user confirm the removal, False otherwise
        """
        response = yesno(_('Do you want to remove %s ?') %
                         (quote(str(item)), ),
                         parent=None,
                         default=gtk.RESPONSE_OK,
                         buttons=((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
                                  (gtk.STOCK_REMOVE, gtk.RESPONSE_OK)))
        return response == gtk.RESPONSE_OK

    def set_list_type(self, list_type):
        """Sets the kind of list type.
        @param list_type:
        """
        if not isinstance(list_type, ListType):
            raise TypeError("list_type must be a ListType enum")

        self.add_button.set_property('visible',
                                     (list_type != ListType.READONLY
                                      and list_type != ListType.REMOVEONLY
                                      and list_type != ListType.UNADDABLE))
        self.remove_button.set_property(
            'visible', (list_type != ListType.READONLY
                        and list_type != ListType.UNREMOVABLE))
        self.edit_button.set_property('visible',
                                      (list_type != ListType.READONLY
                                       and list_type != ListType.UNEDITABLE
                                       and list_type != ListType.REMOVEONLY))
        if list_type in [ListType.READONLY, ListType.REMOVEONLY]:
            padding = 0
        else:
            padding = 6
        self._set_child_packing(padding)
        self._list_type = list_type

    def clear(self):
        """Removes all the items in the list"""
        self.list.clear()

    # Callbacks

    def _on_list__selection_changed(self, list, selection):
        object_selected = selection is not None
        self.remove_button.set_sensitive(object_selected)
        self.edit_button.set_sensitive(object_selected)
        self.emit('selection-changed', selection)

    def _on_list__row_activated(self, list, item):
        if (self._list_type != ListType.READONLY
                and self._list_type != ListType.UNEDITABLE):
            self._edit_item(item)

    def _on_add_button__clicked(self, button):
        self._add_item()

    def _on_remove_button__clicked(self, button):
        self._remove_item(self.list.get_selected())

    def _on_edit_button__clicked(self, button):
        self._edit_item(self.list.get_selected())
Пример #18
0
class DataTests(unittest.TestCase):
    """In all this tests we use the same configuration for a list"""
    def setUp(self):
        self.win = Gtk.Window()
        self.win.set_default_size(400, 400)
        self.list = ObjectList([Column('name'), Column('age')])
        self.win.add(self.list)
        refresh_gui()

    def tearDown(self):
        self.win.destroy()
        del self.win

    def testAddingOneInstance(self):
        # we should have two columns now
        self.assertEqual(2, len(self.list.get_columns()))

        person = Person('henrique', 21)
        self.list.append(person)

        refresh_gui()

        # usually you don't use the model directly, but tests are all about
        # breaking APIs, right?
        self.assertEqual(self.list[0], person)
        self.assertEqual(self.list[0].name, 'henrique')
        self.assertEqual(self.list[0].age, 21)

        # we still have to columns, right?
        self.assertEqual(2, len(self.list.get_columns()))

    def testAddingAObjectList(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        self.assertEqual(len(self.list), len(persons))

    def testAddingABunchOfInstances(self):
        global persons

        for person in persons:
            self.list.append(person)
            refresh_gui()

        self.assertEqual(len(self.list), len(persons))

    def testRemovingOneInstance(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        # we are going to remove Kiko
        person = persons[2]

        self.list.remove(person)

        self.assertEqual(len(self.list), len(persons) - 1)

        # now let's remove something that is not on the list
        #new_person = Person('Evandro', 24)
        #self.assertRaises(ValueError, self.list.remove, new_person)

        # note that even a new person with the same values as a person
        # in the list is not considered to be in the list
        #existing_person = Person('Gustavo', 25)
        #self.assertRaises(ValueError, self.list.remove,
        #                  existing_person)

    def testClearObjectList(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        self.list.clear()

        self.assertEqual(len(self.list), 0)

    def testUpdatingOneInstance(self):
        global persons

        self.list.add_list(persons)
        refresh_gui()

        persons[0].age = 29
        self.list.update(persons[0])

        refresh_gui()

        # Do we have the same number of instances that we had before ?
        self.assertEqual(len(self.list), len(persons))

        # Trying to find our updated instance in the list
        self.assertEqual(self.list[0].age, 29)

        # let's be evil
        new_person = Person('Nando', 32)
        self.assertRaises(ValueError, self.list.update, new_person)

    def testContains(self):
        global persons

        self.list.add_list(persons)
        self.assertEqual(persons[0] in self.list, True)

        new_person = Person('Nando', 32)
        self.assertEqual(new_person in self.list, False)

    def testSelect(self):
        first = persons[0]
        self.list.add_list(persons)
        self.list.select(first)
        self.assertEqual(self.list.get_selected(), first)

        self.list.remove(first)
        self.assertRaises(ValueError, self.list.select, first)
Пример #19
0
class PluginManagerDialog(BasicDialog):
    size = (500, 350)
    title = _(u'Plugin Manager')
    help_section = 'plugin'

    def __init__(self, store):
        header = _(u'Select the plugin you want to activate and click in '
                   'the apply button.')
        BasicDialog.__init__(self, hide_footer=False,
                             size=PluginManagerDialog.size,
                             title=PluginManagerDialog.title,
                             header_text=header)
        self.store = store
        self._manager = get_plugin_manager()
        self._setup_widgets()

    def _update_widgets(self):
        selected = self.klist.get_selected()
        assert selected

        self.ok_button.set_sensitive(selected.can_activate())

    def _setup_widgets(self):
        self.set_ok_label(_(u'Activate'), Gtk.STOCK_APPLY)
        self.ok_button.set_sensitive(False)
        plugins = []

        for name in sorted(self._manager.available_plugins_names):
            desc = self._manager.get_description_by_name(name)
            plugins.append(_PluginModel(name, name in
                                        self._manager.installed_plugins_names,
                                        desc))

        self.klist = ObjectList(self._get_columns(), plugins,
                                Gtk.SelectionMode.BROWSE)
        self.klist.set_headers_visible(False)
        self.klist.connect("selection-changed",
                           self._on_klist__selection_changed)
        self.main.remove(self.main.get_child())
        self.main.add(self.klist)
        self.klist.show()

    def _get_columns(self):
        return [Column('is_active', title=_('Active'), width=20, data_type=bool),
                Column('icon', data_type=str, width=24, use_stock=True,
                       icon_size=Gtk.IconSize.BUTTON),
                Column('description', data_type=str, expand=True,
                       use_markup=True)]

    def _enable_plugin(self, plugin_model):
        plugin_name = plugin_model.name
        # This should not really be necessary, but there may be deadlocks when
        # activating the plugin. See bug 5272
        default_store = get_default_store()
        default_store.commit()
        with new_store() as store:
            self._manager.install_plugin(store, plugin_name)
        self._manager.activate_plugin(plugin_name)

        info(_("The plugin %s was successfully activated. Please, restart all "
               "Stoq instances connected to this installation.") % (plugin_name, ))

    #
    # BasicDialog
    #

    def confirm(self):
        msg = _("Are you sure you want activate this plugin?\n"
                "Please note that, once activated you will not "
                "be able to disable it.")
        response = yesno(msg, Gtk.ResponseType.NO,
                         _("Activate plugin"), _("Not now"))

        if response:
            self._enable_plugin(self.klist.get_selected())
            self.close()

    #
    # Callbacks
    #

    def _on_klist__selection_changed(self, list, data):
        self._update_widgets()
Пример #20
0
class PaymentMethodsDialog(BasicDialog):
    # TODO Bug 2406 will avoid duplicating code here
    size = (400, 400)
    title = _("Payment Method Settings")

    # TODO: implement editor for 'multiple' payment method.
    METHOD_EDITORS = {u'card': CardPaymentMethodEditor,
                      u'money': PaymentMethodEditor,
                      u'check': PaymentMethodEditor,
                      u'credit': PaymentMethodEditor,
                      u'bill': PaymentMethodEditor,
                      u'deposit': PaymentMethodEditor,
                      u'store_credit': PaymentMethodEditor}

    def __init__(self, store):
        BasicDialog.__init__(self,
                             hide_footer=True, size=PaymentMethodsDialog.size,
                             title=PaymentMethodsDialog.title)
        self._can_edit = False
        self.store = store
        self._setup_list()
        self._setup_slaves()

    def _setup_slaves(self):
        self._toolbar_slave = SearchEditorToolBar()
        self._toolbar_slave.connect("edit", self._on_edit_button__clicked)
        self._toolbar_slave.new_button.hide()
        self._toolbar_slave.edit_button.set_sensitive(False)
        self.attach_slave("extra_holder", self._toolbar_slave)

    def _setup_list(self):
        methods = PaymentMethod.get_editable_methods(self.store)
        self.klist = ObjectList(self._get_columns(), methods,
                                Gtk.SelectionMode.BROWSE)
        self.klist.connect("selection-changed",
                           self._on_klist__selection_changed)
        self.klist.connect("row-activated", self._on_klist__row_activated)
        self.klist.connect("cell-edited", self.on_cell_edited)
        self.main.remove(self.main.get_child())
        self.main.add(self.klist)
        self.klist.show()

    def _get_columns(self):
        return [Column('description', title=_('Payment Method'), data_type=str,
                       expand=True),
                Column('is_active', title=_('Active'), data_type=bool,
                       editable=True)]

    def _edit_item(self, item):
        editor = self.METHOD_EDITORS.get(item.method_name, None)

        if not editor:
            raise TypeError('Invalid payment method adapter: %s'
                            % item.method_name)

        store = api.new_store()
        item = store.fetch(item)
        retval = run_dialog(editor, self, store, item)
        store.confirm(retval)
        store.close()

    #
    # Callbacks
    #

    def on_cell_edited(self, klist, obj, attr):
        # All the payment methods could be (de)activate, except the 'money'
        # payment method.
        if obj.method_name != u'money':
            store = obj.store
            store.commit()
        else:
            obj.is_active = True

    def _on_klist__selection_changed(self, list, data):
        self._can_edit = (data and
                          data.method_name in self.METHOD_EDITORS.keys())
        self._toolbar_slave.edit_button.set_sensitive(self._can_edit)

    def _on_edit_button__clicked(self, toolbar_slave):
        assert self._can_edit
        self._edit_item(self.klist.get_selected())

    def _on_klist__row_activated(self, list, data):
        if not self._can_edit:
            return

        self._edit_item(data)
Пример #21
0
class ShortcutsEditor(BasicDialog):
    size = (700, 400)
    title = _("Keyboard shortcuts")

    def __init__(self):
        BasicDialog.__init__(self,
                             size=ShortcutsEditor.size,
                             title=ShortcutsEditor.title)
        self._create_ui()

    def _create_ui(self):
        self.cancel_button.hide()

        hbox = gtk.HBox(spacing=6)
        self.main.remove(self.main.get_child())
        self.main.add(hbox)
        hbox.show()

        self.categories = ObjectList(
            [Column('label', sorted=True, expand=True)],
            get_binding_categories(), gtk.SELECTION_BROWSE)
        self.categories.connect('selection-changed',
                                self._on_categories__selection_changed)
        self.categories.set_headers_visible(False)
        self.categories.set_size_request(200, -1)
        hbox.pack_start(self.categories, False, False)
        self.categories.show()

        box = gtk.VBox(spacing=6)
        hbox.pack_start(box)
        box.show()

        self.shortcuts = ObjectList(self._get_columns(), [],
                                    gtk.SELECTION_BROWSE)
        box.pack_start(self.shortcuts)
        self.shortcuts.show()

        self._label = gtk.Label(
            _("You need to restart Stoq for the changes to take effect"))
        box.pack_start(self._label, False, False, 6)

        box.show()

        defaults_button = gtk.Button(_("Reset defaults"))
        defaults_button.connect('clicked', self._on_defaults_button__clicked)
        self.action_area.pack_start(defaults_button, False, False, 6)
        self.action_area.reorder_child(defaults_button, 0)
        defaults_button.show()

    def _on_categories__selection_changed(self, categories, category):
        if not category:
            return
        self.shortcuts.add_list(get_bindings(category.name), clear=True)

    def _on_defaults_button__clicked(self, button):
        old = self.categories.get_selected()
        api.user_settings.remove('shortcuts')
        remove_user_bindings()
        self._label.show()
        self.categories.refresh()
        self.categories.select(old)

    def _get_columns(self):
        return [
            Column('description',
                   _("Description"),
                   data_type=str,
                   expand=True,
                   sorted=True),
            ShortcutColumn('shortcut', _("Shortcut"), self)
        ]

    def set_binding(self, binding):
        set_user_binding(binding.name, binding.shortcut)
        d = api.user_settings.get('shortcuts', {})
        d[binding.name] = binding.shortcut
        self._label.show()

    def remove_binding(self, binding):
        remove_user_binding(binding.name)
        d = api.user_settings.get('shortcuts', {})
        try:
            del d[binding.name]
        except KeyError:
            pass
        self._label.show()
Пример #22
0
class PluginManagerDialog(BasicDialog):
    size = (500, 350)
    title = _(u'Plugin Manager')
    help_section = 'plugin'

    def __init__(self, store):
        header = _(u'Select the plugin you want to activate and click in '
                   'the apply button.')
        BasicDialog.__init__(self,
                             hide_footer=False,
                             size=PluginManagerDialog.size,
                             title=PluginManagerDialog.title,
                             header_text=header)
        self.store = store
        self._manager = get_plugin_manager()
        self._setup_widgets()

    def _update_widgets(self):
        selected = self.klist.get_selected()
        self.ok_button.set_sensitive(selected and selected.can_activate())

    def _setup_widgets(self):
        self.set_ok_label(_(u'Activate'), Gtk.STOCK_APPLY)
        self.ok_button.set_sensitive(False)
        plugins = []

        for name in sorted(self._manager.available_plugins_names):
            desc = self._manager.get_description_by_name(name)
            plugins.append(
                _PluginModel(name, name
                             in self._manager.installed_plugins_names, desc))

        self.klist = ObjectList(self._get_columns(), plugins,
                                Gtk.SelectionMode.BROWSE)
        self.klist.set_headers_visible(False)
        self.klist.connect("selection-changed",
                           self._on_klist__selection_changed)
        self.main.remove(self.main.get_child())
        self.main.add(self.klist)
        self.klist.show()

    def _get_columns(self):
        return [
            Column('is_active', title=_('Active'), width=20, data_type=bool),
            Column('icon',
                   data_type=str,
                   width=24,
                   use_stock=True,
                   icon_size=Gtk.IconSize.BUTTON),
            Column('description', data_type=str, expand=True, use_markup=True)
        ]

    def _enable_plugin(self, plugin_model):
        plugin_name = plugin_model.name
        # This should not really be necessary, but there may be deadlocks when
        # activating the plugin. See bug 5272
        default_store = get_default_store()
        default_store.commit()
        with new_store() as store:
            self._manager.install_plugin(store, plugin_name)
        self._manager.activate_plugin(plugin_name)

        info(
            _("The plugin %s was successfully activated. Please, restart all "
              "Stoq instances connected to this installation.") %
            (plugin_name, ))

    #
    # BasicDialog
    #

    def confirm(self):
        msg = _("Are you sure you want activate this plugin?\n"
                "Please note that, once activated you will not "
                "be able to disable it.")
        response = yesno(msg, Gtk.ResponseType.NO, _("Activate plugin"),
                         _("Not now"))

        if response:
            self._enable_plugin(self.klist.get_selected())
            self.close()

    #
    # Callbacks
    #

    def _on_klist__selection_changed(self, list, data):
        self._update_widgets()
Пример #23
0
class search_results_window:
    """A results window for a searching for a recipe"""

    def __init__ (self, searchline="", mode=gtk.SELECTION_SINGLE):
        """Search for the name like searchstring"""
        self.window=gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("destroy", self.exit)
        self.window.set_size_request(600, 250)
        self.vbox=gtk.VBox(homogeneous=False,spacing=0)
        self.window.add(self.vbox)

        self.cur=con.cursor()
        if searchline.count(':') !=0:
            search_options=dict(self.parse_search_string(searchline))
            print search_options
            results=[[5, 'Foo2', 'yummy food'], 
                     [6, 'Foobar', 'best foobar evar!']]
        else:
            searchline="%" + searchline + "%"
            self.cur.execute("""SELECT recipe_id,name,description FROM recipes WHERE name LIKE %s""", (searchline,))
            results=self.cur.fetchall()

        my_columns = [  Column("name", title="Name", sorted=True),
                        Column("desc", title="Description")
                     ]
        self.objectlist = ObjectList(my_columns, mode=mode)
        self.objectlist.set_size_request(600,225)
        recipes=[RecipeInfo(x) for x in results]
        self.objectlist.add_list(recipes)
        self.b=gtk.Button("Show Selected")
        self.b.connect("clicked", self.show_recipe2)
        self.vbox.add(self.objectlist)
        self.vbox.add(self.b)

        self.window.show_all()

    def parse_search_string(self,s):
        foo,indexes=findall(':',s)
        p=0
        l=[]
        s_len=len(s)
        for i in indexes:
            for j in range(i+1,s_len+1):
                #print (j,s_len)
                if j >= s_len:
                    l.append(s[p:j].strip())
                    break
                else:
                    if s[j] == " ":
                        t=j
                    if s[j] == ':':
                        l.append(s[p:t].strip())
                        p=t
                        break
        return [[y.lower() for y in x.split(':')] for x in l]
    def show_recipe2(self,widget,*data):
        recipe=self.objectlist.get_selected()
        self.window.hide()
        current_recipe(recipe_id=recipe.r_id)

    def show_recipe(self,widget,data):
        self.window.hide()
        recipe=current_recipe(recipe_id=data)

    def exit(self, widget):
        main_window.window.show()
Пример #24
0
class MethodTest(unittest.TestCase):
    def setUp(self):
        self.klist = ObjectList([Column('name', sorted=True)],
                                [Settable(name='first')])

    def testNonZero(self):
        self.assertEqual(self.klist.__nonzero__(), True)
        self.klist.remove(self.klist[0])
        self.assertEqual(self.klist.__nonzero__(), True)
        if not self.klist:
            raise AssertionError

    def testIter(self):
        for item1 in self.klist:
            pass
        for item2 in iter(self.klist):
            self.assertEqual(item1, item2)

    def testGetItem(self):
        self.klist.append(Settable(name='second'))
        model = self.klist.get_model()
        item1 = model[0][0]
        item2 = model[1][0]
        self.assertEqual(self.klist[0], item1)
        self.assertEqual(self.klist[:1], [item1])
        self.assertEqual(self.klist[-1:], [item2])
        self.assertRaises(TypeError, self.klist.__getitem__, None)

    def testSetItem(self):
        self.klist[0] = Settable(name='second')
        self.assertRaises(NotImplementedError, self.klist.__setitem__,
                          slice(0), None)
        self.assertRaises(TypeError, self.klist.__setitem__, None, None)

    def testIndex(self):
        self.assertRaises(NotImplementedError, self.klist.index, 0, start=0)
        self.assertRaises(NotImplementedError, self.klist.index, 0, stop=0)

        self.assertEqual(self.klist.index(self.klist[0]), 0)
        self.assertRaises(ValueError, self.klist.index, None)

    def testCount(self):
        item = self.klist[0]
        self.assertEqual(self.klist.count(item), 1)
        self.klist.append(item)
        self.assertEqual(self.klist.count(item), 2)
        self.klist.clear()
        self.assertEqual(self.klist.count(item), 0)

    def testPop(self):
        self.assertRaises(NotImplementedError, self.klist.pop, None)

    def testReverse(self):
        self.assertRaises(NotImplementedError, self.klist.reverse, 1, 2)

    def testSort(self):
        self.assertRaises(NotImplementedError, self.klist.sort, 1, 2)

    def testSelectPath(self):
        self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_NONE)
        self.assertRaises(TypeError, self.klist.select_paths, (0,))
        self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_SINGLE)
        self.klist.select_paths((0,))

    def testSelect(self):
        self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_NONE)
        self.assertRaises(TypeError, self.klist.select, None)
        self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_SINGLE)

    def testGetSelected(self):
        item = self.klist[0]
        self.klist.select(item)
        self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_SINGLE)
        self.assertEqual(self.klist.get_selected(), item)

    def testGetSelectedRows(self):
        self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        item = self.klist[0]
        self.klist.select(item)
        self.assertEqual(self.klist.get_selected_rows(), [item])

    def testGetNextAndPrevious(self):
        self.klist.append(Settable(name='second'))
        self.klist.append(Settable(name='third'))
        item1, item2, item3 = self.klist

        self.assertEqual(self.klist.get_next(item1), item2)
        self.assertEqual(self.klist.get_next(item2), item3)
        self.assertEqual(self.klist.get_next(item3), item1)
        self.assertRaises(ValueError, self.klist.get_next, None)

        self.assertEqual(self.klist.get_previous(item1), item3)
        self.assertEqual(self.klist.get_previous(item2), item1)
        self.assertEqual(self.klist.get_previous(item3), item2)
        self.assertRaises(ValueError, self.klist.get_previous, None)

    def testInsert(self):
        self.klist = ObjectList([Column('name')])
        self.assertEqual(list(self.klist), [])

        self.klist.insert(0, Settable(name='one'))
        self.assertEqual(self.klist[0].name, 'one')

        self.klist.insert(0, Settable(name='two'))
        self.assertEqual(self.klist[0].name, 'two')
        self.assertEqual(self.klist[1].name, 'one')

        self.klist.insert(1, Settable(name='three'))
        self.assertEqual(self.klist[0].name, 'two')
        self.assertEqual(self.klist[1].name, 'three')
        self.assertEqual(self.klist[2].name, 'one')
Пример #25
0
class Connections(GladeWidget):
    gladeFile = "connections.glade"

    gsignal("have-connection", bool)
    gsignal("connection-activated", object)
    gsignal("connections-cleared")

    def __init__(self):
        GladeWidget.__init__(self)

        columns = [
            Column("host", title=_("Hostname"), searchable=True),
            Column(
                "timestamp", title=_("Last used"), sorted=True, order=gtk.SORT_DESCENDING, format_func=format_timestamp
            ),
        ]
        self._connections = ObjectList(columns, objects=getRecentConnections(), mode=gtk.SELECTION_SINGLE)
        self._connections.connect("row-activated", self._on_objectlist_row_activated)
        self._connections.connect("selection-changed", self._on_objectlist_selection_changed)
        self._connections.set_size_request(-1, 160)
        self.page.pack_start(self._connections)
        self.page.reorder_child(self._connections, 0)
        self._connections.get_treeview().set_search_equal_func(self._searchEqual)
        self._connections.show()
        self._updateButtons()

    def _updateButtons(self):
        canClear = hasRecentConnections()
        self.button_clear.set_sensitive(canClear)
        self.button_clear_all.set_sensitive(canClear)
        if not canClear:
            self.emit("connections-cleared")

    def _searchEqual(self, model, column, key, iter):
        connection = model.get(iter, column)[0]
        if key in connection.name:
            return False

        # True means doesn't match
        return True

    def _clear_all(self):
        for conn in self._connections:
            os.unlink(conn.filename)
        self._connections.clear()

    def _clear(self, conn):
        self._connections.remove(conn)
        os.unlink(conn.filename)

    # Public API

    def grab_focus(self):
        if len(self._connections):
            self._connections.select(self._connections[0])
        self._connections.grab_focus()

    def get_selected(self):
        return self._connections.get_selected()

    def update(self, connection):
        os.utime(connection.filename, None)

    # Callbacks

    def on_button_clear_clicked(self, button):
        conn = self._connections.get_selected()
        if conn:
            self._clear(conn)
        self._updateButtons()

    def on_button_clear_all_clicked(self, button):
        self._clear_all()
        self._updateButtons()

    def _on_objectlist_row_activated(self, connections, connection):
        self.emit("connection-activated", connection)

    def _on_objectlist_selection_changed(self, connections, connection):
        self.emit("have-connection", bool(connection))
Пример #26
0
class PaymentMethodsDialog(BasicDialog):
    # TODO Bug 2406 will avoid duplicating code here
    size = (400, 400)
    title = _("Payment Method Settings")

    # TODO: implement editor for 'multiple' payment method.
    METHOD_EDITORS = {
        u'card': CardPaymentMethodEditor,
        u'money': PaymentMethodEditor,
        u'check': PaymentMethodEditor,
        u'credit': PaymentMethodEditor,
        u'bill': PaymentMethodEditor,
        u'deposit': PaymentMethodEditor,
        u'store_credit': PaymentMethodEditor
    }

    def __init__(self, store):
        BasicDialog.__init__(self,
                             hide_footer=True,
                             size=PaymentMethodsDialog.size,
                             title=PaymentMethodsDialog.title)
        self._can_edit = False
        self.store = store
        self._setup_list()
        self._setup_slaves()

    def _setup_slaves(self):
        self._toolbar_slave = SearchEditorToolBar()
        self._toolbar_slave.connect("edit", self._on_edit_button__clicked)
        self._toolbar_slave.new_button.hide()
        self._toolbar_slave.edit_button.set_sensitive(False)
        self.attach_slave("extra_holder", self._toolbar_slave)

    def _setup_list(self):
        methods = PaymentMethod.get_editable_methods(self.store)
        self.klist = ObjectList(self._get_columns(), methods,
                                Gtk.SelectionMode.BROWSE)
        self.klist.connect("selection-changed",
                           self._on_klist__selection_changed)
        self.klist.connect("row-activated", self._on_klist__row_activated)
        self.klist.connect("cell-edited", self.on_cell_edited)
        self.main.remove(self.main.get_child())
        self.main.add(self.klist)
        self.klist.show()

    def _get_columns(self):
        return [
            Column('description',
                   title=_('Payment Method'),
                   data_type=str,
                   expand=True),
            Column('is_active',
                   title=_('Active'),
                   data_type=bool,
                   editable=True)
        ]

    def _edit_item(self, item):
        editor = self.METHOD_EDITORS.get(item.method_name, None)

        if not editor:
            raise TypeError('Invalid payment method adapter: %s' %
                            item.method_name)

        store = api.new_store()
        item = store.fetch(item)
        retval = run_dialog(editor, self, store, item)
        store.confirm(retval)
        store.close()

    #
    # Callbacks
    #

    def on_cell_edited(self, klist, obj, attr):
        # All the payment methods could be (de)activate, except the 'money'
        # payment method.
        if obj.method_name != u'money':
            store = obj.store
            store.commit()
        else:
            obj.is_active = True

    def _on_klist__selection_changed(self, list, data):
        self._can_edit = (data
                          and data.method_name in self.METHOD_EDITORS.keys())
        self._toolbar_slave.edit_button.set_sensitive(self._can_edit)

    def _on_edit_button__clicked(self, toolbar_slave):
        assert self._can_edit
        self._edit_item(self.klist.get_selected())

    def _on_klist__row_activated(self, list, data):
        if not self._can_edit:
            return

        self._edit_item(data)
Пример #27
0
class ListContainer(Gtk.Box):
    """A ListContainer is an :class:`ObjectList` with buttons to be able
    to modify the content of the list.
    Depending on the list_mode, @see :class:`set_list_mode` you will
    have add, remove and edit buttons.

    Signals
    =======
      - B{add-item} (returns item):
        - emitted when the add button is clicked, you're expected to
          return an object here
      - B{remove-item} (item, returns bool):
        - emitted when removing an item,
          you can block the removal from the list by returning False
      - B{edit-item} (item):
        - emitted when editing an item
          you can block the update afterwards by returning False

    :ivar add_button: add button
    :type add_button: :class:`Gtk.Button`
    :ivar remove_button: remove button
    :type remove_button: :class:`Gtk.Button`
    :ivar edit_button: edit button
    :type edit_button: :class:`Gtk.Button`
    """

    __gtype_name__ = 'ListContainer'
    gsignal('add-item', retval=object)
    gsignal('remove-item', object, retval=bool)
    gsignal('edit-item', object, retval=bool)
    gsignal('selection-changed', object)

    def __init__(self, columns, orientation=Gtk.Orientation.VERTICAL):
        """
        Create a new ListContainer object.
        :param columns: columns for the :class:`kiwi.ui.objectlist.ObjectList`
        :type columns: a list of :class:`kiwi.ui.objectlist.Columns`
        :param orientation: the position where the buttons will be
            placed: at the right (vertically) or at the bottom (horizontally)
            of the list. Defaults to the right of the list.
        :type: Gtk.Orientation.HORIZONTAL or Gtk.Orientation.VERTICAL
        """
        self._list_type = None

        super(ListContainer,
              self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self._orientation = orientation

        self._create_ui(columns)
        self.set_list_type(ListType.NORMAL)

    # Private API

    def _create_ui(self, columns):
        self.list = ObjectList(columns)
        self.list.connect('selection-changed',
                          self._on_list__selection_changed)
        self.list.connect('row-activated', self._on_list__row_activated)

        self.add_button = Gtk.Button(stock=Gtk.STOCK_ADD)
        self.add_button.connect('clicked', self._on_add_button__clicked)

        self.remove_button = Gtk.Button(stock=Gtk.STOCK_REMOVE)
        self.remove_button.set_sensitive(False)
        self.remove_button.connect('clicked', self._on_remove_button__clicked)

        self.edit_button = Gtk.Button(stock=Gtk.STOCK_EDIT)
        self.edit_button.set_sensitive(False)
        self.edit_button.connect('clicked', self._on_edit_button__clicked)

        self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)

        if self._orientation == Gtk.Orientation.VERTICAL:
            self.pack_start(self.list, True, True, 0)
            self.list.show()
            self._add_buttons_to_box(self._vbox)
            self._pack_vbox()
        elif self._orientation == Gtk.Orientation.HORIZONTAL:
            self._vbox.pack_start(self.list, True, True, 0)
            self.list.show()
            hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
            self._add_buttons_to_box(hbox)
            self._vbox.pack_start(hbox, False, True, 0)
            hbox.show()
            self._pack_vbox()
        else:
            raise TypeError(
                "buttons_orientation must be Gtk.Orientation.VERTICAL "
                " or Gtk.Orientation.HORIZONTAL")

    def _add_buttons_to_box(self, box):
        box.pack_start(self.add_button, False, True, 0)
        box.pack_start(self.remove_button, False, True, 0)
        box.pack_start(self.edit_button, False, True, 0)

    def _pack_vbox(self):
        self.pack_start(self._vbox, False, True, 6)
        self._vbox.show()

    def _set_child_packing(self, padding):
        expand = self._orientation == Gtk.Orientation.HORIZONTAL

        self.set_child_packing(self._vbox, expand, True, padding,
                               Gtk.PackType.START)

    def _add_item(self):
        retval = self.emit('add-item')
        if retval is None:
            return
        elif isinstance(retval, NotImplementedError):
            raise retval

        self.list.append(retval)
        self.list.refresh()

    def _remove_item(self, item):
        retval = self.emit('remove-item', item)
        if retval:
            self.list.remove(item)

    def _edit_item(self, item):
        retval = self.emit('edit-item', item)
        if retval:
            self.list.update(item)

    # Public API

    def add_item(self, item):
        """Appends an item to the list
        :param item: item to append
        """
        self.list.append(item)

    def add_items(self, items):
        """Appends a list of items to the list
        :param items: items to add
        :type items: a sequence of items
        """
        self.list.extend(items)

    def remove_item(self, item):
        """Removes an item from the list
        :param item: item to remove
        """
        self.list.remove(item)

    def update_item(self, item):
        """Updates an item in the list.
        You should call this if you change the object
        :param item: item to update
        """
        self.list.update(item)

    def default_remove(self, item):
        """Asks the user confirmation for removal of an item.
        :param item: a description of the item that will be removed
        :returns: True if the user confirm the removal, False otherwise
        """
        response = yesno(_('Do you want to remove %s ?') %
                         (GLib.markup_escape_text(str(item)), ),
                         parent=None,
                         default=Gtk.ResponseType.OK,
                         buttons=((Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL),
                                  (Gtk.STOCK_REMOVE, Gtk.ResponseType.OK)))
        return response == Gtk.ResponseType.OK

    def set_list_type(self, list_type):
        """Sets the kind of list type.
        :param list_type:
        """
        if not isinstance(list_type, ListType):
            raise TypeError("list_type must be a ListType enum")

        self.add_button.set_property(
            'visible', list_type not in [
                ListType.READONLY, ListType.REMOVEONLY, ListType.UNADDABLE
            ])
        self.remove_button.set_property(
            'visible', list_type
            not in [ListType.READONLY, ListType.ADDONLY, ListType.UNREMOVABLE])
        self.edit_button.set_property(
            'visible', list_type not in [
                ListType.READONLY, ListType.ADDONLY, ListType.UNEDITABLE,
                ListType.REMOVEONLY
            ])
        if list_type in [ListType.READONLY, ListType.REMOVEONLY]:
            padding = 0
        else:
            padding = 6
        self._set_child_packing(padding)
        self._list_type = list_type

    def clear(self):
        """Removes all the items in the list"""
        self.list.clear()

    # Callbacks

    def _on_list__selection_changed(self, list, selection):
        object_selected = selection is not None
        self.remove_button.set_sensitive(object_selected)
        self.edit_button.set_sensitive(object_selected)
        self.emit('selection-changed', selection)

    def _on_list__row_activated(self, list, item):
        if self._list_type not in [
                ListType.READONLY, ListType.ADDONLY, ListType.UNEDITABLE
        ]:
            self._edit_item(item)

    def _on_add_button__clicked(self, button):
        self._add_item()

    def _on_remove_button__clicked(self, button):
        self._remove_item(self.list.get_selected())

    def _on_edit_button__clicked(self, button):
        self._edit_item(self.list.get_selected())