/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 nautilus-bzr.py

Allow bzr-gtk and Bazaar versions to be out of sync. No longer warn about 
newer versions of Bazaar, on the assumption the API is sufficiently stable now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
#
3
3
# Copyright (C) 2006 Jeff Bailey
4
4
# Copyright (C) 2006 Wouter van Heyst
5
 
# Copyright (C) 2006-2011 Jelmer Vernooij <jelmer@samba.org>
6
 
#
7
 
# This program is free software; you can redistribute it and/or modify
8
 
# it under the terms of the GNU General Public License as published by
9
 
# the Free Software Foundation; either version 3 of the License, or
10
 
# (at your option) any later version.
11
 
 
12
 
# This program is distributed in the hope that it will be useful,
13
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
# GNU General Public License for more details.
16
 
 
17
 
# You should have received a copy of the GNU General Public License
18
 
# along with this program; if not, write to the Free Software
19
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 
 
21
 
# Installation:
22
 
# setup.py can install nautilus-bzr to the right system folder, if pkg-config
23
 
# is present.
24
 
#
25
 
# You can also install nautilus-bzr manually by copying it (or linking it from)
26
 
# ~/.local/share/nautilus-python/extensions/nautilus-bzr.py
27
 
 
28
 
from gi.repository import Gtk, GObject, Nautilus
29
 
from bzrlib.controldir import ControlDir
30
 
from bzrlib.errors import (
31
 
    NotBranchError,
32
 
    NoWorkingTree,
33
 
    )
34
 
from bzrlib.ignores import tree_ignores_add_patterns
35
 
from bzrlib.tree import InterTree
 
5
# Copyright (C) 2006 Jelmer Vernooij
 
6
#
 
7
# Published under the GNU GPL
 
8
 
 
9
import gtk
 
10
import nautilus
 
11
import bzrlib
 
12
from bzrlib.bzrdir import BzrDir
 
13
from bzrlib.errors import NotBranchError
 
14
from bzrlib.errors import NoWorkingTree
 
15
from bzrlib.errors import UnsupportedProtocol
 
16
from bzrlib.workingtree import WorkingTree
 
17
from bzrlib.branch import Branch
 
18
from bzrlib.tree import file_status
36
19
 
37
20
from bzrlib.plugin import load_plugins
38
21
load_plugins()
39
22
 
40
 
from bzrlib.plugins.gtk.i18n import _i18n
41
 
 
42
 
 
43
 
class BazaarExtension(Nautilus.MenuProvider, Nautilus.ColumnProvider,
44
 
        Nautilus.InfoProvider, Nautilus.PropertyPageProvider,
45
 
        Nautilus.LocationWidgetProvider, GObject.GObject):
46
 
    """Nautilus extension providing Bazaar integration."""
47
 
 
 
23
from bzrlib.plugins.gtk import cmd_visualise, cmd_gannotate
 
24
 
 
25
class BzrExtension(nautilus.MenuProvider, nautilus.ColumnProvider, nautilus.InfoProvider):
48
26
    def __init__(self):
49
27
        pass
50
28
 
51
 
    @classmethod
52
 
    def _open_bzrdir(cls, vfs_file):
53
 
        uri = vfs_file.get_uri()
54
 
        controldir, path = ControlDir.open_containing(uri)
55
 
        return controldir, path
56
 
 
57
 
    @classmethod
58
 
    def _open_tree(cls, vfs_file):
59
 
        controldir, path = cls._open_bzrdir(vfs_file)
60
 
        return controldir.open_workingtree(), path
61
 
 
62
 
    def add_cb(self, menu, tree, path):
 
29
    def add_cb(self, menu, vfs_file):
 
30
        # We can only cope with local files
 
31
        if vfs_file.get_uri_scheme() != 'file':
 
32
            return
 
33
 
 
34
        file = vfs_file.get_uri()
 
35
        try:
 
36
            tree, path = WorkingTree.open_containing(file)
 
37
        except NotBranchError:
 
38
            return
 
39
 
63
40
        tree.add(path)
64
41
 
65
 
    def ignore_cb(self, menu, tree, path):
66
 
        # We can only cope with local files
67
 
        tree_ignores_add_patterns(tree, [path])
68
 
        #FIXME: Add path to ignore file
69
 
 
70
 
    def unignore_cb(self, menu, tree, path):
71
 
        pass
72
 
        # We can only cope with local files
73
 
        #FIXME
74
 
 
75
 
    def diff_cb(self, menu, tree, path=None):
 
42
        return
 
43
 
 
44
    def ignore_cb(self, menu, vfs_file):
 
45
        # We can only cope with local files
 
46
        if vfs_file.get_uri_scheme() != 'file':
 
47
            return
 
48
 
 
49
        file = vfs_file.get_uri()
 
50
        try:
 
51
            tree, path = WorkingTree.open_containing(file)
 
52
        except NotBranchError:
 
53
            return
 
54
 
 
55
        #FIXME
 
56
 
 
57
        return
 
58
 
 
59
    def unignore_cb(self, menu, vfs_file):
 
60
        # We can only cope with local files
 
61
        if vfs_file.get_uri_scheme() != 'file':
 
62
            return
 
63
 
 
64
        file = vfs_file.get_uri()
 
65
        try:
 
66
            tree, path = WorkingTree.open_containing(file)
 
67
        except NotBranchError:
 
68
            return
 
69
 
 
70
        #FIXME
 
71
 
 
72
        return
 
73
 
 
74
    def diff_cb(self, menu, vfs_file):
 
75
        # We can only cope with local files
 
76
        if vfs_file.get_uri_scheme() != 'file':
 
77
            return
 
78
 
 
79
        file = vfs_file.get_uri()
 
80
        try:
 
81
            tree, path = WorkingTree.open_containing(file)
 
82
        except NotBranchError:
 
83
            return
 
84
 
76
85
        from bzrlib.plugins.gtk.diff import DiffWindow
77
86
        window = DiffWindow()
78
 
        window.set_diff(tree.branch._get_nick(local=True), tree, 
79
 
                        tree.branch.basis_tree())
 
87
        window.set_diff(tree.branch.nick, tree, tree.branch.basis_tree())
80
88
        window.show()
81
89
 
 
90
        return
 
91
 
82
92
    def newtree_cb(self, menu, vfs_file):
83
 
        controldir, path = self._open_bzrdir(vfs_file)
84
 
        controldir.create_workingtree()
85
 
 
86
 
    def remove_cb(self, menu, tree, path):
 
93
        # We can only cope with local files
 
94
        if vfs_file.get_uri_scheme() != 'file':
 
95
            return
 
96
 
 
97
        file = vfs_file.get_uri()
 
98
 
 
99
        # We only want to continue here if we get a NotBranchError
 
100
        try:
 
101
            tree, path = WorkingTree.open_containing(file)
 
102
        except NotBranchError:
 
103
            BzrDir.create_standalone_workingtree(file)
 
104
 
 
105
    def remove_cb(self, menu, vfs_file):
 
106
        # We can only cope with local files
 
107
        if vfs_file.get_uri_scheme() != 'file':
 
108
            return
 
109
 
 
110
        file = vfs_file.get_uri()
 
111
        try:
 
112
            tree, path = WorkingTree.open_containing(file)
 
113
        except NotBranchError:
 
114
            return
 
115
 
87
116
        tree.remove(path)
88
117
 
89
 
    def annotate_cb(self, menu, tree, path, file_id):
90
 
        from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
91
 
        win = GAnnotateWindow()
92
 
        win.show()
93
 
        win.annotate(tree, tree.branch, file_id)
94
 
        Gtk.main()
 
118
    def annotate_cb(self, menu, vfs_file):
 
119
        # We can only cope with local files
 
120
        if vfs_file.get_uri_scheme() != 'file':
 
121
            return
 
122
 
 
123
        file = vfs_file.get_uri()
 
124
 
 
125
        vis = cmd_gannotate()
 
126
        vis.run(file)
95
127
 
96
128
    def clone_cb(self, menu, vfs_file=None):
 
129
        # We can only cope with local files
 
130
        if vfs_file.get_uri_scheme() != 'file':
 
131
            return
 
132
 
97
133
        from bzrlib.plugins.gtk.branch import BranchDialog
98
 
        controldir, path = self._open_bzrdir(vfs_file)
99
 
 
 
134
        
100
135
        dialog = BranchDialog(vfs_file.get_name())
101
136
        response = dialog.run()
102
 
        if response != Gtk.ResponseType.NONE:
 
137
        if response != gtk.RESPONSE_NONE:
103
138
            dialog.hide()
104
139
            dialog.destroy()
105
 
 
106
 
    def commit_cb(self, menu, tree, path=None):
 
140
 
 
141
    def commit_cb(self, menu, vfs_file=None):
 
142
        # We can only cope with local files
 
143
        if vfs_file.get_uri_scheme() != 'file':
 
144
            return
 
145
 
 
146
        file = vfs_file.get_uri()
 
147
        tree = None
 
148
        branch = None
 
149
        try:
 
150
            tree, path = WorkingTree.open_containing(file)
 
151
            branch = tree.branch
 
152
        except NotBranchError, e:
 
153
            path = e.path
 
154
            #return
 
155
        except NoWorkingTree, e:
 
156
            path = e.base
 
157
            try:
 
158
                (branch, path) = Branch.open_containing(path)
 
159
            except NotBranchError, e:
 
160
                path = e.path
 
161
 
107
162
        from bzrlib.plugins.gtk.commit import CommitDialog
108
 
        dialog = CommitDialog(tree, path)
 
163
        dialog = CommitDialog(tree, path, not branch)
109
164
        response = dialog.run()
110
 
        if response != Gtk.ResponseType.NONE:
 
165
        if response != gtk.RESPONSE_NONE:
111
166
            dialog.hide()
112
167
            dialog.destroy()
113
168
 
114
 
    def log_cb(self, menu, controldir, path=None):
115
 
        from bzrlib.plugins.gtk.viz import BranchWindow
116
 
        branch = controldir.open_branch()
117
 
        pp = BranchWindow(branch, [branch.last_revision()], None)
118
 
        pp.show()
119
 
        Gtk.main()
120
 
 
121
 
    def pull_cb(self, menu, controldir, path=None):
 
169
    def log_cb(self, menu, vfs_file):
 
170
        # We can only cope with local files
 
171
        if vfs_file.get_uri_scheme() != 'file':
 
172
            return
 
173
 
 
174
        file = vfs_file.get_uri()
 
175
 
 
176
        # We only want to continue here if we get a NotBranchError
 
177
        try:
 
178
            tree, path = WorkingTree.open_containing(file)
 
179
        except NotBranchError:
 
180
            return
 
181
 
 
182
        vis = cmd_visualise()
 
183
        vis.run(file)
 
184
 
 
185
        return
 
186
 
 
187
    def pull_cb(self, menu, vfs_file):
 
188
        # We can only cope with local files
 
189
        if vfs_file.get_uri_scheme() != 'file':
 
190
            return
 
191
 
 
192
        file = vfs_file.get_uri()
 
193
 
 
194
        # We only want to continue here if we get a NotBranchError
 
195
        try:
 
196
            tree, path = WorkingTree.open_containing(file)
 
197
        except NotBranchError:
 
198
            return
 
199
 
122
200
        from bzrlib.plugins.gtk.pull import PullDialog
123
 
        dialog = PullDialog(controldir.open_workingtree(), path)
 
201
        dialog = PullDialog(tree, path)
124
202
        dialog.display()
125
 
        Gtk.main()
126
 
 
127
 
    def merge_cb(self, menu, tree, path=None):
 
203
        gtk.main()
 
204
 
 
205
    def merge_cb(self, menu, vfs_file):
 
206
        # We can only cope with local files
 
207
        if vfs_file.get_uri_scheme() != 'file':
 
208
            return
 
209
 
 
210
        file = vfs_file.get_uri()
 
211
 
 
212
        # We only want to continue here if we get a NotBranchError
 
213
        try:
 
214
            tree, path = WorkingTree.open_containing(file)
 
215
        except NotBranchError:
 
216
            return
 
217
 
128
218
        from bzrlib.plugins.gtk.merge import MergeDialog
129
219
        dialog = MergeDialog(tree, path)
130
 
        dialog.run()
131
 
        dialog.destroy()
132
 
 
133
 
    def create_tree_cb(self, menu, controldir):
134
 
        controldir.create_workingtree()
 
220
        dialog.display()
 
221
        gtk.main()
135
222
 
136
223
    def get_background_items(self, window, vfs_file):
 
224
        items = []
 
225
        file = vfs_file.get_uri()
137
226
        try:
138
 
            controldir, path = self._open_bzrdir(vfs_file)
139
 
        except NotBranchError:
 
227
            tree, path = WorkingTree.open_containing(file)
 
228
        except UnsupportedProtocol:
140
229
            return
141
 
        try:
142
 
            branch = controldir.open_branch()
143
230
        except NotBranchError:
144
 
            items = []
145
 
            item = Nautilus.MenuItem(name='BzrNautilus::newtree',
146
 
                                 label='Make directory versioned',
147
 
                                 tip='Create new Bazaar tree in this folder',
148
 
                                 icon='')
 
231
            item = nautilus.MenuItem('BzrNautilus::newtree',
 
232
                                 'Make directory versioned',
 
233
                                 'Create new Bazaar tree in this folder')
149
234
            item.connect('activate', self.newtree_cb, vfs_file)
150
235
            items.append(item)
151
236
 
152
 
            item = Nautilus.MenuItem(name='BzrNautilus::clone',
153
 
                                 label='Checkout Bazaar branch ...',
154
 
                                 tip='Checkout Existing Bazaar Branch',
155
 
                                 icon='')
 
237
            item = nautilus.MenuItem('BzrNautilus::clone',
 
238
                                 'Checkout Bazaar branch',
 
239
                                 'Checkout Existing Bazaar Branch')
156
240
            item.connect('activate', self.clone_cb, vfs_file)
157
241
            items.append(item)
 
242
 
158
243
            return items
159
244
 
160
 
        items = []
161
 
 
162
 
        nautilus_integration = self.check_branch_enabled(branch)
163
 
        if not nautilus_integration:
164
 
            item = Nautilus.MenuItem(name='BzrNautilus::enable',
165
 
                                     label='Enable Bazaar Plugin for this Branch',
166
 
                                     tip='Enable Bazaar plugin for nautilus',
167
 
                                     icon='')
168
 
            item.connect('activate', self.toggle_integration, True, branch)
169
 
            return [item]
170
 
        else:
171
 
            item = Nautilus.MenuItem(name='BzrNautilus::disable',
172
 
                                     label='Disable Bazaar Plugin this Branch',
173
 
                                     tip='Disable Bazaar plugin for nautilus',
174
 
                                     icon='')
175
 
            item.connect('activate', self.toggle_integration, False, branch)
176
 
            items.append(item)
177
 
 
178
 
        item = Nautilus.MenuItem(name='BzrNautilus::log',
179
 
                             label='History ...',
180
 
                             tip='Show Bazaar history',
181
 
                             icon='')
182
 
        item.connect('activate', self.log_cb, controldir)
183
 
        items.append(item)
184
 
 
185
 
        item = Nautilus.MenuItem(name='BzrNautilus::pull',
186
 
                             label='Pull ...',
187
 
                             tip='Pull from another branch',
188
 
                             icon='')
189
 
        item.connect('activate', self.pull_cb, controldir)
190
 
        items.append(item)
191
 
 
192
 
        try:
193
 
            tree = controldir.open_workingtree()
194
 
        except NoWorkingTree:
195
 
            item = Nautilus.MenuItem(name='BzrNautilus::create_tree',
196
 
                                 label='Create working tree...',
197
 
                                 tip='Create a working tree for this branch',
198
 
                                 icon='')
199
 
            item.connect('activate', self.create_tree_cb, controldir)
200
 
            items.append(item)
201
 
        else:
202
 
            item = Nautilus.MenuItem(name='BzrNautilus::merge',
203
 
                                 label='Merge ...',
204
 
                                 tip='Merge from another branch',
205
 
                                 icon='')
206
 
            item.connect('activate', self.merge_cb, tree, path)
207
 
            items.append(item)
208
 
 
209
 
            item = Nautilus.MenuItem(name='BzrNautilus::commit',
210
 
                                 label='Commit ...',
211
 
                                 tip='Commit Changes',
212
 
                                 icon='')
213
 
            item.connect('activate', self.commit_cb, tree, path)
214
 
            items.append(item)
 
245
        item = nautilus.MenuItem('BzrNautilus::log',
 
246
                             'Log',
 
247
                             'Show Bazaar history')
 
248
        item.connect('activate', self.log_cb, vfs_file)
 
249
        items.append(item)
 
250
 
 
251
        item = nautilus.MenuItem('BzrNautilus::pull',
 
252
                             'Pull',
 
253
                             'Pull from another branch')
 
254
        item.connect('activate', self.pull_cb, vfs_file)
 
255
        items.append(item)
 
256
 
 
257
        item = nautilus.MenuItem('BzrNautilus::merge',
 
258
                             'Merge',
 
259
                             'Merge from another branch')
 
260
        item.connect('activate', self.merge_cb, vfs_file)
 
261
        items.append(item)
 
262
 
 
263
        item = nautilus.MenuItem('BzrNautilus::commit',
 
264
                             'Commit',
 
265
                             'Commit Changes')
 
266
        item.connect('activate', self.commit_cb, vfs_file)
 
267
        items.append(item)
215
268
 
216
269
        return items
217
270
 
218
 
    def _get_file_menuitems(self, tree, intertree, path):
219
 
        file_id = tree.path2id(path)
220
 
        if file_id is None:
221
 
            item = Nautilus.MenuItem(name='BzrNautilus::add',
222
 
                                 label='Add',
223
 
                                 tip='Add as versioned file',
224
 
                                 icon='')
225
 
            item.connect('activate', self.add_cb, tree, path)
226
 
            yield item
227
 
 
228
 
            item = Nautilus.MenuItem(name='BzrNautilus::ignore',
229
 
                                 label='Ignore',
230
 
                                 tip='Ignore file for versioning',
231
 
                                 icon='')
232
 
            item.connect('activate', self.ignore_cb, tree, path)
233
 
            yield item
234
 
        elif tree.is_ignored(path):
235
 
            item = Nautilus.MenuItem(name='BzrNautilus::unignore',
236
 
                                 label='Unignore',
237
 
                                 tip='Unignore file for versioning',
238
 
                                 icon='')
239
 
            item.connect('activate', self.unignore_cb, tree, path)
240
 
            yield item
241
 
        else:
242
 
            item = Nautilus.MenuItem(name='BzrNautilus::log',
243
 
                             label='History ...',
244
 
                             tip='List changes',
245
 
                             icon='')
246
 
            item.connect('activate', self.log_cb, tree.bzrdir, path)
247
 
            yield item
248
 
 
249
 
            if not intertree.file_content_matches(file_id, file_id):
250
 
                item = Nautilus.MenuItem(name='BzrNautilus::diff',
251
 
                                 label='View Changes ...',
252
 
                                 tip='Show differences',
253
 
                                 icon='')
254
 
                item.connect('activate', self.diff_cb, tree, path)
255
 
                yield item
256
 
 
257
 
                item = Nautilus.MenuItem(name='BzrNautilus::commit',
258
 
                             label='Commit ...',
259
 
                             tip='Commit Changes',
260
 
                             icon='')
261
 
                item.connect('activate', self.commit_cb, tree, path)
262
 
                yield item
263
 
 
264
 
            item = Nautilus.MenuItem(name='BzrNautilus::remove',
265
 
                                 label='Remove',
266
 
                                 tip='Remove this file from versioning',
267
 
                                 icon='')
268
 
            item.connect('activate', self.remove_cb, tree, path)
269
 
            yield item
270
 
 
271
 
            item = Nautilus.MenuItem(name='BzrNautilus::annotate',
272
 
                         label='Annotate ...',
273
 
                         tip='Annotate File Data',
274
 
                         icon='')
275
 
            item.connect('activate', self.annotate_cb, tree, path, file_id)
276
 
            yield item
277
271
 
278
272
    def get_file_items(self, window, files):
279
273
        items = []
280
 
        trees = {}
281
 
 
282
 
        try:
283
 
            for vfs_file in files:
284
 
                controldir, path = self._open_bzrdir(vfs_file)
285
 
 
286
 
                try:
287
 
                    tree = trees[controldir.user_url]
288
 
                except KeyError:
289
 
                    try:
290
 
                        tree = controldir.open_workingtree()
291
 
                    except NoWorkingTree:
292
 
                        continue
293
 
                    trees[controldir.user_url] = tree
294
 
                    tree.lock_read()
295
 
 
296
 
                nautilus_integration = self.check_branch_enabled(tree.branch)
297
 
                if not nautilus_integration:
298
 
                    continue
299
 
 
300
 
                intertree = InterTree.get(tree.basis_tree(), tree)
301
 
                items.extend(list(self._get_file_menuitems(tree, intertree, path)))
302
 
        finally:
303
 
            for tree in trees.itervalues():
 
274
 
 
275
        wtfiles = {}
 
276
        for vfs_file in files:
 
277
            # We can only cope with local files
 
278
            if vfs_file.get_uri_scheme() != 'file':
 
279
                return
 
280
 
 
281
            file = vfs_file.get_uri()
 
282
            try:
 
283
                tree, path = WorkingTree.open_containing(file)
 
284
            except NotBranchError:
 
285
                if not vfs_file.is_directory():
 
286
                    return
 
287
                item = nautilus.MenuItem('BzrNautilus::newtree',
 
288
                                     'Make directory versioned',
 
289
                                     'Create new Bazaar tree in %s' % vfs_file.get_name())
 
290
                item.connect('activate', self.newtree_cb, vfs_file)
 
291
                return item,
 
292
            # Refresh the list of filestatuses in the working tree
 
293
            if path not in wtfiles.keys():
 
294
                tree.lock_read()
 
295
                for rpath, file_class, kind, id, entry in tree.list_files():
 
296
                    wtfiles[rpath] = file_class
304
297
                tree.unlock()
 
298
                wtfiles[u''] = 'V'
 
299
 
 
300
            if wtfiles[path] == '?':
 
301
                item = nautilus.MenuItem('BzrNautilus::add',
 
302
                                     'Add',
 
303
                                     'Add as versioned file')
 
304
                item.connect('activate', self.add_cb, vfs_file)
 
305
                items.append(item)
 
306
 
 
307
                item = nautilus.MenuItem('BzrNautilus::ignore',
 
308
                                     'Ignore',
 
309
                                     'Ignore file for versioning')
 
310
                item.connect('activate', self.ignore_cb, vfs_file)
 
311
                items.append(item)
 
312
            elif wtfiles[path] == 'I':
 
313
                item = nautilus.MenuItem('BzrNautilus::unignore',
 
314
                                     'Unignore',
 
315
                                     'Unignore file for versioning')
 
316
                item.connect('activate', self.unignore_cb, vfs_file)
 
317
                items.append(item)
 
318
            elif wtfiles[path] == 'V':
 
319
                item = nautilus.MenuItem('BzrNautilus::log',
 
320
                                 'Log',
 
321
                                 'List changes')
 
322
                item.connect('activate', self.log_cb, vfs_file)
 
323
                items.append(item)
 
324
 
 
325
                item = nautilus.MenuItem('BzrNautilus::diff',
 
326
                                 'Diff',
 
327
                                 'Show differences')
 
328
                item.connect('activate', self.diff_cb, vfs_file)
 
329
                items.append(item)
 
330
 
 
331
                item = nautilus.MenuItem('BzrNautilus::remove',
 
332
                                     'Remove',
 
333
                                     'Remove this file from versioning')
 
334
                item.connect('activate', self.remove_cb, vfs_file)
 
335
                items.append(item)
 
336
 
 
337
                item = nautilus.MenuItem('BzrNautilus::annotate',
 
338
                             'Annotate',
 
339
                             'Annotate File Data')
 
340
                item.connect('activate', self.annotate_cb, vfs_file)
 
341
                items.append(item)
 
342
 
 
343
                item = nautilus.MenuItem('BzrNautilus::commit',
 
344
                             'Commit',
 
345
                             'Commit Changes')
 
346
                item.connect('activate', self.commit_cb, vfs_file)
 
347
                items.append(item)
305
348
 
306
349
        return items
307
350
 
308
351
    def get_columns(self):
309
 
        return [
310
 
            Nautilus.Column(name="BzrNautilus::bzr_status",
311
 
                            attribute="bzr_status",
312
 
                            label="Status",
313
 
                            description="Version control status"),
314
 
            Nautilus.Column(name="BzrNautilus::bzr_revision",
315
 
                            attribute="bzr_revision",
316
 
                            label="Revision",
317
 
                            description="Last change revision"),
318
 
            ]
319
 
 
320
 
    def _file_summary(self, tree, basis_tree, intertree, path):
321
 
        file_revision = ""
 
352
        return nautilus.Column("BzrNautilus::bzr_status",
 
353
                               "bzr_status",
 
354
                               "Bzr Status",
 
355
                               "Version control status"),
 
356
 
 
357
    def update_file_info(self, file):
 
358
        if file.get_uri_scheme() != 'file':
 
359
            return
 
360
        
 
361
        try:
 
362
            tree, path = WorkingTree.open_containing(file.get_uri())
 
363
        except NotBranchError:
 
364
            return
 
365
 
322
366
        emblem = None
323
 
 
324
 
        file_id = tree.path2id(path)
325
 
        if file_id is None:
326
 
            if tree.is_ignored(path):
327
 
                status = 'ignored'
328
 
                emblem = 'bzr-ignored'
329
 
            else:
330
 
                status = 'unversioned'
331
 
            file_revision = "N/A"
332
 
        elif tree.has_filename(path): # Still present
333
 
            if not intertree.file_content_matches(file_id, file_id):
334
 
                if not basis_tree.has_id(file_id):
335
 
                    emblem = 'bzr-added'
 
367
        status = None
 
368
 
 
369
        if tree.has_filename(path):
 
370
            emblem = 'cvs-controlled'
 
371
            status = 'unchanged'
 
372
            id = tree.path2id(path)
 
373
 
 
374
            delta = tree.changes_from(tree.branch.basis_tree())
 
375
            if delta.touches_file_id(id):
 
376
                emblem = 'cvs-modified'
 
377
                status = 'modified'
 
378
            for f, _, _ in delta.added:
 
379
                if f == path:
 
380
                    emblem = 'cvs-added'
336
381
                    status = 'added'
337
 
                    file_revision = "new file"
338
 
                elif basis_tree.path2id(file_id) != path:
339
 
                    status = 'bzr-renamed'
340
 
                    status = 'renamed from %s' % basis_tree.path2id(file_id)
341
 
                else:
342
 
                    emblem = 'bzr-modified'
343
 
                    status = 'modified'
344
 
            else:
345
 
                emblem = 'bzr-controlled'
346
 
                status = 'unchanged'
347
 
        elif basis_tree.has_filename(path):
348
 
            emblem = 'bzr-removed'
 
382
 
 
383
            for of, f, _, _, _, _ in delta.renamed:
 
384
                if f == path:
 
385
                    status = 'renamed from %s' % f
 
386
 
 
387
        elif tree.branch.basis_tree().has_filename(path):
 
388
            emblem = 'cvs-removed'
349
389
            status = 'removed'
350
390
        else:
351
391
            # FIXME: Check for ignored files
352
392
            status = 'unversioned'
353
 
        return (status, emblem, file_revision)
354
 
 
355
 
    def update_file_info(self, vfs_file):
356
 
        try:
357
 
            controldir, path = self._open_bzrdir(vfs_file)
358
 
        except NotBranchError:
359
 
            return
360
 
 
361
 
        try:
362
 
            tree = controldir.open_workingtree()
363
 
        except NoWorkingTree:
364
 
            return
365
 
 
366
 
        tree.lock_read()
367
 
        try:
368
 
            nautilus_integration = self.check_branch_enabled(tree.branch)
369
 
            if not nautilus_integration:
370
 
                return
371
 
 
372
 
            basis_tree = tree.basis_tree()
373
 
            intertree = InterTree.get(basis_tree, tree)
374
 
 
375
 
            basis_tree.lock_read()
376
 
            try:
377
 
                (status, emblem, file_revision) = self._file_summary(tree, basis_tree, intertree, path)
378
 
            finally:
379
 
                basis_tree.unlock()
380
 
            if emblem is not None:
381
 
                vfs_file.add_emblem(emblem)
382
 
            vfs_file.add_string_attribute('bzr_status', status)
383
 
            vfs_file.add_string_attribute('bzr_revision', file_revision)
384
 
        finally:
385
 
            tree.unlock()
386
 
 
387
 
    def check_branch_enabled(self, branch):
388
 
        # Supports global disable, but there is currently no UI to do this
389
 
        config = branch.get_config_stack()
390
 
        return config.get("nautilus_integration")
391
 
 
392
 
    def toggle_integration(self, menu, action, branch):
393
 
        config = branch.get_config_stack()
394
 
        config.set("nautilus_integration", action)
395
 
 
396
 
    def get_property_pages(self, files):
397
 
        pages = []
398
 
        for vfs_file in files:
399
 
            try:
400
 
                controldir, path = self._open_bzrdir(vfs_file)
401
 
            except NotBranchError:
402
 
                continue
403
 
 
404
 
            try:
405
 
                tree = controldir.open_workingtree()
406
 
            except NoWorkingTree:
407
 
                continue
408
 
 
409
 
            tree.lock_read()
410
 
            try:
411
 
                file_id = tree.path2id(path)
412
 
                pages.append(PropertyPageFile(tree, file_id, path))
413
 
                pages.append(PropertyPageBranch(tree.branch))
414
 
            finally:
415
 
                tree.unlock()
416
 
        return pages
417
 
 
418
 
    def get_widget(self, uri, window):
419
 
        controldir, path = ControlDir.open_containing(uri)
420
 
        try:
421
 
            tree = controldir.open_workingtree()
422
 
        except NoWorkingTree:
423
 
            return
424
 
        ret = Gtk.HBox(False, 4)
425
 
        text = 'This is a Bazaar working tree. '
426
 
        get_shelf_manager = getattr(tree, 'get_shelf_manager', None)
427
 
        if get_shelf_manager is not None:
428
 
            manager = get_shelf_manager()
429
 
            shelves = manager.active_shelves()
430
 
            if len(shelves) == 0:
431
 
                pass
432
 
            elif len(shelves) == 1:
433
 
                text += '1 shelf exists. '
434
 
            else:
435
 
                text += '%d shelf exists. ' % len(shelves)
436
 
        label = Gtk.Label(text)
437
 
        label.show()
438
 
        ret.pack_start(label, True, True, 0)
439
 
        ret.show_all()
440
 
        return ret
441
 
 
442
 
 
443
 
class PropertyPageFile(Nautilus.PropertyPage):
444
 
 
445
 
    def __init__(self, tree, file_id, path):
446
 
        self.tree = tree
447
 
        self.file_id = file_id
448
 
        self.path = path
449
 
        label = Gtk.Label('File Version')
450
 
        label.show()
451
 
 
452
 
        table = self._create_table()
453
 
 
454
 
        super(PropertyPageFile, self).__init__(label=label,
455
 
            name="BzrNautilus::file_page", page=table)
456
 
 
457
 
    def _create_table(self):
458
 
        table = Gtk.Table(homogeneous=False, columns=2, rows=3)
459
 
 
460
 
        table.attach(Gtk.Label(_i18n('File id:')), 0, 1, 0, 1)
461
 
        table.attach(Gtk.Label(self.file_id), 1, 2, 0, 1)
462
 
 
463
 
        table.attach(Gtk.Label(_i18n('SHA1Sum:')), 0, 1, 1, 2)
464
 
        table.attach(Gtk.Label(self.tree.get_file_sha1(self.file_id, self.path)), 1, 1, 1, 2)
465
 
 
466
 
        basis_tree = self.tree.revision_tree(self.tree.last_revision())
467
 
        last_revision = basis_tree.get_file_revision(self.file_id)
468
 
 
469
 
        table.attach(Gtk.Label(_i18n('Last Change Revision:')), 0, 1, 2, 3)
470
 
        revno = ".".join([str(x) for x in
471
 
            self.tree.branch.revision_id_to_dotted_revno(last_revision)])
472
 
        table.attach(Gtk.Label(revno), 1, 1, 2, 3)
473
 
 
474
 
        table.attach(Gtk.Label(_i18n('Last Change Author:')), 0, 1, 3, 4)
475
 
        rev = self.tree.branch.repository.get_revision(last_revision)
476
 
        table.attach(Gtk.Label("\n".join(rev.get_apparent_authors())), 1, 1, 3, 4)
477
 
 
478
 
        table.show_all()
479
 
        return table
480
 
 
481
 
 
482
 
class PropertyPageBranch(Nautilus.PropertyPage):
483
 
 
484
 
    def __init__(self, branch):
485
 
        self.branch = branch
486
 
        label = Gtk.Label('Branch')
487
 
        label.show()
488
 
 
489
 
        table = self._create_table()
490
 
 
491
 
        super(PropertyPageBranch, self).__init__(label=label,
492
 
            name="BzrNautilus::branch_page", page=table)
493
 
 
494
 
    def _create_location_entry(self, get_location, set_location):
495
 
        location = get_location()
496
 
        ret = Gtk.Entry()
497
 
        if location is not None:
498
 
            ret.set_text(location)
499
 
        return ret
500
 
 
501
 
    def _create_table(self):
502
 
        table = Gtk.Table(homogeneous=False, columns=2, rows=6)
503
 
 
504
 
        self._push_location_entry = self._create_location_entry(
505
 
            self.branch.get_push_location, self.branch.set_push_location)
506
 
        self._parent_location_entry = self._create_location_entry(
507
 
            self.branch.get_parent, self.branch.set_parent)
508
 
        self._bound_location_entry = self._create_location_entry(
509
 
            self.branch.get_bound_location, self.branch.set_bound_location)
510
 
        self._public_location_entry = self._create_location_entry(
511
 
            self.branch.get_public_branch, self.branch.set_public_branch)
512
 
        self._submit_location_entry = self._create_location_entry(
513
 
            self.branch.get_submit_branch, self.branch.set_submit_branch)
514
 
 
515
 
        table.attach(Gtk.Label(_i18n('Push location:')), 0, 1, 0, 1)
516
 
        table.attach(self._push_location_entry, 1, 2, 0, 1)
517
 
 
518
 
        table.attach(Gtk.Label(_i18n('Parent location:')), 0, 1, 1, 2)
519
 
        table.attach(self._parent_location_entry, 1, 1, 1, 2)
520
 
 
521
 
        table.attach(Gtk.Label(_i18n('Bound location:')), 0, 1, 2, 3)
522
 
        table.attach(self._bound_location_entry, 1, 1, 2, 3)
523
 
 
524
 
        table.attach(Gtk.Label(_i18n('Public location:')), 0, 1, 3, 4)
525
 
        table.attach(self._public_location_entry, 1, 1, 3, 4)
526
 
 
527
 
        table.attach(Gtk.Label(_i18n('Submit location:')), 0, 1, 4, 5)
528
 
        table.attach(self._submit_location_entry, 1, 1, 4, 5)
529
 
 
530
 
        self._append_revisions_only = Gtk.CheckButton(_i18n('Append revisions only'))
531
 
        value = self.branch.get_append_revisions_only()
532
 
        if value is None:
533
 
            value = False
534
 
        self._append_revisions_only.set_active(value)
535
 
        table.attach(self._append_revisions_only, 0, 2, 5, 6)
536
 
 
537
 
        table.show_all()
538
 
        return table
 
393
        
 
394
        if emblem is not None:
 
395
            file.add_emblem(emblem)
 
396
        file.add_string_attribute('bzr_status', status)