/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: Vincent Ladeuil
  • Date: 2008-06-10 15:25:47 UTC
  • mto: This revision was merged to the branch mainline in revision 504.
  • Revision ID: v.ladeuil+lp@free.fr-20080610152547-dwmil1p8pd0mfpnl
Fix third failing test (thanks to jam).

* tests/test_commit.py:
(TestPendingRevisions.test_pending_revisions_multi_merge): Fix
provided by jam: bzr we now filter the pending merges so that only
the 'heads()' are included. We just ensure that the pending merges
contain the unique tips for the ancestries.

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
 
gannotate         GTK+ annotate.
19
 
gbranch           GTK+ branching.
20
 
gcheckout         GTK+ checkout.
 
18
commit-notify     Start the graphical notifier of commits.
 
19
gannotate         GTK+ annotate. 
 
20
gbranch           GTK+ branching. 
 
21
gcheckout         GTK+ checkout. 
21
22
gcommit           GTK+ commit dialog.
22
 
gconflicts        GTK+ conflicts.
23
 
gdiff             Show differences in working tree in a GTK+ Window.
 
23
gconflicts        GTK+ conflicts. 
 
24
gdiff             Show differences in working tree in a GTK+ Window. 
 
25
ghandle-patch     Display and optionally merge a merge directive or patch.
24
26
ginit             Initialise a new branch.
25
 
gloom             GTK+ loom browse dialog
26
 
gmerge            GTK+ merge dialog
27
 
gmissing          GTK+ missing revisions dialog.
28
 
gpreferences      GTK+ preferences dialog.
 
27
gmissing          GTK+ missing revisions dialog. 
 
28
gpreferences      GTK+ preferences dialog. 
29
29
gpush             GTK+ push.
30
30
gsend             GTK+ send merge directive.
31
31
gstatus           GTK+ status dialog.
32
32
gtags             Manage branch tags.
33
 
visualise         Graphically visualise this branch.
 
33
visualise         Graphically visualise this branch. 
34
34
"""
35
35
 
36
 
from __future__ import absolute_import
37
 
 
38
 
import os
39
36
import sys
40
37
 
41
 
if getattr(sys, "frozen", None) is not None: # we run bzr.exe
42
 
 
43
 
    # FIXME: Unless a better packaging solution is found, the following
44
 
    # provides a workaround for https://bugs.launchpad.net/bzr/+bug/388790 Also
45
 
    # see https://code.edge.launchpad.net/~vila/bzr-gtk/388790-windows-setup
46
 
    # for more details about while it's needed.
47
 
 
48
 
    # NOTE: _lib must be ahead of bzrlib or sax.saxutils (in olive) fails
49
 
    here = os.path.dirname(__file__)
50
 
    sys.path.insert(0, os.path.join(here, '_lib'))
51
 
    sys.path.append(os.path.join(here, '_lib/gtk-2.0'))
52
 
 
53
 
 
54
38
import bzrlib
55
 
import bzrlib.api
56
 
from bzrlib.commands import plugin_cmds
57
39
 
58
 
from bzrlib.plugins.gtk.info import (
59
 
    bzr_plugin_version as version_info,
60
 
    bzr_compatible_versions,
61
 
    )
 
40
version_info = (0, 95, 0, 'dev', 1)
62
41
 
63
42
if version_info[3] == 'final':
64
43
    version_string = '%d.%d.%d' % version_info[:3]
66
45
    version_string = '%d.%d.%d%s%d' % version_info
67
46
__version__ = version_string
68
47
 
69
 
bzrlib.api.require_any_api(bzrlib, bzr_compatible_versions)
70
 
 
 
48
required_bzrlib = (1, 3)
 
49
 
 
50
def check_bzrlib_version(desired):
 
51
    """Check that bzrlib is compatible.
 
52
 
 
53
    If version is < bzr-gtk version, assume incompatible.
 
54
    """
 
55
    bzrlib_version = bzrlib.version_info[:2]
 
56
    try:
 
57
        from bzrlib.trace import warning
 
58
    except ImportError:
 
59
        # get the message out any way we can
 
60
        from warnings import warn as warning
 
61
    if bzrlib_version < desired:
 
62
        from bzrlib.errors import BzrError
 
63
        warning('Installed Bazaar version %s is too old to be used with bzr-gtk'
 
64
                ' %s.' % (bzrlib.__version__, __version__))
 
65
        raise BzrError('Version mismatch: %r, %r' % (version_info, bzrlib.version_info) )
 
66
 
 
67
 
 
68
if version_info[2] == "final":
 
69
    check_bzrlib_version(required_bzrlib)
 
70
 
 
71
from bzrlib.trace import warning
71
72
if __name__ != 'bzrlib.plugins.gtk':
72
 
    from bzrlib.trace import warning
73
73
    warning("Not running as bzrlib.plugins.gtk, things may break.")
74
74
 
 
75
from bzrlib.lazy_import import lazy_import
 
76
lazy_import(globals(), """
 
77
from bzrlib import (
 
78
    branch,
 
79
    builtins,
 
80
    errors,
 
81
    merge_directive,
 
82
    workingtree,
 
83
    )
 
84
""")
 
85
 
 
86
from bzrlib.commands import Command, register_command, display_command
 
87
from bzrlib.errors import NotVersionedError, BzrCommandError, NoSuchFile
 
88
from bzrlib.option import Option
 
89
 
 
90
import os.path
 
91
 
 
92
def import_pygtk():
 
93
    try:
 
94
        import pygtk
 
95
    except ImportError:
 
96
        raise errors.BzrCommandError("PyGTK not installed.")
 
97
    pygtk.require('2.0')
 
98
    return pygtk
 
99
 
75
100
 
76
101
def set_ui_factory():
77
 
    from bzrlib.plugins.gtk.ui import GtkUIFactory
 
102
    import_pygtk()
 
103
    from ui import GtkUIFactory
78
104
    import bzrlib.ui
79
105
    bzrlib.ui.ui_factory = GtkUIFactory()
80
106
 
81
107
 
82
 
def data_basedirs():
83
 
    return [os.path.dirname(__file__),
 
108
def data_path():
 
109
    return os.path.dirname(__file__)
 
110
 
 
111
 
 
112
def icon_path(*args):
 
113
    basedirs = [os.path.join(data_path()),
84
114
             "/usr/share/bzr-gtk", 
85
115
             "/usr/local/share/bzr-gtk"]
86
 
 
87
 
 
88
 
def data_path(*args):
89
 
    for basedir in data_basedirs():
90
 
        path = os.path.join(basedir, *args)
 
116
    for basedir in basedirs:
 
117
        path = os.path.join(basedir, 'icons', *args)
91
118
        if os.path.exists(path):
92
119
            return path
93
120
    return None
94
121
 
95
122
 
96
 
def icon_path(*args):
97
 
    return data_path(os.path.join('icons', *args))
98
 
 
99
 
 
100
 
commands = {
101
 
    "gannotate": ["gblame", "gpraise"],
102
 
    "gbranch": [],
103
 
    "gcheckout": [],
104
 
    "gcommit": ["gci"],
105
 
    "gconflicts": [],
106
 
    "gdiff": [],
107
 
    "ginit": [],
108
 
    "gmerge": [],
109
 
    "gmissing": [],
110
 
    "gpreferences": [],
111
 
    "gpush": [],
112
 
    "gsend": [],
113
 
    "gstatus": ["gst"],
114
 
    "gtags": [],
115
 
    "visualise": ["visualize", "vis", "viz", 'glog'],
116
 
    }
117
 
 
118
 
try:
119
 
    from bzrlib.plugins import loom
120
 
except ImportError:
121
 
    pass # Loom plugin doesn't appear to be present
122
 
else:
123
 
    commands["gloom"] = []
124
 
 
125
 
for cmd, aliases in commands.iteritems():
126
 
    plugin_cmds.register_lazy("cmd_%s" % cmd, aliases,
127
 
                              "bzrlib.plugins.gtk.commands")
128
 
 
129
 
def save_commit_messages(*args):
130
 
    from bzrlib.plugins.gtk import commitmsgs
131
 
    commitmsgs.save_commit_messages(*args)
132
 
 
133
 
try:
134
 
    from bzrlib.hooks import install_lazy_named_hook
135
 
except ImportError:
136
 
    from bzrlib.branch import Branch
137
 
    Branch.hooks.install_named_hook('post_uncommit',
138
 
                                    save_commit_messages,
139
 
                                    "Saving commit messages for gcommit")
140
 
else:
141
 
    install_lazy_named_hook("bzrlib.branch", "Branch.hooks",
142
 
        'post_uncommit', save_commit_messages, "Saving commit messages for gcommit")
143
 
 
144
 
try:
145
 
    from bzrlib.registry import register_lazy
146
 
except ImportError:
147
 
    from bzrlib import config
148
 
    option_registry = getattr(config, "option_registry", None)
149
 
    if option_registry is not None:
150
 
        config.option_registry.register_lazy('nautilus_integration',
151
 
                'bzrlib.plugins.gtk.config', 'opt_nautilus_integration')
152
 
else:
153
 
    register_lazy("bzrlib.config", "option_registry",
154
 
        'nautilus_integration', 'bzrlib.plugins.gtk.config',
155
 
        'opt_nautilus_integration')
156
 
 
157
 
 
158
 
def load_tests(basic_tests, module, loader):
159
 
    testmod_names = [
160
 
        'tests',
161
 
        ]
 
123
class GTKCommand(Command):
 
124
    """Abstract class providing GTK specific run commands."""
 
125
 
 
126
    def open_display(self):
 
127
        pygtk = import_pygtk()
 
128
        try:
 
129
            import gtk
 
130
        except RuntimeError, e:
 
131
            if str(e) == "could not open display":
 
132
                raise NoDisplayError
 
133
        set_ui_factory()
 
134
        return gtk
 
135
 
 
136
    def run(self):
 
137
        self.open_display()
 
138
        dialog = self.get_gtk_dialog(os.path.abspath('.'))
 
139
        dialog.run()
 
140
 
 
141
 
 
142
class cmd_gbranch(GTKCommand):
 
143
    """GTK+ branching.
 
144
    
 
145
    """
 
146
 
 
147
    def get_gtk_dialog(self, path):
 
148
        from bzrlib.plugins.gtk.branch import BranchDialog
 
149
        return BranchDialog(path)
 
150
 
 
151
 
 
152
class cmd_gcheckout(GTKCommand):
 
153
    """ GTK+ checkout.
 
154
    
 
155
    """
 
156
    
 
157
    def get_gtk_dialog(self, path):
 
158
        from bzrlib.plugins.gtk.checkout import CheckoutDialog
 
159
        return CheckoutDialog(path)
 
160
 
 
161
 
 
162
 
 
163
class cmd_gpush(GTKCommand):
 
164
    """ GTK+ push.
 
165
    
 
166
    """
 
167
    takes_args = [ "location?" ]
 
168
 
 
169
    def run(self, location="."):
 
170
        (br, path) = branch.Branch.open_containing(location)
 
171
        self.open_display()
 
172
        from push import PushDialog
 
173
        dialog = PushDialog(br.repository, br.last_revision(), br)
 
174
        dialog.run()
 
175
 
 
176
 
 
177
 
 
178
class cmd_gdiff(GTKCommand):
 
179
    """Show differences in working tree in a GTK+ Window.
 
180
    
 
181
    Otherwise, all changes for the tree are listed.
 
182
    """
 
183
    takes_args = ['filename?']
 
184
    takes_options = ['revision']
 
185
 
 
186
    @display_command
 
187
    def run(self, revision=None, filename=None):
 
188
        set_ui_factory()
 
189
        wt = workingtree.WorkingTree.open_containing(".")[0]
 
190
        wt.lock_read()
 
191
        try:
 
192
            branch = wt.branch
 
193
            if revision is not None:
 
194
                if len(revision) == 1:
 
195
                    tree1 = wt
 
196
                    revision_id = revision[0].as_revision_id(tree1.branch)
 
197
                    tree2 = branch.repository.revision_tree(revision_id)
 
198
                elif len(revision) == 2:
 
199
                    revision_id_0 = revision[0].as_revision_id(branch)
 
200
                    tree2 = branch.repository.revision_tree(revision_id_0)
 
201
                    revision_id_1 = revision[1].as_revision_id(branch)
 
202
                    tree1 = branch.repository.revision_tree(revision_id_1)
 
203
            else:
 
204
                tree1 = wt
 
205
                tree2 = tree1.basis_tree()
 
206
 
 
207
            from diff import DiffWindow
 
208
            import gtk
 
209
            window = DiffWindow()
 
210
            window.connect("destroy", gtk.main_quit)
 
211
            window.set_diff("Working Tree", tree1, tree2)
 
212
            if filename is not None:
 
213
                tree_filename = wt.relpath(filename)
 
214
                try:
 
215
                    window.set_file(tree_filename)
 
216
                except NoSuchFile:
 
217
                    if (tree1.path2id(tree_filename) is None and 
 
218
                        tree2.path2id(tree_filename) is None):
 
219
                        raise NotVersionedError(filename)
 
220
                    raise BzrCommandError('No changes found for file "%s"' % 
 
221
                                          filename)
 
222
            window.show()
 
223
 
 
224
            gtk.main()
 
225
        finally:
 
226
            wt.unlock()
 
227
 
 
228
 
 
229
def start_viz_window(branch, revisions, limit=None):
 
230
    """Start viz on branch with revision revision.
 
231
    
 
232
    :return: The viz window object.
 
233
    """
 
234
    from viz import BranchWindow
 
235
    return BranchWindow(branch, revisions, limit)
 
236
 
 
237
 
 
238
class cmd_visualise(Command):
 
239
    """Graphically visualise this branch.
 
240
 
 
241
    Opens a graphical window to allow you to see the history of the branch
 
242
    and relationships between revisions in a visual manner,
 
243
 
 
244
    The default starting point is latest revision on the branch, you can
 
245
    specify a starting point with -r revision.
 
246
    """
 
247
    takes_options = [
 
248
        "revision",
 
249
        Option('limit', "Maximum number of revisions to display.",
 
250
               int, 'count')]
 
251
    takes_args = [ "locations*" ]
 
252
    aliases = [ "visualize", "vis", "viz" ]
 
253
 
 
254
    def run(self, locations_list, revision=None, limit=None):
 
255
        set_ui_factory()
 
256
        if locations_list is None:
 
257
            locations_list = ["."]
 
258
        revids = []
 
259
        for location in locations_list:
 
260
            (br, path) = branch.Branch.open_containing(location)
 
261
            if revision is None:
 
262
                revids.append(br.last_revision())
 
263
            else:
 
264
                revids.append(revision[0].as_revision_id(br))
 
265
        import gtk
 
266
        pp = start_viz_window(br, revids, limit)
 
267
        pp.connect("destroy", lambda w: gtk.main_quit())
 
268
        pp.show()
 
269
        gtk.main()
 
270
 
 
271
 
 
272
class cmd_gannotate(GTKCommand):
 
273
    """GTK+ annotate.
 
274
    
 
275
    Browse changes to FILENAME line by line in a GTK+ window.
 
276
    """
 
277
 
 
278
    takes_args = ["filename", "line?"]
 
279
    takes_options = [
 
280
        Option("all", help="Show annotations on all lines."),
 
281
        Option("plain", help="Don't highlight annotation lines."),
 
282
        Option("line", type=int, argname="lineno",
 
283
               help="Jump to specified line number."),
 
284
        "revision",
 
285
    ]
 
286
    aliases = ["gblame", "gpraise"]
 
287
    
 
288
    def run(self, filename, all=False, plain=False, line='1', revision=None):
 
289
        gtk = self.open_display()
 
290
 
 
291
        try:
 
292
            line = int(line)
 
293
        except ValueError:
 
294
            raise BzrCommandError('Line argument ("%s") is not a number.' % 
 
295
                                  line)
 
296
 
 
297
        from annotate.gannotate import GAnnotateWindow
 
298
        from annotate.config import GAnnotateConfig
 
299
        from bzrlib.bzrdir import BzrDir
 
300
 
 
301
        wt, br, path = BzrDir.open_containing_tree_or_branch(filename)
 
302
        if wt is not None:
 
303
            tree = wt
 
304
        else:
 
305
            tree = br.basis_tree()
 
306
 
 
307
        file_id = tree.path2id(path)
 
308
 
 
309
        if file_id is None:
 
310
            raise NotVersionedError(filename)
 
311
        if revision is not None:
 
312
            if len(revision) != 1:
 
313
                raise BzrCommandError("Only 1 revion may be specified.")
 
314
            revision_id = revision[0].as_revision_id(br)
 
315
            tree = br.repository.revision_tree(revision_id)
 
316
        else:
 
317
            revision_id = getattr(tree, 'get_revision_id', lambda: None)()
 
318
 
 
319
        window = GAnnotateWindow(all, plain, branch=br)
 
320
        window.connect("destroy", lambda w: gtk.main_quit())
 
321
        config = GAnnotateConfig(window)
 
322
        window.show()
 
323
        br.lock_read()
 
324
        if wt is not None:
 
325
            wt.lock_read()
 
326
        try:
 
327
            window.annotate(tree, br, file_id)
 
328
            window.jump_to_line(line)
 
329
            gtk.main()
 
330
        finally:
 
331
            br.unlock()
 
332
            if wt is not None:
 
333
                wt.unlock()
 
334
 
 
335
 
 
336
 
 
337
class cmd_gcommit(GTKCommand):
 
338
    """GTK+ commit dialog
 
339
 
 
340
    Graphical user interface for committing revisions"""
 
341
 
 
342
    aliases = [ "gci" ]
 
343
    takes_args = []
 
344
    takes_options = []
 
345
 
 
346
    def run(self, filename=None):
 
347
        import os
 
348
        self.open_display()
 
349
        from commit import CommitDialog
 
350
        from bzrlib.errors import (BzrCommandError,
 
351
                                   NotBranchError,
 
352
                                   NoWorkingTree)
 
353
 
 
354
        wt = None
 
355
        br = None
 
356
        try:
 
357
            (wt, path) = workingtree.WorkingTree.open_containing(filename)
 
358
            br = wt.branch
 
359
        except NoWorkingTree, e:
 
360
            from dialog import error_dialog
 
361
            error_dialog(_i18n('Directory does not have a working tree'),
 
362
                         _i18n('Operation aborted.'))
 
363
            return 1 # should this be retval=3?
 
364
 
 
365
        # It is a good habit to keep things locked for the duration, but it
 
366
        # could cause difficulties if someone wants to do things in another
 
367
        # window... We could lock_read() until we actually go to commit
 
368
        # changes... Just a thought.
 
369
        wt.lock_write()
 
370
        try:
 
371
            dlg = CommitDialog(wt)
 
372
            return dlg.run()
 
373
        finally:
 
374
            wt.unlock()
 
375
 
 
376
 
 
377
class cmd_gstatus(GTKCommand):
 
378
    """GTK+ status dialog
 
379
 
 
380
    Graphical user interface for showing status 
 
381
    information."""
 
382
    
 
383
    aliases = [ "gst" ]
 
384
    takes_args = ['PATH?']
 
385
    takes_options = ['revision']
 
386
 
 
387
    def run(self, path='.', revision=None):
 
388
        import os
 
389
        gtk = self.open_display()
 
390
        from status import StatusDialog
 
391
        (wt, wt_path) = workingtree.WorkingTree.open_containing(path)
 
392
        
 
393
        if revision is not None:
 
394
            try:
 
395
                revision_id = revision[0].as_revision_id(wt.branch)
 
396
            except:
 
397
                from bzrlib.errors import BzrError
 
398
                raise BzrError('Revision %r doesn\'t exist' % revision[0].user_spec )
 
399
        else:
 
400
            revision_id = None
 
401
 
 
402
        status = StatusDialog(wt, wt_path, revision_id)
 
403
        status.connect("destroy", gtk.main_quit)
 
404
        status.run()
 
405
 
 
406
 
 
407
class cmd_gsend(GTKCommand):
 
408
    """GTK+ send merge directive.
 
409
 
 
410
    """
 
411
    def run(self):
 
412
        (br, path) = branch.Branch.open_containing(".")
 
413
        gtk = self.open_display()
 
414
        from bzrlib.plugins.gtk.mergedirective import SendMergeDirectiveDialog
 
415
        from StringIO import StringIO
 
416
        dialog = SendMergeDirectiveDialog(br)
 
417
        if dialog.run() == gtk.RESPONSE_OK:
 
418
            outf = StringIO()
 
419
            outf.writelines(dialog.get_merge_directive().to_lines())
 
420
            mail_client = br.get_config().get_mail_client()
 
421
            mail_client.compose_merge_request(dialog.get_mail_to(), "[MERGE]", 
 
422
                outf.getvalue())
 
423
 
 
424
            
 
425
 
 
426
 
 
427
class cmd_gconflicts(GTKCommand):
 
428
    """GTK+ conflicts.
 
429
    
 
430
    Select files from the list of conflicts and run an external utility to
 
431
    resolve them.
 
432
    """
 
433
    def run(self):
 
434
        (wt, path) = workingtree.WorkingTree.open_containing('.')
 
435
        self.open_display()
 
436
        from bzrlib.plugins.gtk.conflicts import ConflictsDialog
 
437
        dialog = ConflictsDialog(wt)
 
438
        dialog.run()
 
439
 
 
440
 
 
441
class cmd_gpreferences(GTKCommand):
 
442
    """ GTK+ preferences dialog.
 
443
 
 
444
    """
 
445
    def run(self):
 
446
        self.open_display()
 
447
        from bzrlib.plugins.gtk.preferences import PreferencesWindow
 
448
        dialog = PreferencesWindow()
 
449
        dialog.run()
 
450
 
 
451
 
 
452
class cmd_gmissing(Command):
 
453
    """ GTK+ missing revisions dialog.
 
454
 
 
455
    """
 
456
    takes_args = ["other_branch?"]
 
457
    def run(self, other_branch=None):
 
458
        pygtk = import_pygtk()
 
459
        try:
 
460
            import gtk
 
461
        except RuntimeError, e:
 
462
            if str(e) == "could not open display":
 
463
                raise NoDisplayError
 
464
 
 
465
        from bzrlib.plugins.gtk.missing import MissingWindow
 
466
        from bzrlib.branch import Branch
 
467
 
 
468
        local_branch = Branch.open_containing(".")[0]
 
469
        if other_branch is None:
 
470
            other_branch = local_branch.get_parent()
 
471
            
 
472
            if other_branch is None:
 
473
                raise errors.BzrCommandError("No peer location known or specified.")
 
474
        remote_branch = Branch.open_containing(other_branch)[0]
 
475
        set_ui_factory()
 
476
        local_branch.lock_read()
 
477
        try:
 
478
            remote_branch.lock_read()
 
479
            try:
 
480
                dialog = MissingWindow(local_branch, remote_branch)
 
481
                dialog.run()
 
482
            finally:
 
483
                remote_branch.unlock()
 
484
        finally:
 
485
            local_branch.unlock()
 
486
 
 
487
 
 
488
class cmd_ginit(GTKCommand):
 
489
    def run(self):
 
490
        self.open_display()
 
491
        from initialize import InitDialog
 
492
        dialog = InitDialog(os.path.abspath(os.path.curdir))
 
493
        dialog.run()
 
494
 
 
495
 
 
496
class cmd_gtags(GTKCommand):
 
497
    def run(self):
 
498
        br = branch.Branch.open_containing('.')[0]
 
499
        
 
500
        gtk = self.open_display()
 
501
        from tags import TagsWindow
 
502
        window = TagsWindow(br)
 
503
        window.show()
 
504
        gtk.main()
 
505
 
 
506
 
 
507
commands = [
 
508
    cmd_gannotate, 
 
509
    cmd_gbranch,
 
510
    cmd_gcheckout, 
 
511
    cmd_gcommit, 
 
512
    cmd_gconflicts, 
 
513
    cmd_gdiff,
 
514
    cmd_ginit,
 
515
    cmd_gmissing, 
 
516
    cmd_gpreferences, 
 
517
    cmd_gpush, 
 
518
    cmd_gsend,
 
519
    cmd_gstatus,
 
520
    cmd_gtags,
 
521
    cmd_visualise
 
522
    ]
 
523
 
 
524
for cmd in commands:
 
525
    register_command(cmd)
 
526
 
 
527
 
 
528
class cmd_commit_notify(GTKCommand):
 
529
    """Run the bzr commit notifier.
 
530
 
 
531
    This is a background program which will pop up a notification on the users
 
532
    screen when a commit occurs.
 
533
    """
 
534
 
 
535
    def run(self):
 
536
        from notify import NotifyPopupMenu
 
537
        gtk = self.open_display()
 
538
        menu = NotifyPopupMenu()
 
539
        icon = gtk.status_icon_new_from_file(icon_path("bzr-icon-64.png"))
 
540
        icon.connect('popup-menu', menu.display)
 
541
 
 
542
        import cgi
 
543
        import dbus
 
544
        import dbus.service
 
545
        import pynotify
 
546
        from bzrlib.bzrdir import BzrDir
 
547
        from bzrlib import errors
 
548
        from bzrlib.osutils import format_date
 
549
        from bzrlib.transport import get_transport
 
550
        if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
 
551
            import dbus.glib
 
552
        BROADCAST_INTERFACE = "org.bazaarvcs.plugins.dbus.Broadcast"
 
553
        bus = dbus.SessionBus()
 
554
 
 
555
        def catch_branch(revision_id, urls):
 
556
            # TODO: show all the urls, or perhaps choose the 'best'.
 
557
            url = urls[0]
 
558
            try:
 
559
                if isinstance(revision_id, unicode):
 
560
                    revision_id = revision_id.encode('utf8')
 
561
                transport = get_transport(url)
 
562
                a_dir = BzrDir.open_from_transport(transport)
 
563
                branch = a_dir.open_branch()
 
564
                revno = branch.revision_id_to_revno(revision_id)
 
565
                revision = branch.repository.get_revision(revision_id)
 
566
                summary = 'New revision %d in %s' % (revno, url)
 
567
                body  = 'Committer: %s\n' % revision.committer
 
568
                body += 'Date: %s\n' % format_date(revision.timestamp,
 
569
                    revision.timezone)
 
570
                body += '\n'
 
571
                body += revision.message
 
572
                body = cgi.escape(body)
 
573
                nw = pynotify.Notification(summary, body)
 
574
                def start_viz(notification=None, action=None, data=None):
 
575
                    """Start the viz program."""
 
576
                    pp = start_viz_window(branch, revision_id)
 
577
                    pp.show()
 
578
                def start_branch(notification=None, action=None, data=None):
 
579
                    """Start a Branch dialog"""
 
580
                    from bzrlib.plugins.gtk.branch import BranchDialog
 
581
                    bd = BranchDialog(remote_path=url)
 
582
                    bd.run()
 
583
                nw.add_action("inspect", "Inspect", start_viz, None)
 
584
                nw.add_action("branch", "Branch", start_branch, None)
 
585
                nw.set_timeout(5000)
 
586
                nw.show()
 
587
            except Exception, e:
 
588
                print e
 
589
                raise
 
590
        bus.add_signal_receiver(catch_branch,
 
591
                                dbus_interface=BROADCAST_INTERFACE,
 
592
                                signal_name="Revision")
 
593
        pynotify.init("bzr commit-notify")
 
594
        gtk.main()
 
595
 
 
596
register_command(cmd_commit_notify)
 
597
 
 
598
 
 
599
class cmd_gselftest(GTKCommand):
 
600
    """Version of selftest that displays a notification at the end"""
 
601
 
 
602
    takes_args = builtins.cmd_selftest.takes_args
 
603
    takes_options = builtins.cmd_selftest.takes_options
 
604
    _see_also = ['selftest']
 
605
 
 
606
    def run(self, *args, **kwargs):
 
607
        import cgi
 
608
        import sys
 
609
        default_encoding = sys.getdefaultencoding()
 
610
        # prevent gtk from blowing up later
 
611
        gtk = import_pygtk()
 
612
        # prevent gtk from messing with default encoding
 
613
        import pynotify
 
614
        if sys.getdefaultencoding() != default_encoding:
 
615
            reload(sys)
 
616
            sys.setdefaultencoding(default_encoding)
 
617
        result = builtins.cmd_selftest().run(*args, **kwargs)
 
618
        if result == 0:
 
619
            summary = 'Success'
 
620
            body = 'Selftest succeeded in "%s"' % os.getcwd()
 
621
        if result == 1:
 
622
            summary = 'Failure'
 
623
            body = 'Selftest failed in "%s"' % os.getcwd()
 
624
        pynotify.init("bzr gselftest")
 
625
        note = pynotify.Notification(cgi.escape(summary), cgi.escape(body))
 
626
        note.set_timeout(pynotify.EXPIRES_NEVER)
 
627
        note.show()
 
628
 
 
629
 
 
630
register_command(cmd_gselftest)
 
631
 
 
632
 
 
633
class cmd_test_gtk(GTKCommand):
 
634
    """Version of selftest that just runs the gtk test suite."""
 
635
 
 
636
    takes_options = ['verbose',
 
637
                     Option('one', short_name='1',
 
638
                            help='Stop when one test fails.'),
 
639
                     Option('benchmark', help='Run the benchmarks.'),
 
640
                     Option('lsprof-timed',
 
641
                     help='Generate lsprof output for benchmarked'
 
642
                          ' sections of code.'),
 
643
                     Option('list-only',
 
644
                     help='List the tests instead of running them.'),
 
645
                     Option('randomize', type=str, argname="SEED",
 
646
                     help='Randomize the order of tests using the given'
 
647
                          ' seed or "now" for the current time.'),
 
648
                    ]
 
649
    takes_args = ['testspecs*']
 
650
 
 
651
    def run(self, verbose=None, one=False, benchmark=None,
 
652
            lsprof_timed=None, list_only=False, randomize=None,
 
653
            testspecs_list=None):
 
654
        from bzrlib import __path__ as bzrlib_path
 
655
        from bzrlib.tests import selftest
 
656
 
 
657
        print '%10s: %s' % ('bzrlib', bzrlib_path[0])
 
658
        if benchmark:
 
659
            print 'No benchmarks yet'
 
660
            return 3
 
661
 
 
662
            test_suite_factory = bench_suite
 
663
            if verbose is None:
 
664
                verbose = True
 
665
            # TODO: should possibly lock the history file...
 
666
            benchfile = open(".perf_history", "at", buffering=1)
 
667
        else:
 
668
            test_suite_factory = test_suite
 
669
            if verbose is None:
 
670
                verbose = False
 
671
            benchfile = None
 
672
 
 
673
        if testspecs_list is not None:
 
674
            pattern = '|'.join(testspecs_list)
 
675
        else:
 
676
            pattern = ".*"
 
677
 
 
678
        try:
 
679
            result = selftest(verbose=verbose,
 
680
                              pattern=pattern,
 
681
                              stop_on_failure=one,
 
682
                              test_suite_factory=test_suite_factory,
 
683
                              lsprof_timed=lsprof_timed,
 
684
                              bench_history=benchfile,
 
685
                              list_only=list_only,
 
686
                              random_seed=randomize,
 
687
                             )
 
688
        finally:
 
689
            if benchfile is not None:
 
690
                benchfile.close()
 
691
 
 
692
register_command(cmd_test_gtk)
 
693
 
 
694
 
 
695
class cmd_ghandle_patch(GTKCommand):
 
696
    """Display a patch or merge directive, possibly merging.
 
697
 
 
698
    This is a helper, meant to be launched from other programs like browsers
 
699
    or email clients.  Since these programs often do not allow parameters to
 
700
    be provided, a "handle-patch" script is included.
 
701
    """
 
702
 
 
703
    takes_args = ['path']
 
704
 
 
705
    def run(self, path):
 
706
        try:
 
707
            from bzrlib.plugins.gtk.diff import (DiffController,
 
708
                                                 MergeDirectiveController)
 
709
            if path == '-':
 
710
                lines = sys.stdin.readlines()
 
711
            else:
 
712
                lines = open(path, 'rb').readlines()
 
713
            lines = [l.replace('\r\n', '\n') for l in lines]
 
714
            try:
 
715
                directive = merge_directive.MergeDirective.from_lines(lines)
 
716
            except errors.NotAMergeDirective:
 
717
                controller = DiffController(path, lines)
 
718
            else:
 
719
                controller = MergeDirectiveController(path, directive)
 
720
            window = controller.window
 
721
            window.show()
 
722
            gtk = self.open_display()
 
723
            window.connect("destroy", gtk.main_quit)
 
724
        except Exception, e:
 
725
            from dialog import error_dialog
 
726
            error_dialog('Error', str(e))
 
727
            raise
 
728
        gtk.main()
 
729
 
 
730
 
 
731
register_command(cmd_ghandle_patch)
 
732
 
 
733
 
 
734
import gettext
 
735
gettext.install('olive-gtk')
 
736
 
 
737
# Let's create a specialized alias to protect '_' from being erased by other
 
738
# uses of '_' as an anonymous variable (think pdb for one).
 
739
_i18n = gettext.gettext
 
740
 
 
741
class NoDisplayError(BzrCommandError):
 
742
    """gtk could not find a proper display"""
 
743
 
 
744
    def __str__(self):
 
745
        return "No DISPLAY. Unable to run GTK+ application."
 
746
 
 
747
 
 
748
def test_suite():
 
749
    from unittest import TestSuite
 
750
    import tests
162
751
    import sys
163
752
    default_encoding = sys.getdefaultencoding()
164
753
    try:
165
 
        result = basic_tests
 
754
        result = TestSuite()
166
755
        try:
167
 
            import gi.repository.Gtk
168
 
        except ImportError:
169
 
            return basic_tests
170
 
        basic_tests.addTest(loader.loadTestsFromModuleNames(
171
 
                ["%s.%s" % (__name__, tmn) for tmn in testmod_names]))
 
756
            import_pygtk()
 
757
        except errors.BzrCommandError:
 
758
            return result
 
759
        result.addTest(tests.test_suite())
172
760
    finally:
173
761
        if sys.getdefaultencoding() != default_encoding:
174
762
            reload(sys)
175
763
            sys.setdefaultencoding(default_encoding)
176
 
    return basic_tests
177
 
 
 
764
    return result