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

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

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

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

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

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

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

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

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

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

        if not result:
            return

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

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

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

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

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

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

    #
    # Hooks
    #

    def get_items(self):
        return []

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

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

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

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

    #
    # Public API
    #

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

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

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

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

        return button

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

        self.message_label.set_markup(message)

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

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

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

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

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

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

    #
    # Signal handlers
    #

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

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

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

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

    def on_delete_button__clicked(self, button):
        self._clear()
Esempio n. 2
0
class GTKProject(SlaveDelegate):
    """A facade of kmcos.types.Project so that
    pygtk can display in a TreeView.
    """
    def __init__(self, parent, menubar):
        self.project_data = ObjectTree([
            Column('name', use_markup=True, data_type=str, sorted=True),
            Column('info')
        ])

        self.project_data.connect('row-activated', self.on_row_activated)
        self.model_tree = Project()
        self._set_treeview_hooks()

        self.menubar = menubar

        self.set_parent(parent)

        self.filename = ''

        self.undo_stack = UndoStack(self.model_tree.__repr__, self.import_file,
                                    self.project_data.select, menubar,
                                    self.meta, 'Initialization')

        SlaveDelegate.__init__(self, toplevel=self.project_data)

    def _set_treeview_hooks(self):
        """Fudge function to import to access function to kmcos.types.Project
        to kmcos.gui.GTKProject.
        """
        self.project_data.clear()
        # Meta
        self.meta = self.project_data.append(None, self.model_tree.meta)
        self.model_tree.meta = self.meta

        # Layer List
        self.model_tree.add_layer = self.add_layer
        self.layer_list = self.project_data.append(None,
                                                   self.model_tree.layer_list)
        self.get_layers = lambda: \
            sorted(self.project_data.get_descendants(self.layer_list),
                   key=lambda x: x.name)
        self.model_tree.get_layers = self.get_layers
        self.lattice = self.layer_list

        # Parameter List
        self.parameter_list = self.project_data.append(
            None, self.model_tree.parameter_list)
        self.add_parameter = lambda parameter: \
            self.project_data.append(self.parameter_list, parameter)
        self.model_tree.add_parameter = self.add_parameter
        self.get_parameters = lambda: \
            sorted(self.project_data.get_descendants(self.parameter_list),
                   key=lambda x: x.name)
        self.model_tree.get_parameters = self.get_parameters

        # Species List
        self.species_list = self.project_data.append(
            None, self.model_tree.species_list)
        self.add_species = lambda species: \
            self.project_data.append(self.species_list, species)
        self.model_tree.add_species = self.add_species
        self.get_speciess = lambda: \
            sorted(self.project_data.get_descendants(self.species_list),
                   key=lambda x: x.name)
        self.model_tree.get_speciess = self.get_speciess

        # Process List
        self.process_list = self.project_data.append(
            None, self.model_tree.process_list)
        self.add_process = lambda process:\
            self.project_data.append(self.process_list, process)
        self.model_tree.add_process = self.add_process
        self.get_processes = lambda: \
            sorted(self.project_data.get_descendants(self.process_list),
                   key=lambda x: x.name)
        self.model_tree.get_processes = self.get_processes

        # Output List
        self.output_list = self.project_data.append(
            None, self.model_tree.output_list)
        self.add_output = lambda output:\
            self.project_data.append(self.output_list, output)
        self.model_tree.add_output = self.add_output
        self.get_outputs = lambda:  \
            sorted(self.project_data.get_descendants(self.output_list),
                   key=lambda x: x.name)
        self.model_tree.get_outputs = self.get_outputs

    def add_layer(self, layer):
        self.project_data.append(self.layer_list, layer)
        if len(self.get_layers()) == 1:
            self.set_default_layer(layer.name)
            self.set_substrate_layer(layer.name)
        return layer

    def set_default_species(self, species):
        self.model_tree.species_list.default_species = species

    def set_substrate_layer(self, layer):
        self.model_tree.layer_list.substrate_layer = layer

    def set_default_layer(self, layer):
        self.model_tree.layer_list.default_layer = layer

    def update(self, model):
        """Update the object tree."""
        self.project_data.update(model)

    def on_row_activated(self, _tree, data):
        if isinstance(data, Layer):
            data.active = not data.active

    def get_name(self):
        """Return project name."""
        if self.filename:
            return os.path.basename(self.filename)
        else:
            return 'Untitled'

    def __repr__(self):
        return str(self.model_tree)

    def import_file(self, filename):
        """Import XML project file into editor GUI,
        unfolding the object tree.

        """
        self.filename = filename
        self.model_tree.import_file(filename)
        self.expand_all()

    def expand_all(self):
        """Expand all list of the project tree
        """
        self.project_data.expand(self.species_list)
        self.project_data.expand(self.layer_list)
        self.project_data.expand(self.parameter_list)
        self.project_data.expand(self.process_list)
        self.project_data.expand(self.output_list)

    def on_key_press(self, _, event):
        """When the user hits the keyboard focusing the treeview
        this event is triggered. Right now the only supported function
        is to deleted the selected item
        """
        selection = self.project_data.get_selected()
        if gtk.gdk.keyval_name(event.keyval) == 'Delete':
            if (isinstance(selection, Species)
                    or isinstance(selection, Process)
                    or isinstance(selection, Parameter)
                    or isinstance(selection, Layer)):
                if kiwi.ui.dialogs.yesno(
                    "Do you really want to delete '%s'?" \
                        % selection.name) == gtk.RESPONSE_YES:
                    self.project_data.remove(selection)

    def on_project_data__selection_changed(self, _, elem):
        """When a new item is selected in the treeview this function
        loads the main area of the window with the corresponding form
        and data.
        """
        slave = self.get_parent().get_slave('workarea')
        if slave:
            self.get_parent().detach_slave('workarea')
        if isinstance(elem, Layer):
            if self.meta.model_dimension in [1, 3]:
                self.get_parent().toast('Only 2d supported')
                return
            self.undo_stack.start_new_action('Edit Layer %s' % elem.name, elem)
            form = LayerEditor(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Meta):
            self.undo_stack.start_new_action('Edit Meta', elem)
            meta_form = MetaForm(self.meta, self)
            self.get_parent().attach_slave('workarea', meta_form)
            meta_form.focus_toplevel()
            meta_form.focus_topmost()
        elif isinstance(elem, OutputList):
            self.undo_stack.start_new_action('Edit Output', elem)
            form = OutputForm(self.output_list, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Parameter):
            self.undo_stack.start_new_action('Edit Parameter %s' % elem.name,
                                             elem)
            form = ParameterForm(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Process):
            if self.meta.model_dimension in [1, 3]:
                self.get_parent().toast('Only 2d supported')
                return
            self.undo_stack.start_new_action('Edit Process %s' % elem.name,
                                             elem)
            form = ProcessForm(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, ProcessList):
            if self.meta.model_dimension in [1, 3]:
                self.get_parent().toast('Only 2d supported')
                return
            self.undo_stack.start_new_action('Batch process editing', elem)
            form = BatchProcessForm(self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Species):
            self.undo_stack.start_new_action('Edit species', elem)
            form = SpeciesForm(elem, self.project_data)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, SpeciesList):
            self.undo_stack.start_new_action('Edit default species', elem)
            form = SpeciesListForm(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, LayerList):
            self.undo_stack.start_new_action('Edit lattice', elem)
            dimension = self.meta.model_dimension
            form = LatticeForm(elem, dimension, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        else:
            self.get_parent().toast('Not implemented, yet(%s).' % type(elem))
Esempio n. 3
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 = ObjectTree(self.get_columns())
        self.populate()

        self.pack_start(self.klist, True, True, 0)
        self.klist.show()

        if len(self.klist) and self.get_details_dialog_class():
            self.button_box = Gtk.HButtonBox()
            self.button_box.set_layout(Gtk.ButtonBoxStyle.START)

            details_button = Gtk.Button.new_with_label(self.details_lbl)
            self.button_box.pack_start(details_button, True, True, 0)
            details_button.set_sensitive(bool(self.klist.get_selected()))
            details_button.show()

            self.pack_end(self.button_box, False, False, 0)
            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(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))
Esempio n. 4
0
class AdditionListSlave(SearchSlave):
    """A slave that offers a simple list and its management.

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

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

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

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

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

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

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

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

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

        if not result:
            return

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

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

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

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

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

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

    #
    # Hooks
    #

    def get_items(self):
        return []

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

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

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

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

    #
    # Public API
    #

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

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

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

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

        return button

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

        self.message_label.set_markup(message)

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

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

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

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

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

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

    #
    # Signal handlers
    #

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

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

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

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

    def on_delete_button__clicked(self, button):
        self._clear()
Esempio n. 5
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 = ObjectTree(self.get_columns())
        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))
Esempio n. 6
0
class NoteUI(GladeSlaveDelegate):

	def __init__(self, parent, mo):
		self.parent = parent
		self.mo = mo
		self.factory = Factory()

		# Set up the user interface
		GladeSlaveDelegate.__init__(self, gladefile="mo_tab_notes", toplevel_name="window_main")
	
		noteColumns = [
			Column("summary", title='Title', data_type=str),
		]
		#self.treeview_note = ObjectList(noteColumns)
		self.treeview_note = ObjectTree(noteColumns)
		self.vbox_notelist.add(self.treeview_note)

		# Connect signals
		self.treeview_note.connect('row-activated', self.treeview_note__row_activated)
		self.treeview_note.connect('selection-changed', self.treeview_note__selection_changed)
		self.treeview_note.connect('key-press-event', self.treeview_note__key_press_event)

		self.refresh()

	def refresh(self):
		self.treeview_note.clear()

		for journal in self.mo.cal_model.get_journals():
			parent = self.mo.cal_model.get_model_by_uid(journal.get_related_to())
			self.treeview_note.append(parent, journal)

	def on_toolbutton_add__clicked(self, *args):
		journal = self.factory.journal()
		journal = miniorganizer.ui.NoteEditUI(self.mo, journal).run()
		if journal:
			self.mo.cal_model.add(journal)
			self.treeview_note.append(None, journal)
			self.parent.menuitem_save.set_sensitive(True)

	def on_toolbutton_addsub__clicked(self, *args):
		parent_journal = self.treeview_note.get_selected()
		journal = self.factory.journal(parent_journal)
		if miniorganizer.ui.NoteEditUI(self.mo, journal).run():
			self.mo.cal_model.add(journal)
			self.treeview_note.append(parent_journal, journal)
			self.treeview_note.expand(parent_journal)
			self.parent.menuitem_save.set_sensitive(True)
		
	def on_toolbutton_edit__clicked(self, *args):
		sel_note = self.treeview_note.get_selected()
		self.treeview_note__row_activated(self.treeview_note, sel_note)

	def on_toolbutton_remove__clicked(self, *args):
		sel_note = self.treeview_note.get_selected()
		if sel_note:
			children = self.mo.cal_model.get_models_related_by_uid(sel_note.get_uid())

			if children:
				response = dialogs.warning('This note contains sub-notes. Removing it will also remove the sub-notes. Is this what you want?', buttons=gtk.BUTTONS_YES_NO)
				if response != gtk.RESPONSE_YES:
					return

			self.mo.cal_model.delete(sel_note, True)
			self.treeview_note.remove(sel_note, True)
			self.parent.menuitem_save.set_sensitive(True)

	def treeview_note__selection_changed(self, list, selection):
		has_selection = selection is not None
		self.toolbutton_addsub.set_sensitive(has_selection)
		self.toolbutton_remove.set_sensitive(has_selection)
		self.toolbutton_edit.set_sensitive(has_selection)
	
	def treeview_note__row_activated(self, list, object):
		sel_note = self.treeview_note.get_selected()
		note = miniorganizer.ui.NoteEditUI(self.mo, sel_note).run()

	def treeview_note__key_press_event(self, treeview, event):
		if event.keyval == gtk.keysyms.Delete:
			self.on_toolbutton_remove__clicked()
Esempio n. 7
0
class GTKProject(SlaveDelegate):
    """A facade of kmos.types.Project so that
    pygtk can display in a TreeView.
    """
    def __init__(self, parent, menubar):
        self.project_data = ObjectTree([Column('name',
                                               use_markup=True,
                                               data_type=str,
                                               sorted=True),
                                        Column('info')])

        self.project_data.connect('row-activated', self.on_row_activated)
        self.model_tree = Project()
        self._set_treeview_hooks()

        self.menubar = menubar

        self.set_parent(parent)


        self.filename = ''

        self.undo_stack = UndoStack(
            self.model_tree.__repr__,
            self.import_xml_file,
            self.project_data.select,
            menubar,
            self.meta,
            'Initialization')

        SlaveDelegate.__init__(self, toplevel=self.project_data)

    def _set_treeview_hooks(self):
        """Fudge function to import to access function to kmos.types.Project
        to kmos.gui.GTKProject.
        """
        self.project_data.clear()
        # Meta
        self.meta = self.project_data.append(None, self.model_tree.meta)
        self.model_tree.meta = self.meta

        # Layer List
        self.model_tree.add_layer = self.add_layer
        self.layer_list = self.project_data.append(None,
                                   self.model_tree.layer_list)
        self.get_layers = lambda :\
            sorted(self.project_data.get_descendants(self.layer_list),
                   key=lambda x: x.name)
        self.model_tree.get_layers = self.get_layers
        self.lattice = self.layer_list

        # Parameter List
        self.parameter_list = self.project_data.append(None,
                                       self.model_tree.parameter_list)
        self.add_parameter = lambda parameter :\
            self.project_data.append(self.parameter_list, parameter)
        self.model_tree.add_parameter = self.add_parameter
        self.get_parameters = lambda :\
            sorted(self.project_data.get_descendants(self.parameter_list),
                   key=lambda x: x.name)
        self.model_tree.get_parameters = self.get_parameters

        # Species List
        self.species_list = self.project_data.append(None,
                                   self.model_tree.species_list)
        self.add_species = lambda species :\
            self.project_data.append(self.species_list, species)
        self.model_tree.add_species = self.add_species
        self.get_speciess = lambda :\
            sorted(self.project_data.get_descendants(self.species_list),
                   key=lambda x: x.name)
        self.model_tree.get_speciess = self.get_speciess


        # Process List
        self.process_list = self.project_data.append(None,
                                     self.model_tree.process_list)
        self.add_process = lambda process:\
            self.project_data.append(self.process_list, process)
        self.model_tree.add_process = self.add_process
        self.get_processes = lambda :\
            sorted(self.project_data.get_descendants(self.process_list),
                   key=lambda x: x.name)
        self.model_tree.get_processes = self.get_processes

        # Output List
        self.output_list = self.project_data.append(None,
                                self.model_tree.output_list)
        self.add_output = lambda output:\
            self.project_data.append(self.output_list, output)
        self.model_tree.add_output = self.add_output
        self.get_outputs = lambda : \
            sorted(self.project_data.get_descendants(self.output_list),
                   key=lambda x: x.name)
        self.model_tree.get_outputs = self.get_outputs

    def add_layer(self, layer):
        self.project_data.append(self.layer_list, layer)
        if len(self.get_layers()) == 1 :
            self.set_default_layer(layer.name)
            self.set_substrate_layer(layer.name)

    def set_default_species(self, species):
        self.model_tree.species_list.default_species = species

    def set_substrate_layer(self, layer):
        self.model_tree.layer_list.substrate_layer = layer

    def set_default_layer(self, layer):
        self.model_tree.layer_list.default_layer = layer

    def update(self, model):
        self.project_data.update(model)

    def on_row_activated(self, tree, data):
        if isinstance(data, Layer):
            data.active = not data.active

    def get_name(self):
        if self.filename:
            return os.path.basename(self.filename)
        else:
            return 'Untitled'

    def __repr__(self):
        return str(self.model_tree)

    def import_xml_file(self, filename):
        self.model_tree.import_xml_file(filename)
        self.expand_all()

    def expand_all(self):
        """Expand all list of the project tree
        """
        self.project_data.expand(self.species_list)
        self.project_data.expand(self.layer_list)
        self.project_data.expand(self.parameter_list)
        self.project_data.expand(self.process_list)
        self.project_data.expand(self.output_list)

    def on_key_press(self, _, event):
        """When the user hits the keyboard focusing the treeview
        this event is triggered. Right now the only supported function
        is to deleted the selected item
        """
        selection = self.project_data.get_selected()
        if gtk.gdk.keyval_name(event.keyval) == 'Delete':
            if(isinstance(selection, Species)
            or isinstance(selection, Process)
            or isinstance(selection, Parameter)
            or isinstance(selection, Layer)):
                if kiwi.ui.dialogs.yesno(
                    "Do you really want to delete '%s'?" \
                        % selection.name) == gtk.RESPONSE_YES:
                    self.project_data.remove(selection)

    def on_project_data__selection_changed(self, _, elem):
        """When a new item is selected in the treeview this function
        loads the main area of the window with the corresponding form
        and data.
        """
        slave = self.get_parent().get_slave('workarea')
        if slave:
            self.get_parent().detach_slave('workarea')
        if isinstance(elem, Layer):
            if self.meta.model_dimension in [1, 3]:
                self.get_parent().toast('Only 2d supported')
                return
            self.undo_stack.start_new_action('Edit Layer %s' % elem.name,
                                                               elem)
            form = LayerEditor(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Meta):
            self.undo_stack.start_new_action('Edit Meta', elem)
            meta_form = MetaForm(self.meta, self)
            self.get_parent().attach_slave('workarea', meta_form)
            meta_form.focus_toplevel()
            meta_form.focus_topmost()
        elif isinstance(elem, OutputList):
            self.undo_stack.start_new_action('Edit Output', elem)
            form = OutputForm(self.output_list, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Parameter):
            self.undo_stack.start_new_action('Edit Parameter %s' % elem.name,
                                                                   elem)
            form = ParameterForm(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Process):
            if self.meta.model_dimension in [1, 3]:
                self.get_parent().toast('Only 2d supported')
                return
            self.undo_stack.start_new_action('Edit Process %s' % elem.name,
                                                                 elem)
            form = ProcessForm(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, ProcessList):
            if self.meta.model_dimension in [1, 3]:
                self.get_parent().toast('Only 2d supported')
                return
            self.undo_stack.start_new_action('Batch process editing', elem)
            form = BatchProcessForm(self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, Species):
            self.undo_stack.start_new_action('Edit species', elem)
            form = SpeciesForm(elem, self.project_data)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, SpeciesList):
            self.undo_stack.start_new_action('Edit default species', elem)
            form = SpeciesListForm(elem, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        elif isinstance(elem, LayerList):
            self.undo_stack.start_new_action('Edit lattice', elem)
            dimension = self.meta.model_dimension
            form = LatticeForm(elem, dimension, self)
            self.get_parent().attach_slave('workarea', form)
            form.focus_topmost()
        else:
            self.get_parent().toast('Not implemented, yet(%s).' % type(elem))
Esempio n. 8
0
class Window(object):
	"""This is the browser application."""

	def __init__(self):
		## FIXME: The GUI should appear straight away, connecting should
		## happen after!
		# Set up the main window
		self.window = gtk.Window()
		self.window.set_title("PubSub Browser")
		self.window.set_default_size(400, 400)
		self.window.connect("destroy", self.quit)

		# Divide it vertically
		self.vbox = gtk.VBox()
		self.window.add(self.vbox)

		# This holds the location entry and the Get button
		self.top_box = gtk.HBox()
		self.vbox.pack_start(self.top_box, expand=False)

		self.location_label = gtk.Label("Server:")
		self.top_box.pack_start(self.location_label, expand=False)

		# This is where the server location is given
		self.location_entry = gtk.Entry()
		self.top_box.pack_start(self.location_entry)

		# This button run get_button_released to fetch the server's nodes
		self.get_button = gtk.Button(label="Get")
		self.get_button.connect("released", self.get_button_released)
		self.top_box.pack_end(self.get_button, expand=False)

		# Draw the tree using Kiwi, since plain GTK is a pain :P

		# The attribute is the data, ie. a Column looking for attribute
		# "foo", when appended by an object bar, will show bar.foo

		# We can put multiple things into a column (eg. an icon and a
		# label) by making 2 columns and passing the first to the second
		self.tree_columns = [\
			Column(attribute='icon', title='Nodes', use_stock=True, \
			justify=gtk.JUSTIFY_LEFT, icon_size=gtk.ICON_SIZE_MENU), \
			Column(attribute='name', justify=gtk.JUSTIFY_LEFT, column='icon')]
		self.tree_columns[0].expand = False
		self.tree_columns[1].expand = False
		self.tree_view = ObjectTree(self.tree_columns)
		self.tree_view.connect("selection-changed", self.selection_changed)
		self.vbox.pack_start(self.tree_view)

		# This holds the Add button and the Delete button
		self.bottom_box = gtk.HBox()
		self.vbox.pack_end(self.bottom_box, expand=False)

		# Make the Delete button, which runs the delete_button_released method
		self.delete_button = gtk.Button(stock=gtk.STOCK_REMOVE)
		try:
			# Attempt to change the label of the stock button
			delete_label = self.delete_button.get_children()[0]
			delete_label = delete_label.get_children()[0].get_children()[1]
			delete_label = delete_label.set_label("Delete Selection")
		except:
			# If it fails then just go back to the default
			self.delete_button = gtk.Button(stock=gtk.STOCK_REMOVE)
		self.delete_button.connect("released", self.delete_button_released)
		self.delete_button.set_sensitive(False)
		self.bottom_box.pack_start(self.delete_button, expand=True)

		# Make the Properties button, which runs the properties_button_released method
		self.properties_button = gtk.Button(stock=gtk.STOCK_PROPERTIES)
		try:
			# Attempt to change the label of the stock button
			properties_label = self.properties_button.get_children()[0]
			properties_label = properties_label.get_children()[0].get_children()[1]
			properties_label = properties_label.set_label("Node Properties...")
		except:
			# If it fails then just go back to the default
			self.properties_button = gtk.Button(stock=gtk.STOCK_PROPERTIES)
		self.properties_button.connect("released", self.properties_button_released)
		self.properties_button.set_sensitive(False)
		self.bottom_box.pack_start(self.properties_button, expand=True)

		# Make the Add button, which runs the add_button_released method
		self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
		try:
			# Attempt to change the label of the stock button
			add_label = self.add_button.get_children()[0]
			add_label = add_label.get_children()[0].get_children()[1]
			add_label = add_label.set_label("Add Child...")
		except:
			# If it fails then just go back to the default
			self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
		self.add_button.connect("released", self.add_button_released)
		self.add_button.set_sensitive(False)
		self.bottom_box.pack_end(self.add_button, expand=True)

		# This handles our XMPP connection. Feel free to change the JID
		# and password
		self.client = pubsubclient.PubSubClient("test1@localhost", "test")

		# Using the tree to store everything seems to have a few
		# glitches, so we use a regular list as our definitive memory
		self.known = []

	def selection_changed(self, list, object):
		self.add_button.set_sensitive(True)
		if type(object) == type(Node()):
			self.delete_button.set_sensitive(True)
			self.properties_button.set_sensitive(True)
		elif type(object) == type(Server()):
			self.delete_button.set_sensitive(False)
			self.properties_button.set_sensitive(False)

	def get_button_released(self, arg):
		"""This is run when the Get button is pressed. It adds the given
		server to the tree and runs get_nodes with that server."""
		listed_server = False		# Assume this server is not listed
		# Check every row of the tree to see if it matches given server
		for known_server in self.tree_view:
			if type(known_server) == type(Server()) and \
				self.location_entry.get_text() == str(known_server):
				listed_server = True		# True if server is listed
		# If we didn't find the server in the tree then add it
		if not listed_server:
			server = Server(name=self.location_entry.get_text())
			server.icon = gtk.STOCK_NETWORK
			self.tree_view.append(None, server)
			self.known.append(server)
		# Get the PubSub nodes on this server
		self.client.get_nodes(self.location_entry.get_text(), None, return_function=self.handle_incoming)

	def handle_incoming(self, nodes):
		# Go through each new node
		for node in nodes:
			# Assume we do not already know about this node
			node_is_known = False
			# Go through each node that we know about
			for known_entry in self.known:
				# See if this node is the same as the known node being checked
				if known_entry.name == node.name:	## FIXME: Needs to check server
					node_is_known = True
			if not node_is_known:
				parent = None
				for known_entry in self.known:
					if known_entry.name == node.parent.name:		## FIXME: Needs to check server
						parent = known_entry

				self.known.append(node)
				self.tree_view.append(parent, node)

				node.get_information(self.client, self.handle_information)

			node.get_sub_nodes(self.client, self.handle_incoming)

	def handle_information(self, node):
		known = False
		if node.type == 'leaf':
			if node.name is not None and node.server is not None:
				for known_entry in self.known:
					if known_entry.name == node.name:
						known_entry.set_type('leaf')
						known_entry.icon = gtk.STOCK_FILE
		elif node.type == 'collection':
			if node.name is not None and node.server is not None:
				for known_entry in self.known:
					if known_entry.name == node.name and known_entry.server == node.server:
						known_entry.set_type('collection')
						known_entry.icon = gtk.STOCK_DIRECTORY
			node.get_sub_nodes(self.client, self.handle_incoming)

	def handle_node_creation(self, return_value):
		if return_value == 0:
			self.tree_view.get_selected().get_sub_nodes(self.client, self.handle_incoming)
		else:
			print return_value

	def delete_button_released(self, args):
		to_delete = self.tree_view.get_selected()
		if type(to_delete) == type(Node()):
			self.client.delete_a_node(to_delete.server, str(to_delete), return_function=self.handle_deleted)

	def handle_deleted(self, return_value):
		if return_value == 0:
			deleted = self.tree_view.get_selected()
			self.tree_view.remove(deleted)
			self.known.remove(deleted)
		else:
			print return_value

	def add_button_released(self, args):
		self.add_window = {}
		self.add_window["parent"] = self.tree_view.get_selected()
		self.add_window["window"] = gtk.Window()
		self.add_window["window"].set_title("Add new node to " + self.add_window["parent"].name)
		self.add_window["vbox"] = gtk.VBox()
		self.add_window["window"].add(self.add_window["vbox"])

		self.add_window["top_box"] = gtk.HBox()
		self.add_window["vbox"].pack_start(self.add_window["top_box"], expand=False)
		self.add_window["name_label"] = gtk.Label("Name:")
		self.add_window["name_entry"] = gtk.Entry()
		self.add_window["top_box"].pack_start(self.add_window["name_label"], expand=False)
		self.add_window["top_box"].pack_end(self.add_window["name_entry"], expand=True)

		self.add_window["bottom_box"] = gtk.HBox()
		self.add_window["vbox"].pack_end(self.add_window["bottom_box"], expand=False)
		self.add_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.add_window["add_button"].connect("released", self.add_add_released)
		self.add_window["bottom_box"].pack_end(self.add_window["add_button"], expand=False)

		self.add_window["middle_box"] = gtk.HBox()
		self.add_window["vbox"].pack_end(self.add_window["middle_box"], expand=False)
		self.add_window["type_label"] = gtk.Label("Type:")
		self.add_window["type_select"] = gtk.combo_box_new_text()
		self.add_window["type_select"].append_text("Leaf")
		self.add_window["type_select"].append_text("Collection")
		self.add_window["middle_box"].pack_start(self.add_window["type_label"], expand=False)
		self.add_window["middle_box"].pack_end(self.add_window["type_select"], expand=True)

		self.add_window["window"].show_all()

	def add_add_released(self, args):
		name = self.add_window["name_entry"].get_text()
		node_type = self.add_window["type_select"].get_active_text()
		parent = self.add_window["parent"]
		self.add_node(name, node_type, parent)
		self.add_window["window"].destroy()
		del(self.add_window)

	def add_node(self, name, node_type, parent):
		"""Request a new child node of parent. For top-level a node
		parent should be a Server. node_type is either "leaf" or
		"collection"."""
		# This half runs if the parent is a Server
		if type(parent) == type(pubsubclient.Server()):
			# Request a top-level leaf node
			if node_type == 'Leaf':
				self.client.get_new_leaf_node(parent, \
					self.add_window["name_entry"].get_text(), None, None, return_function=self.handle_node_creation)
			# Request a top-level collection node
			elif node_type == 'Collection':
				self.client.get_new_collection_node(parent, \
					self.add_window["name_entry"].get_text(), None, None, return_function=self.handle_node_creation)

		# This half runs if the parent is a Node
		elif type(parent) == type(pubsubclient.Node()):
			# Request a child leaf node
			if node_type == 'Leaf':
				self.client.get_new_leaf_node(parent.server, \
					self.add_window["name_entry"].get_text(), parent, None, return_function=self.handle_node_creation)
			# Request a child collection node
			elif node_type == 'Collection':
				self.client.get_new_collection_node(parent.server, \
					self.add_window["name_entry"].get_text(), parent, None, return_function=self.handle_node_creation)

	def properties_button_released(self, args):
		## FIXME: This should allow multiple properties windows to be open at once
		# Setup a window containing a notebook
		self.properties_window = {}
		self.properties_window["node"] = self.tree_view.get_selected()
		self.properties_window["window"] = gtk.Window()
		self.properties_window["window"].set_title("Properties of " + str(self.properties_window["node"]))
		self.properties_window["window"].set_default_size(350, 450)
		self.properties_window["notebook"] = gtk.Notebook()
		self.properties_window["window"].add(self.properties_window["notebook"])

		# Add the Metadata page
		self.properties_window["metadata_page"] = gtk.VBox()
		self.properties_window["metadata_label"] = gtk.Label("Metadata")
		self.properties_window["notebook"].append_page(self.properties_window["metadata_page"], tab_label=self.properties_window["metadata_label"])

		# Add the Affiliations page
		self.properties_window["affiliations_page"] = gtk.VBox()
		self.properties_window["affiliations_label"] = gtk.Label("Affiliations")
		self.properties_window["notebook"].append_page(self.properties_window["affiliations_page"], tab_label=self.properties_window["affiliations_label"])

		node = self.tree_view.get_selected()
		node.get_information(self.client, self.information_received)
		node.request_all_affiliated_entities(self.client, return_function=self.properties_received)

	def information_received(self, info_dict):
		# Generate the contents of the Metadata page
		## FIXME: It would be awesome if this were generated automatically from what is found :)
		# The Name entry
		self.properties_window["name_box"] = gtk.HBox()
		self.properties_window["metadata_page"].pack_start(self.properties_window["name_box"], expand=False)
		self.properties_window["name_label"] = gtk.Label("Name:")
		self.properties_window["name_box"].pack_start(self.properties_window["name_label"], expand=False)
		self.properties_window["name_entry"] = gtk.Entry()
		self.properties_window["name_entry"].set_text(str(self.properties_window["node"]))		# Default to Node's name
		self.properties_window["name_box"].pack_start(self.properties_window["name_entry"], expand=True)
		self.properties_window["name_set"] = gtk.Button(label="Set")
		self.properties_window["name_set"].connect("released", self.set_name)
		self.properties_window["name_box"].pack_end(self.properties_window["name_set"], expand=False)

		# The Title entry
		## FIXME: This should default to the node's title
		self.properties_window["title_box"] = gtk.HBox()
		self.properties_window["metadata_page"].pack_start(self.properties_window["title_box"], expand=False)
		self.properties_window["title_label"] = gtk.Label("Title:")
		self.properties_window["title_box"].pack_start(self.properties_window["title_label"], expand=False)
		self.properties_window["title_entry"] = gtk.Entry()
		self.properties_window["title_box"].pack_start(self.properties_window["title_entry"], expand=True)
		self.properties_window["title_set"] = gtk.Button(label="Set")
		self.properties_window["title_set"].connect("released", self.set_title)
		self.properties_window["title_box"].pack_end(self.properties_window["title_set"], expand=False)

	def properties_received(self, affiliation_dictionary):
		# Add a warning about affiliation status
		self.properties_window["warning_label"] = gtk.Label("Note that a Jabber ID can only be in one state at a time. Adding a Jabber ID to a category will remove it from the others. There must always be at least one owner.")
		self.properties_window["warning_label"].set_line_wrap(True)
		self.properties_window["affiliations_page"].pack_start(self.properties_window["warning_label"], expand=False)

		# Owners frame
		self.properties_window["owners_frame"] = gtk.Frame("Owners")
		self.properties_window["affiliations_page"].pack_start(self.properties_window["owners_frame"], expand=True)
		self.properties_window["owners_box"] = gtk.HBox()
		self.properties_window["owners_frame"].add(self.properties_window["owners_box"])

		# Owners list
		self.properties_window["owners_column"] = Column(attribute="name", title="Jabber ID")
		self.properties_window["owners"] = ObjectList([self.properties_window["owners_column"]])
		self.properties_window["owners_box"].pack_start(self.properties_window["owners"], expand=True)

		# Add Owner button
		self.properties_window["owners_buttons"] = gtk.VBox()
		self.properties_window["owners_box"].pack_end(self.properties_window["owners_buttons"], expand=False)
		self.properties_window["add_owner"] = gtk.Button(stock=gtk.STOCK_ADD)
		try:
			# Attempt to change the label of the stock button
			label = self.properties_window["add_owner"].get_children()[0]
			label = label.get_children()[0].get_children()[1]
			label = label.set_label("Add...")
		except:
			# If it fails then just go back to the default
			self.properties_window["add_owner"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.properties_window["add_owner"].connect("released", self.add_owner)
		self.properties_window["owners_buttons"].pack_start(self.properties_window["add_owner"], expand=False)

		# Remove Owner button
		self.properties_window["remove_owner"] = gtk.Button(stock=gtk.STOCK_REMOVE)
		self.properties_window["remove_owner"].connect("released", self.remove_owner)
		self.properties_window["owners_buttons"].pack_end(self.properties_window["remove_owner"], expand=False)

		# Publishers frame
		self.properties_window["publishers_frame"] = gtk.Frame("Publishers")
		self.properties_window["affiliations_page"].pack_start(self.properties_window["publishers_frame"], expand=True)
		self.properties_window["publishers_box"] = gtk.HBox()
		self.properties_window["publishers_frame"].add(self.properties_window["publishers_box"])

		# Add Publisher button
		self.properties_window["publishers_buttons"] = gtk.VBox()
		self.properties_window["publishers_box"].pack_end(self.properties_window["publishers_buttons"], expand=False)
		self.properties_window["add_publisher"] = gtk.Button(stock=gtk.STOCK_ADD)
		try:
			# Attempt to change the label of the stock button
			label = self.properties_window["add_publisher"].get_children()[0]
			label = label.get_children()[0].get_children()[1]
			label = label.set_label("Add...")
		except:
			# If it fails then just go back to the default
			self.properties_window["add_publisher"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.properties_window["add_publisher"].connect("released", self.add_publisher)
		self.properties_window["publishers_buttons"].pack_start(self.properties_window["add_publisher"], expand=False)

		# Remove Publisher button
		self.properties_window["remove_publisher"] = gtk.Button(stock=gtk.STOCK_REMOVE)
		self.properties_window["remove_publisher"].connect("released", self.remove_publisher)
		self.properties_window["publishers_buttons"].pack_end(self.properties_window["remove_publisher"], expand=False)

		# Publishers list
		self.properties_window["publishers_column"] = Column(attribute="name", title="Jabber ID")
		self.properties_window["publishers"] = ObjectList([self.properties_window["publishers_column"]])
		self.properties_window["publishers_box"].pack_start(self.properties_window["publishers"], expand=True)

		# Outcasts frame
		self.properties_window["outcasts_frame"] = gtk.Frame("Outcasts")
		self.properties_window["affiliations_page"].pack_start(self.properties_window["outcasts_frame"], expand=True)
		self.properties_window["outcasts_box"] = gtk.HBox()
		self.properties_window["outcasts_frame"].add(self.properties_window["outcasts_box"])

		# Add Outcast button
		self.properties_window["outcasts_buttons"] = gtk.VBox()
		self.properties_window["outcasts_box"].pack_end(self.properties_window["outcasts_buttons"], expand=False)
		self.properties_window["add_outcast"] = gtk.Button(stock=gtk.STOCK_ADD)
		try:
			# Attempt to change the label of the stock button
			label = self.properties_window["add_outcast"].get_children()[0]
			label = label.get_children()[0].get_children()[1]
			label = label.set_label("Add...")
		except:
			# If it fails then just go back to the default
			self.properties_window["add_outcast"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.properties_window["add_outcast"].connect("released", self.add_outcast)
		self.properties_window["outcasts_buttons"].pack_start(self.properties_window["add_outcast"], expand=False)

		# Remove Outcast button
		self.properties_window["remove_outcast"] = gtk.Button(stock=gtk.STOCK_REMOVE)
		self.properties_window["remove_outcast"].connect("released", self.remove_outcast)
		self.properties_window["outcasts_buttons"].pack_end(self.properties_window["remove_outcast"], expand=False)

		# Outcasts list
		self.properties_window["outcasts_column"] = Column(attribute="name", title="Jabber ID")
		self.properties_window["outcasts"] = ObjectList([self.properties_window["outcasts_column"]])
		self.properties_window["outcasts_box"].pack_start(self.properties_window["outcasts"], expand=True)

		self.properties_window["window"].show_all()

		if "owner" in affiliation_dictionary.keys():
			for jid in affiliation_dictionary["owner"]:
				self.properties_window["owners"].append(jid)
		if "publisher" in affiliation_dictionary.keys():
			for jid in affiliation_dictionary["publisher"]:
				self.properties_window["publishers"].append(jid)
		if "outcast" in affiliation_dictionary.keys():
			for jid in affiliation_dictionary["outcast"]:
				self.properties_window["outcasts"].append(jid)

	def set_name(self, args):
		pass

	def set_title(self, args):
		pass

	def add_owner(self, args):
		self.add_owner_window = {}
		self.add_owner_window["window"] = gtk.Window()
		self.add_owner_window["window"].set_title("Add owner to " + str(self.tree_view.get_selected()))
		self.add_owner_window["box"] = gtk.HBox()
		self.add_owner_window["window"].add(self.add_owner_window["box"])
		self.add_owner_window["jid_label"] = gtk.Label("Jabber ID:")
		self.add_owner_window["box"].pack_start(self.add_owner_window["jid_label"], expand=False)
		self.add_owner_window["jid_entry"] = gtk.Entry()
		self.add_owner_window["box"].pack_start(self.add_owner_window["jid_entry"], expand=False)
		self.add_owner_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.add_owner_window["add_button"].connect("released", self.add_owner_send)
		self.add_owner_window["box"].pack_end(self.add_owner_window["add_button"], expand=False)
		self.add_owner_window["window"].show_all()

	def add_owner_send(self, args):
		self.add_owner_window["jid"] = JID(self.add_owner_window["jid_entry"].get_text())
		self.tree_view.get_selected().modify_affiliations(self.client, {self.add_owner_window["jid"]:"owner"}, self.owner_added)

	def owner_added(self, reply):
		if reply == 0:
			self.properties_window["owners"].append(self.add_owner_window["jid"])
			for jid in self.properties_window["publishers"]:
				if str(jid) == str(self.add_owner_window["jid"]):
					self.properties_window["publishers"].remove(jid)
			for jid in self.properties_window["outcasts"]:
				if str(jid) == str(self.add_owner_window["jid"]):
					self.properties_window["outcasts"].remove(jid)
		else:
			print "Error"
		self.add_owner_window["window"].destroy()
		del self.add_owner_window

	def remove_owner(self, args):
		self.tree_view.get_selected().modify_affiliations(self.client, {self.properties_window["owners"].get_selected():"none"}, self.owner_removed)

	def owner_removed(self, reply):
		if reply == 0:
			self.properties_window["owners"].remove(self.properties_window["owners"].get_selected())
		else:
			print "Error"

	def add_publisher(self, args):
		self.add_publisher_window = {}
		self.add_publisher_window["window"] = gtk.Window()
		self.add_publisher_window["window"].set_title("Add publisher to " + str(self.tree_view.get_selected()))
		self.add_publisher_window["hbox"] = gtk.HBox()
		self.add_publisher_window["window"].add(self.add_publisher_window["hbox"])
		self.add_publisher_window["jid_label"] = gtk.Label("Jabber ID:")
		self.add_publisher_window["hbox"].pack_start(self.add_publisher_window["jid_label"], expand=False)
		self.add_publisher_window["jid_entry"] = gtk.Entry()
		self.add_publisher_window["hbox"].pack_start(self.add_publisher_window["jid_entry"], expand=False)
		self.add_publisher_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.add_publisher_window["add_button"].connect("released", self.add_publisher_send)
		self.add_publisher_window["hbox"].pack_end(self.add_publisher_window["add_button"], expand=False)
		self.add_publisher_window["window"].show_all()

	def add_publisher_send(self, args):
		self.add_publisher_window["jid"] = JID(self.add_publisher_window["jid_entry"].get_text())
		self.tree_view.get_selected().modify_affiliations(self.client, {self.add_publisher_window["jid"]:"publisher"}, self.publisher_added)

	def publisher_added(self, reply):
		if reply == 0:
			self.properties_window["publishers"].append(self.add_publisher_window["jid"])
			for jid in self.properties_window["owners"]:
				if str(jid) == str(self.add_publisher_window["jid"]):
					self.properties_window["owners"].remove(jid)
			for jid in self.properties_window["outcasts"]:
				if str(jid) == str(self.add_publisher_window["jid"]):
					self.properties_window["outcasts"].remove(jid)
		else:
			print "Error"
		self.add_publisher_window["window"].destroy()
		del self.add_publisher_window

	def remove_publisher(self, args):
		self.tree_view.get_selected().modify_affiliations(self.client, {self.properties_window["publishers"].get_selected():"none"}, self.publisher_removed)

	def publisher_removed(self, reply):
		if reply == 0:
			self.properties_window["publishers"].remove(self.properties_window["publishers"].get_selected())
		else:
			print "Error"

	def add_outcast(self, args):
		self.add_outcast_window = {}
		self.add_outcast_window["window"] = gtk.Window()
		self.add_outcast_window["window"].set_title("Add outcast to " + str(self.tree_view.get_selected()))
		self.add_outcast_window["hbox"] = gtk.HBox()
		self.add_outcast_window["window"].add(self.add_outcast_window["hbox"])
		self.add_outcast_window["jid_label"] = gtk.Label("Jabber ID:")
		self.add_outcast_window["hbox"].pack_start(self.add_outcast_window["jid_label"], expand=False)
		self.add_outcast_window["jid_entry"] = gtk.Entry()
		self.add_outcast_window["hbox"].pack_start(self.add_outcast_window["jid_entry"], expand=False)
		self.add_outcast_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD)
		self.add_outcast_window["add_button"].connect("released", self.add_outcast_send)
		self.add_outcast_window["hbox"].pack_end(self.add_outcast_window["add_button"], expand=False)
		self.add_outcast_window["window"].show_all()

	def add_outcast_send(self, args):
		self.add_outcast_window["jid"] = JID(self.add_outcast_window["jid_entry"].get_text())
		self.tree_view.get_selected().modify_affiliations(self.client, {self.add_outcast_window["jid"]:"outcast"}, self.outcast_added)

	def outcast_added(self, reply):
		if reply == 0:
			self.properties_window["outcasts"].append(self.add_outcast_window["jid"])
			for jid in self.properties_window["publishers"]:
				if str(jid) == str(self.add_outcast_window["jid"]):
					self.properties_window["publishers"].remove(jid)
			for jid in self.properties_window["owners"]:
				if str(jid) == str(self.add_outcast_window["jid"]):
					self.properties_window["owners"].remove(jid)
		else:
			print "Error"
		self.add_outcast_window["window"].destroy()
		del self.add_outcast_window

	def remove_outcast(self, args):
		self.tree_view.get_selected().modify_affiliations(self.client, {self.properties_window["outcasts"].get_selected():"none"}, self.outcast_removed)

	def outcast_removed(self, reply):
		if reply == 0:
			self.properties_window["outcasts"].remove(self.properties_window["outcasts"].get_selected())
		else:
			print "Error"

	def main(self):
		self.window.show_all()
		self.client.connect()
		#gobject.idle_add(self.idle_process, priority=gobject.PRIORITY_LOW)
		gobject.timeout_add(250, self.idle_process)
		gtk.main()

	def idle_process(self):
		"""A PubSubClient needs its process method run regularly. This
		method can be used to do that in gobject.timeout or idle_add."""
		self.client.process()
		return True

	def quit(self, arg):
		"""Exits the application. Runs when a signal to quit is received."""
		gtk.main_quit()
Esempio n. 9
0
class EventUI(GladeSlaveDelegate):
	
	show_ranges = ['day', 'week', 'month', 'year']

	def __init__(self, parent, mo):
		self.log = logging.getLogger('MINICAL')
		self.parent = parent
		self.mo = mo
		self.factory = Factory()

		self.__stop_auto_highlight = False # Disable automatic highlighting of events.
		self.__stop_auto_dayjump = False   # Disable automatically jumping to the start of the event on selection.
		self.__stop_auto_treeview_update = False   # FIXME

		GladeSlaveDelegate.__init__(self, gladefile='mo_tab_events', toplevel_name='window_main')

		# Set up the user interface
		eventColumns = [
			Column('start', title='Start', data_type=datetime.datetime, sorted=True),
			Column('end', title='End', data_type=datetime.datetime),
			Column('summaryformat', title='Summary', use_markup=True),
			Column('duration', title='Duration', justify=gtk.JUSTIFY_RIGHT)
		]
		self.treeview_event = ObjectTree(eventColumns)
		self.vbox_eventslist.add(self.treeview_event)
		self.combobox_display_range.set_active(self.show_ranges.index(self.mo.config['events.default_show'].lower()))
		cal_options = gtk.CALENDAR_WEEK_START_MONDAY
		if self.mo.config['events.cal_show_weeknr']:
			cal_options |= gtk.CALENDAR_SHOW_WEEK_NUMBERS
		self.calendar.set_display_options((self.calendar.get_display_options() | cal_options))

		# Connect signals
		self.treeview_event.connect('selection-changed', self.treeview_event__selection_changed)
		self.treeview_event.connect('row-activated', self.treeview_event__row_activated)
		self.treeview_event.connect('key-press-event', self.treeview_event__key_press_event)

		self.on_toolbutton_today__clicked()
	
	def refresh(self):
		"""
		Refresh the entire events tab. This clears everything and rebuilds it.
		Call this when events are removed outside of this class.
		"""
		self.treeview_event.clear()
		self.calendar.clear_marks()
		self.on_calendar__month_changed(self.calendar)
		self.treeview_event__update()

	def on_toolbutton_add__clicked(self, *args):
		now = datetime.datetime.now()
		sel_day = self.calendar.get_date()
		start = datetime.datetime(sel_day[0], sel_day[1]+1, sel_day[2], now.hour, now.minute)
		end = start + datetime.timedelta(hours=+1)

		event = self.factory.event(start, end)
		event = miniorganizer.ui.EventEditUI(self.mo, event).run()
		if event:
			self.mo.cal_model.add(event)
			self.treeview_event.append(None, event)
			self.on_calendar__month_changed(self.calendar)
			self.on_calendar__day_selected(self.calendar)
			self.parent.menuitem_save.set_sensitive(True)

	def on_toolbutton_remove__clicked(self, *args):
		sel_event = self.treeview_event.get_selected()
		sel_real_event = getattr(sel_event, 'real_event', sel_event) # Delete real event instead of recurring event

		if sel_event != sel_real_event:
			response = dialogs.yesno('This is a recurring event. Deleting it will delete all recurrences. Are you sure you want to delete it?')
			if response == gtk.RESPONSE_NO:
				return
			else:
				sel_event = sel_real_event

		if sel_event:
			self.mo.cal_model.delete(sel_event)
			self.treeview_event.remove(sel_event)
			self.on_calendar__month_changed(self.calendar)
			self.on_calendar__day_selected(self.calendar)
			self.parent.menuitem_save.set_sensitive(True)

	def on_toolbutton_edit__clicked(self, *args):
		sel_event = self.treeview_event.get_selected()
		self.treeview_event__row_activated(self.treeview_event, sel_event)

	def on_toolbutton_today__clicked(self, *args):
		today_dt = datetime.date.today()
		self.calendar.select_month(today_dt.month - 1, today_dt.year)
		self.calendar.select_day(today_dt.day)

	def on_calendar__month_changed(self, calendar, *args):
		self.calendar.clear_marks()
		sel_date = self.calendar.get_date()

		month_start = datetime.datetime(sel_date[0], sel_date[1]+1, 1)
		month_end = month_start + relativedelta(months=+1, seconds=-1)

		events = self.mo.cal_model.get_events() + self.mo.cal_model.get_events_recurring(month_start, month_end)
		for event in events:
			event_start = event.get_start()
			event_end = event.get_end()

			self.log.debug('Event %s, start: %s, end %s' % (event.get_summary(), event_start, event_end))
			# If the event falls in the month, mark the days the event spans in
			# the calendar.
			if (month_start >= event_start and month_start <= event_end) or \
               (month_end >= event_start and month_end <= event_end) or \
               (event_start >= month_start and event_end <= month_end):
				# Walk through the days of the event, marking them.
				delta_iter = datetime.datetime(*event_start.timetuple()[0:3])
				while True:
					if delta_iter.year == month_start.year and delta_iter.month == month_start.month:
						self.calendar.mark_day(delta_iter.day)
					delta_iter = delta_iter + datetime.timedelta(days=+1)
					if delta_iter >= event_end:
						break

	def on_calendar__day_selected(self, calendar, *args):
		# Make sure the correct display range is shown.
		self.on_combobox_display_range__changed()

		# Retrieve the day the user selected.
		sel_day = self.calendar.get_date()
		day_start = datetime.datetime(sel_day[0], sel_day[1]+1, sel_day[2])
		day_end = day_start + datetime.timedelta(days=+1)

		display_month = datetime.datetime(day_start.year, day_start.month, 1)

		# Highlight an event if it starts on the selected day.
		highlight_events = []
		events = [event for event in self.treeview_event]
		for event in events:
			event_start = event.get_start()
			event_end = event.get_end()

			# If this is the first event that starts on the day the user
			# selected, highlight the item in the list of events.
			if event_start >= day_start and event_start < day_end:
				highlight_events.insert(0, event)
			# If the selected day occurs during an event, highlight it. We
			# append it to the list of events to be highlighted, so it'll only
			# be highlighted if no event actually starts on that day.
			elif (day_start > event_start and day_start < event_end) or \
                 (day_end > event_start and day_end < event_end) or \
                 (event_start > day_start and event_end < day_end):
				highlight_events.append(event)

		# Highlight the first event on the day the user selected, unless the
		# user manually selected an event.
		if not self.__stop_auto_highlight:
			if highlight_events and highlight_events[0] in self.treeview_event:
				self.__stop_auto_dayjump = True
				self.treeview_event.select(highlight_events[0], True)
				self.__stop_auto_dayjump = False
			else:
				self.treeview_event.unselect_all()
			
	def on_calendar__day_selected_double_click(self, *args):
		self.on_toolbutton_add__clicked()

	def on_combobox_display_range__changed(self, *args):
		# Get the currently selected date in the calendar.
		sel_date = self.calendar.get_date()
		sel_dt_start = datetime.datetime(sel_date[0], sel_date[1]+1, sel_date[2])
		sel_dt_end = sel_dt_start + datetime.timedelta(days=+1)

		# Determine the start and end of the period that needs to be shown.
		display_range = self.combobox_display_range.get_active_text()
		if display_range == 'Day':
			display_start = sel_dt_start
			display_end = display_start + datetime.timedelta(days=+1, seconds=-1)
			text = '%s' % (display_start.strftime('%a %b %d %Y'))
		elif display_range == 'Week':
			display_start = sel_dt_start + datetime.timedelta(days=-sel_dt_start.weekday())
			display_end = display_start + datetime.timedelta(weeks=+1, seconds=-1)
			text = '%s - %s' % (display_start.strftime('%a %b %d %Y'), display_end.strftime('%a %b %d %Y'))
		elif display_range == 'Month':
			display_start = sel_dt_start + datetime.timedelta(days=-(sel_dt_start.day - 1))
			display_end = display_start + relativedelta(months=+1, seconds=-1)
			text = '%s' % (display_start.strftime('%b %Y'))
		elif display_range == 'Year':
			display_start = datetime.datetime(sel_dt_start.year, 1, 1)
			display_end = display_start + relativedelta(years=+1, seconds=-1)
			text = '%s' % (display_start.strftime('%Y'))
		else:
			raise Exception('No selected display range!')
			
		# Update the displayed range
		self.displayed_range.set_text(text)

		self.display_start = display_start
		self.display_end = display_end

		self.treeview_event__update()

	def treeview_event__update(self):
		if self.__stop_auto_treeview_update:
			return

		# First, remove all the recurring events, because they're generated on
		# the fly, so we can't know which ones in the list we need to remove.
		# Therefor we remove them every time.
		events_rm = []
		for event in self.treeview_event:
			if hasattr(event, 'real_event'):
				events_rm.append(event)
		for event in events_rm:
			self.treeview_event.remove(event)

		# Add the events for the displayed range to the list
		events = self.mo.cal_model.get_events() + self.mo.cal_model.get_events_recurring(self.display_start, self.display_end)
		for event in events:
			event_start = event.get_start()
			event_end = event.get_end()

			# If the currently displayed range includes an event, add it to the list.
			if (self.display_start >= event_start and self.display_start < event_end) or \
               (self.display_end >= event_start and self.display_end < event_end) or \
               (event_start >= self.display_start and event_end < self.display_end):
				if not event in self.treeview_event:
					self.treeview_event.append(None, event)
			# Otherwise, we remove it from the list, if it's present.
			else:
				if event in self.treeview_event:
					self.treeview_event.remove(event)
		
	def treeview_event__row_activated(self, list, object):
		# FIXME: This might be more complicated than it needs to be. See todo.py's row_activated.
		sel_event = self.treeview_event.get_selected()
		sel_event = getattr(sel_event, 'real_event', sel_event) # Edit real event instead of recurring event
		event = miniorganizer.ui.EventEditUI(self.mo, sel_event).run()
		self.on_calendar__month_changed(self.calendar)
		self.on_calendar__day_selected(self.calendar)
		if sel_event in self.treeview_event:
			self.treeview_event.select(sel_event, True)
		self.parent.menuitem_save.set_sensitive(True)

	def treeview_event__selection_changed(self, list, selection):
		# Stop the treeview from automatically updating itself because that
		# will remove the recurring events and regenerate them (with different
		# instance IDs) which means the selection may be invalid.
		self.__stop_auto_treeview_update = True

		sel_event = self.treeview_event.get_selected()
		has_selection = sel_event is not None

		# Enable / disable toolbuttons
		self.toolbutton_remove.set_sensitive(has_selection)
		self.toolbutton_edit.set_sensitive(has_selection)

		# Do not jump to the day of the event. This is needed because an event
		# can be automatically selected even if it doesn't start on a
		# particular day.
		if self.__stop_auto_dayjump:
			self.__stop_auto_treeview_update = False
			return

		# Stop this selection from being overwritten.
		self.__stop_auto_highlight = True

		if has_selection:
			# Make the calendar jump to the day on which this event begins.
			sel_event_start = sel_event.get_start()
			self.calendar.select_month(sel_event_start.month - 1, sel_event_start.year)
			self.calendar.select_day(sel_event_start.day)

		# Enable automatic highlighting of items
		self.__stop_auto_highlight = False
		self.__stop_auto_treeview_update = False
	
	def treeview_event__key_press_event(self, treeview, event):
		if event.keyval == gtk.keysyms.Delete:
			self.on_toolbutton_remove__clicked()
Esempio n. 10
0
class TodoUI(GladeSlaveDelegate):
	
	def __init__(self, parent, mo):
		self.parent = parent
		self.mo = mo
		self.factory = Factory()

		GladeSlaveDelegate.__init__(self, gladefile="mo_tab_todo", toplevel_name="window_main")

		# Set up the user interface
		todoColumns = [
			Column("done", title='Done', data_type=bool, editable=True),
			Column("summaryformat", title='Summary', use_markup=True),
			Column('priority', title='Priority', sorted=True, order=gtk.SORT_DESCENDING),
			ColoredColumn('due', title='Due', data_type=datetime.datetime, color='red', data_func=self.color_due),
			Column('created', title='Created', data_type=datetime.datetime)
		]
		self.treeview_todo = ObjectTree(todoColumns)
		self.vbox_todolist.add(self.treeview_todo)

		# Connect signals
		self.treeview_todo.connect('row-activated', self.treeview_todo__row_activated)
		self.treeview_todo.connect('selection-changed', self.treeview_todo__selection_changed)
		self.treeview_todo.connect('key-press-event', self.treeview_todo__key_press_event)

		self.refresh()

	def refresh(self):
		"""
		Refresh the entire todo tab. This clears everything and rebuilds it.
		Call this when todos are removed outside of this class.
		"""
		self.treeview_todo.clear()

		# Fill the user interface with information
		for todo in self.mo.cal_model.get_todos():
			parent = self.mo.cal_model.get_model_by_uid(todo.get_related_to())
			self.treeview_todo.append(parent, todo)
	
	def color_due(self, value):
		if not value:
			return(False)
		return(value < datetime.datetime.now())

	def on_toolbutton_add__clicked(self, *args):
		todo = self.factory.todo()
		todo = miniorganizer.ui.TodoEditUI(self.mo, todo).run()
		if todo:
			self.mo.cal_model.add(todo)
			self.treeview_todo.append(None, todo)
			self.parent.menuitem_save.set_sensitive(True)

	def on_toolbutton_edit__clicked(self, *args):
		sel_todo = self.treeview_todo.get_selected()
		self.treeview_todo__row_activated(self.treeview_todo, sel_todo)

	def on_toolbutton_remove__clicked(self, *args):
		sel_todo = self.treeview_todo.get_selected()
		if sel_todo:
			children = self.mo.cal_model.get_models_related_by_uid(sel_todo.get_uid())

			if children:
				response = dialogs.warning('This Todo contains sub-todos. Removing it will also remove the sub-todos. Is this what you want?', buttons=gtk.BUTTONS_YES_NO)
				if response != gtk.RESPONSE_YES:
					return

			self.treeview_todo.remove(sel_todo, True)
			self.mo.cal_model.delete(sel_todo, True)
			self.parent.menuitem_save.set_sensitive(True)

	def on_toolbutton_addsub__clicked(self, *args):
		parent_todo = self.treeview_todo.get_selected()
		todo = self.factory.todo(parent_todo)
		if miniorganizer.ui.TodoEditUI(self.mo, todo).run():
			self.mo.cal_model.add(todo)
			self.treeview_todo.append(parent_todo, todo)
			self.treeview_todo.expand(parent_todo)
			self.parent.menuitem_save.set_sensitive(True)
		
	def treeview_todo__selection_changed(self, list, selection):
		has_selection = selection is not None
		self.toolbutton_addsub.set_sensitive(has_selection)
		self.toolbutton_remove.set_sensitive(has_selection)
		self.toolbutton_edit.set_sensitive(has_selection)
	
	def treeview_todo__row_activated(self, list, object):
		todo = miniorganizer.ui.TodoEditUI(self.mo, object).run()
		self.parent.menuitem_save.set_sensitive(True)
	
	def treeview_todo__key_press_event(self, treeview, event):
		if event.keyval == gtk.keysyms.Delete:
			self.on_toolbutton_remove__clicked()