# Copyright (C) 2007 by Szilveszter Farkas (Phanatic) # Copyright (C) 2007 by Jelmer Vernooij # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA try: import pygtk pygtk.require("2.0") except: pass import gtk from bzrlib.plugins.gtk.logview import LogView from dialog import error_dialog from revidbox import RevisionSelectionBox class TagsWindow(gtk.Window): """ Tags window. Allows the user to view/add/remove tags. """ def __init__(self, branch, parent=None): """ Initialize the Tags window. """ gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) # Get arguments self.branch = branch self._parent = parent # Create the widgets self._button_add = gtk.Button(stock=gtk.STOCK_ADD) self._button_remove = gtk.Button(stock=gtk.STOCK_REMOVE) self._button_refresh = gtk.Button(stock=gtk.STOCK_REFRESH) self._button_close = gtk.Button(stock=gtk.STOCK_CLOSE) self._model = gtk.ListStore(str, str) self._treeview_tags = gtk.TreeView(self._model) self._scrolledwindow_tags = gtk.ScrolledWindow() self._logview = LogView() self._hbox = gtk.HBox() self._vbox_buttons = gtk.VBox() self._vbox_buttons_top = gtk.VBox() self._vbox_buttons_bottom = gtk.VBox() self._vpaned = gtk.VPaned() # Set callbacks self._button_add.connect('clicked', self._on_add_clicked) self._button_close.connect('clicked', self._on_close_clicked) self._button_refresh.connect('clicked', self._on_refresh_clicked) self._button_remove.connect('clicked', self._on_remove_clicked) self._treeview_tags.connect('cursor-changed', self._on_treeview_changed) self.connect('key-press-event', self._on_key_pressed) if parent is None: self.connect('delete-event', gtk.main_quit) # Set properties self.set_title(_("Tags")) self.set_default_size(600, 400) self._scrolledwindow_tags.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self._hbox.set_border_width(5) self._hbox.set_spacing(5) self._vbox_buttons.set_size_request(100, -1) self._vbox_buttons_top.set_spacing(3) self._vpaned.set_position(200) # Construct the dialog self._scrolledwindow_tags.add(self._treeview_tags) self._vbox_buttons_top.pack_start(self._button_add, False, False) self._vbox_buttons_top.pack_start(self._button_remove, False, False) self._vbox_buttons_top.pack_start(self._button_refresh, False, False) self._vbox_buttons_bottom.pack_start(self._button_close, False, False) self._vbox_buttons.pack_start(self._vbox_buttons_top, True, True) self._vbox_buttons.pack_start(self._vbox_buttons_bottom, False, False) self._vpaned.add1(self._scrolledwindow_tags) self._vpaned.add2(self._logview) self._hbox.pack_start(self._vpaned, True, True) self._hbox.pack_start(self._vbox_buttons, False, True) self.add(self._hbox) # Default to no tags self._no_tags = True # Load the tags self._load_tags() # Display everything self._hbox.show_all() def _load_tags(self): """ Load the tags into the TreeView. """ tvcol_name = gtk.TreeViewColumn(_("Tag Name"), gtk.CellRendererText(), text=0) tvcol_name.set_resizable(True) tvcol_revid = gtk.TreeViewColumn(_("Revision ID"), gtk.CellRendererText(), text=1) tvcol_revid.set_resizable(True) self._treeview_tags.append_column(tvcol_name) self._treeview_tags.append_column(tvcol_revid) self._treeview_tags.set_search_column(0) self._refresh_tags() def _refresh_tags(self): """ Refresh the list of tags. """ self._model.clear() if self.branch.supports_tags(): tags = self.branch.tags.get_tag_dict() if len(tags) > 0: self._no_tags = False for name, target in tags.items(): self._model.append([name, target]) self._button_add.set_sensitive(True) self._button_remove.set_sensitive(True) else: self._no_tags = True self._no_tags_available() else: self._no_tags = True self._tags_not_supported() self._treeview_tags.set_model(self._model) def _tags_not_supported(self): """ Tags are not supported. """ self._model.append([_("Tags are not supported by this branch format. Please upgrade."), ""]) self._button_add.set_sensitive(False) self._button_remove.set_sensitive(False) def _no_tags_available(self): """ No tags in the branch. """ self._model.append([_("No tagged revisions in the branch."), ""]) self._button_add.set_sensitive(True) self._button_remove.set_sensitive(False) def _on_add_clicked(self, widget): """ Add button event handler. """ dialog = AddTagDialog(self.branch.repository, None, self.branch, self) response = dialog.run() if response != gtk.RESPONSE_NONE: dialog.hide() if response == gtk.RESPONSE_OK: self.branch.tags.set_tag(dialog.tagname, dialog._revid) self._refresh_tags() dialog.destroy() def _on_close_clicked(self, widget): """ Close button event handler. """ self.destroy() if self._parent is None: gtk.main_quit() def _on_key_pressed(self, widget, event): """ Key press event handler. """ keyname = gtk.gdk.keyval_name(event.keyval) func = getattr(self, '_on_key_press_' + keyname, None) if func: return func(event) def _on_key_press_w(self, event): if event.state & gtk.gdk.CONTROL_MASK: self.destroy() if self._parent is None: gtk.main_quit() def _on_key_press_q(self, event): if event.state & gtk.gdk.CONTROL_MASK: gtk.main_quit() def _on_refresh_clicked(self, widget): """ Refresh button event handler. """ self._refresh_tags() def _on_remove_clicked(self, widget): """ Remove button event handler. """ (path, col) = self._treeview_tags.get_cursor() if path is None: return tag = self._model[path][0] dialog = RemoveTagDialog(tag, self) response = dialog.run() if response != gtk.RESPONSE_NONE: dialog.hide() if response == gtk.RESPONSE_OK: self.branch.tags.delete_tag(tag) self._refresh_tags() dialog.destroy() def _on_treeview_changed(self, *args): """ When a user clicks on a tag. """ # Refresh LogView only if there are tags available if not self._no_tags: (path, col) = self._treeview_tags.get_cursor() revision = self._model[path][1] self._logview.set_revision(self.branch.repository.get_revision(revision)) class RemoveTagDialog(gtk.Dialog): """ Confirm removal of tag. """ def __init__(self, tagname, parent): gtk.Dialog.__init__(self, title="Remove tag", parent=parent, flags=0, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) # Get the arguments self.tag = tagname # Create the widgets self._hbox = gtk.HBox() self._vbox_question = gtk.VBox() self._image_question = gtk.image_new_from_stock(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG) self._label_title = gtk.Label() self._label_question = gtk.Label() self._button_remove = gtk.Button(_("_Remove tag"), use_underline=True) # Set callbacks self._button_remove.connect('clicked', self._on_remove_clicked) # Set properties self._hbox.set_border_width(5) self._hbox.set_spacing(5) self._vbox_question.set_spacing(3) self._label_title.set_markup(_("Remove tag?")) self._label_title.set_alignment(0.0, 0.5) self._label_question.set_markup(_("Are you sure you want to remove the tag: %s?") % self.tag) self._label_question.set_alignment(0.0, 0.5) self._button_remove.set_image(gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_BUTTON)) self._button_remove.set_flags(gtk.CAN_DEFAULT) # Construct the dialog self._vbox_question.pack_start(self._label_title) self._vbox_question.pack_start(self._label_question) self._hbox.pack_start(self._image_question) self._hbox.pack_start(self._vbox_question) self.vbox.add(self._hbox) self.action_area.pack_end(self._button_remove) # Display dialog self.vbox.show_all() # Default to Commit button self._button_remove.grab_default() def _on_remove_clicked(self, widget): """ Remove button event handler. """ self.response(gtk.RESPONSE_OK) class AddTagDialog(gtk.Dialog): """ Add tag dialog. """ def __init__(self, repository, revid=None, branch=None, parent=None): """ Initialize Add tag dialog. """ gtk.Dialog.__init__(self, title="Add tag", parent=parent, flags=0, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) # Get arguments self._repository = repository self._revid = revid self._branch = branch # Create the widgets self._button_add = gtk.Button(_("_Add tag"), use_underline=True) self._table = gtk.Table(2, 2) self._label_name = gtk.Label(_("Tag Name:")) self._label_revid = gtk.Label(_("Revision ID:")) self._entry_name = gtk.Entry() if self._revid is not None: self._hbox_revid = gtk.Label(self._revid) else: self._hbox_revid = RevisionSelectionBox(self._branch) # Set callbacks self._button_add.connect('clicked', self._on_add_clicked) # Set properties self._label_name.set_alignment(0, 0.5) self._label_revid.set_alignment(0, 0.5) self._button_add.set_image(gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON)) self._button_add.set_flags(gtk.CAN_DEFAULT) # Construct the dialog self._table.attach(self._label_name, 0, 1, 0, 1) self._table.attach(self._label_revid, 0, 1, 1, 2) self._table.attach(self._entry_name, 1, 2, 0, 1) self._table.attach(self._hbox_revid, 1, 2, 1, 2) self.vbox.add(self._table) self.action_area.pack_end(self._button_add) # Show the dialog self.vbox.show_all() def _on_add_clicked(self, widget): """ Add button clicked handler. """ if len(self._entry_name.get_text()) == 0: error_dialog(_("No tag name specified"), _("You have to specify the tag's desired name.")) return if self._revid is None: if self._hbox_revid.get_revision_id() is None: self._revid = self._branch.last_revision() else: self._revid = self.hbox_revid.get_revision_id() self.tagname = self._entry_name.get_text() self.response(gtk.RESPONSE_OK)