/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
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
17
import os.path
18
import re
19
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
20
try:
21
    import pygtk
22
    pygtk.require("2.0")
23
except:
24
    pass
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
25
0.13.11 by Jelmer Vernooij
Bunch of small fixes, cleanups and simplifications.
26
import gtk
27
import gobject
28
import pango
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
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
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
33
try:
34
    from bzrlib.uncommit import SavedCommitMessagesManager
35
except ImportError: # old bzrlib
36
    can_save_commit_messages = False
37
else:
38
    can_save_commit_messages = True
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
39
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
40
from bzrlib.plugins.gtk import _i18n
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
41
from bzrlib.plugins.gtk.dialog import question_dialog
42
from bzrlib.plugins.gtk.errors import show_bzr_error
93.1.6 by Alexander Belchenko
detecting name of glade file doing in separate module (olive.gladefile)
43
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
44
try:
45
    import dbus
46
    import dbus.glib
180 by Jelmer Vernooij
Don't obtain handle to network manager until it's actually needed.
47
    have_dbus = True
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
48
except ImportError:
180 by Jelmer Vernooij
Don't obtain handle to network manager until it's actually needed.
49
    have_dbus = False
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
50
278.1.4 by John Arbash Meinel
Just playing around.
51
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
52
def pending_revisions(wt):
53
    """Return a list of pending merges or None if there are none of them.
54
55
    Arguably this should be a core function, and
56
    ``bzrlib.status.show_pending_merges`` should be built on top of it.
57
58
    :return: [(rev, [children])]
59
    """
60
    parents = wt.get_parent_ids()
61
    if len(parents) < 2:
62
        return None
63
64
    # The basic pending merge algorithm uses the same algorithm as
65
    # bzrlib.status.show_pending_merges
66
    pending = parents[1:]
67
    branch = wt.branch
68
    last_revision = parents[0]
69
70
    if last_revision is not None:
71
        try:
72
            ignore = set(branch.repository.get_ancestry(last_revision,
73
                                                        topo_sorted=False))
74
        except errors.NoSuchRevision:
75
            # the last revision is a ghost : assume everything is new
76
            # except for it
77
            ignore = set([None, last_revision])
78
    else:
79
        ignore = set([None])
80
81
    pm = []
82
    for merge in pending:
83
        ignore.add(merge)
84
        try:
85
            rev = branch.repository.get_revision(merge)
86
            children = []
87
            pm.append((rev, children))
88
89
            # This does need to be topo sorted, so we search backwards
90
            inner_merges = branch.repository.get_ancestry(merge)
91
            assert inner_merges[0] is None
92
            inner_merges.pop(0)
93
            for mmerge in reversed(inner_merges):
94
                if mmerge in ignore:
95
                    continue
96
                rev = branch.repository.get_revision(mmerge)
97
                children.append(rev)
98
99
                ignore.add(mmerge)
100
        except errors.NoSuchRevision:
101
            print "DEBUG: NoSuchRevision:", merge
102
103
    return pm
104
105
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
106
_newline_variants_re = re.compile(r'\r\n?')
107
def _sanitize_and_decode_message(utf8_message):
108
    """Turn a utf-8 message into a sanitized Unicode message."""
109
    fixed_newline = _newline_variants_re.sub('\n', utf8_message)
110
    return fixed_newline.decode('utf-8')
111
112
135 by Jelmer Vernooij
Throw out the old CommitDialog code and use the new code instead, also for 'gcommit'.
113
class CommitDialog(gtk.Dialog):
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
114
    """Implementation of Commit."""
115
116
    def __init__(self, wt, selected=None, parent=None):
602 by Jelmer Vernooij
Show working tree in gcommit dialog window.
117
        gtk.Dialog.__init__(self, title="Commit to %s" % wt.basedir,
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
118
                            parent=parent, flags=0,)
119
        self.connect('delete-event', self._on_delete_window)
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
120
        self._question_dialog = question_dialog
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
121
625.7.1 by Szilveszter Farkas
Added a GTK type hint to allow the Commit window to maximize.
122
        self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
278.1.24 by John Arbash Meinel
Actually show the commit button.
123
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
124
        self._wt = wt
278.1.34 by John Arbash Meinel
Cleanup, we are still ignoring the 'selected' property, and the 'wtpath'
125
        # TODO: Do something with this value, it is used by Olive
126
        #       It used to set all changes but this one to False
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
127
        self._selected = selected
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
128
        self._enable_per_file_commits = True
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
129
        self._commit_all_changes = True
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
130
        self.committed_revision_id = None # Nothing has been committed yet
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
131
        if can_save_commit_messages:
132
            self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
133
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
134
        self.setup_params()
135
        self.construct()
136
        self.fill_in_data()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
137
138
    def setup_params(self):
139
        """Setup the member variables for state."""
140
        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.
141
        self._delta = None
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
142
        self._pending = pending_revisions(self._wt)
143
144
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
145
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
146
    def fill_in_data(self):
147
        # Now that we are built, handle changes to the view based on the state
148
        self._fill_in_pending()
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
149
        self._fill_in_diff()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
150
        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.
151
        self._fill_in_checkout()
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
152
        self._fill_in_per_file_info()
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
153
154
    def _fill_in_pending(self):
155
        if not self._pending:
156
            self._pending_box.hide()
157
            return
158
159
        # TODO: We'd really prefer this to be a nested list
160
        for rev, children in self._pending:
161
            rev_info = self._rev_to_pending_info(rev)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
162
            self._pending_store.append([
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
163
                rev_info['revision_id'],
164
                rev_info['date'],
165
                rev_info['committer'],
166
                rev_info['summary'],
167
                ])
168
            for child in children:
169
                rev_info = self._rev_to_pending_info(child)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
170
                self._pending_store.append([
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
171
                    rev_info['revision_id'],
172
                    rev_info['date'],
173
                    rev_info['committer'],
174
                    rev_info['summary'],
175
                    ])
176
        self._pending_box.show()
177
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
178
    def _fill_in_files(self):
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
179
        # 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.
180
        # While we fill in the view, hide the store
181
        store = self._files_store
182
        self._treeview_files.set_model(None)
183
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
184
        added = _i18n('added')
185
        removed = _i18n('removed')
186
        renamed = _i18n('renamed')
187
        renamed_and_modified = _i18n('renamed and modified')
188
        modified = _i18n('modified')
189
        kind_changed = _i18n('kind changed')
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
190
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
191
        # The store holds:
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
192
        # [file_id, real path, checkbox, display path, changes type, message]
450 by Aaron Bentley
Update to use new Tree.iter_changes
193
        # iter_changes returns:
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
194
        # (file_id, (path_in_source, path_in_target),
195
        #  changed_content, versioned, parent, name, kind,
196
        #  executable)
197
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
198
        all_enabled = (self._selected is None)
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
199
        # The first entry is always the 'whole tree'
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
200
        all_iter = store.append([None, None, all_enabled, 'All Files', '', ''])
201
        initial_cursor = store.get_path(all_iter)
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
202
        # should we pass specific_files?
203
        self._wt.lock_read()
204
        self._basis_tree.lock_read()
205
        try:
450 by Aaron Bentley
Update to use new Tree.iter_changes
206
            from diff import iter_changes_to_status
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
207
            if can_save_commit_messages:
208
                saved_file_messages = self._saved_commit_messages_manager.get()[1]
209
            else:
210
                saved_file_messages = {}
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
211
            for (file_id, real_path, change_type, display_path
450 by Aaron Bentley
Update to use new Tree.iter_changes
212
                ) in iter_changes_to_status(self._basis_tree, self._wt):
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
213
                if self._selected and real_path != self._selected:
214
                    enabled = False
215
                else:
216
                    enabled = True
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
217
                try:
218
                    default_message = saved_file_messages[file_id]
219
                except KeyError:
220
                    default_message = ''
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
221
                item_iter = store.append([
222
                    file_id,
223
                    real_path.encode('UTF-8'),
224
                    enabled,
225
                    display_path.encode('UTF-8'),
226
                    change_type,
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
227
                    default_message, # Initial comment
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
228
                    ])
229
                if self._selected and enabled:
230
                    initial_cursor = store.get_path(item_iter)
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
231
        finally:
232
            self._basis_tree.unlock()
233
            self._wt.unlock()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
234
235
        self._treeview_files.set_model(store)
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
236
        self._last_selected_file = None
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
237
        # This sets the cursor, which causes the expander to close, which
238
        # causes the _file_message_text_view to never get realized. So we have
239
        # to give it a little kick, or it warns when we try to grab the focus
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
240
        self._treeview_files.set_cursor(initial_cursor)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
241
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
242
        def _realize_file_message_tree_view(*args):
243
            self._file_message_text_view.realize()
244
        self.connect_after('realize', _realize_file_message_tree_view)
245
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
246
    def _fill_in_diff(self):
247
        self._diff_view.set_trees(self._wt, self._basis_tree)
248
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
249
    def _fill_in_checkout(self):
250
        if not self._is_checkout:
251
            self._check_local.hide()
252
            return
253
        if have_dbus:
254
            bus = dbus.SystemBus()
445 by Szilveszter Farkas (Phanatic)
Fix a traceback if NetworkManager is not available (#199513).
255
            try:
256
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
257
                                           '/org/freedesktop/NetworkManager')
446 by Szilveszter Farkas (Phanatic)
Hot fix. Sorry dudes.
258
            except dbus.DBusException:
445 by Szilveszter Farkas (Phanatic)
Fix a traceback if NetworkManager is not available (#199513).
259
                mutter("networkmanager not available.")
260
                self._check_local.show()
261
                return
262
            
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
263
            dbus_iface = dbus.Interface(proxy_obj,
264
                                        'org.freedesktop.NetworkManager')
265
            try:
266
                # 3 is the enum value for STATE_CONNECTED
267
                self._check_local.set_active(dbus_iface.state() != 3)
268
            except dbus.DBusException, e:
269
                # Silently drop errors. While DBus may be
270
                # available, NetworkManager doesn't necessarily have to be
271
                mutter("unable to get networkmanager state: %r" % e)
272
        self._check_local.show()
273
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
274
    def _fill_in_per_file_info(self):
275
        config = self._wt.branch.get_config()
276
        enable_per_file_commits = config.get_user_option('per_file_commits')
277
        if (enable_per_file_commits is None
278
            or enable_per_file_commits.lower()
279
                not in ('y', 'yes', 'on', 'enable', '1', 't', 'true')):
280
            self._enable_per_file_commits = False
281
        else:
282
            self._enable_per_file_commits = True
283
        if not self._enable_per_file_commits:
284
            self._file_message_expander.hide()
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
285
            self._global_message_label.set_markup(_i18n('<b>Commit Message</b>'))
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
286
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
287
    def _compute_delta(self):
288
        self._delta = self._wt.changes_from(self._basis_tree)
289
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
290
    def construct(self):
291
        """Build up the dialog widgets."""
292
        # The primary pane which splits it into left and right (adjustable)
293
        # sections.
278.1.4 by John Arbash Meinel
Just playing around.
294
        self._hpane = gtk.HPaned()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
295
296
        self._construct_left_pane()
297
        self._construct_right_pane()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
298
        self._construct_action_pane()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
299
300
        self.vbox.pack_start(self._hpane)
301
        self._hpane.show()
302
        self.set_focus(self._global_message_text_view)
303
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
304
        self._construct_accelerators()
305
        self._set_sizes()
306
307
    def _set_sizes(self):
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
308
        # This seems like a reasonable default, we might like it to
309
        # be a bit wider, so that by default we can fit an 80-line diff in the
310
        # diff window.
311
        # Alternatively, we should be saving the last position/size rather than
312
        # setting it to a fixed value every time we start up.
313
        screen = self.get_screen()
314
        monitor = 0 # We would like it to be the monitor we are going to
315
                    # display on, but I don't know how to figure that out
316
                    # Only really useful for freaks like me that run dual
317
                    # monitor, with different sizes on the monitors
318
        monitor_rect = screen.get_monitor_geometry(monitor)
319
        width = int(monitor_rect.width * 0.66)
320
        height = int(monitor_rect.height * 0.66)
321
        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
322
        self._hpane.set_position(300)
323
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
324
    def _construct_accelerators(self):
325
        group = gtk.AccelGroup()
326
        group.connect_group(gtk.gdk.keyval_from_name('N'),
327
                            gtk.gdk.CONTROL_MASK, 0, self._on_accel_next)
328
        self.add_accel_group(group)
329
500.2.1 by Scott Scriven
Made 'gcommit' ignore the escape key ('close' signal).
330
        # ignore the escape key (avoid closing the window)
500.2.2 by Scott Scriven
Simpler/cleaner way to ignore the 'close' signal.
331
        self.connect_object('close', self.emit_stop_by_name, 'close')
500.2.1 by Scott Scriven
Made 'gcommit' ignore the escape key ('close' signal).
332
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
333
    def _construct_left_pane(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
334
        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.
335
        self._construct_file_list()
336
        self._construct_pending_list()
337
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
338
        self._check_local = gtk.CheckButton(_i18n("_Only commit locally"),
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
339
                                            use_underline=True)
340
        self._left_pane_box.pack_end(self._check_local, False, False)
341
        self._check_local.set_active(False)
342
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
343
        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.
344
        self._left_pane_box.show()
345
346
    def _construct_right_pane(self):
347
        # TODO: I really want to make it so the diff view gets more space than
348
        # the global commit message, and the per-file commit message gets even
349
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
350
        # commit, and 1 for file commit, and it looked good. But I don't seem
351
        # to have a way to do that with the gtk boxes... :( (Which is extra
352
        # 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.
353
        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.
354
        self._right_pane_table.set_row_spacings(5)
355
        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.
356
        self._right_pane_table_row = 0
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
357
        self._construct_diff_view()
358
        self._construct_file_message()
359
        self._construct_global_message()
360
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
361
        self._right_pane_table.show()
362
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
363
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
364
    def _construct_action_pane(self):
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
365
        self._button_cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
366
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
367
        self._button_cancel.show()
368
        self.action_area.pack_end(self._button_cancel)
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
369
        self._button_commit = gtk.Button(_i18n("Comm_it"), use_underline=True)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
370
        self._button_commit.connect('clicked', self._on_commit_clicked)
371
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
278.1.24 by John Arbash Meinel
Actually show the commit button.
372
        self._button_commit.show()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
373
        self.action_area.pack_end(self._button_commit)
278.1.24 by John Arbash Meinel
Actually show the commit button.
374
        self._button_commit.grab_default()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
375
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
376
    def _add_to_right_table(self, widget, weight, expanding=False):
377
        """Add another widget to the table
378
379
        :param widget: The object to add
380
        :param weight: How many rows does this widget get to request
381
        :param expanding: Should expand|fill|shrink be set?
382
        """
383
        end_row = self._right_pane_table_row + weight
384
        options = 0
385
        expand_opts = gtk.EXPAND | gtk.FILL | gtk.SHRINK
386
        if expanding:
387
            options = expand_opts
388
        self._right_pane_table.attach(widget, 0, 1,
389
                                      self._right_pane_table_row, end_row,
390
                                      xoptions=expand_opts, yoptions=options)
391
        self._right_pane_table_row = end_row
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
392
393
    def _construct_file_list(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
394
        self._files_box = gtk.VBox(homogeneous=False, spacing=0)
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
395
        file_label = gtk.Label(_i18n('Files'))
278.1.41 by John Arbash Meinel
For the moment, just hide the section headings Files and Diff for *.
396
        # file_label.show()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
397
        self._files_box.pack_start(file_label, expand=False)
398
278.1.42 by John Arbash Meinel
start playing with using a radial box, rather than an entry in the list
399
        self._commit_all_files_radio = gtk.RadioButton(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
400
            None, _i18n("Commit all changes"))
278.1.42 by John Arbash Meinel
start playing with using a radial box, rather than an entry in the list
401
        self._files_box.pack_start(self._commit_all_files_radio, expand=False)
402
        self._commit_all_files_radio.show()
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
403
        self._commit_all_files_radio.connect('toggled',
404
            self._toggle_commit_selection)
278.1.42 by John Arbash Meinel
start playing with using a radial box, rather than an entry in the list
405
        self._commit_selected_radio = gtk.RadioButton(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
406
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
278.1.42 by John Arbash Meinel
start playing with using a radial box, rather than an entry in the list
407
        self._files_box.pack_start(self._commit_selected_radio, expand=False)
408
        self._commit_selected_radio.show()
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
409
        self._commit_selected_radio.connect('toggled',
410
            self._toggle_commit_selection)
411
        if self._pending:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
412
            self._commit_all_files_radio.set_label(_i18n('Commit all changes*'))
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
413
            self._commit_all_files_radio.set_sensitive(False)
414
            self._commit_selected_radio.set_sensitive(False)
278.1.42 by John Arbash Meinel
start playing with using a radial box, rather than an entry in the list
415
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
416
        scroller = gtk.ScrolledWindow()
417
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
418
        self._treeview_files = gtk.TreeView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
419
        self._treeview_files.show()
420
        scroller.add(self._treeview_files)
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
421
        scroller.set_shadow_type(gtk.SHADOW_IN)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
422
        scroller.show()
423
        self._files_box.pack_start(scroller,
424
                                   expand=True, fill=True)
425
        self._files_box.show()
426
        self._left_pane_box.pack_start(self._files_box)
427
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
428
        # Keep note that all strings stored in a ListStore must be UTF-8
429
        # strings. GTK does not support directly setting and restoring Unicode
430
        # objects.
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
431
        liststore = gtk.ListStore(
432
            gobject.TYPE_STRING,  # [0] file_id
433
            gobject.TYPE_STRING,  # [1] real path
434
            gobject.TYPE_BOOLEAN, # [2] checkbox
435
            gobject.TYPE_STRING,  # [3] display path
436
            gobject.TYPE_STRING,  # [4] changes type
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
437
            gobject.TYPE_STRING,  # [5] commit message
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
438
            )
439
        self._files_store = liststore
440
        self._treeview_files.set_model(liststore)
441
        crt = gtk.CellRendererToggle()
278.1.39 by John Arbash Meinel
To disable a checkbox it is set_property('activatable', False),
442
        crt.set_property('activatable', not bool(self._pending))
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
443
        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.
444
        if self._pending:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
445
            name = _i18n('Commit*')
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
446
        else:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
447
            name = _i18n('Commit')
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
448
        commit_col = gtk.TreeViewColumn(name, crt, active=2)
449
        commit_col.set_visible(False)
450
        self._treeview_files.append_column(commit_col)
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
451
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Path'),
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
452
                                           gtk.CellRendererText(), text=3))
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
453
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Type'),
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
454
                                           gtk.CellRendererText(), text=4))
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
455
        self._treeview_files.connect('cursor-changed',
456
                                     self._on_treeview_files_cursor_changed)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
457
458
    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,
459
        if model[path][0] is None: # No file_id means 'All Files'
460
            new_val = not model[path][2]
461
            for node in model:
462
                node[2] = new_val
463
        else:
464
            model[path][2] = not model[path][2]
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
465
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
466
    def _toggle_commit_selection(self, button):
467
        all_files = self._commit_all_files_radio.get_active()
468
        if self._commit_all_changes != all_files:
469
            checked_col = self._treeview_files.get_column(0)
470
            self._commit_all_changes = all_files
471
            if all_files:
472
                checked_col.set_visible(False)
473
            else:
474
                checked_col.set_visible(True)
475
            renderer = checked_col.get_cell_renderers()[0]
476
            renderer.set_property('activatable', not all_files)
477
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
478
    def _construct_pending_list(self):
479
        # Pending information defaults to hidden, we put it all in 1 box, so
480
        # that we can show/hide all of them at once
481
        self._pending_box = gtk.VBox()
482
        self._pending_box.hide()
483
484
        pending_message = gtk.Label()
485
        pending_message.set_markup(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
486
            _i18n('<i>* Cannot select specific files when merging</i>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
487
        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.
488
        pending_message.show()
489
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
490
        pending_label = gtk.Label(_i18n('Pending Revisions'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
491
        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.
492
        pending_label.show()
493
494
        scroller = gtk.ScrolledWindow()
495
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
496
        self._treeview_pending = gtk.TreeView()
497
        scroller.add(self._treeview_pending)
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
498
        scroller.set_shadow_type(gtk.SHADOW_IN)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
499
        scroller.show()
500
        self._pending_box.pack_start(scroller,
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
501
                                     expand=True, fill=True, padding=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
502
        self._treeview_pending.show()
503
        self._left_pane_box.pack_start(self._pending_box)
504
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
505
        liststore = gtk.ListStore(gobject.TYPE_STRING, # revision_id
506
                                  gobject.TYPE_STRING, # date
507
                                  gobject.TYPE_STRING, # committer
508
                                  gobject.TYPE_STRING, # summary
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
509
                                 )
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
510
        self._pending_store = liststore
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
511
        self._treeview_pending.set_model(liststore)
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
512
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Date'),
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
513
                                             gtk.CellRendererText(), text=1))
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
514
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Committer'),
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
515
                                             gtk.CellRendererText(), text=2))
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
516
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Summary'),
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
517
                                             gtk.CellRendererText(), text=3))
518
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
519
    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.
520
        from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
521
278.1.41 by John Arbash Meinel
For the moment, just hide the section headings Files and Diff for *.
522
        # TODO: jam 2007-10-30 The diff label is currently disabled. If we
523
        #       decide that we really don't ever want to display it, we should
524
        #       actually remove it, and other references to it, along with the
525
        #       tests that it is set properly.
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
526
        self._diff_label = gtk.Label(_i18n('Diff for whole tree'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
527
        self._diff_label.set_alignment(0, 0)
528
        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.
529
        self._add_to_right_table(self._diff_label, 1, False)
278.1.41 by John Arbash Meinel
For the moment, just hide the section headings Files and Diff for *.
530
        # self._diff_label.show()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
531
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
532
        self._diff_view = DiffView()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
533
        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.
534
        self._diff_view.show()
535
536
    def _construct_file_message(self):
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
537
        scroller = gtk.ScrolledWindow()
538
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
539
540
        self._file_message_text_view = gtk.TextView()
541
        scroller.add(self._file_message_text_view)
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
542
        scroller.set_shadow_type(gtk.SHADOW_IN)
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
543
        scroller.show()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
544
545
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
546
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
547
        self._file_message_text_view.set_accepts_tab(False)
548
        self._file_message_text_view.show()
549
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
550
        self._file_message_expander = gtk.Expander(_i18n('File commit message'))
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
551
        self._file_message_expander.set_expanded(True)
552
        self._file_message_expander.add(scroller)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
553
        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.
554
        self._file_message_expander.show()
555
556
    def _construct_global_message(self):
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
557
        self._global_message_label = gtk.Label(_i18n('Global Commit Message'))
558
        self._global_message_label.set_markup(
559
            _i18n('<b>Global Commit Message</b>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
560
        self._global_message_label.set_alignment(0, 0)
561
        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.
562
        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.
563
        # 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.
564
        self._global_message_label.show()
565
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
566
        scroller = gtk.ScrolledWindow()
567
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
568
569
        self._global_message_text_view = gtk.TextView()
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
570
        if can_save_commit_messages:
571
            self._set_global_commit_message(self._saved_commit_messages_manager.get()[0])
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
572
        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.
573
        scroller.add(self._global_message_text_view)
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
574
        scroller.set_shadow_type(gtk.SHADOW_IN)
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
575
        scroller.show()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
576
        self._add_to_right_table(scroller, 2, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
577
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
578
        self._file_message_text_view.set_accepts_tab(False)
579
        self._global_message_text_view.show()
580
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
581
    def _on_treeview_files_cursor_changed(self, treeview):
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
582
        treeselection = treeview.get_selection()
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
583
        (model, selection) = treeselection.get_selected()
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
584
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
585
        if selection is not None:
586
            path, display_path = model.get(selection, 1, 3)
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
587
            self._diff_label.set_text(_i18n('Diff for ') + display_path)
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
588
            if path is None:
589
                self._diff_view.show_diff(None)
590
            else:
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
591
                self._diff_view.show_diff([path.decode('UTF-8')])
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
592
            self._update_per_file_info(selection)
593
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
594
    def _on_accel_next(self, accel_group, window, keyval, modifier):
595
        # We don't really care about any of the parameters, because we know
596
        # where this message came from
597
        tree_selection = self._treeview_files.get_selection()
598
        (model, selection) = tree_selection.get_selected()
599
        if selection is None:
600
            next = None
601
        else:
602
            next = model.iter_next(selection)
603
604
        if next is None:
605
            # We have either made it to the end of the list, or nothing was
606
            # selected. Either way, select All Files, and jump to the global
607
            # commit message.
608
            self._treeview_files.set_cursor((0,))
609
            self._global_message_text_view.grab_focus()
610
        else:
611
            # Set the cursor to this entry, and jump to the per-file commit
612
            # message
613
            self._treeview_files.set_cursor(model.get_path(next))
614
            self._file_message_text_view.grab_focus()
615
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
616
    def _save_current_file_message(self):
617
        if self._last_selected_file is None:
618
            return # Nothing to save
619
        text_buffer = self._file_message_text_view.get_buffer()
620
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
621
                                        text_buffer.get_end_iter())
622
        last_selected = self._files_store.get_iter(self._last_selected_file)
623
        self._files_store.set_value(last_selected, 5, cur_text)
624
625
    def _update_per_file_info(self, selection):
626
        # The node is changing, so cache the current message
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
627
        if not self._enable_per_file_commits:
628
            return
629
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
630
        self._save_current_file_message()
631
        text_buffer = self._file_message_text_view.get_buffer()
632
        file_id, display_path, message = self._files_store.get(selection, 0, 3, 5)
633
        if file_id is None: # Whole tree
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
634
            self._file_message_expander.set_label(_i18n('File commit message'))
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
635
            self._file_message_expander.set_expanded(False)
636
            self._file_message_expander.set_sensitive(False)
637
            text_buffer.set_text('')
638
            self._last_selected_file = None
639
        else:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
640
            self._file_message_expander.set_label(_i18n('Commit message for ')
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
641
                                                  + display_path)
642
            self._file_message_expander.set_expanded(True)
643
            self._file_message_expander.set_sensitive(True)
644
            text_buffer.set_text(message)
645
            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.
646
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
647
    def _get_specific_files(self):
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
648
        """Return the list of selected paths, and file info.
649
650
        :return: ([unicode paths], [{utf-8 file info}]
651
        """
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
652
        self._save_current_file_message()
653
        files = []
654
        records = iter(self._files_store)
655
        rec = records.next() # Skip the All Files record
656
        assert rec[0] is None, "Are we skipping the wrong record?"
657
658
        file_info = []
659
        for record in records:
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
660
            if self._commit_all_changes or record[2]:# [2] checkbox
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
661
                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.
662
                path = record[1]    # [1] real path
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
663
                # [5] commit message
664
                file_message = _sanitize_and_decode_message(record[5])
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
665
                files.append(path.decode('UTF-8'))
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
666
                if self._enable_per_file_commits and file_message:
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
667
                    # All of this needs to be utf-8 information
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
668
                    file_message = file_message.encode('UTF-8')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
669
                    file_info.append({'path':path, 'file_id':file_id,
670
                                     'message':file_message})
671
        file_info.sort(key=lambda x:(x['path'], x['file_id']))
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
672
        if self._enable_per_file_commits:
673
            return files, file_info
674
        else:
675
            return files, []
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
676
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
677
    @show_bzr_error
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
678
    def _on_cancel_clicked(self, button):
679
        """ Cancel button clicked handler. """
680
        self._do_cancel()
681
682
    @show_bzr_error
683
    def _on_delete_window(self, source, event):
684
        """ Delete window handler. """
685
        self._do_cancel()
686
687
    def _do_cancel(self):
688
        """If requested, saves commit messages when cancelling gcommit; they are re-used by a next gcommit"""
689
        if can_save_commit_messages:
635.2.8 by Vincent Ladeuil
Start testing patch behavior.
690
            mgr = SavedCommitMessagesManager()
691
            self._saved_commit_messages_manager = mgr
692
            mgr.insert(self._get_global_commit_message(),
693
                       self._get_specific_files()[1])
694
            if mgr.is_not_empty(): # maybe worth saving
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
695
                response = self._question_dialog(
635.2.8 by Vincent Ladeuil
Start testing patch behavior.
696
                    _i18n('Commit cancelled'),
697
                    _i18n('Do you want to save your commit messages ?'),
698
                    parent=self)
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
699
                if response == gtk.RESPONSE_NO:
700
                     # save nothing and destroy old comments if any
635.2.8 by Vincent Ladeuil
Start testing patch behavior.
701
                    mgr = SavedCommitMessagesManager()
702
            mgr.save(self._wt, self._wt.branch)
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
703
        self.response(gtk.RESPONSE_CANCEL) # close window
704
705
    @show_bzr_error
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
706
    def _on_commit_clicked(self, button):
707
        """ Commit button clicked handler. """
708
        self._do_commit()
709
710
    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.
711
        message = self._get_global_commit_message()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
712
713
        if message == '':
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
714
            response = self._question_dialog(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
715
                _i18n('Commit with an empty message?'),
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
716
                _i18n('You can describe your commit intent in the message.'),
717
                parent=self)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
718
            if response == gtk.RESPONSE_NO:
719
                # Kindly give focus to message area
720
                self._global_message_text_view.grab_focus()
721
                return
722
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
723
        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.
724
        if self._pending:
725
            specific_files = None
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
726
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
727
        local = self._check_local.get_active()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
728
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
729
        # All we care about is if there is a single unknown, so if this loop is
730
        # entered, then there are unknown files.
731
        # TODO: jam 20071002 It seems like this should cancel the dialog
732
        #       entirely, since there isn't a way for them to add the unknown
733
        #       files at this point.
734
        for path in self._wt.unknowns():
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
735
            response = self._question_dialog(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
736
                _i18n("Commit with unknowns?"),
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
737
                _i18n("Unknown files exist in the working tree. Commit anyway?"),
738
                parent=self)
739
                # Doesn't set a parent for the dialog..
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
740
            if response == gtk.RESPONSE_NO:
741
                return
742
            break
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
743
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
744
        rev_id = None
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
745
        revprops = {}
746
        if file_info:
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
747
            revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
748
        try:
749
            rev_id = self._wt.commit(message,
750
                       allow_pointless=False,
751
                       strict=False,
752
                       local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
753
                       specific_files=specific_files,
754
                       revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
755
        except errors.PointlessCommit:
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
756
            response = self._question_dialog(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
757
                _i18n('Commit with no changes?'),
758
                _i18n('There are no changes in the working tree.'
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
759
                      ' Do you want to commit anyway?'),
760
                parent=self)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
761
            if response == gtk.RESPONSE_YES:
762
                rev_id = self._wt.commit(message,
763
                               allow_pointless=True,
764
                               strict=False,
765
                               local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
766
                               specific_files=specific_files,
767
                               revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
768
        self.committed_revision_id = rev_id
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
769
        # destroy old comments if any
770
        if can_save_commit_messages:
771
            SavedCommitMessagesManager().save(self._wt, self._wt.branch)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
772
        self.response(gtk.RESPONSE_OK)
773
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
774
    def _get_global_commit_message(self):
775
        buf = self._global_message_text_view.get_buffer()
776
        start, end = buf.get_bounds()
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
777
        text = buf.get_text(start, end)
778
        return _sanitize_and_decode_message(text)
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
779
780
    def _set_global_commit_message(self, message):
781
        """Just a helper for the test suite."""
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
782
        if isinstance(message, unicode):
783
            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.
784
        self._global_message_text_view.get_buffer().set_text(message)
785
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
786
    def _set_file_commit_message(self, message):
787
        """Helper for the test suite."""
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
788
        if isinstance(message, unicode):
789
            message = message.encode('UTF-8')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
790
        self._file_message_text_view.get_buffer().set_text(message)
791
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
792
    @staticmethod
793
    def _rev_to_pending_info(rev):
794
        """Get the information from a pending merge."""
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
795
        from bzrlib.osutils import format_date
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
796
        rev_dict = {}
797
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
798
        rev_dict['summary'] = rev.get_summary()
799
        rev_dict['date'] = format_date(rev.timestamp,
800
                                       rev.timezone or 0,
801
                                       'original', date_fmt="%Y-%m-%d",
802
                                       show_offset=False)
803
        rev_dict['revision_id'] = rev.revision_id
804
        return rev_dict