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