* Jelmer Vernooij <[EMAIL PROTECTED]> wrote: > * Scott Scriven wrote: > > The changes are available in my branch: > > > > lp:~toykeeper/bzr-gtk/bzr-vis-enhancements/ > > I like this in general. I would prefer to see the diff panel > below the revision metadata and file list, but I can live with > the current patch being merged.
It now includes an option to specify bottom-right or just bottom. The file list is still attached to the diff text, since it's a single widget. > - Please don't use hasattr() since it swallows exceptions Fixed. > - Please make not showing diffs the default Fixed. -- Scott
# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: [EMAIL PROTECTED] # target_branch: https://code.launchpad.net/~bzr-gtk/bzr-gtk/trunk # testament_sha1: c1b530f0d543f3994fa18ed642f0538f84820b48 # timestamp: 2008-07-17 06:07:55 -0600 # source_branch: https://code.launchpad.net/~bzr-gtk/bzr-gtk/trunk # base_revision_id: [EMAIL PROTECTED] # # Begin patch === modified file 'NEWS' --- NEWS 2008-07-01 21:56:25 +0000 +++ NEWS 2008-07-11 17:36:56 +0000 @@ -11,6 +11,12 @@ * Add "Send Merge Directive" button in bzr viz. (Jelmer Vernooij) + * Added an optional diff panel to bzr vis. (Scott Scriven) + + * Made bzr vis remember whether the toolbar is visible. (Scott Scriven) + + * Made bzr vis remember window and panel sizes. (Scott Scriven) + BUG FIXES * Replace _() calls by _i18n() calls. (Vincent Ladeuil, #187283) @@ -30,6 +36,10 @@ * List network drives in Olive. (Kevin Light, #244308) + * Removed some redundant screen redrawing in bzr vis. (Scott Scriven) + + * Fixed overflowing labels in bzr vis. (Scott Scriven) + CHANGES * Moved notify icon code to separate script. (Jelmer Vernooij) === modified file 'branchview/treeview.py' --- branchview/treeview.py 2008-06-30 20:11:24 +0000 +++ branchview/treeview.py 2008-07-10 06:23:54 +0000 @@ -325,6 +325,7 @@ if set_tooltip is not None: set_tooltip(treemodel.MESSAGE) + self._prev_cursor_path = None self.treeview.connect("cursor-changed", self._on_selection_changed) @@ -400,7 +401,8 @@ def _on_selection_changed(self, treeview): """callback for when the treeview changes.""" (path, focus) = treeview.get_cursor() - if path is not None: + if (path is not None) and (path != self._prev_cursor_path): + self._prev_cursor_path = path # avoid emitting twice per click self.iter = self.model.get_iter(path) self.emit('revision-selected') === modified file 'diff.py' --- diff.py 2008-05-22 02:33:40 +0000 +++ diff.py 2008-07-17 11:24:26 +0000 @@ -330,9 +330,10 @@ # text view def set_diff_text_sections(self, sections): - self.diff_view = DiffFileView() + if getattr(self, 'diff_view', None) is None: + self.diff_view = DiffFileView() + self.pack2(self.diff_view) self.diff_view.show() - self.pack2(self.diff_view) for oldname, newname, patch in sections: self.diff_view._diffs[newname] = str(patch) if newname is None: @@ -346,8 +347,9 @@ Compares the two trees and populates the window with the differences. """ - self.diff_view = DiffView() - self.pack2(self.diff_view) + if getattr(self, 'diff_view', None) is None: + self.diff_view = DiffView() + self.pack2(self.diff_view) self.diff_view.show() self.diff_view.set_trees(rev_tree, parent_tree) self.rev_tree = rev_tree @@ -380,6 +382,7 @@ self.model.append(titer, [ path, path ]) self.treeview.expand_all() + self.diff_view.show_diff(None) def set_file(self, file_path): """Select the current file to display""" === modified file 'revisionview.py' --- revisionview.py 2008-06-29 19:18:34 +0000 +++ revisionview.py 2008-07-11 15:46:32 +0000 @@ -509,7 +509,7 @@ table.resize(max(len(revids), 1), 2) for idx, revid in enumerate(revids): - align = gtk.Alignment(0.0, 0.0) + align = gtk.Alignment(0.0, 0.0, 1, 1) widgets.append(align) table.attach(align, 1, 2, idx, idx + 1, gtk.EXPAND | gtk.FILL, gtk.FILL) @@ -532,12 +532,16 @@ hbox.pack_start(button, expand=False, fill=True) button.show() - button = gtk.Button(revid) + button = gtk.Button() + revid_label = gtk.Label(str(revid)) + revid_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE) + revid_label.set_alignment(0.0, 0.5) + button.add(revid_label) button.connect("clicked", lambda w, r: self.set_revision(self._repository.get_revision(r)), revid) button.set_use_underline(False) - hbox.pack_start(button, expand=False, fill=True) - button.show() + hbox.pack_start(button, expand=True, fill=True) + button.show_all() def _create_general(self): vbox = gtk.VBox(False, 6) @@ -566,102 +570,91 @@ self.table.set_col_spacings(6) self.table.show() - align = gtk.Alignment(1.0, 0.5) + row = 0 + label = gtk.Label() + label.set_alignment(1.0, 0.5) label.set_markup("<b>Revision Id:</b>") - align.add(label) - self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL) - align.show() + self.table.attach(label, 0, 1, row, row+1, gtk.FILL, gtk.FILL) label.show() - align = gtk.Alignment(0.0, 0.5) revision_id = gtk.Label() + revision_id.set_ellipsize(pango.ELLIPSIZE_MIDDLE) + revision_id.set_alignment(0.0, 0.5) revision_id.set_selectable(True) self.connect('notify::revision', lambda w, p: revision_id.set_text(self._revision.revision_id)) - align.add(revision_id) - self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL) - align.show() + self.table.attach(revision_id, 1, 2, row, row+1, gtk.EXPAND | gtk.FILL, gtk.FILL) revision_id.show() - align = gtk.Alignment(1.0, 0.5) + row += 1 self.author_label = gtk.Label() + self.author_label.set_alignment(1.0, 0.5) self.author_label.set_markup("<b>Author:</b>") - align.add(self.author_label) - self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL) - align.show() + self.table.attach(self.author_label, 0, 1, row, row+1, gtk.FILL, gtk.FILL) self.author_label.show() - align = gtk.Alignment(0.0, 0.5) self.author = gtk.Label() + self.author.set_ellipsize(pango.ELLIPSIZE_END) + self.author.set_alignment(0.0, 0.5) self.author.set_selectable(True) - align.add(self.author) - self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL) - align.show() + self.table.attach(self.author, 1, 2, row, row+1, gtk.EXPAND | gtk.FILL, gtk.FILL) self.author.show() self.author.hide() - align = gtk.Alignment(1.0, 0.5) + row += 1 label = gtk.Label() + label.set_alignment(1.0, 0.5) label.set_markup("<b>Committer:</b>") - align.add(label) - self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL) - align.show() + self.table.attach(label, 0, 1, row, row+1, gtk.FILL, gtk.FILL) label.show() - align = gtk.Alignment(0.0, 0.5) self.committer = gtk.Label() + self.committer.set_ellipsize(pango.ELLIPSIZE_END) + self.committer.set_alignment(0.0, 0.5) self.committer.set_selectable(True) - align.add(self.committer) - self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL) - align.show() + self.table.attach(self.committer, 1, 2, row, row+1, gtk.EXPAND | gtk.FILL, gtk.FILL) self.committer.show() - align = gtk.Alignment(0.0, 0.5) + row += 1 label = gtk.Label() + label.set_alignment(1.0, 0.5) label.set_markup("<b>Branch nick:</b>") - align.add(label) - self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL) + self.table.attach(label, 0, 1, row, row+1, gtk.FILL, gtk.FILL) label.show() - align.show() - align = gtk.Alignment(0.0, 0.5) self.branchnick_label = gtk.Label() + self.branchnick_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE) + self.branchnick_label.set_alignment(0.0, 0.5) self.branchnick_label.set_selectable(True) - align.add(self.branchnick_label) - self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL) + self.table.attach(self.branchnick_label, 1, 2, row, row+1, gtk.EXPAND | gtk.FILL, gtk.FILL) self.branchnick_label.show() - align.show() - align = gtk.Alignment(1.0, 0.5) + row += 1 label = gtk.Label() + label.set_alignment(1.0, 0.5) label.set_markup("<b>Timestamp:</b>") - align.add(label) - self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL) - align.show() + self.table.attach(label, 0, 1, row, row+1, gtk.FILL, gtk.FILL) label.show() - align = gtk.Alignment(0.0, 0.5) self.timestamp = gtk.Label() + self.timestamp.set_ellipsize(pango.ELLIPSIZE_END) + self.timestamp.set_alignment(0.0, 0.5) self.timestamp.set_selectable(True) - align.add(self.timestamp) - self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL) - align.show() + self.table.attach(self.timestamp, 1, 2, row, row+1, gtk.EXPAND | gtk.FILL, gtk.FILL) self.timestamp.show() - align = gtk.Alignment(1.0, 0.5) + row += 1 self.tags_label = gtk.Label() + self.tags_label.set_alignment(1.0, 0.5) self.tags_label.set_markup("<b>Tags:</b>") - align.add(self.tags_label) - align.show() - self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL) + self.table.attach(self.tags_label, 0, 1, row, row+1, gtk.FILL, gtk.FILL) self.tags_label.show() - align = gtk.Alignment(0.0, 0.5) self.tags_list = gtk.Label() - align.add(self.tags_list) - self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL) - align.show() + self.tags_list.set_ellipsize(pango.ELLIPSIZE_MIDDLE) + self.tags_list.set_alignment(0.0, 0.5) + self.table.attach(self.tags_list, 1, 2, row, row+1, gtk.EXPAND | gtk.FILL, gtk.FILL) self.tags_list.show() self.connect('notify::revision', self._add_tags) === modified file 'viz/branchwin.py' --- viz/branchwin.py 2008-07-01 21:54:21 +0000 +++ viz/branchwin.py 2008-07-17 12:06:33 +0000 @@ -48,6 +48,8 @@ self.maxnum = maxnum self.config = GlobalConfig() + self._sizes = {} # window and widget sizes + if self.config.get_user_option('viz-compact-view') == 'yes': self.compact_view = True else: @@ -60,7 +62,13 @@ monitor = screen.get_monitor_geometry(0) width = int(monitor.width * 0.75) height = int(monitor.height * 0.75) + # user-configured window size + size = self._load_size('viz-window-size') + if size: + width, height = size self.set_default_size(width, height) + self.set_size_request(width/3, height/3) + self.connect("size-allocate", self._on_size_allocate, 'viz-window-size') # FIXME AndyFitz! icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON) @@ -104,13 +112,15 @@ self.add(vbox) self.paned = gtk.VPaned() - self.paned.pack1(self.construct_top(), resize=True, shrink=False) - self.paned.pack2(self.construct_bottom(), resize=False, shrink=True) + self.paned.pack1(self.construct_top(), resize=False, shrink=True) + self.paned.pack2(self.construct_bottom(), resize=True, shrink=False) self.paned.show() - vbox.pack_start(self.construct_menubar(), expand=False, fill=True) - vbox.pack_start(self.construct_navigation(), expand=False, fill=True) - + nav = self.construct_navigation() + menubar = self.construct_menubar() + vbox.pack_start(menubar, expand=False, fill=True) + vbox.pack_start(nav, expand=False, fill=True) + vbox.pack_start(self.paned, expand=True, fill=True) vbox.set_focus_child(self.paned) @@ -161,14 +171,31 @@ view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar") view_menu_toolbar.set_active(True) + if self.config.get_user_option('viz-toolbar-visible') == 'False': + view_menu_toolbar.set_active(False) + self.toolbar.hide() view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed) view_menu_compact = gtk.CheckMenuItem("Show Compact Graph") view_menu_compact.set_active(self.compact_view) view_menu_compact.connect('activate', self._brokenlines_toggled_cb) + view_menu_diffs = gtk.CheckMenuItem("Show Diffs") + view_menu_diffs.set_active(False) + if self.config.get_user_option('viz-show-diffs') == 'True': + view_menu_diffs.set_active(True) + view_menu_diffs.connect('toggled', self._diff_visibility_changed) + + view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs") + view_menu_wide_diffs.set_active(False) + if self.config.get_user_option('viz-wide-diffs') == 'True': + view_menu_wide_diffs.set_active(True) + view_menu_wide_diffs.connect('toggled', self._diff_placement_changed) + view_menu.add(view_menu_toolbar) view_menu.add(view_menu_compact) + view_menu.add(view_menu_diffs) + view_menu.add(view_menu_wide_diffs) view_menu.add(gtk.SeparatorMenuItem()) self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column") @@ -275,6 +302,15 @@ align = gtk.Alignment(0.0, 0.0, 1.0, 1.0) align.set_padding(5, 0, 0, 0) align.add(self.treeview) + # user-configured size + size = self._load_size('viz-graph-size') + if size: + width, height = size + align.set_size_request(width, height) + else: + (width, height) = self.get_size() + align.set_size_request(width, int(height / 2.5)) + align.connect('size-allocate', self._on_size_allocate, 'viz-graph-size') align.show() return align @@ -302,15 +338,37 @@ def construct_bottom(self): """Construct the bottom half of the window.""" + if self.config.get_user_option('viz-wide-diffs') == 'True': + self.diff_paned = gtk.VPaned() + else: + self.diff_paned = gtk.HPaned() + (width, height) = self.get_size() + self.diff_paned.set_size_request(20, 20) # shrinkable + from bzrlib.plugins.gtk.revisionview import RevisionView self.revisionview = RevisionView(branch=self.branch) - (width, height) = self.get_size() - self.revisionview.set_size_request(width, int(height / 2.5)) + self.revisionview.set_size_request(width/3, int(height / 2.5)) + # user-configured size + size = self._load_size('viz-revisionview-size') + if size: + width, height = size + self.revisionview.set_size_request(width, height) + self.revisionview.connect('size-allocate', self._on_size_allocate, 'viz-revisionview-size') self.revisionview.show() self.revisionview.set_show_callback(self._show_clicked_cb) self.revisionview.connect('notify::revision', self._go_clicked_cb) self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags()) - return self.revisionview + self.diff_paned.pack1(self.revisionview) + + from bzrlib.plugins.gtk.diff import DiffWidget + self.diff = DiffWidget() + self.diff_paned.pack2(self.diff) + + self.diff_paned.show_all() + if self.config.get_user_option('viz-show-diffs') != 'True': + self.diff.hide() + + return self.diff_paned def _tag_selected_cb(self, menuitem, revid): self.treeview.set_revision_id(revid) @@ -367,7 +425,9 @@ self.revisionview.set_revision(revision) self.revisionview.set_children(children) - + + self.update_diff_panel(revision, parents) + def _tree_revision_activated(self, widget, path, col): # TODO: more than one parent """Callback for when a treeview row gets activated.""" @@ -439,9 +499,38 @@ def _toolbar_visibility_changed(self, col): if col.get_active(): - self.toolbar.show() + self.toolbar.show() else: self.toolbar.hide() + self.config.set_user_option('viz-toolbar-visible', col.get_active()) + + def _make_diff_nonzero_size(self): + """make sure the diff isn't zero-width or zero-height""" + alloc = self.diff.get_allocation() + if (alloc.width < 10) or (alloc.height < 10): + width, height = self.get_size() + self.revisionview.set_size_request(width/3, int(height / 2.5)) + + def _diff_visibility_changed(self, col): + """Hide or show the diff panel.""" + if col.get_active(): + self.diff.show() + self._make_diff_nonzero_size() + else: + self.diff.hide() + self.config.set_user_option('viz-show-diffs', str(col.get_active())) + self.update_diff_panel() + + def _diff_placement_changed(self, col): + """Toggle the diff panel's position.""" + self.config.set_user_option('viz-wide-diffs', str(col.get_active())) + + old = self.paned.get_child2() + self.paned.remove(old) + self.paned.pack2(self.construct_bottom(), resize=True, shrink=False) + self._make_diff_nonzero_size() + + self.treeview.emit('revision-selected') def _show_about_cb(self, w): dialog = AboutDialog() @@ -473,6 +562,34 @@ self.go_menu_tags.show_all() + def _load_size(self, name): + """Read and parse 'name' from self.config. + The value is a string, formatted as WIDTHxHEIGHT + Returns None, or (width, height) + """ + size = self.config.get_user_option(name) + if size: + width, height = [int(num) for num in size.split('x')] + # avoid writing config every time we start + self._sizes[name] = (width, height) + return width, height + return None + + def _on_size_allocate(self, widget, allocation, name): + """When window has been resized, save the new size.""" + width, height = 0, 0 + if name in self._sizes: + width, height = self._sizes[name] + + size_changed = (width != allocation.width) or \ + (height != allocation.height) + + if size_changed: + width, height = allocation.width, allocation.height + self._sizes[name] = (width, height) + value = '%sx%s' % (width, height) + self.config.set_user_option(name, value) + def show_diff(self, revid=None, parentid=None): """Open a new window to show a diff between the given revisions.""" from bzrlib.plugins.gtk.diff import DiffWindow @@ -488,4 +605,25 @@ window.set_diff(description, rev_tree, parent_tree) window.show() - + def update_diff_panel(self, revision=None, parents=None): + """Show the current revision in the diff panel.""" + if self.config.get_user_option('viz-show-diffs') != 'True': + return + + if not revision: # default to selected row + revision = self.treeview.get_revision() + if (not revision) or (revision == NULL_REVISION): + return + + if not parents: # default to selected row's parents + parents = self.treeview.get_parents() + if len(parents) == 0: + parent_id = None + else: + parent_id = parents[0] + + rev_tree = self.branch.repository.revision_tree(revision.revision_id) + parent_tree = self.branch.repository.revision_tree(parent_id) + + self.diff.set_diff(rev_tree, parent_tree) + self.diff.show_all() # Begin bundle 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 aAZxO9c6EchchN/jmX+n/4u5IpwoSADQBw+A
-- bzr-gtk mailing list [email protected] Modify settings or unsubscribe at: https://lists.canonical.com/mailman/listinfo/bzr-gtk
