/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
2
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
7
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
12
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
try:
18
    import pygtk
19
    pygtk.require("2.0")
20
except:
21
    pass
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
22
0.13.11 by Jelmer Vernooij
Bunch of small fixes, cleanups and simplifications.
23
import gtk
24
import gobject
25
import pango
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
26
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
27
import os.path
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
28
import re
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
29
248 by Jelmer Vernooij
Merge fixes for #127392 and #127381)
30
from bzrlib import errors, osutils
235.1.5 by Mateusz Korniak
Missing mutter import added.
31
from bzrlib.trace import mutter
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
32
from bzrlib.util import bencode
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
33
153 by Jelmer Vernooij
Fix references to dialog.
34
from dialog import error_dialog, question_dialog
132 by Jelmer Vernooij
Use decorator for catching and showing bzr-gtk errors graphically. Eventually, this should go away and should be handled by the ui factory.
35
from errors import show_bzr_error
93.1.6 by Alexander Belchenko
detecting name of glade file doing in separate module (olive.gladefile)
36
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
37
try:
38
    import dbus
39
    import dbus.glib
180 by Jelmer Vernooij
Don't obtain handle to network manager until it's actually needed.
40
    have_dbus = True
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
41
except ImportError:
180 by Jelmer Vernooij
Don't obtain handle to network manager until it's actually needed.
42
    have_dbus = False
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
43
278.1.4 by John Arbash Meinel
Just playing around.
44
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
45
def pending_revisions(wt):
46
    """Return a list of pending merges or None if there are none of them.
47
48
    Arguably this should be a core function, and
49
    ``bzrlib.status.show_pending_merges`` should be built on top of it.
50
51
    :return: [(rev, [children])]
52
    """
53
    parents = wt.get_parent_ids()
54
    if len(parents) < 2:
55
        return None
56
57
    # The basic pending merge algorithm uses the same algorithm as
58
    # bzrlib.status.show_pending_merges
59
    pending = parents[1:]
60
    branch = wt.branch
61
    last_revision = parents[0]
62
63
    if last_revision is not None:
64
        try:
65
            ignore = set(branch.repository.get_ancestry(last_revision,
66
                                                        topo_sorted=False))
67
        except errors.NoSuchRevision:
68
            # the last revision is a ghost : assume everything is new
69
            # except for it
70
            ignore = set([None, last_revision])
71
    else:
72
        ignore = set([None])
73
74
    pm = []
75
    for merge in pending:
76
        ignore.add(merge)
77
        try:
78
            rev = branch.repository.get_revision(merge)
79
            children = []
80
            pm.append((rev, children))
81
82
            # This does need to be topo sorted, so we search backwards
83
            inner_merges = branch.repository.get_ancestry(merge)
84
            assert inner_merges[0] is None
85
            inner_merges.pop(0)
86
            for mmerge in reversed(inner_merges):
87
                if mmerge in ignore:
88
                    continue
89
                rev = branch.repository.get_revision(mmerge)
90
                children.append(rev)
91
92
                ignore.add(mmerge)
93
        except errors.NoSuchRevision:
94
            print "DEBUG: NoSuchRevision:", merge
95
96
    return pm
97
98
135 by Jelmer Vernooij
Throw out the old CommitDialog code and use the new code instead, also for 'gcommit'.
99
class CommitDialog(gtk.Dialog):
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
100
    """Implementation of Commit."""
101
102
    def __init__(self, wt, selected=None, parent=None):
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
103
        gtk.Dialog.__init__(self, title="Commit - Olive",
104
                                  parent=parent,
105
                                  flags=0,
106
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
278.1.24 by John Arbash Meinel
Actually show the commit button.
107
        self._question_dialog = question_dialog
108
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
109
        self._wt = wt
110
        self._selected = selected
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
111
        self.committed_revision_id = None # Nothing has been committed yet
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
112
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
113
        self.setup_params()
114
        self.construct()
115
        self.fill_in_data()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
116
117
    def setup_params(self):
118
        """Setup the member variables for state."""
119
        self._basis_tree = self._wt.basis_tree()
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
120
        self._delta = None
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
121
        self._pending = pending_revisions(self._wt)
122
123
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
124
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
125
    def fill_in_data(self):
126
        # Now that we are built, handle changes to the view based on the state
127
        self._fill_in_pending()
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
128
        self._fill_in_diff()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
129
        self._fill_in_files()
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
130
        self._fill_in_checkout()
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
131
132
    def _fill_in_pending(self):
133
        if not self._pending:
134
            self._pending_box.hide()
135
            return
136
137
        # TODO: We'd really prefer this to be a nested list
138
        for rev, children in self._pending:
139
            rev_info = self._rev_to_pending_info(rev)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
140
            self._pending_store.append([
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
141
                rev_info['revision_id'],
142
                rev_info['date'],
143
                rev_info['committer'],
144
                rev_info['summary'],
145
                ])
146
            for child in children:
147
                rev_info = self._rev_to_pending_info(child)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
148
                self._pending_store.append([
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
149
                    rev_info['revision_id'],
150
                    rev_info['date'],
151
                    rev_info['committer'],
152
                    rev_info['summary'],
153
                    ])
154
        self._pending_box.show()
155
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
156
    def _fill_in_files(self):
157
        # We should really use _iter_changes, and then add a progress bar of
158
        # some kind.
159
        # While we fill in the view, hide the store
160
        store = self._files_store
161
        self._treeview_files.set_model(None)
162
163
        added = _('added')
164
        removed = _('removed')
165
        renamed = _('renamed')
166
        renamed_and_modified = _('renamed and modified')
167
        modified = _('modified')
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
168
        kind_changed = _('kind changed')
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
169
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
170
        # The store holds:
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
171
        # [file_id, real path, checkbox, display path, changes type, message]
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
172
        # _iter_changes returns:
173
        # (file_id, (path_in_source, path_in_target),
174
        #  changed_content, versioned, parent, name, kind,
175
        #  executable)
176
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
177
        # The first entry is always the 'whole tree'
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
178
        store.append([None, None, True, 'All Files', '', ''])
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
179
        # should we pass specific_files?
180
        self._wt.lock_read()
181
        self._basis_tree.lock_read()
182
        try:
183
            for (file_id, paths, changed_content, versioned, parent_ids, names,
184
                 kinds, executables) in self._wt._iter_changes(self._basis_tree):
185
186
                # Skip the root entry.
187
                if parent_ids == (None, None):
188
                    continue
189
190
                change_type = None
191
                if kinds[0] is None:
192
                    source_marker = ''
193
                else:
194
                    source_marker = osutils.kind_marker(kinds[0])
195
                if kinds[1] is None:
196
                    assert kinds[0] is not None
197
                    marker = osutils.kind_marker(kinds[0])
198
                else:
199
                    marker = osutils.kind_marker(kinds[1])
200
201
                real_path = paths[1]
202
                if real_path is None:
203
                    real_path = paths[0]
204
                assert real_path is not None
205
                display_path = real_path + marker
206
207
                present_source = versioned[0] and kinds[0] is not None
208
                present_target = versioned[1] and kinds[1] is not None
209
210
                if present_source != present_target:
211
                    if present_target:
212
                        change_type = added
213
                    else:
214
                        change_type = removed
215
                elif names[0] != names[1] or parent_ids[0] != parent_ids[1]:
216
                    # Renamed
217
                    if changed_content or executables[0] != executables[1]:
218
                        # and modified
219
                        change_type = renamed_and_modified
220
                    else:
221
                        change_type = renamed
222
                    display_path = (paths[0] + source_marker
223
                                    + ' => ' + paths[1] + marker)
224
                elif kinds[0] != kinds[1]:
225
                    change_type = kind_changed
226
                    display_path = (paths[0] + source_marker
227
                                    + ' => ' + paths[1] + marker)
228
                elif changed_content is True or executables[0] != executables[1]:
229
                    change_type = modified
230
                else:
231
                    assert False, "How did we get here?"
232
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
233
                store.append([file_id, real_path, True, display_path,
234
                              change_type, ''])
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
235
        finally:
236
            self._basis_tree.unlock()
237
            self._wt.unlock()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
238
239
        self._treeview_files.set_model(store)
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
240
        self._last_selected_file = None
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
241
        self._treeview_files.set_cursor(0)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
242
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
243
    def _fill_in_diff(self):
244
        self._diff_view.set_trees(self._wt, self._basis_tree)
245
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
246
    def _fill_in_checkout(self):
247
        if not self._is_checkout:
248
            self._check_local.hide()
249
            return
250
        if have_dbus:
251
            bus = dbus.SystemBus()
252
            proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
253
                                       '/org/freedesktop/NetworkManager')
254
            dbus_iface = dbus.Interface(proxy_obj,
255
                                        'org.freedesktop.NetworkManager')
256
            try:
257
                # 3 is the enum value for STATE_CONNECTED
258
                self._check_local.set_active(dbus_iface.state() != 3)
259
            except dbus.DBusException, e:
260
                # Silently drop errors. While DBus may be
261
                # available, NetworkManager doesn't necessarily have to be
262
                mutter("unable to get networkmanager state: %r" % e)
263
        self._check_local.show()
264
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
265
    def _compute_delta(self):
266
        self._delta = self._wt.changes_from(self._basis_tree)
267
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
268
    def construct(self):
269
        """Build up the dialog widgets."""
270
        # The primary pane which splits it into left and right (adjustable)
271
        # sections.
278.1.4 by John Arbash Meinel
Just playing around.
272
        self._hpane = gtk.HPaned()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
273
274
        self._construct_left_pane()
275
        self._construct_right_pane()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
276
        self._construct_action_pane()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
277
278
        self.vbox.pack_start(self._hpane)
279
        self._hpane.show()
280
        self.set_focus(self._global_message_text_view)
281
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
282
        # This seems like a reasonable default, we might like it to
283
        # be a bit wider, so that by default we can fit an 80-line diff in the
284
        # diff window.
285
        # Alternatively, we should be saving the last position/size rather than
286
        # setting it to a fixed value every time we start up.
287
        screen = self.get_screen()
288
        monitor = 0 # We would like it to be the monitor we are going to
289
                    # display on, but I don't know how to figure that out
290
                    # Only really useful for freaks like me that run dual
291
                    # monitor, with different sizes on the monitors
292
        monitor_rect = screen.get_monitor_geometry(monitor)
293
        width = int(monitor_rect.width * 0.66)
294
        height = int(monitor_rect.height * 0.66)
295
        self.set_default_size(width, height)
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
296
        self._hpane.set_position(300)
297
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
298
    def _construct_left_pane(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
299
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
300
        self._construct_file_list()
301
        self._construct_pending_list()
302
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
303
        self._check_local = gtk.CheckButton(_("_Only commit locally"),
304
                                            use_underline=True)
305
        self._left_pane_box.pack_end(self._check_local, False, False)
306
        self._check_local.set_active(False)
307
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
308
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=False)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
309
        self._left_pane_box.show()
310
311
    def _construct_right_pane(self):
312
        # TODO: I really want to make it so the diff view gets more space than
313
        # the global commit message, and the per-file commit message gets even
314
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
315
        # commit, and 1 for file commit, and it looked good. But I don't seem
316
        # to have a way to do that with the gtk boxes... :( (Which is extra
317
        # weird since wx uses gtk on Linux...)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
318
        self._right_pane_table = gtk.Table(rows=10, columns=1, homogeneous=False)
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
319
        self._right_pane_table.set_row_spacings(5)
320
        self._right_pane_table.set_col_spacings(5)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
321
        self._right_pane_table_row = 0
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
322
        self._construct_diff_view()
323
        self._construct_file_message()
324
        self._construct_global_message()
325
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
326
        self._right_pane_table.show()
327
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
328
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
329
    def _construct_action_pane(self):
330
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
331
        self._button_commit.connect('clicked', self._on_commit_clicked)
332
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
278.1.24 by John Arbash Meinel
Actually show the commit button.
333
        self._button_commit.show()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
334
        self.action_area.pack_end(self._button_commit)
278.1.24 by John Arbash Meinel
Actually show the commit button.
335
        self._button_commit.grab_default()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
336
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
337
    def _add_to_right_table(self, widget, weight, expanding=False):
338
        """Add another widget to the table
339
340
        :param widget: The object to add
341
        :param weight: How many rows does this widget get to request
342
        :param expanding: Should expand|fill|shrink be set?
343
        """
344
        end_row = self._right_pane_table_row + weight
345
        options = 0
346
        expand_opts = gtk.EXPAND | gtk.FILL | gtk.SHRINK
347
        if expanding:
348
            options = expand_opts
349
        self._right_pane_table.attach(widget, 0, 1,
350
                                      self._right_pane_table_row, end_row,
351
                                      xoptions=expand_opts, yoptions=options)
352
        self._right_pane_table_row = end_row
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
353
354
    def _construct_file_list(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
355
        self._files_box = gtk.VBox(homogeneous=False, spacing=0)
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
356
        file_label = gtk.Label(_('Files'))
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
357
        file_label.show()
358
        self._files_box.pack_start(file_label, expand=False)
359
360
        scroller = gtk.ScrolledWindow()
361
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
362
        self._treeview_files = gtk.TreeView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
363
        self._treeview_files.show()
364
        scroller.add(self._treeview_files)
365
        scroller.show()
366
        scroller.set_shadow_type(gtk.SHADOW_IN)
367
        self._files_box.pack_start(scroller,
368
                                   expand=True, fill=True)
369
        self._files_box.show()
370
        self._left_pane_box.pack_start(self._files_box)
371
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
372
        liststore = gtk.ListStore(
373
            gobject.TYPE_STRING,  # [0] file_id
374
            gobject.TYPE_STRING,  # [1] real path
375
            gobject.TYPE_BOOLEAN, # [2] checkbox
376
            gobject.TYPE_STRING,  # [3] display path
377
            gobject.TYPE_STRING,  # [4] changes type
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
378
            gobject.TYPE_STRING,  # [5] commit message
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
379
            )
380
        self._files_store = liststore
381
        self._treeview_files.set_model(liststore)
382
        crt = gtk.CellRendererToggle()
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
383
        crt.set_active(not bool(self._pending))
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
384
        crt.connect("toggled", self._toggle_commit, self._files_store)
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
385
        if self._pending:
386
            name = _('Commit*')
387
        else:
388
            name = _('Commit')
389
        self._treeview_files.append_column(gtk.TreeViewColumn(name,
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
390
                                           crt, active=2))
391
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
392
                                           gtk.CellRendererText(), text=3))
393
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
394
                                           gtk.CellRendererText(), text=4))
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
395
        self._treeview_files.connect('cursor-changed',
396
                                     self._on_treeview_files_cursor_changed)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
397
398
    def _toggle_commit(self, cell, path, model):
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
399
        if model[path][0] is None: # No file_id means 'All Files'
400
            new_val = not model[path][2]
401
            for node in model:
402
                node[2] = new_val
403
        else:
404
            model[path][2] = not model[path][2]
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
405
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
406
    def _construct_pending_list(self):
407
        # Pending information defaults to hidden, we put it all in 1 box, so
408
        # that we can show/hide all of them at once
409
        self._pending_box = gtk.VBox()
410
        self._pending_box.hide()
411
412
        pending_message = gtk.Label()
413
        pending_message.set_markup(
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
414
            _('<i>* Cannot select specific files when merging</i>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
415
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
416
        pending_message.show()
417
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
418
        pending_label = gtk.Label(_('Pending Revisions'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
419
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
420
        pending_label.show()
421
422
        scroller = gtk.ScrolledWindow()
423
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
424
        self._treeview_pending = gtk.TreeView()
425
        scroller.add(self._treeview_pending)
426
        scroller.show()
427
        scroller.set_shadow_type(gtk.SHADOW_IN)
428
        self._pending_box.pack_start(scroller,
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
429
                                     expand=True, fill=True, padding=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
430
        self._treeview_pending.show()
431
        self._left_pane_box.pack_start(self._pending_box)
432
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
433
        liststore = gtk.ListStore(gobject.TYPE_STRING, # revision_id
434
                                  gobject.TYPE_STRING, # date
435
                                  gobject.TYPE_STRING, # committer
436
                                  gobject.TYPE_STRING, # summary
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
437
                                 )
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
438
        self._pending_store = liststore
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
439
        self._treeview_pending.set_model(liststore)
440
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Date'),
441
                                             gtk.CellRendererText(), text=1))
442
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Committer'),
443
                                             gtk.CellRendererText(), text=2))
444
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Summary'),
445
                                             gtk.CellRendererText(), text=3))
446
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
447
    def _construct_diff_view(self):
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
448
        from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
449
450
        self._diff_label = gtk.Label(_('Diff for whole tree'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
451
        self._diff_label.set_alignment(0, 0)
452
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
453
        self._add_to_right_table(self._diff_label, 1, False)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
454
        self._diff_label.show()
455
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
456
        self._diff_view = DiffView()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
457
        self._add_to_right_table(self._diff_view, 4, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
458
        self._diff_view.show()
459
460
    def _construct_file_message(self):
461
        file_message_box = gtk.VBox()
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
462
        scroller = gtk.ScrolledWindow()
463
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
464
465
        self._file_message_text_view = gtk.TextView()
466
        scroller.add(self._file_message_text_view)
467
        scroller.show()
468
        scroller.set_shadow_type(gtk.SHADOW_IN)
469
        file_message_box.pack_start(scroller, expand=True, fill=True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
470
471
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
472
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
473
        self._file_message_text_view.set_accepts_tab(False)
474
        self._file_message_text_view.show()
475
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
476
        self._file_message_expander = gtk.Expander(_('File commit message'))
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
477
        self._file_message_expander.add(file_message_box)
478
        file_message_box.show()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
479
        self._add_to_right_table(self._file_message_expander, 1, False)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
480
        self._file_message_expander.show()
481
482
    def _construct_global_message(self):
483
        self._global_message_label = gtk.Label(_('Global Commit Message'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
484
        self._global_message_label.set_alignment(0, 0)
485
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
486
        self._add_to_right_table(self._global_message_label, 1, False)
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
487
        # Can we remove the spacing between the label and the box?
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
488
        self._global_message_label.show()
489
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
490
        scroller = gtk.ScrolledWindow()
491
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
492
493
        self._global_message_text_view = gtk.TextView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
494
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
495
        scroller.add(self._global_message_text_view)
496
        scroller.show()
497
        scroller.set_shadow_type(gtk.SHADOW_IN)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
498
        self._add_to_right_table(scroller, 2, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
499
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
500
        self._file_message_text_view.set_accepts_tab(False)
501
        self._global_message_text_view.show()
502
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
503
    def _on_treeview_files_cursor_changed(self, treeview):
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
504
        treeselection = treeview.get_selection()
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
505
        (model, selection) = treeselection.get_selected()
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
506
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
507
        if selection is not None:
508
            path, display_path = model.get(selection, 1, 3)
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
509
            self._diff_label.set_text(_('Diff for ') + display_path)
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
510
            if path is None:
511
                self._diff_view.show_diff(None)
512
            else:
513
                self._diff_view.show_diff([path])
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
514
            self._update_per_file_info(selection)
515
516
    def _save_current_file_message(self):
517
        if self._last_selected_file is None:
518
            return # Nothing to save
519
        text_buffer = self._file_message_text_view.get_buffer()
520
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
521
                                        text_buffer.get_end_iter())
522
        last_selected = self._files_store.get_iter(self._last_selected_file)
523
        self._files_store.set_value(last_selected, 5, cur_text)
524
525
    def _update_per_file_info(self, selection):
526
        # The node is changing, so cache the current message
527
        self._save_current_file_message()
528
        text_buffer = self._file_message_text_view.get_buffer()
529
        file_id, display_path, message = self._files_store.get(selection, 0, 3, 5)
530
        if file_id is None: # Whole tree
531
            self._file_message_expander.set_label(_('File commit message'))
532
            self._file_message_expander.set_expanded(False)
533
            self._file_message_expander.set_sensitive(False)
534
            text_buffer.set_text('')
535
            self._last_selected_file = None
536
        else:
537
            self._file_message_expander.set_label(_('Commit message for ')
538
                                                  + display_path)
539
            self._file_message_expander.set_expanded(True)
540
            self._file_message_expander.set_sensitive(True)
541
            text_buffer.set_text(message)
542
            self._last_selected_file = self._files_store.get_path(selection)
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
543
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
544
    def _get_specific_files(self):
545
        self._save_current_file_message()
546
        files = []
547
        records = iter(self._files_store)
548
        rec = records.next() # Skip the All Files record
549
        assert rec[0] is None, "Are we skipping the wrong record?"
550
551
        file_info = []
552
        for record in records:
553
            if record[2]: # [2] checkbox
554
                file_id = record[0]
555
                path = record[1]
556
                file_message = record[5]
557
                files.append(record[1]) # [1] real path
558
                if file_message:
559
                    file_info.append({'path':path, 'file_id':file_id,
560
                                     'message':file_message})
561
        file_info.sort(key=lambda x:(x['path'], x['file_id']))
562
        return files, file_info
563
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
564
    @show_bzr_error
565
    def _on_commit_clicked(self, button):
566
        """ Commit button clicked handler. """
567
        self._do_commit()
568
569
    def _do_commit(self):
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
570
        message = self._get_global_commit_message()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
571
572
        if message == '':
573
            response = self._question_dialog(
574
                            _('Commit with an empty message?'),
575
                            _('You can describe your commit intent in the message.'))
576
            if response == gtk.RESPONSE_NO:
577
                # Kindly give focus to message area
578
                self._global_message_text_view.grab_focus()
579
                return
580
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
581
        specific_files, file_info = self._get_specific_files()
278.1.28 by John Arbash Meinel
Ensure that we can set per-file messages even during a merge.
582
        if self._pending:
583
            specific_files = None
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
584
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
585
        local = self._check_local.get_active()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
586
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
587
        # All we care about is if there is a single unknown, so if this loop is
588
        # entered, then there are unknown files.
589
        # TODO: jam 20071002 It seems like this should cancel the dialog
590
        #       entirely, since there isn't a way for them to add the unknown
591
        #       files at this point.
592
        for path in self._wt.unknowns():
593
            response = self._question_dialog(
594
                _("Commit with unknowns?"),
595
                _("Unknown files exist in the working tree. Commit anyway?"))
596
            if response == gtk.RESPONSE_NO:
597
                return
598
            break
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
599
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
600
        rev_id = None
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
601
        revprops = {}
602
        if file_info:
603
            revprops['file-info'] = bencode.bencode(file_info)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
604
        try:
605
            rev_id = self._wt.commit(message,
606
                       allow_pointless=False,
607
                       strict=False,
608
                       local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
609
                       specific_files=specific_files,
610
                       revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
611
        except errors.PointlessCommit:
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
612
            response = self._question_dialog(
613
                                _('Commit with no changes?'),
614
                                _('There are no changes in the working tree.'
615
                                  ' Do you want to commit anyway?'))
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
616
            if response == gtk.RESPONSE_YES:
617
                rev_id = self._wt.commit(message,
618
                               allow_pointless=True,
619
                               strict=False,
620
                               local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
621
                               specific_files=specific_files,
622
                               revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
623
        self.committed_revision_id = rev_id
624
        self.response(gtk.RESPONSE_OK)
625
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
626
    def _get_global_commit_message(self):
627
        buf = self._global_message_text_view.get_buffer()
628
        start, end = buf.get_bounds()
629
        return buf.get_text(start, end).decode('utf-8')
630
631
    def _set_global_commit_message(self, message):
632
        """Just a helper for the test suite."""
633
        self._global_message_text_view.get_buffer().set_text(message)
634
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
635
    def _set_file_commit_message(self, message):
636
        """Helper for the test suite."""
637
        self._file_message_text_view.get_buffer().set_text(message)
638
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
639
    @staticmethod
640
    def _rev_to_pending_info(rev):
641
        """Get the information from a pending merge."""
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
642
        from bzrlib.osutils import format_date
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
643
644
        rev_dict = {}
645
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
646
        rev_dict['summary'] = rev.get_summary()
647
        rev_dict['date'] = format_date(rev.timestamp,
648
                                       rev.timezone or 0,
649
                                       'original', date_fmt="%Y-%m-%d",
650
                                       show_offset=False)
651
        rev_dict['revision_id'] = rev.revision_id
652
        return rev_dict
653
654
655
# class CommitDialog(gtk.Dialog):
656
#     """ New implementation of the Commit dialog. """
657
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
658
#         """ Initialize the Commit Dialog. """
659
#         gtk.Dialog.__init__(self, title="Commit - Olive",
660
#                                   parent=parent,
661
#                                   flags=0,
662
#                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
663
#         
664
#         # Get arguments
665
#         self.wt = wt
666
#         self.wtpath = wtpath
667
#         self.notbranch = notbranch
668
#         self.selected = selected
669
#         
670
#         # Set the delta
671
#         self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
672
#         self.delta = self.wt.changes_from(self.old_tree)
673
#         
674
#         # Get pending merges
675
#         self.pending = self._pending_merges(self.wt)
676
#         
677
#         # Do some preliminary checks
678
#         self._is_checkout = False
679
#         self._is_pending = False
680
#         if self.wt is None and not self.notbranch:
681
#             error_dialog(_('Directory does not have a working tree'),
682
#                          _('Operation aborted.'))
683
#             self.close()
684
#             return
685
# 
686
#         if self.notbranch:
687
#             error_dialog(_('Directory is not a branch'),
688
#                          _('You can perform this action only in a branch.'))
689
#             self.close()
690
#             return
691
#         else:
692
#             if self.wt.branch.get_bound_location() is not None:
693
#                 # we have a checkout, so the local commit checkbox must appear
694
#                 self._is_checkout = True
695
#             
696
#             if self.pending:
697
#                 # There are pending merges, file selection not supported
698
#                 self._is_pending = True
699
#         
700
#         # Create the widgets
701
#         # This is the main horizontal box, which is used to separate the commit
702
#         # info from the diff window.
703
#         self._hpane = gtk.HPaned()
704
#         self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
705
#         self._expander_files = gtk.Expander(_("File(s) to commit"))
706
#         self._vpaned_main = gtk.VPaned()
707
#         self._scrolledwindow_files = gtk.ScrolledWindow()
708
#         self._scrolledwindow_message = gtk.ScrolledWindow()
709
#         self._treeview_files = gtk.TreeView()
710
#         self._vbox_message = gtk.VBox()
711
#         self._label_message = gtk.Label(_("Commit message:"))
712
#         self._textview_message = gtk.TextView()
713
#         
714
#         if self._is_pending:
715
#             self._expander_merges = gtk.Expander(_("Pending merges"))
716
#             self._vpaned_list = gtk.VPaned()
717
#             self._scrolledwindow_merges = gtk.ScrolledWindow()
718
#             self._treeview_merges = gtk.TreeView()
719
# 
720
#         # Set callbacks
721
#         self._button_commit.connect('clicked', self._on_commit_clicked)
722
#         self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
723
#         self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
724
#         
725
#         # Set properties
726
#         self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
727
#                                               gtk.POLICY_AUTOMATIC)
728
#         self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
729
#                                                 gtk.POLICY_AUTOMATIC)
730
#         self._textview_message.modify_font(pango.FontDescription("Monospace"))
731
#         self.set_default_size(500, 500)
732
#         self._vpaned_main.set_position(200)
733
#         self._button_commit.set_flags(gtk.CAN_DEFAULT)
734
# 
735
#         if self._is_pending:
736
#             self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
737
#                                                    gtk.POLICY_AUTOMATIC)
738
#             self._treeview_files.set_sensitive(False)
739
#         
740
#         # Construct the dialog
741
#         self.action_area.pack_end(self._button_commit)
742
#         
743
#         self._scrolledwindow_files.add(self._treeview_files)
744
#         self._scrolledwindow_message.add(self._textview_message)
745
#         
746
#         self._expander_files.add(self._scrolledwindow_files)
747
#         
748
#         self._vbox_message.pack_start(self._label_message, False, False)
749
#         self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
750
#         
751
#         if self._is_pending:        
752
#             self._expander_merges.add(self._scrolledwindow_merges)
753
#             self._scrolledwindow_merges.add(self._treeview_merges)
754
#             self._vpaned_list.add1(self._expander_files)
755
#             self._vpaned_list.add2(self._expander_merges)
756
#             self._vpaned_main.add1(self._vpaned_list)
757
#         else:
758
#             self._vpaned_main.add1(self._expander_files)
759
# 
760
#         self._vpaned_main.add2(self._vbox_message)
761
#         
762
#         self._hpane.pack1(self._vpaned_main)
763
#         self.vbox.pack_start(self._hpane, expand=True, fill=True)
764
#         if self._is_checkout: 
765
#             self._check_local = gtk.CheckButton(_("_Only commit locally"),
766
#                                                 use_underline=True)
767
#             self.vbox.pack_start(self._check_local, False, False)
768
#             if have_dbus:
769
#                 bus = dbus.SystemBus()
770
#                 proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
771
#                               '/org/freedesktop/NetworkManager')
772
#                 dbus_iface = dbus.Interface(
773
#                         proxy_obj, 'org.freedesktop.NetworkManager')
774
#                 try:
775
#                     # 3 is the enum value for STATE_CONNECTED
776
#                     self._check_local.set_active(dbus_iface.state() != 3)
777
#                 except dbus.DBusException, e:
778
#                     # Silently drop errors. While DBus may be 
779
#                     # available, NetworkManager doesn't necessarily have to be
780
#                     mutter("unable to get networkmanager state: %r" % e)
781
#                 
782
#         # Create the file list
783
#         self._create_file_view()
784
#         # Create the pending merges
785
#         self._create_pending_merges()
786
#         self._create_diff_view()
787
#         
788
#         # Expand the corresponding expander
789
#         if self._is_pending:
790
#             self._expander_merges.set_expanded(True)
791
#         else:
792
#             self._expander_files.set_expanded(True)
793
#         
794
#         # Display dialog
795
#         self.vbox.show_all()
796
#         
797
#         # Default to Commit button
798
#         self._button_commit.grab_default()
799
#     
800
#     def _show_diff_view(self, treeview):
801
#         # FIXME: the diff window freezes for some reason
802
#         treeselection = treeview.get_selection()
803
#         (model, iter) = treeselection.get_selected()
804
# 
805
#         if iter is not None:
806
#             selected = model.get_value(iter, 3) # Get the real_path attribute
807
#             self._diff_display.show_diff([selected])
808
# 
809
#     def _on_treeview_files_cursor_changed(self, treeview):
810
#         self._show_diff_view(treeview)
811
#         
812
#     def _on_treeview_files_row_activated(self, treeview, path, view_column):
813
#         self._show_diff_view(treeview)
814
#     
815
#     @show_bzr_error
816
#     def _on_commit_clicked(self, button):
817
#         """ Commit button clicked handler. """
818
#         textbuffer = self._textview_message.get_buffer()
819
#         start, end = textbuffer.get_bounds()
820
#         message = textbuffer.get_text(start, end).decode('utf-8')
821
#         
822
#         if not self.pending:
823
#             specific_files = self._get_specific_files()
824
#         else:
825
#             specific_files = None
826
# 
827
#         if message == '':
828
#             response = question_dialog(_('Commit with an empty message?'),
829
#                                        _('You can describe your commit intent in the message.'))
830
#             if response == gtk.RESPONSE_NO:
831
#                 # Kindly give focus to message area
832
#                 self._textview_message.grab_focus()
833
#                 return
834
# 
835
#         if self._is_checkout:
836
#             local = self._check_local.get_active()
837
#         else:
838
#             local = False
839
# 
840
#         if list(self.wt.unknowns()) != []:
841
#             response = question_dialog(_("Commit with unknowns?"),
842
#                _("Unknown files exist in the working tree. Commit anyway?"))
843
#             if response == gtk.RESPONSE_NO:
844
#                 return
845
#         
846
#         try:
847
#             self.wt.commit(message,
848
#                        allow_pointless=False,
849
#                        strict=False,
850
#                        local=local,
851
#                        specific_files=specific_files)
852
#         except errors.PointlessCommit:
853
#             response = question_dialog(_('Commit with no changes?'),
854
#                                        _('There are no changes in the working tree.'))
855
#             if response == gtk.RESPONSE_YES:
856
#                 self.wt.commit(message,
857
#                                allow_pointless=True,
858
#                                strict=False,
859
#                                local=local,
860
#                                specific_files=specific_files)
861
#         self.response(gtk.RESPONSE_OK)
862
# 
863
#     def _pending_merges(self, wt):
864
#         """ Return a list of pending merges or None if there are none of them. """
865
#         parents = wt.get_parent_ids()
866
#         if len(parents) < 2:
867
#             return None
868
#         
869
#         import re
870
#         from bzrlib.osutils import format_date
871
#         
872
#         pending = parents[1:]
873
#         branch = wt.branch
874
#         last_revision = parents[0]
875
#         
876
#         if last_revision is not None:
877
#             try:
878
#                 ignore = set(branch.repository.get_ancestry(last_revision))
879
#             except errors.NoSuchRevision:
880
#                 # the last revision is a ghost : assume everything is new 
881
#                 # except for it
882
#                 ignore = set([None, last_revision])
883
#         else:
884
#             ignore = set([None])
885
#         
886
#         pm = []
887
#         for merge in pending:
888
#             ignore.add(merge)
889
#             try:
890
#                 m_revision = branch.repository.get_revision(merge)
891
#                 
892
#                 rev = {}
893
#                 rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
894
#                 rev['summary'] = m_revision.get_summary()
895
#                 rev['date'] = format_date(m_revision.timestamp,
896
#                                           m_revision.timezone or 0, 
897
#                                           'original', date_fmt="%Y-%m-%d",
898
#                                           show_offset=False)
899
#                 
900
#                 pm.append(rev)
901
#                 
902
#                 inner_merges = branch.repository.get_ancestry(merge)
903
#                 assert inner_merges[0] is None
904
#                 inner_merges.pop(0)
905
#                 inner_merges.reverse()
906
#                 for mmerge in inner_merges:
907
#                     if mmerge in ignore:
908
#                         continue
909
#                     mm_revision = branch.repository.get_revision(mmerge)
910
#                     
911
#                     rev = {}
912
#                     rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
913
#                     rev['summary'] = mm_revision.get_summary()
914
#                     rev['date'] = format_date(mm_revision.timestamp,
915
#                                               mm_revision.timezone or 0, 
916
#                                               'original', date_fmt="%Y-%m-%d",
917
#                                               show_offset=False)
918
#                 
919
#                     pm.append(rev)
920
#                     
921
#                     ignore.add(mmerge)
922
#             except errors.NoSuchRevision:
923
#                 print "DEBUG: NoSuchRevision:", merge
924
#         
925
#         return pm
926
# 
927
#     def _create_file_view(self):
928
#         self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
929
#                                          gobject.TYPE_STRING,    # [1] path to display
930
#                                          gobject.TYPE_STRING,    # [2] changes type
931
#                                          gobject.TYPE_STRING)    # [3] real path
932
#         self._treeview_files.set_model(self._file_store)
933
#         crt = gtk.CellRendererToggle()
934
#         crt.set_property("activatable", True)
935
#         crt.connect("toggled", self._toggle_commit, self._file_store)
936
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
937
#                                      crt, active=0))
938
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
939
#                                      gtk.CellRendererText(), text=1))
940
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
941
#                                      gtk.CellRendererText(), text=2))
942
# 
943
#         for path, id, kind in self.delta.added:
944
#             marker = osutils.kind_marker(kind)
945
#             if self.selected is not None:
946
#                 if path == os.path.join(self.wtpath, self.selected):
947
#                     self._file_store.append([ True, path+marker, _('added'), path ])
948
#                 else:
949
#                     self._file_store.append([ False, path+marker, _('added'), path ])
950
#             else:
951
#                 self._file_store.append([ True, path+marker, _('added'), path ])
952
# 
953
#         for path, id, kind in self.delta.removed:
954
#             marker = osutils.kind_marker(kind)
955
#             if self.selected is not None:
956
#                 if path == os.path.join(self.wtpath, self.selected):
957
#                     self._file_store.append([ True, path+marker, _('removed'), path ])
958
#                 else:
959
#                     self._file_store.append([ False, path+marker, _('removed'), path ])
960
#             else:
961
#                 self._file_store.append([ True, path+marker, _('removed'), path ])
962
# 
963
#         for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
964
#             marker = osutils.kind_marker(kind)
965
#             if text_modified or meta_modified:
966
#                 changes = _('renamed and modified')
967
#             else:
968
#                 changes = _('renamed')
969
#             if self.selected is not None:
970
#                 if newpath == os.path.join(self.wtpath, self.selected):
971
#                     self._file_store.append([ True,
972
#                                               oldpath+marker + '  =>  ' + newpath+marker,
973
#                                               changes,
974
#                                               newpath
975
#                                             ])
976
#                 else:
977
#                     self._file_store.append([ False,
978
#                                               oldpath+marker + '  =>  ' + newpath+marker,
979
#                                               changes,
980
#                                               newpath
981
#                                             ])
982
#             else:
983
#                 self._file_store.append([ True,
984
#                                           oldpath+marker + '  =>  ' + newpath+marker,
985
#                                           changes,
986
#                                           newpath
987
#                                         ])
988
# 
989
#         for path, id, kind, text_modified, meta_modified in self.delta.modified:
990
#             marker = osutils.kind_marker(kind)
991
#             if self.selected is not None:
992
#                 if path == os.path.join(self.wtpath, self.selected):
993
#                     self._file_store.append([ True, path+marker, _('modified'), path ])
994
#                 else:
995
#                     self._file_store.append([ False, path+marker, _('modified'), path ])
996
#             else:
997
#                 self._file_store.append([ True, path+marker, _('modified'), path ])
998
#     
999
#     def _create_pending_merges(self):
1000
#         if not self.pending:
1001
#             return
1002
#         
1003
#         liststore = gtk.ListStore(gobject.TYPE_STRING,
1004
#                                   gobject.TYPE_STRING,
1005
#                                   gobject.TYPE_STRING)
1006
#         self._treeview_merges.set_model(liststore)
1007
#         
1008
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
1009
#                                             gtk.CellRendererText(), text=0))
1010
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
1011
#                                             gtk.CellRendererText(), text=1))
1012
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
1013
#                                             gtk.CellRendererText(), text=2))
1014
#         
1015
#         for item in self.pending:
1016
#             liststore.append([ item['date'],
1017
#                                item['committer'],
1018
#                                item['summary'] ])
1019
#     
1020
# 
1021
#     def _create_diff_view(self):
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
1022
#         from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
1023
# 
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
1024
#         self._diff_display = DiffView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
1025
#         self._diff_display.set_trees(self.wt, self.wt.basis_tree())
1026
#         self._diff_display.show_diff(None)
1027
#         self._diff_display.show()
1028
#         self._hpane.pack2(self._diff_display)
1029
# 
1030
#     def _get_specific_files(self):
1031
#         ret = []
1032
#         it = self._file_store.get_iter_first()
1033
#         while it:
1034
#             if self._file_store.get_value(it, 0):
1035
#                 # get real path from hidden column 3
1036
#                 ret.append(self._file_store.get_value(it, 3))
1037
#             it = self._file_store.iter_next(it)
1038
# 
1039
#         return ret
1040
#     
1041
#     def _toggle_commit(self, cell, path, model):
1042
#         model[path][0] = not model[path][0]
1043
#         return