/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to __init__.py

  • Committer: Jasper Groenewegen
  • Date: 2008-07-27 12:01:40 UTC
  • mfrom: (576.3.2 improve-merge)
  • mto: This revision was merged to the branch mainline in revision 579.
  • Revision ID: colbrac@xs4all.nl-20080727120140-1agdlzkc9fumjk5f
Merge merge dialog improvements

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
"""Graphical support for Bazaar using GTK.
16
16
 
17
17
This plugin includes:
18
 
commit-notify     Start the graphical notifier of commits.
19
18
gannotate         GTK+ annotate. 
20
19
gbranch           GTK+ branching. 
21
20
gcheckout         GTK+ checkout. 
22
 
gcommit           GTK+ commit dialog 
 
21
gcommit           GTK+ commit dialog.
23
22
gconflicts        GTK+ conflicts. 
24
23
gdiff             Show differences in working tree in a GTK+ Window. 
25
24
ginit             Initialise a new branch.
 
25
gmerge            GTK+ merge dialog
26
26
gmissing          GTK+ missing revisions dialog. 
27
27
gpreferences      GTK+ preferences dialog. 
28
 
gpush             GTK+ push. 
29
 
gstatus           GTK+ status dialog 
 
28
gpush             GTK+ push.
 
29
gsend             GTK+ send merge directive.
 
30
gstatus           GTK+ status dialog.
30
31
gtags             Manage branch tags.
31
32
visualise         Graphically visualise this branch. 
32
33
"""
33
34
 
 
35
import sys
 
36
 
34
37
import bzrlib
35
38
 
36
 
__version__ = '0.91.0'
37
 
version_info = tuple(int(n) for n in __version__.split('.'))
38
 
 
 
39
version_info = (0, 95, 0, 'dev', 1)
 
40
 
 
41
if version_info[3] == 'final':
 
42
    version_string = '%d.%d.%d' % version_info[:3]
 
43
else:
 
44
    version_string = '%d.%d.%d%s%d' % version_info
 
45
__version__ = version_string
 
46
 
 
47
required_bzrlib = (1, 3)
39
48
 
40
49
def check_bzrlib_version(desired):
41
50
    """Check that bzrlib is compatible.
42
51
 
43
52
    If version is < bzr-gtk version, assume incompatible.
44
 
    If version == bzr-gtk version, assume completely compatible
45
 
    If version == bzr-gtk version + 1, assume compatible, with deprecations
46
 
    Otherwise, assume incompatible.
47
53
    """
48
 
    desired_plus = (desired[0], desired[1]+1)
49
54
    bzrlib_version = bzrlib.version_info[:2]
50
 
    if bzrlib_version == desired or (bzrlib_version == desired_plus and
51
 
                                     bzrlib.version_info[3] == 'dev'):
52
 
        return
53
55
    try:
54
56
        from bzrlib.trace import warning
55
57
    except ImportError:
59
61
        from bzrlib.errors import BzrError
60
62
        warning('Installed Bazaar version %s is too old to be used with bzr-gtk'
61
63
                ' %s.' % (bzrlib.__version__, __version__))
62
 
        raise BzrError('Version mismatch: %r' % (version_info,) )
63
 
    else:
64
 
        warning('bzr-gtk is not up to date with installed bzr version %s.'
65
 
                ' \nThere should be a newer version available, e.g. %i.%i.' 
66
 
                % (bzrlib.__version__, bzrlib_version[0], bzrlib_version[1]))
67
 
 
68
 
 
69
 
check_bzrlib_version(version_info[:2])
 
64
        raise BzrError('Version mismatch: %r, %r' % (version_info, bzrlib.version_info) )
 
65
 
 
66
 
 
67
if version_info[2] == "final":
 
68
    check_bzrlib_version(required_bzrlib)
70
69
 
71
70
from bzrlib.trace import warning
72
71
if __name__ != 'bzrlib.plugins.gtk':
78
77
    branch,
79
78
    builtins,
80
79
    errors,
 
80
    merge_directive,
81
81
    workingtree,
82
82
    )
83
83
""")
104
104
    bzrlib.ui.ui_factory = GtkUIFactory()
105
105
 
106
106
 
107
 
def data_path():
108
 
    return os.path.dirname(__file__)
109
 
 
 
107
def data_basedirs():
 
108
    return [os.path.dirname(__file__),
 
109
             "/usr/share/bzr-gtk", 
 
110
             "/usr/local/share/bzr-gtk"]
 
111
 
 
112
 
 
113
def data_path(*args):
 
114
    for basedir in data_basedirs():
 
115
        path = os.path.join(basedir, *args)
 
116
        if os.path.exists(path):
 
117
            return path
 
118
    return None
 
119
 
 
120
 
 
121
def icon_path(*args):
 
122
    return data_path(os.path.join('icons', *args))
 
123
 
 
124
 
 
125
def open_display():
 
126
    pygtk = import_pygtk()
 
127
    try:
 
128
        import gtk
 
129
    except RuntimeError, e:
 
130
        if str(e) == "could not open display":
 
131
            raise NoDisplayError
 
132
    set_ui_factory()
 
133
    return gtk
 
134
 
110
135
 
111
136
class GTKCommand(Command):
112
137
    """Abstract class providing GTK specific run commands."""
113
138
 
114
 
    def open_display(self):
115
 
        pygtk = import_pygtk()
116
 
        try:
117
 
            import gtk
118
 
        except RuntimeError, e:
119
 
            if str(e) == "could not open display":
120
 
                raise NoDisplayError
121
 
        set_ui_factory()
122
 
        return gtk
123
 
 
124
139
    def run(self):
125
 
        self.open_display()
 
140
        open_display()
126
141
        dialog = self.get_gtk_dialog(os.path.abspath('.'))
127
142
        dialog.run()
128
143
 
156
171
 
157
172
    def run(self, location="."):
158
173
        (br, path) = branch.Branch.open_containing(location)
159
 
        self.open_display()
 
174
        open_display()
160
175
        from push import PushDialog
161
176
        dialog = PushDialog(br.repository, br.last_revision(), br)
162
177
        dialog.run()
181
196
            if revision is not None:
182
197
                if len(revision) == 1:
183
198
                    tree1 = wt
184
 
                    revision_id = revision[0].in_history(branch).rev_id
 
199
                    revision_id = revision[0].as_revision_id(tree1.branch)
185
200
                    tree2 = branch.repository.revision_tree(revision_id)
186
201
                elif len(revision) == 2:
187
 
                    revision_id_0 = revision[0].in_history(branch).rev_id
 
202
                    revision_id_0 = revision[0].as_revision_id(branch)
188
203
                    tree2 = branch.repository.revision_tree(revision_id_0)
189
 
                    revision_id_1 = revision[1].in_history(branch).rev_id
 
204
                    revision_id_1 = revision[1].as_revision_id(branch)
190
205
                    tree1 = branch.repository.revision_tree(revision_id_1)
191
206
            else:
192
207
                tree1 = wt
214
229
            wt.unlock()
215
230
 
216
231
 
217
 
def start_viz_window(branch, revision, limit=None):
 
232
def start_viz_window(branch, revisions, limit=None):
218
233
    """Start viz on branch with revision revision.
219
234
    
220
235
    :return: The viz window object.
221
236
    """
222
 
    from viz.branchwin import BranchWindow
223
 
    branch.lock_read()
224
 
    pp = BranchWindow()
225
 
    pp.set_branch(branch, revision, limit)
226
 
    # cleanup locks when the window is closed
227
 
    pp.connect("destroy", lambda w: branch.unlock())
228
 
    return pp
 
237
    from viz import BranchWindow
 
238
    return BranchWindow(branch, revisions, limit)
229
239
 
230
240
 
231
241
class cmd_visualise(Command):
241
251
        "revision",
242
252
        Option('limit', "Maximum number of revisions to display.",
243
253
               int, 'count')]
244
 
    takes_args = [ "location?" ]
 
254
    takes_args = [ "locations*" ]
245
255
    aliases = [ "visualize", "vis", "viz" ]
246
256
 
247
 
    def run(self, location=".", revision=None, limit=None):
 
257
    def run(self, locations_list, revision=None, limit=None):
248
258
        set_ui_factory()
249
 
        (br, path) = branch.Branch.open_containing(location)
250
 
        br.lock_read()
251
 
        try:
 
259
        if locations_list is None:
 
260
            locations_list = ["."]
 
261
        revids = []
 
262
        for location in locations_list:
 
263
            (br, path) = branch.Branch.open_containing(location)
252
264
            if revision is None:
253
 
                revid = br.last_revision()
254
 
                if revid is None:
255
 
                    return
 
265
                revids.append(br.last_revision())
256
266
            else:
257
 
                (revno, revid) = revision[0].in_history(br)
258
 
 
259
 
            import gtk
260
 
            pp = start_viz_window(br, revid, limit)
261
 
            pp.connect("destroy", lambda w: gtk.main_quit())
262
 
            pp.show()
263
 
            gtk.main()
264
 
        finally:
265
 
            br.unlock()
 
267
                revids.append(revision[0].as_revision_id(br))
 
268
        import gtk
 
269
        pp = start_viz_window(br, revids, limit)
 
270
        pp.connect("destroy", lambda w: gtk.main_quit())
 
271
        pp.show()
 
272
        gtk.main()
266
273
 
267
274
 
268
275
class cmd_gannotate(GTKCommand):
282
289
    aliases = ["gblame", "gpraise"]
283
290
    
284
291
    def run(self, filename, all=False, plain=False, line='1', revision=None):
285
 
        gtk = self.open_display()
 
292
        gtk = open_display()
286
293
 
287
294
        try:
288
295
            line = int(line)
307
314
        if revision is not None:
308
315
            if len(revision) != 1:
309
316
                raise BzrCommandError("Only 1 revion may be specified.")
310
 
            revision_id = revision[0].in_history(br).rev_id
 
317
            revision_id = revision[0].as_revision_id(br)
311
318
            tree = br.repository.revision_tree(revision_id)
312
319
        else:
313
320
            revision_id = getattr(tree, 'get_revision_id', lambda: None)()
314
321
 
315
 
        window = GAnnotateWindow(all, plain)
 
322
        window = GAnnotateWindow(all, plain, branch=br)
316
323
        window.connect("destroy", lambda w: gtk.main_quit())
317
 
        window.set_title(path + " - gannotate")
318
324
        config = GAnnotateConfig(window)
319
325
        window.show()
320
326
        br.lock_read()
335
341
    """GTK+ commit dialog
336
342
 
337
343
    Graphical user interface for committing revisions"""
338
 
    
 
344
 
339
345
    aliases = [ "gci" ]
340
346
    takes_args = []
341
347
    takes_options = []
342
348
 
343
349
    def run(self, filename=None):
344
350
        import os
345
 
        self.open_display()
 
351
        open_display()
346
352
        from commit import CommitDialog
347
353
        from bzrlib.errors import (BzrCommandError,
348
354
                                   NotBranchError,
354
360
            (wt, path) = workingtree.WorkingTree.open_containing(filename)
355
361
            br = wt.branch
356
362
        except NoWorkingTree, e:
357
 
            path = e.base
358
 
            (br, path) = branch.Branch.open_containing(path)
359
 
 
360
 
        commit = CommitDialog(wt, path, not br)
361
 
        commit.run()
362
 
 
 
363
            from dialog import error_dialog
 
364
            error_dialog(_i18n('Directory does not have a working tree'),
 
365
                         _i18n('Operation aborted.'))
 
366
            return 1 # should this be retval=3?
 
367
 
 
368
        # It is a good habit to keep things locked for the duration, but it
 
369
        # could cause difficulties if someone wants to do things in another
 
370
        # window... We could lock_read() until we actually go to commit
 
371
        # changes... Just a thought.
 
372
        wt.lock_write()
 
373
        try:
 
374
            dlg = CommitDialog(wt)
 
375
            return dlg.run()
 
376
        finally:
 
377
            wt.unlock()
363
378
 
364
379
 
365
380
class cmd_gstatus(GTKCommand):
370
385
    
371
386
    aliases = [ "gst" ]
372
387
    takes_args = ['PATH?']
373
 
    takes_options = []
 
388
    takes_options = ['revision']
374
389
 
375
 
    def run(self, path='.'):
 
390
    def run(self, path='.', revision=None):
376
391
        import os
377
 
        gtk = self.open_display()
 
392
        gtk = open_display()
378
393
        from status import StatusDialog
379
394
        (wt, wt_path) = workingtree.WorkingTree.open_containing(path)
380
 
        status = StatusDialog(wt, wt_path)
 
395
        
 
396
        if revision is not None:
 
397
            try:
 
398
                revision_id = revision[0].as_revision_id(wt.branch)
 
399
            except:
 
400
                from bzrlib.errors import BzrError
 
401
                raise BzrError('Revision %r doesn\'t exist' % revision[0].user_spec )
 
402
        else:
 
403
            revision_id = None
 
404
 
 
405
        status = StatusDialog(wt, wt_path, revision_id)
381
406
        status.connect("destroy", gtk.main_quit)
382
407
        status.run()
383
408
 
384
409
 
 
410
class cmd_gsend(GTKCommand):
 
411
    """GTK+ send merge directive.
 
412
 
 
413
    """
 
414
    def run(self):
 
415
        (br, path) = branch.Branch.open_containing(".")
 
416
        gtk = open_display()
 
417
        from bzrlib.plugins.gtk.mergedirective import SendMergeDirectiveDialog
 
418
        from StringIO import StringIO
 
419
        dialog = SendMergeDirectiveDialog(br)
 
420
        if dialog.run() == gtk.RESPONSE_OK:
 
421
            outf = StringIO()
 
422
            outf.writelines(dialog.get_merge_directive().to_lines())
 
423
            mail_client = br.get_config().get_mail_client()
 
424
            mail_client.compose_merge_request(dialog.get_mail_to(), "[MERGE]", 
 
425
                outf.getvalue())
 
426
 
 
427
            
 
428
 
385
429
 
386
430
class cmd_gconflicts(GTKCommand):
387
 
    """ GTK+ conflicts.
 
431
    """GTK+ conflicts.
388
432
    
389
433
    Select files from the list of conflicts and run an external utility to
390
434
    resolve them.
391
435
    """
392
436
    def run(self):
393
437
        (wt, path) = workingtree.WorkingTree.open_containing('.')
394
 
        self.open_display()
 
438
        open_display()
395
439
        from bzrlib.plugins.gtk.conflicts import ConflictsDialog
396
440
        dialog = ConflictsDialog(wt)
397
441
        dialog.run()
398
442
 
399
443
 
400
 
 
401
444
class cmd_gpreferences(GTKCommand):
402
445
    """ GTK+ preferences dialog.
403
446
 
404
447
    """
405
448
    def run(self):
406
 
        self.open_display()
 
449
        open_display()
407
450
        from bzrlib.plugins.gtk.preferences import PreferencesWindow
408
451
        dialog = PreferencesWindow()
409
452
        dialog.run()
410
453
 
411
454
 
 
455
class cmd_gmerge(Command):
 
456
    """ GTK+ merge dialog
 
457
    
 
458
    """
 
459
    takes_args = ["merge_from_path?"]
 
460
    def run(self, merge_from_path=None):
 
461
        from bzrlib import workingtree
 
462
        from bzrlib.plugins.gtk.dialog import error_dialog
 
463
        from bzrlib.plugins.gtk.merge import MergeDialog
 
464
        
 
465
        (wt, path) = workingtree.WorkingTree.open_containing('.')
 
466
        old_tree = wt.branch.repository.revision_tree(wt.branch.last_revision())
 
467
        delta = wt.changes_from(old_tree)
 
468
        if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
 
469
            error_dialog(_i18n('There are local changes in the branch'),
 
470
                         _i18n('Please commit or revert the changes before merging.'))
 
471
        else:
 
472
            parent_branch_path = wt.branch.get_parent()
 
473
            merge = MergeDialog(wt, path, parent_branch_path)
 
474
            response = merge.run()
 
475
            merge.destroy()
 
476
 
412
477
 
413
478
class cmd_gmissing(Command):
414
479
    """ GTK+ missing revisions dialog.
448
513
 
449
514
class cmd_ginit(GTKCommand):
450
515
    def run(self):
451
 
        self.open_display()
 
516
        open_display()
452
517
        from initialize import InitDialog
453
518
        dialog = InitDialog(os.path.abspath(os.path.curdir))
454
519
        dialog.run()
458
523
    def run(self):
459
524
        br = branch.Branch.open_containing('.')[0]
460
525
        
461
 
        gtk = self.open_display()
 
526
        gtk = open_display()
462
527
        from tags import TagsWindow
463
528
        window = TagsWindow(br)
464
529
        window.show()
466
531
 
467
532
 
468
533
commands = [
 
534
    cmd_gannotate, 
 
535
    cmd_gbranch,
 
536
    cmd_gcheckout, 
 
537
    cmd_gcommit, 
 
538
    cmd_gconflicts, 
 
539
    cmd_gdiff,
 
540
    cmd_ginit,
 
541
    cmd_gmerge,
469
542
    cmd_gmissing, 
470
543
    cmd_gpreferences, 
471
 
    cmd_gconflicts, 
 
544
    cmd_gpush, 
 
545
    cmd_gsend,
472
546
    cmd_gstatus,
473
 
    cmd_gcommit, 
474
 
    cmd_gannotate, 
475
 
    cmd_visualise, 
476
 
    cmd_gdiff,
477
 
    cmd_gpush, 
478
 
    cmd_gcheckout, 
479
 
    cmd_gbranch,
480
 
    cmd_ginit,
481
 
    cmd_gtags
 
547
    cmd_gtags,
 
548
    cmd_visualise
482
549
    ]
483
550
 
484
551
for cmd in commands:
485
552
    register_command(cmd)
486
553
 
487
554
 
488
 
class cmd_commit_notify(GTKCommand):
489
 
    """Run the bzr commit notifier.
490
 
 
491
 
    This is a background program which will pop up a notification on the users
492
 
    screen when a commit occurs.
493
 
    """
494
 
 
495
 
    def run(self):
496
 
        from notify import NotifyPopupMenu
497
 
        gtk = self.open_display()
498
 
        menu = NotifyPopupMenu()
499
 
        icon = gtk.status_icon_new_from_file(os.path.join(data_path(), "bzr-icon-64.png"))
500
 
        icon.connect('popup-menu', menu.display)
501
 
 
502
 
        import cgi
503
 
        import dbus
504
 
        import dbus.service
505
 
        import pynotify
506
 
        from bzrlib.bzrdir import BzrDir
507
 
        from bzrlib import errors
508
 
        from bzrlib.osutils import format_date
509
 
        from bzrlib.transport import get_transport
510
 
        if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
511
 
            import dbus.glib
512
 
        from bzrlib.plugins.dbus import activity
513
 
        bus = dbus.SessionBus()
514
 
        # get the object so we can subscribe to callbacks from it.
515
 
        broadcast_service = bus.get_object(
516
 
            activity.Broadcast.DBUS_NAME,
517
 
            activity.Broadcast.DBUS_PATH)
518
 
 
519
 
        def catch_branch(revision_id, urls):
520
 
            # TODO: show all the urls, or perhaps choose the 'best'.
521
 
            url = urls[0]
522
 
            try:
523
 
                if isinstance(revision_id, unicode):
524
 
                    revision_id = revision_id.encode('utf8')
525
 
                transport = get_transport(url)
526
 
                a_dir = BzrDir.open_from_transport(transport)
527
 
                branch = a_dir.open_branch()
528
 
                revno = branch.revision_id_to_revno(revision_id)
529
 
                revision = branch.repository.get_revision(revision_id)
530
 
                summary = 'New revision %d in %s' % (revno, url)
531
 
                body  = 'Committer: %s\n' % revision.committer
532
 
                body += 'Date: %s\n' % format_date(revision.timestamp,
533
 
                    revision.timezone)
534
 
                body += '\n'
535
 
                body += revision.message
536
 
                body = cgi.escape(body)
537
 
                nw = pynotify.Notification(summary, body)
538
 
                def start_viz(notification=None, action=None, data=None):
539
 
                    """Start the viz program."""
540
 
                    pp = start_viz_window(branch, revision_id)
541
 
                    pp.show()
542
 
                def start_branch(notification=None, action=None, data=None):
543
 
                    """Start a Branch dialog"""
544
 
                    from bzrlib.plugins.gtk.branch import BranchDialog
545
 
                    bd = BranchDialog(remote_path=url)
546
 
                    bd.run()
547
 
                nw.add_action("inspect", "Inspect", start_viz, None)
548
 
                nw.add_action("branch", "Branch", start_branch, None)
549
 
                nw.set_timeout(5000)
550
 
                nw.show()
551
 
            except Exception, e:
552
 
                print e
553
 
                raise
554
 
        broadcast_service.connect_to_signal("Revision", catch_branch,
555
 
            dbus_interface=activity.Broadcast.DBUS_INTERFACE)
556
 
        pynotify.init("bzr commit-notify")
557
 
        gtk.main()
558
 
 
559
 
register_command(cmd_commit_notify)
560
 
 
561
 
 
562
555
class cmd_gselftest(GTKCommand):
563
556
    """Version of selftest that displays a notification at the end"""
564
557
 
593
586
register_command(cmd_gselftest)
594
587
 
595
588
 
 
589
class cmd_test_gtk(GTKCommand):
 
590
    """Version of selftest that just runs the gtk test suite."""
 
591
 
 
592
    takes_options = ['verbose',
 
593
                     Option('one', short_name='1',
 
594
                            help='Stop when one test fails.'),
 
595
                     Option('benchmark', help='Run the benchmarks.'),
 
596
                     Option('lsprof-timed',
 
597
                     help='Generate lsprof output for benchmarked'
 
598
                          ' sections of code.'),
 
599
                     Option('list-only',
 
600
                     help='List the tests instead of running them.'),
 
601
                     Option('randomize', type=str, argname="SEED",
 
602
                     help='Randomize the order of tests using the given'
 
603
                          ' seed or "now" for the current time.'),
 
604
                    ]
 
605
    takes_args = ['testspecs*']
 
606
 
 
607
    def run(self, verbose=None, one=False, benchmark=None,
 
608
            lsprof_timed=None, list_only=False, randomize=None,
 
609
            testspecs_list=None):
 
610
        from bzrlib import __path__ as bzrlib_path
 
611
        from bzrlib.tests import selftest
 
612
 
 
613
        print '%10s: %s' % ('bzrlib', bzrlib_path[0])
 
614
        if benchmark:
 
615
            print 'No benchmarks yet'
 
616
            return 3
 
617
 
 
618
            test_suite_factory = bench_suite
 
619
            if verbose is None:
 
620
                verbose = True
 
621
            # TODO: should possibly lock the history file...
 
622
            benchfile = open(".perf_history", "at", buffering=1)
 
623
        else:
 
624
            test_suite_factory = test_suite
 
625
            if verbose is None:
 
626
                verbose = False
 
627
            benchfile = None
 
628
 
 
629
        if testspecs_list is not None:
 
630
            pattern = '|'.join(testspecs_list)
 
631
        else:
 
632
            pattern = ".*"
 
633
 
 
634
        try:
 
635
            result = selftest(verbose=verbose,
 
636
                              pattern=pattern,
 
637
                              stop_on_failure=one,
 
638
                              test_suite_factory=test_suite_factory,
 
639
                              lsprof_timed=lsprof_timed,
 
640
                              bench_history=benchfile,
 
641
                              list_only=list_only,
 
642
                              random_seed=randomize,
 
643
                             )
 
644
        finally:
 
645
            if benchfile is not None:
 
646
                benchfile.close()
 
647
 
 
648
register_command(cmd_test_gtk)
 
649
 
 
650
 
 
651
 
596
652
import gettext
597
653
gettext.install('olive-gtk')
598
654
 
 
655
# Let's create a specialized alias to protect '_' from being erased by other
 
656
# uses of '_' as an anonymous variable (think pdb for one).
 
657
_i18n = gettext.gettext
599
658
 
600
659
class NoDisplayError(BzrCommandError):
601
660
    """gtk could not find a proper display"""
611
670
    default_encoding = sys.getdefaultencoding()
612
671
    try:
613
672
        result = TestSuite()
 
673
        try:
 
674
            import_pygtk()
 
675
        except errors.BzrCommandError:
 
676
            return result
614
677
        result.addTest(tests.test_suite())
615
678
    finally:
616
679
        if sys.getdefaultencoding() != default_encoding: