/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):
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
157
        # We should really use add a progress bar of some kind.
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
158
        # While we fill in the view, hide the store
159
        store = self._files_store
160
        self._treeview_files.set_model(None)
161
162
        added = _('added')
163
        removed = _('removed')
164
        renamed = _('renamed')
165
        renamed_and_modified = _('renamed and modified')
166
        modified = _('modified')
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
167
        kind_changed = _('kind changed')
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
168
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
169
        # The store holds:
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
170
        # [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
171
        # _iter_changes returns:
172
        # (file_id, (path_in_source, path_in_target),
173
        #  changed_content, versioned, parent, name, kind,
174
        #  executable)
175
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
176
        # The first entry is always the 'whole tree'
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
177
        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
178
        # should we pass specific_files?
179
        self._wt.lock_read()
180
        self._basis_tree.lock_read()
181
        try:
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
182
            from diff import _iter_changes_to_status
183
            for (file_id, real_path, change_type, display_path
184
                ) in _iter_changes_to_status(self._basis_tree, self._wt):
185
                store.append([file_id, real_path.encode('UTF-8'),
186
                              True, display_path.encode('UTF-8'),
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
187
                              change_type, ''])
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
188
        finally:
189
            self._basis_tree.unlock()
190
            self._wt.unlock()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
191
192
        self._treeview_files.set_model(store)
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
193
        self._last_selected_file = None
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
194
        self._treeview_files.set_cursor(0)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
195
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
196
    def _fill_in_diff(self):
197
        self._diff_view.set_trees(self._wt, self._basis_tree)
198
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
199
    def _fill_in_checkout(self):
200
        if not self._is_checkout:
201
            self._check_local.hide()
202
            return
203
        if have_dbus:
204
            bus = dbus.SystemBus()
205
            proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
206
                                       '/org/freedesktop/NetworkManager')
207
            dbus_iface = dbus.Interface(proxy_obj,
208
                                        'org.freedesktop.NetworkManager')
209
            try:
210
                # 3 is the enum value for STATE_CONNECTED
211
                self._check_local.set_active(dbus_iface.state() != 3)
212
            except dbus.DBusException, e:
213
                # Silently drop errors. While DBus may be
214
                # available, NetworkManager doesn't necessarily have to be
215
                mutter("unable to get networkmanager state: %r" % e)
216
        self._check_local.show()
217
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
218
    def _compute_delta(self):
219
        self._delta = self._wt.changes_from(self._basis_tree)
220
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
221
    def construct(self):
222
        """Build up the dialog widgets."""
223
        # The primary pane which splits it into left and right (adjustable)
224
        # sections.
278.1.4 by John Arbash Meinel
Just playing around.
225
        self._hpane = gtk.HPaned()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
226
227
        self._construct_left_pane()
228
        self._construct_right_pane()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
229
        self._construct_action_pane()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
230
231
        self.vbox.pack_start(self._hpane)
232
        self._hpane.show()
233
        self.set_focus(self._global_message_text_view)
234
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
235
        # This seems like a reasonable default, we might like it to
236
        # be a bit wider, so that by default we can fit an 80-line diff in the
237
        # diff window.
238
        # Alternatively, we should be saving the last position/size rather than
239
        # setting it to a fixed value every time we start up.
240
        screen = self.get_screen()
241
        monitor = 0 # We would like it to be the monitor we are going to
242
                    # display on, but I don't know how to figure that out
243
                    # Only really useful for freaks like me that run dual
244
                    # monitor, with different sizes on the monitors
245
        monitor_rect = screen.get_monitor_geometry(monitor)
246
        width = int(monitor_rect.width * 0.66)
247
        height = int(monitor_rect.height * 0.66)
248
        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
249
        self._hpane.set_position(300)
250
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
251
    def _construct_left_pane(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
252
        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.
253
        self._construct_file_list()
254
        self._construct_pending_list()
255
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
256
        self._check_local = gtk.CheckButton(_("_Only commit locally"),
257
                                            use_underline=True)
258
        self._left_pane_box.pack_end(self._check_local, False, False)
259
        self._check_local.set_active(False)
260
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
261
        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.
262
        self._left_pane_box.show()
263
264
    def _construct_right_pane(self):
265
        # TODO: I really want to make it so the diff view gets more space than
266
        # the global commit message, and the per-file commit message gets even
267
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
268
        # commit, and 1 for file commit, and it looked good. But I don't seem
269
        # to have a way to do that with the gtk boxes... :( (Which is extra
270
        # 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.
271
        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.
272
        self._right_pane_table.set_row_spacings(5)
273
        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.
274
        self._right_pane_table_row = 0
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
275
        self._construct_diff_view()
276
        self._construct_file_message()
277
        self._construct_global_message()
278
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
279
        self._right_pane_table.show()
280
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
281
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
282
    def _construct_action_pane(self):
283
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
284
        self._button_commit.connect('clicked', self._on_commit_clicked)
285
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
278.1.24 by John Arbash Meinel
Actually show the commit button.
286
        self._button_commit.show()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
287
        self.action_area.pack_end(self._button_commit)
278.1.24 by John Arbash Meinel
Actually show the commit button.
288
        self._button_commit.grab_default()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
289
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
290
    def _add_to_right_table(self, widget, weight, expanding=False):
291
        """Add another widget to the table
292
293
        :param widget: The object to add
294
        :param weight: How many rows does this widget get to request
295
        :param expanding: Should expand|fill|shrink be set?
296
        """
297
        end_row = self._right_pane_table_row + weight
298
        options = 0
299
        expand_opts = gtk.EXPAND | gtk.FILL | gtk.SHRINK
300
        if expanding:
301
            options = expand_opts
302
        self._right_pane_table.attach(widget, 0, 1,
303
                                      self._right_pane_table_row, end_row,
304
                                      xoptions=expand_opts, yoptions=options)
305
        self._right_pane_table_row = end_row
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
306
307
    def _construct_file_list(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
308
        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.
309
        file_label = gtk.Label(_('Files'))
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
310
        file_label.show()
311
        self._files_box.pack_start(file_label, expand=False)
312
313
        scroller = gtk.ScrolledWindow()
314
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
315
        self._treeview_files = gtk.TreeView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
316
        self._treeview_files.show()
317
        scroller.add(self._treeview_files)
318
        scroller.show()
319
        scroller.set_shadow_type(gtk.SHADOW_IN)
320
        self._files_box.pack_start(scroller,
321
                                   expand=True, fill=True)
322
        self._files_box.show()
323
        self._left_pane_box.pack_start(self._files_box)
324
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
325
        # Keep note that all strings stored in a ListStore must be UTF-8
326
        # strings. GTK does not support directly setting and restoring Unicode
327
        # objects.
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
328
        liststore = gtk.ListStore(
329
            gobject.TYPE_STRING,  # [0] file_id
330
            gobject.TYPE_STRING,  # [1] real path
331
            gobject.TYPE_BOOLEAN, # [2] checkbox
332
            gobject.TYPE_STRING,  # [3] display path
333
            gobject.TYPE_STRING,  # [4] changes type
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
334
            gobject.TYPE_STRING,  # [5] commit message
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
335
            )
336
        self._files_store = liststore
337
        self._treeview_files.set_model(liststore)
338
        crt = gtk.CellRendererToggle()
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
339
        crt.set_active(not bool(self._pending))
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
340
        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.
341
        if self._pending:
342
            name = _('Commit*')
343
        else:
344
            name = _('Commit')
345
        self._treeview_files.append_column(gtk.TreeViewColumn(name,
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
346
                                           crt, active=2))
347
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
348
                                           gtk.CellRendererText(), text=3))
349
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
350
                                           gtk.CellRendererText(), text=4))
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
351
        self._treeview_files.connect('cursor-changed',
352
                                     self._on_treeview_files_cursor_changed)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
353
354
    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,
355
        if model[path][0] is None: # No file_id means 'All Files'
356
            new_val = not model[path][2]
357
            for node in model:
358
                node[2] = new_val
359
        else:
360
            model[path][2] = not model[path][2]
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
361
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
362
    def _construct_pending_list(self):
363
        # Pending information defaults to hidden, we put it all in 1 box, so
364
        # that we can show/hide all of them at once
365
        self._pending_box = gtk.VBox()
366
        self._pending_box.hide()
367
368
        pending_message = gtk.Label()
369
        pending_message.set_markup(
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
370
            _('<i>* Cannot select specific files when merging</i>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
371
        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.
372
        pending_message.show()
373
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
374
        pending_label = gtk.Label(_('Pending Revisions'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
375
        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.
376
        pending_label.show()
377
378
        scroller = gtk.ScrolledWindow()
379
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
380
        self._treeview_pending = gtk.TreeView()
381
        scroller.add(self._treeview_pending)
382
        scroller.show()
383
        scroller.set_shadow_type(gtk.SHADOW_IN)
384
        self._pending_box.pack_start(scroller,
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
385
                                     expand=True, fill=True, padding=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
386
        self._treeview_pending.show()
387
        self._left_pane_box.pack_start(self._pending_box)
388
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
389
        liststore = gtk.ListStore(gobject.TYPE_STRING, # revision_id
390
                                  gobject.TYPE_STRING, # date
391
                                  gobject.TYPE_STRING, # committer
392
                                  gobject.TYPE_STRING, # summary
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
393
                                 )
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
394
        self._pending_store = liststore
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
395
        self._treeview_pending.set_model(liststore)
396
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Date'),
397
                                             gtk.CellRendererText(), text=1))
398
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Committer'),
399
                                             gtk.CellRendererText(), text=2))
400
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Summary'),
401
                                             gtk.CellRendererText(), text=3))
402
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
403
    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.
404
        from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
405
406
        self._diff_label = gtk.Label(_('Diff for whole tree'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
407
        self._diff_label.set_alignment(0, 0)
408
        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.
409
        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.
410
        self._diff_label.show()
411
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
412
        self._diff_view = DiffView()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
413
        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.
414
        self._diff_view.show()
415
416
    def _construct_file_message(self):
417
        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.
418
        scroller = gtk.ScrolledWindow()
419
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
420
421
        self._file_message_text_view = gtk.TextView()
422
        scroller.add(self._file_message_text_view)
423
        scroller.show()
424
        scroller.set_shadow_type(gtk.SHADOW_IN)
425
        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.
426
427
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
428
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
429
        self._file_message_text_view.set_accepts_tab(False)
430
        self._file_message_text_view.show()
431
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
432
        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.
433
        self._file_message_expander.add(file_message_box)
434
        file_message_box.show()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
435
        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.
436
        self._file_message_expander.show()
437
438
    def _construct_global_message(self):
439
        self._global_message_label = gtk.Label(_('Global Commit Message'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
440
        self._global_message_label.set_alignment(0, 0)
441
        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.
442
        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.
443
        # 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.
444
        self._global_message_label.show()
445
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
446
        scroller = gtk.ScrolledWindow()
447
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
448
449
        self._global_message_text_view = gtk.TextView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
450
        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.
451
        scroller.add(self._global_message_text_view)
452
        scroller.show()
453
        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.
454
        self._add_to_right_table(scroller, 2, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
455
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
456
        self._file_message_text_view.set_accepts_tab(False)
457
        self._global_message_text_view.show()
458
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
459
    def _on_treeview_files_cursor_changed(self, treeview):
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
460
        treeselection = treeview.get_selection()
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
461
        (model, selection) = treeselection.get_selected()
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
462
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
463
        if selection is not None:
464
            path, display_path = model.get(selection, 1, 3)
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
465
            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,
466
            if path is None:
467
                self._diff_view.show_diff(None)
468
            else:
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
469
                self._diff_view.show_diff([path.decode('UTF-8')])
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
470
            self._update_per_file_info(selection)
471
472
    def _save_current_file_message(self):
473
        if self._last_selected_file is None:
474
            return # Nothing to save
475
        text_buffer = self._file_message_text_view.get_buffer()
476
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
477
                                        text_buffer.get_end_iter())
478
        last_selected = self._files_store.get_iter(self._last_selected_file)
479
        self._files_store.set_value(last_selected, 5, cur_text)
480
481
    def _update_per_file_info(self, selection):
482
        # The node is changing, so cache the current message
483
        self._save_current_file_message()
484
        text_buffer = self._file_message_text_view.get_buffer()
485
        file_id, display_path, message = self._files_store.get(selection, 0, 3, 5)
486
        if file_id is None: # Whole tree
487
            self._file_message_expander.set_label(_('File commit message'))
488
            self._file_message_expander.set_expanded(False)
489
            self._file_message_expander.set_sensitive(False)
490
            text_buffer.set_text('')
491
            self._last_selected_file = None
492
        else:
493
            self._file_message_expander.set_label(_('Commit message for ')
494
                                                  + display_path)
495
            self._file_message_expander.set_expanded(True)
496
            self._file_message_expander.set_sensitive(True)
497
            text_buffer.set_text(message)
498
            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.
499
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
500
    def _get_specific_files(self):
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
501
        """Return the list of selected paths, and file info.
502
503
        :return: ([unicode paths], [{utf-8 file info}]
504
        """
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
505
        self._save_current_file_message()
506
        files = []
507
        records = iter(self._files_store)
508
        rec = records.next() # Skip the All Files record
509
        assert rec[0] is None, "Are we skipping the wrong record?"
510
511
        file_info = []
512
        for record in records:
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
513
            if record[2]:           # [2] checkbox
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
514
                file_id = record[0] # [0] file_id
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
515
                path = record[1]    # [1] real path
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
516
                file_message = record[5] # [5] commit message
517
                files.append(path.decode('UTF-8'))
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
518
                if file_message:
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
519
                    # All of this needs to be utf-8 information
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
520
                    file_info.append({'path':path, 'file_id':file_id,
521
                                     'message':file_message})
522
        file_info.sort(key=lambda x:(x['path'], x['file_id']))
523
        return files, file_info
524
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
525
    @show_bzr_error
526
    def _on_commit_clicked(self, button):
527
        """ Commit button clicked handler. """
528
        self._do_commit()
529
530
    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.
531
        message = self._get_global_commit_message()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
532
533
        if message == '':
534
            response = self._question_dialog(
535
                            _('Commit with an empty message?'),
536
                            _('You can describe your commit intent in the message.'))
537
            if response == gtk.RESPONSE_NO:
538
                # Kindly give focus to message area
539
                self._global_message_text_view.grab_focus()
540
                return
541
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
542
        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.
543
        if self._pending:
544
            specific_files = None
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
545
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
546
        local = self._check_local.get_active()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
547
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
548
        # All we care about is if there is a single unknown, so if this loop is
549
        # entered, then there are unknown files.
550
        # TODO: jam 20071002 It seems like this should cancel the dialog
551
        #       entirely, since there isn't a way for them to add the unknown
552
        #       files at this point.
553
        for path in self._wt.unknowns():
554
            response = self._question_dialog(
555
                _("Commit with unknowns?"),
556
                _("Unknown files exist in the working tree. Commit anyway?"))
557
            if response == gtk.RESPONSE_NO:
558
                return
559
            break
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
560
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
561
        rev_id = None
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
562
        revprops = {}
563
        if file_info:
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
564
            revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
565
        try:
566
            rev_id = self._wt.commit(message,
567
                       allow_pointless=False,
568
                       strict=False,
569
                       local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
570
                       specific_files=specific_files,
571
                       revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
572
        except errors.PointlessCommit:
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
573
            response = self._question_dialog(
574
                                _('Commit with no changes?'),
575
                                _('There are no changes in the working tree.'
576
                                  ' Do you want to commit anyway?'))
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
577
            if response == gtk.RESPONSE_YES:
578
                rev_id = self._wt.commit(message,
579
                               allow_pointless=True,
580
                               strict=False,
581
                               local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
582
                               specific_files=specific_files,
583
                               revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
584
        self.committed_revision_id = rev_id
585
        self.response(gtk.RESPONSE_OK)
586
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
587
    def _get_global_commit_message(self):
588
        buf = self._global_message_text_view.get_buffer()
589
        start, end = buf.get_bounds()
590
        return buf.get_text(start, end).decode('utf-8')
591
592
    def _set_global_commit_message(self, message):
593
        """Just a helper for the test suite."""
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
594
        if isinstance(message, unicode):
595
            message = message.encode('UTF-8')
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
596
        self._global_message_text_view.get_buffer().set_text(message)
597
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
598
    def _set_file_commit_message(self, message):
599
        """Helper for the test suite."""
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
600
        if isinstance(message, unicode):
601
            message = message.encode('UTF-8')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
602
        self._file_message_text_view.get_buffer().set_text(message)
603
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
604
    @staticmethod
605
    def _rev_to_pending_info(rev):
606
        """Get the information from a pending merge."""
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
607
        from bzrlib.osutils import format_date
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
608
609
        rev_dict = {}
610
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
611
        rev_dict['summary'] = rev.get_summary()
612
        rev_dict['date'] = format_date(rev.timestamp,
613
                                       rev.timezone or 0,
614
                                       'original', date_fmt="%Y-%m-%d",
615
                                       show_offset=False)
616
        rev_dict['revision_id'] = rev.revision_id
617
        return rev_dict
618
619
620
# class CommitDialog(gtk.Dialog):
621
#     """ New implementation of the Commit dialog. """
622
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
623
#         """ Initialize the Commit Dialog. """
624
#         gtk.Dialog.__init__(self, title="Commit - Olive",
625
#                                   parent=parent,
626
#                                   flags=0,
627
#                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
628
#         
629
#         # Get arguments
630
#         self.wt = wt
631
#         self.wtpath = wtpath
632
#         self.notbranch = notbranch
633
#         self.selected = selected
634
#         
635
#         # Set the delta
636
#         self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
637
#         self.delta = self.wt.changes_from(self.old_tree)
638
#         
639
#         # Get pending merges
640
#         self.pending = self._pending_merges(self.wt)
641
#         
642
#         # Do some preliminary checks
643
#         self._is_checkout = False
644
#         self._is_pending = False
645
#         if self.wt is None and not self.notbranch:
646
#             error_dialog(_('Directory does not have a working tree'),
647
#                          _('Operation aborted.'))
648
#             self.close()
649
#             return
650
# 
651
#         if self.notbranch:
652
#             error_dialog(_('Directory is not a branch'),
653
#                          _('You can perform this action only in a branch.'))
654
#             self.close()
655
#             return
656
#         else:
657
#             if self.wt.branch.get_bound_location() is not None:
658
#                 # we have a checkout, so the local commit checkbox must appear
659
#                 self._is_checkout = True
660
#             
661
#             if self.pending:
662
#                 # There are pending merges, file selection not supported
663
#                 self._is_pending = True
664
#         
665
#         # Create the widgets
666
#         # This is the main horizontal box, which is used to separate the commit
667
#         # info from the diff window.
668
#         self._hpane = gtk.HPaned()
669
#         self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
670
#         self._expander_files = gtk.Expander(_("File(s) to commit"))
671
#         self._vpaned_main = gtk.VPaned()
672
#         self._scrolledwindow_files = gtk.ScrolledWindow()
673
#         self._scrolledwindow_message = gtk.ScrolledWindow()
674
#         self._treeview_files = gtk.TreeView()
675
#         self._vbox_message = gtk.VBox()
676
#         self._label_message = gtk.Label(_("Commit message:"))
677
#         self._textview_message = gtk.TextView()
678
#         
679
#         if self._is_pending:
680
#             self._expander_merges = gtk.Expander(_("Pending merges"))
681
#             self._vpaned_list = gtk.VPaned()
682
#             self._scrolledwindow_merges = gtk.ScrolledWindow()
683
#             self._treeview_merges = gtk.TreeView()
684
# 
685
#         # Set callbacks
686
#         self._button_commit.connect('clicked', self._on_commit_clicked)
687
#         self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
688
#         self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
689
#         
690
#         # Set properties
691
#         self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
692
#                                               gtk.POLICY_AUTOMATIC)
693
#         self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
694
#                                                 gtk.POLICY_AUTOMATIC)
695
#         self._textview_message.modify_font(pango.FontDescription("Monospace"))
696
#         self.set_default_size(500, 500)
697
#         self._vpaned_main.set_position(200)
698
#         self._button_commit.set_flags(gtk.CAN_DEFAULT)
699
# 
700
#         if self._is_pending:
701
#             self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
702
#                                                    gtk.POLICY_AUTOMATIC)
703
#             self._treeview_files.set_sensitive(False)
704
#         
705
#         # Construct the dialog
706
#         self.action_area.pack_end(self._button_commit)
707
#         
708
#         self._scrolledwindow_files.add(self._treeview_files)
709
#         self._scrolledwindow_message.add(self._textview_message)
710
#         
711
#         self._expander_files.add(self._scrolledwindow_files)
712
#         
713
#         self._vbox_message.pack_start(self._label_message, False, False)
714
#         self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
715
#         
716
#         if self._is_pending:        
717
#             self._expander_merges.add(self._scrolledwindow_merges)
718
#             self._scrolledwindow_merges.add(self._treeview_merges)
719
#             self._vpaned_list.add1(self._expander_files)
720
#             self._vpaned_list.add2(self._expander_merges)
721
#             self._vpaned_main.add1(self._vpaned_list)
722
#         else:
723
#             self._vpaned_main.add1(self._expander_files)
724
# 
725
#         self._vpaned_main.add2(self._vbox_message)
726
#         
727
#         self._hpane.pack1(self._vpaned_main)
728
#         self.vbox.pack_start(self._hpane, expand=True, fill=True)
729
#         if self._is_checkout: 
730
#             self._check_local = gtk.CheckButton(_("_Only commit locally"),
731
#                                                 use_underline=True)
732
#             self.vbox.pack_start(self._check_local, False, False)
733
#             if have_dbus:
734
#                 bus = dbus.SystemBus()
735
#                 proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
736
#                               '/org/freedesktop/NetworkManager')
737
#                 dbus_iface = dbus.Interface(
738
#                         proxy_obj, 'org.freedesktop.NetworkManager')
739
#                 try:
740
#                     # 3 is the enum value for STATE_CONNECTED
741
#                     self._check_local.set_active(dbus_iface.state() != 3)
742
#                 except dbus.DBusException, e:
743
#                     # Silently drop errors. While DBus may be 
744
#                     # available, NetworkManager doesn't necessarily have to be
745
#                     mutter("unable to get networkmanager state: %r" % e)
746
#                 
747
#         # Create the file list
748
#         self._create_file_view()
749
#         # Create the pending merges
750
#         self._create_pending_merges()
751
#         self._create_diff_view()
752
#         
753
#         # Expand the corresponding expander
754
#         if self._is_pending:
755
#             self._expander_merges.set_expanded(True)
756
#         else:
757
#             self._expander_files.set_expanded(True)
758
#         
759
#         # Display dialog
760
#         self.vbox.show_all()
761
#         
762
#         # Default to Commit button
763
#         self._button_commit.grab_default()
764
#     
765
#     def _show_diff_view(self, treeview):
766
#         # FIXME: the diff window freezes for some reason
767
#         treeselection = treeview.get_selection()
768
#         (model, iter) = treeselection.get_selected()
769
# 
770
#         if iter is not None:
771
#             selected = model.get_value(iter, 3) # Get the real_path attribute
772
#             self._diff_display.show_diff([selected])
773
# 
774
#     def _on_treeview_files_cursor_changed(self, treeview):
775
#         self._show_diff_view(treeview)
776
#         
777
#     def _on_treeview_files_row_activated(self, treeview, path, view_column):
778
#         self._show_diff_view(treeview)
779
#     
780
#     @show_bzr_error
781
#     def _on_commit_clicked(self, button):
782
#         """ Commit button clicked handler. """
783
#         textbuffer = self._textview_message.get_buffer()
784
#         start, end = textbuffer.get_bounds()
785
#         message = textbuffer.get_text(start, end).decode('utf-8')
786
#         
787
#         if not self.pending:
788
#             specific_files = self._get_specific_files()
789
#         else:
790
#             specific_files = None
791
# 
792
#         if message == '':
793
#             response = question_dialog(_('Commit with an empty message?'),
794
#                                        _('You can describe your commit intent in the message.'))
795
#             if response == gtk.RESPONSE_NO:
796
#                 # Kindly give focus to message area
797
#                 self._textview_message.grab_focus()
798
#                 return
799
# 
800
#         if self._is_checkout:
801
#             local = self._check_local.get_active()
802
#         else:
803
#             local = False
804
# 
805
#         if list(self.wt.unknowns()) != []:
806
#             response = question_dialog(_("Commit with unknowns?"),
807
#                _("Unknown files exist in the working tree. Commit anyway?"))
808
#             if response == gtk.RESPONSE_NO:
809
#                 return
810
#         
811
#         try:
812
#             self.wt.commit(message,
813
#                        allow_pointless=False,
814
#                        strict=False,
815
#                        local=local,
816
#                        specific_files=specific_files)
817
#         except errors.PointlessCommit:
818
#             response = question_dialog(_('Commit with no changes?'),
819
#                                        _('There are no changes in the working tree.'))
820
#             if response == gtk.RESPONSE_YES:
821
#                 self.wt.commit(message,
822
#                                allow_pointless=True,
823
#                                strict=False,
824
#                                local=local,
825
#                                specific_files=specific_files)
826
#         self.response(gtk.RESPONSE_OK)
827
# 
828
#     def _pending_merges(self, wt):
829
#         """ Return a list of pending merges or None if there are none of them. """
830
#         parents = wt.get_parent_ids()
831
#         if len(parents) < 2:
832
#             return None
833
#         
834
#         import re
835
#         from bzrlib.osutils import format_date
836
#         
837
#         pending = parents[1:]
838
#         branch = wt.branch
839
#         last_revision = parents[0]
840
#         
841
#         if last_revision is not None:
842
#             try:
843
#                 ignore = set(branch.repository.get_ancestry(last_revision))
844
#             except errors.NoSuchRevision:
845
#                 # the last revision is a ghost : assume everything is new 
846
#                 # except for it
847
#                 ignore = set([None, last_revision])
848
#         else:
849
#             ignore = set([None])
850
#         
851
#         pm = []
852
#         for merge in pending:
853
#             ignore.add(merge)
854
#             try:
855
#                 m_revision = branch.repository.get_revision(merge)
856
#                 
857
#                 rev = {}
858
#                 rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
859
#                 rev['summary'] = m_revision.get_summary()
860
#                 rev['date'] = format_date(m_revision.timestamp,
861
#                                           m_revision.timezone or 0, 
862
#                                           'original', date_fmt="%Y-%m-%d",
863
#                                           show_offset=False)
864
#                 
865
#                 pm.append(rev)
866
#                 
867
#                 inner_merges = branch.repository.get_ancestry(merge)
868
#                 assert inner_merges[0] is None
869
#                 inner_merges.pop(0)
870
#                 inner_merges.reverse()
871
#                 for mmerge in inner_merges:
872
#                     if mmerge in ignore:
873
#                         continue
874
#                     mm_revision = branch.repository.get_revision(mmerge)
875
#                     
876
#                     rev = {}
877
#                     rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
878
#                     rev['summary'] = mm_revision.get_summary()
879
#                     rev['date'] = format_date(mm_revision.timestamp,
880
#                                               mm_revision.timezone or 0, 
881
#                                               'original', date_fmt="%Y-%m-%d",
882
#                                               show_offset=False)
883
#                 
884
#                     pm.append(rev)
885
#                     
886
#                     ignore.add(mmerge)
887
#             except errors.NoSuchRevision:
888
#                 print "DEBUG: NoSuchRevision:", merge
889
#         
890
#         return pm
891
# 
892
#     def _create_file_view(self):
893
#         self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
894
#                                          gobject.TYPE_STRING,    # [1] path to display
895
#                                          gobject.TYPE_STRING,    # [2] changes type
896
#                                          gobject.TYPE_STRING)    # [3] real path
897
#         self._treeview_files.set_model(self._file_store)
898
#         crt = gtk.CellRendererToggle()
899
#         crt.set_property("activatable", True)
900
#         crt.connect("toggled", self._toggle_commit, self._file_store)
901
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
902
#                                      crt, active=0))
903
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
904
#                                      gtk.CellRendererText(), text=1))
905
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
906
#                                      gtk.CellRendererText(), text=2))
907
# 
908
#         for path, id, kind in self.delta.added:
909
#             marker = osutils.kind_marker(kind)
910
#             if self.selected is not None:
911
#                 if path == os.path.join(self.wtpath, self.selected):
912
#                     self._file_store.append([ True, path+marker, _('added'), path ])
913
#                 else:
914
#                     self._file_store.append([ False, path+marker, _('added'), path ])
915
#             else:
916
#                 self._file_store.append([ True, path+marker, _('added'), path ])
917
# 
918
#         for path, id, kind in self.delta.removed:
919
#             marker = osutils.kind_marker(kind)
920
#             if self.selected is not None:
921
#                 if path == os.path.join(self.wtpath, self.selected):
922
#                     self._file_store.append([ True, path+marker, _('removed'), path ])
923
#                 else:
924
#                     self._file_store.append([ False, path+marker, _('removed'), path ])
925
#             else:
926
#                 self._file_store.append([ True, path+marker, _('removed'), path ])
927
# 
928
#         for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
929
#             marker = osutils.kind_marker(kind)
930
#             if text_modified or meta_modified:
931
#                 changes = _('renamed and modified')
932
#             else:
933
#                 changes = _('renamed')
934
#             if self.selected is not None:
935
#                 if newpath == os.path.join(self.wtpath, self.selected):
936
#                     self._file_store.append([ True,
937
#                                               oldpath+marker + '  =>  ' + newpath+marker,
938
#                                               changes,
939
#                                               newpath
940
#                                             ])
941
#                 else:
942
#                     self._file_store.append([ False,
943
#                                               oldpath+marker + '  =>  ' + newpath+marker,
944
#                                               changes,
945
#                                               newpath
946
#                                             ])
947
#             else:
948
#                 self._file_store.append([ True,
949
#                                           oldpath+marker + '  =>  ' + newpath+marker,
950
#                                           changes,
951
#                                           newpath
952
#                                         ])
953
# 
954
#         for path, id, kind, text_modified, meta_modified in self.delta.modified:
955
#             marker = osutils.kind_marker(kind)
956
#             if self.selected is not None:
957
#                 if path == os.path.join(self.wtpath, self.selected):
958
#                     self._file_store.append([ True, path+marker, _('modified'), path ])
959
#                 else:
960
#                     self._file_store.append([ False, path+marker, _('modified'), path ])
961
#             else:
962
#                 self._file_store.append([ True, path+marker, _('modified'), path ])
963
#     
964
#     def _create_pending_merges(self):
965
#         if not self.pending:
966
#             return
967
#         
968
#         liststore = gtk.ListStore(gobject.TYPE_STRING,
969
#                                   gobject.TYPE_STRING,
970
#                                   gobject.TYPE_STRING)
971
#         self._treeview_merges.set_model(liststore)
972
#         
973
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
974
#                                             gtk.CellRendererText(), text=0))
975
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
976
#                                             gtk.CellRendererText(), text=1))
977
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
978
#                                             gtk.CellRendererText(), text=2))
979
#         
980
#         for item in self.pending:
981
#             liststore.append([ item['date'],
982
#                                item['committer'],
983
#                                item['summary'] ])
984
#     
985
# 
986
#     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.
987
#         from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
988
# 
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
989
#         self._diff_display = DiffView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
990
#         self._diff_display.set_trees(self.wt, self.wt.basis_tree())
991
#         self._diff_display.show_diff(None)
992
#         self._diff_display.show()
993
#         self._hpane.pack2(self._diff_display)
994
# 
995
#     def _get_specific_files(self):
996
#         ret = []
997
#         it = self._file_store.get_iter_first()
998
#         while it:
999
#             if self._file_store.get_value(it, 0):
1000
#                 # get real path from hidden column 3
1001
#                 ret.append(self._file_store.get_value(it, 3))
1002
#             it = self._file_store.iter_next(it)
1003
# 
1004
#         return ret
1005
#     
1006
#     def _toggle_commit(self, cell, path, model):
1007
#         model[path][0] = not model[path][0]
1008
#         return