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