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

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

    domain = 'stoq'
    toplevel_name = gladefile = 'AdditionListSlave'
    widgets = ('add_button',
               'delete_button',
               'klist',
               'list_vbox',
               'edit_button')
    gsignal('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()
Example #2
0
class AdditionListSlave(SearchSlave):
    """A slave that offers a simple list and its management.

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

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

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

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

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

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

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

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

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

        if not result:
            return

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

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

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

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

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

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

    #
    # Hooks
    #

    def get_items(self):
        return []

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

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

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

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

    #
    # Public API
    #

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

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

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

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

        return button

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

        self.message_label.set_markup(message)

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

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

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

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

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

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

    #
    # Signal handlers
    #

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

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

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

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

    def on_delete_button__clicked(self, button):
        self._clear()
Example #3
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()