16
from bzrlib.plugins.gtk.window import Window
 
16
17
from bzrlib.plugins.gtk import icon_path
 
17
 
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
 
18
18
from bzrlib.plugins.gtk.tags import AddTagDialog
 
19
19
from bzrlib.plugins.gtk.preferences import PreferencesWindow
 
20
 
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
 
21
 
from bzrlib.plugins.gtk.window import Window
 
23
 
from bzrlib.config import BranchConfig, GlobalConfig
 
 
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
 
24
21
from bzrlib.revision import Revision, NULL_REVISION
 
25
 
from bzrlib.trace import mutter
 
 
22
from bzrlib.config import BranchConfig
 
 
23
from bzrlib.config import GlobalConfig
 
27
25
class BranchWindow(Window):
 
 
58
54
        self.set_title(branch.nick + " - revision history")
 
60
 
        # user-configured window size
 
61
 
        size = self._load_size('viz-window-size')
 
65
 
            # Use three-quarters of the screen by default
 
66
 
            screen = self.get_screen()
 
67
 
            monitor = screen.get_monitor_geometry(0)
 
68
 
            width = int(monitor.width * 0.75)
 
69
 
            height = int(monitor.height * 0.75)
 
 
56
        # Use three-quarters of the screen by default
 
 
57
        screen = self.get_screen()
 
 
58
        monitor = screen.get_monitor_geometry(0)
 
 
59
        width = int(monitor.width * 0.75)
 
 
60
        height = int(monitor.height * 0.75)
 
70
61
        self.set_default_size(width, height)
 
71
 
        self.set_size_request(width/3, height/3)
 
72
 
        self.connect("size-allocate", self._on_size_allocate, 'viz-window-size')
 
75
64
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
 
 
115
104
        self.paned = gtk.VPaned()
 
116
 
        self.paned.pack1(self.construct_top(), resize=False, shrink=True)
 
117
 
        self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
 
 
105
        self.paned.pack1(self.construct_top(), resize=True, shrink=False)
 
 
106
        self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
 
118
107
        self.paned.show()
 
120
 
        nav = self.construct_navigation()
 
121
 
        menubar = self.construct_menubar()
 
122
 
        vbox.pack_start(menubar, expand=False, fill=True)
 
123
 
        vbox.pack_start(nav, expand=False, fill=True)
 
 
109
        vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
 
 
110
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
 
125
112
        vbox.pack_start(self.paned, expand=True, fill=True)
 
126
113
        vbox.set_focus_child(self.paned)
 
130
117
    def construct_menubar(self):
 
131
118
        menubar = gtk.MenuBar()
 
 
148
135
        edit_menuitem = gtk.MenuItem("_Edit")
 
149
136
        edit_menuitem.set_submenu(edit_menu)
 
 
138
        edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
 
151
140
        edit_menu_branchopts = gtk.MenuItem("Branch Settings")
 
152
141
        edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
 
154
143
        edit_menu_globopts = gtk.MenuItem("Global Settings")
 
155
144
        edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
 
 
146
        edit_menu.add(edit_menu_find)
 
157
147
        edit_menu.add(edit_menu_branchopts)
 
158
148
        edit_menu.add(edit_menu_globopts)
 
 
170
160
        view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
 
171
161
        view_menu_toolbar.set_active(True)
 
172
 
        if self.config.get_user_option('viz-toolbar-visible') == 'False':
 
173
 
            view_menu_toolbar.set_active(False)
 
175
162
        view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
 
177
164
        view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
 
178
165
        view_menu_compact.set_active(self.compact_view)
 
179
166
        view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
 
181
 
        view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
 
182
 
        view_menu_diffs.set_active(False)
 
183
 
        if self.config.get_user_option('viz-show-diffs') == 'True':
 
184
 
            view_menu_diffs.set_active(True)
 
185
 
        view_menu_diffs.connect('toggled', self._diff_visibility_changed)
 
187
 
        view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
 
188
 
        view_menu_wide_diffs.set_active(False)
 
189
 
        if self.config.get_user_option('viz-wide-diffs') == 'True':
 
190
 
            view_menu_wide_diffs.set_active(True)
 
191
 
        view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
 
193
 
        view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
 
194
 
        view_menu_wrap_diffs.set_active(False)
 
195
 
        if self.config.get_user_option('viz-wrap-diffs') == 'True':
 
196
 
            view_menu_wrap_diffs.set_active(True)
 
197
 
        view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
 
199
168
        view_menu.add(view_menu_toolbar)
 
200
169
        view_menu.add(view_menu_compact)
 
201
170
        view_menu.add(gtk.SeparatorMenuItem())
 
202
 
        view_menu.add(view_menu_diffs)
 
203
 
        view_menu.add(view_menu_wide_diffs)
 
204
 
        view_menu.add(view_menu_wrap_diffs)
 
205
 
        view_menu.add(gtk.SeparatorMenuItem())
 
207
172
        self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
 
208
173
        self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
 
 
230
195
        tag_image.set_from_file(icon_path("tag-16.png"))
 
231
196
        self.go_menu_tags = gtk.ImageMenuItem("_Tags")
 
232
197
        self.go_menu_tags.set_image(tag_image)
 
233
 
        self.treeview.connect('refreshed', lambda w: self._update_tags())
 
235
200
        go_menu.add(go_menu_next)
 
236
201
        go_menu.add(go_menu_prev)
 
237
202
        go_menu.add(gtk.SeparatorMenuItem())
 
238
203
        go_menu.add(self.go_menu_tags)
 
240
 
        self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
 
 
205
        revision_menu = gtk.Menu()
 
241
206
        revision_menuitem = gtk.MenuItem("_Revision")
 
242
 
        revision_menuitem.set_submenu(self.revision_menu)
 
 
207
        revision_menuitem.set_submenu(revision_menu)
 
 
209
        revision_menu_diff = gtk.MenuItem("View Changes")
 
 
210
        revision_menu_diff.connect('activate', 
 
 
213
        revision_menu_compare = gtk.MenuItem("Compare with...")
 
 
214
        revision_menu_compare.connect('activate',
 
 
215
                self._compare_with_cb)
 
 
217
        revision_menu_tag = gtk.MenuItem("Tag Revision")
 
 
218
        revision_menu_tag.connect('activate', self._tag_revision_cb)
 
 
220
        revision_menu.add(revision_menu_tag)
 
 
221
        revision_menu.add(revision_menu_diff)
 
 
222
        revision_menu.add(revision_menu_compare)
 
244
224
        branch_menu = gtk.Menu()
 
245
225
        branch_menuitem = gtk.MenuItem("_Branch")
 
 
248
228
        branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
 
249
229
        branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
 
252
 
            from bzrlib.plugins import search
 
254
 
            mutter("Didn't find search plugin")
 
256
 
            branch_menu.add(gtk.SeparatorMenuItem())
 
258
 
            branch_index_menuitem = gtk.MenuItem("_Index")
 
259
 
            branch_index_menuitem.connect('activate', self._branch_index_cb)
 
260
 
            branch_menu.add(branch_index_menuitem)
 
262
 
            branch_search_menuitem = gtk.MenuItem("_Search")
 
263
 
            branch_search_menuitem.connect('activate', self._branch_search_cb)
 
264
 
            branch_menu.add(branch_search_menuitem)
 
266
231
        help_menu = gtk.Menu()
 
267
232
        help_menuitem = gtk.MenuItem("_Help")
 
268
233
        help_menuitem.set_submenu(help_menu)
 
 
308
273
        align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
 
309
274
        align.set_padding(5, 0, 0, 0)
 
310
275
        align.add(self.treeview)
 
311
 
        # user-configured size
 
312
 
        size = self._load_size('viz-graph-size')
 
315
 
            align.set_size_request(width, height)
 
317
 
            (width, height) = self.get_size()
 
318
 
            align.set_size_request(width, int(height / 2.5))
 
319
 
        align.connect('size-allocate', self._on_size_allocate, 'viz-graph-size')
 
 
345
301
    def construct_bottom(self):
 
346
302
        """Construct the bottom half of the window."""
 
347
 
        if self.config.get_user_option('viz-wide-diffs') == 'True':
 
348
 
            self.diff_paned = gtk.VPaned()
 
350
 
            self.diff_paned = gtk.HPaned()
 
351
 
        (width, height) = self.get_size()
 
352
 
        self.diff_paned.set_size_request(20, 20) # shrinkable
 
354
303
        from bzrlib.plugins.gtk.revisionview import RevisionView
 
355
304
        self.revisionview = RevisionView(branch=self.branch)
 
356
 
        self.revisionview.set_size_request(width/3, int(height / 2.5))
 
357
 
        # user-configured size
 
358
 
        size = self._load_size('viz-revisionview-size')
 
361
 
            self.revisionview.set_size_request(width, height)
 
362
 
        self.revisionview.connect('size-allocate', self._on_size_allocate, 'viz-revisionview-size')
 
 
305
        (width, height) = self.get_size()
 
 
306
        self.revisionview.set_size_request(width, int(height / 2.5))
 
363
307
        self.revisionview.show()
 
364
308
        self.revisionview.set_show_callback(self._show_clicked_cb)
 
365
309
        self.revisionview.connect('notify::revision', self._go_clicked_cb)
 
366
310
        self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
 
367
 
        self.diff_paned.pack1(self.revisionview)
 
369
 
        from bzrlib.plugins.gtk.diff import DiffWidget
 
370
 
        self.diff = DiffWidget()
 
371
 
        self.diff_paned.pack2(self.diff)
 
373
 
        self.diff_paned.show_all()
 
374
 
        if self.config.get_user_option('viz-show-diffs') != 'True':
 
377
 
        return self.diff_paned
 
 
311
        return self.revisionview
 
379
313
    def _tag_selected_cb(self, menuitem, revid):
 
380
314
        self.treeview.set_revision_id(revid)
 
 
432
364
            self.revisionview.set_revision(revision)
 
433
365
            self.revisionview.set_children(children)
 
434
 
            self.update_diff_panel(revision, parents)
 
436
367
    def _tree_revision_activated(self, widget, path, col):
 
437
368
        # TODO: more than one parent
 
438
369
        """Callback for when a treeview row gets activated."""
 
 
447
378
        self.show_diff(revision.revision_id, parent_id)
 
448
379
        self.treeview.grab_focus()
 
 
381
    def _menu_diff_cb(self,w):
 
 
382
        (path, focus) = self.treeview.treeview.get_cursor()
 
 
383
        revid = self.treeview.model[path][treemodel.REVID]
 
 
385
        parentids = self.branch.repository.revision_parents(revid)
 
 
387
        if len(parentids) == 0:
 
 
388
            parentid = NULL_REVISION
 
 
390
            parentid = parentids[0]
 
 
392
        self.show_diff(revid,parentid)    
 
450
394
    def _back_clicked_cb(self, *args):
 
451
395
        """Callback for when the back button is clicked."""
 
452
396
        self.treeview.back()
 
 
465
409
        self.show_diff(revid, parentid)
 
466
410
        self.treeview.grab_focus()
 
 
412
    def _compare_with_cb(self,w):
 
 
413
        """Callback for revision 'compare with' menu. Will show a small
 
 
414
            dialog with branch revisions to compare with selected revision in TreeView"""
 
 
416
        from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
 
 
418
        rb = RevisionBrowser(self.branch,self)
 
 
421
        if ret == gtk.RESPONSE_OK:          
 
 
422
            (path, focus) = self.treeview.treeview.get_cursor()
 
 
423
            revid = self.treeview.model[path][treemodel.REVID]
 
 
424
            self.show_diff(revid, rb.selected_revid)
 
468
428
    def _set_revision_cb(self, w, revision_id):
 
469
429
        self.treeview.set_revision_id(revision_id)
 
 
480
440
        self.treeview.set_property('compact', self.compact_view)
 
481
441
        self.treeview.refresh()
 
483
 
    def _branch_index_cb(self, w):
 
484
 
        from bzrlib.plugins.search import index as _mod_index
 
485
 
        _mod_index.index_url(self.branch.base)
 
487
 
    def _branch_search_cb(self, w):
 
488
 
        from bzrlib.plugins.search import index as _mod_index
 
489
 
        from bzrlib.plugins.gtk.search import SearchDialog
 
490
 
        from bzrlib.plugins.search import errors as search_errors
 
 
443
    def _tag_revision_cb(self, w):
 
493
 
            index = _mod_index.open_index_url(self.branch.base)
 
494
 
        except search_errors.NoSearchIndex:
 
495
 
            dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION, 
 
496
 
                buttons=gtk.BUTTONS_OK_CANCEL, 
 
497
 
                message_format="This branch has not been indexed yet. "
 
499
 
            if dialog.run() == gtk.RESPONSE_OK:
 
501
 
                index = _mod_index.index_url(self.branch.base)
 
506
 
        dialog = SearchDialog(index)
 
508
 
        if dialog.run() == gtk.RESPONSE_OK:
 
509
 
            self.set_revision(dialog.get_revision())
 
 
445
            self.treeview.set_sensitive(False)
 
 
446
            dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
 
 
447
            response = dialog.run()
 
 
448
            if response != gtk.RESPONSE_NONE:
 
 
451
                if response == gtk.RESPONSE_OK:
 
 
452
                    self.treeview.add_tag(dialog.tagname, dialog._revid)
 
 
457
            self.treeview.set_sensitive(True)
 
513
459
    def _about_dialog_cb(self, w):
 
514
460
        from bzrlib.plugins.gtk.about import AboutDialog
 
 
522
468
    def _toolbar_visibility_changed(self, col):
 
523
469
        if col.get_active():
 
526
472
            self.toolbar.hide()
 
527
 
        self.config.set_user_option('viz-toolbar-visible', col.get_active())
 
529
 
    def _make_diff_nonzero_size(self):
 
530
 
        """make sure the diff isn't zero-width or zero-height"""
 
531
 
        alloc = self.diff.get_allocation()
 
532
 
        if (alloc.width < 10) or (alloc.height < 10):
 
533
 
            width, height = self.get_size()
 
534
 
            self.revisionview.set_size_request(width/3, int(height / 2.5))
 
536
 
    def _diff_visibility_changed(self, col):
 
537
 
        """Hide or show the diff panel."""
 
540
 
            self._make_diff_nonzero_size()
 
543
 
        self.config.set_user_option('viz-show-diffs', str(col.get_active()))
 
544
 
        self.update_diff_panel()
 
546
 
    def _diff_placement_changed(self, col):
 
547
 
        """Toggle the diff panel's position."""
 
548
 
        self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
 
550
 
        old = self.paned.get_child2()
 
551
 
        self.paned.remove(old)
 
552
 
        self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
 
553
 
        self._make_diff_nonzero_size()
 
555
 
        self.treeview.emit('revision-selected')
 
557
 
    def _diff_wrap_changed(self, widget):
 
558
 
        """Toggle word wrap in the diff widget."""
 
559
 
        self.config.set_user_option('viz-wrap-diffs', widget.get_active())
 
560
 
        self.diff._on_wraplines_toggled(widget)
 
562
474
    def _show_about_cb(self, w):
 
563
475
        dialog = AboutDialog()
 
564
476
        dialog.connect('response', lambda d,r: d.destroy())
 
 
573
485
        if self.branch.supports_tags():
 
574
486
            tags = self.branch.tags.get_tag_dict().items()
 
575
 
            tags.sort(reverse=True)
 
576
489
            for tag, revid in tags:
 
577
490
                tag_image = gtk.Image()
 
578
491
                tag_image.set_from_file(icon_path('tag-16.png'))
 
579
492
                tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
 
580
493
                tag_item.set_image(tag_image)
 
581
494
                tag_item.connect('activate', self._tag_selected_cb, revid)
 
582
 
                tag_item.set_sensitive(self.treeview.has_revision_id(revid))
 
583
495
                menu.add(tag_item)
 
584
496
            self.go_menu_tags.set_submenu(menu)
 
 
590
502
        self.go_menu_tags.show_all()
 
592
 
    def _load_size(self, name):
 
593
 
        """Read and parse 'name' from self.config.
 
594
 
        The value is a string, formatted as WIDTHxHEIGHT
 
595
 
        Returns None, or (width, height)
 
597
 
        size = self.config.get_user_option(name)
 
599
 
            width, height = [int(num) for num in size.split('x')]
 
600
 
            # avoid writing config every time we start
 
601
 
            self._sizes[name] = (width, height)
 
605
 
    def _on_size_allocate(self, widget, allocation, name):
 
606
 
        """When window has been resized, save the new size."""
 
608
 
        if name in self._sizes:
 
609
 
            width, height = self._sizes[name]
 
611
 
        size_changed = (width != allocation.width) or \
 
612
 
                (height != allocation.height)
 
615
 
            width, height = allocation.width, allocation.height
 
616
 
            self._sizes[name] = (width, height)
 
617
 
            value = '%sx%s' % (width, height)
 
618
 
            self.config.set_user_option(name, value)
 
620
504
    def show_diff(self, revid=None, parentid=None):
 
621
505
        """Open a new window to show a diff between the given revisions."""
 
622
506
        from bzrlib.plugins.gtk.diff import DiffWindow
 
 
632
516
        window.set_diff(description, rev_tree, parent_tree)
 
635
 
    def update_diff_panel(self, revision=None, parents=None):
 
636
 
        """Show the current revision in the diff panel."""
 
637
 
        if self.config.get_user_option('viz-show-diffs') != 'True':
 
640
 
        if not revision: # default to selected row
 
641
 
            revision = self.treeview.get_revision()
 
642
 
        if (not revision) or (revision == NULL_REVISION):
 
645
 
        if not parents: # default to selected row's parents
 
646
 
            parents  = self.treeview.get_parents()
 
647
 
        if len(parents) == 0:
 
650
 
            parent_id = parents[0]
 
652
 
        rev_tree    = self.branch.repository.revision_tree(revision.revision_id)
 
653
 
        parent_tree = self.branch.repository.revision_tree(parent_id)
 
655
 
        self.diff.set_diff(rev_tree, parent_tree)
 
656
 
        if self.config.get_user_option('viz-wrap-diffs') == 'True':
 
657
 
            self.diff._on_wraplines_toggled(wrap=True)