/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:
291
            bus = dbus.SystemBus()
445 by Szilveszter Farkas (Phanatic)
Fix a traceback if NetworkManager is not available (#199513).
292
            try:
293
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
294
                                           '/org/freedesktop/NetworkManager')
446 by Szilveszter Farkas (Phanatic)
Hot fix. Sorry dudes.
295
            except dbus.DBusException:
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
296
                trace.mutter("networkmanager not available.")
445 by Szilveszter Farkas (Phanatic)
Fix a traceback if NetworkManager is not available (#199513).
297
                self._check_local.show()
298
                return
777 by Jelmer Vernooij
Avoid the use of Repository.get_ancestry().
299
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
300
            dbus_iface = dbus.Interface(proxy_obj,
301
                                        'org.freedesktop.NetworkManager')
302
            try:
303
                # 3 is the enum value for STATE_CONNECTED
304
                self._check_local.set_active(dbus_iface.state() != 3)
305
            except dbus.DBusException, e:
306
                # Silently drop errors. While DBus may be
307
                # available, NetworkManager doesn't necessarily have to be
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
308
                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.
309
        self._check_local.show()
310
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
311
    def _fill_in_per_file_info(self):
312
        config = self._wt.branch.get_config()
313
        enable_per_file_commits = config.get_user_option('per_file_commits')
314
        if (enable_per_file_commits is None
315
            or enable_per_file_commits.lower()
316
                not in ('y', 'yes', 'on', 'enable', '1', 't', 'true')):
317
            self._enable_per_file_commits = False
318
        else:
319
            self._enable_per_file_commits = True
320
        if not self._enable_per_file_commits:
321
            self._file_message_expander.hide()
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
322
            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.
323
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
324
    def _compute_delta(self):
325
        self._delta = self._wt.changes_from(self._basis_tree)
326
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
327
    def construct(self):
328
        """Build up the dialog widgets."""
329
        # The primary pane which splits it into left and right (adjustable)
330
        # sections.
775.1.5 by Curtis Hovey
Replace Gtk.HPaned() with Gtk.Paned.new(Gtk.Orientation.HORIZONTAL).
331
        self._hpane = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
332
333
        self._construct_left_pane()
334
        self._construct_right_pane()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
335
        self._construct_action_pane()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
336
734.1.4 by Curtis Hovey
Updated commit to gtk3.
337
        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.
338
        self._hpane.show()
339
        self.set_focus(self._global_message_text_view)
340
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
341
        self._construct_accelerators()
342
        self._set_sizes()
343
344
    def _set_sizes(self):
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
345
        # This seems like a reasonable default, we might like it to
346
        # be a bit wider, so that by default we can fit an 80-line diff in the
347
        # diff window.
348
        # Alternatively, we should be saving the last position/size rather than
349
        # setting it to a fixed value every time we start up.
350
        screen = self.get_screen()
351
        monitor = 0 # We would like it to be the monitor we are going to
352
                    # display on, but I don't know how to figure that out
353
                    # Only really useful for freaks like me that run dual
354
                    # monitor, with different sizes on the monitors
355
        monitor_rect = screen.get_monitor_geometry(monitor)
356
        width = int(monitor_rect.width * 0.66)
357
        height = int(monitor_rect.height * 0.66)
358
        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
359
        self._hpane.set_position(300)
360
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
361
    def _construct_accelerators(self):
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
362
        group = Gtk.AccelGroup()
734.1.4 by Curtis Hovey
Updated commit to gtk3.
363
        group.connect(Gdk.keyval_from_name('N'),
364
                      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.
365
        self.add_accel_group(group)
366
500.2.1 by Scott Scriven
Made 'gcommit' ignore the escape key ('close' signal).
367
        # ignore the escape key (avoid closing the window)
500.2.2 by Scott Scriven
Simpler/cleaner way to ignore the 'close' signal.
368
        self.connect_object('close', self.emit_stop_by_name, 'close')
500.2.1 by Scott Scriven
Made 'gcommit' ignore the escape key ('close' signal).
369
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
370
    def _construct_left_pane(self):
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
371
        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.
372
        self._construct_file_list()
373
        self._construct_pending_list()
374
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
375
        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.
376
                                            use_underline=True)
734.1.4 by Curtis Hovey
Updated commit to gtk3.
377
        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.
378
        self._check_local.set_active(False)
379
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
380
        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.
381
        self._left_pane_box.show()
382
383
    def _construct_right_pane(self):
384
        # TODO: I really want to make it so the diff view gets more space than
385
        # the global commit message, and the per-file commit message gets even
386
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
387
        # commit, and 1 for file commit, and it looked good. But I don't seem
388
        # to have a way to do that with the gtk boxes... :( (Which is extra
389
        # weird since wx uses gtk on Linux...)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
390
        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.
391
        self._right_pane_table.set_row_spacings(5)
392
        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.
393
        self._right_pane_table_row = 0
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
394
        self._construct_diff_view()
395
        self._construct_file_message()
396
        self._construct_global_message()
397
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
398
        self._right_pane_table.show()
399
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
400
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
401
    def _construct_action_pane(self):
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
402
        self._button_cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
403
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
404
        self._button_cancel.show()
734.1.4 by Curtis Hovey
Updated commit to gtk3.
405
        self.get_action_area().pack_end(
406
            self._button_cancel, True, True, 0)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
407
        self._button_commit = Gtk.Button(_i18n("Comm_it"), use_underline=True)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
408
        self._button_commit.connect('clicked', self._on_commit_clicked)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
409
        self._button_commit.set_can_default(True)
278.1.24 by John Arbash Meinel
Actually show the commit button.
410
        self._button_commit.show()
734.1.4 by Curtis Hovey
Updated commit to gtk3.
411
        self.get_action_area().pack_end(
412
            self._button_commit, True, True, 0)
278.1.24 by John Arbash Meinel
Actually show the commit button.
413
        self._button_commit.grab_default()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
414
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
415
    def _add_to_right_table(self, widget, weight, expanding=False):
416
        """Add another widget to the table
417
418
        :param widget: The object to add
419
        :param weight: How many rows does this widget get to request
420
        :param expanding: Should expand|fill|shrink be set?
421
        """
422
        end_row = self._right_pane_table_row + weight
423
        options = 0
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
424
        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.
425
        if expanding:
426
            options = expand_opts
427
        self._right_pane_table.attach(widget, 0, 1,
428
                                      self._right_pane_table_row, end_row,
429
                                      xoptions=expand_opts, yoptions=options)
430
        self._right_pane_table_row = end_row
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
431
432
    def _construct_file_list(self):
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
433
        self._files_box = Gtk.VBox(homogeneous=False, spacing=0)
434
        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 *.
435
        # file_label.show()
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
436
        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.
437
734.1.19 by Curtis Hovey
Updated RadioButtons to gtk3.
438
        self._commit_all_files_radio = Gtk.RadioButton.new_with_label(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
439
            None, _i18n("Commit all changes"))
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
440
        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
441
        self._commit_all_files_radio.show()
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
442
        self._commit_all_files_radio.connect('toggled',
443
            self._toggle_commit_selection)
734.1.19 by Curtis Hovey
Updated RadioButtons to gtk3.
444
        self._commit_selected_radio = Gtk.RadioButton.new_with_label_from_widget(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
445
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
446
        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
447
        self._commit_selected_radio.show()
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
448
        self._commit_selected_radio.connect('toggled',
449
            self._toggle_commit_selection)
450
        if self._pending:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
451
            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.
452
            self._commit_all_files_radio.set_sensitive(False)
453
            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
454
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
455
        scroller = Gtk.ScrolledWindow()
456
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
457
        self._treeview_files = Gtk.TreeView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
458
        self._treeview_files.show()
459
        scroller.add(self._treeview_files)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
460
        scroller.set_shadow_type(Gtk.ShadowType.IN)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
461
        scroller.show()
734.1.4 by Curtis Hovey
Updated commit to gtk3.
462
        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.
463
        self._files_box.show()
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
464
        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.
465
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
466
        # Keep note that all strings stored in a ListStore must be UTF-8
467
        # strings. GTK does not support directly setting and restoring Unicode
468
        # objects.
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
469
        liststore = Gtk.ListStore(
470
            GObject.TYPE_STRING,  # [0] file_id
471
            GObject.TYPE_STRING,  # [1] real path
472
            GObject.TYPE_BOOLEAN, # [2] checkbox
473
            GObject.TYPE_STRING,  # [3] display path
474
            GObject.TYPE_STRING,  # [4] changes type
475
            GObject.TYPE_STRING,  # [5] commit message
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
476
            )
477
        self._files_store = liststore
478
        self._treeview_files.set_model(liststore)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
479
        crt = Gtk.CellRendererToggle()
278.1.39 by John Arbash Meinel
To disable a checkbox it is set_property('activatable', False),
480
        crt.set_property('activatable', not bool(self._pending))
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
481
        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.
482
        if self._pending:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
483
            name = _i18n('Commit*')
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
484
        else:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
485
            name = _i18n('Commit')
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
486
        commit_col = Gtk.TreeViewColumn(name, crt, active=2)
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
487
        commit_col.set_visible(False)
488
        self._treeview_files.append_column(commit_col)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
489
        self._treeview_files.append_column(Gtk.TreeViewColumn(_i18n('Path'),
490
                                           Gtk.CellRendererText(), text=3))
491
        self._treeview_files.append_column(Gtk.TreeViewColumn(_i18n('Type'),
492
                                           Gtk.CellRendererText(), text=4))
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
493
        self._treeview_files.connect('cursor-changed',
494
                                     self._on_treeview_files_cursor_changed)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
495
496
    def _toggle_commit(self, cell, path, model):
769 by Jelmer Vernooij
Cope with some strings being unicode when returned by some versions of gtk.
497
        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,
498
            new_val = not model[path][2]
499
            for node in model:
500
                node[2] = new_val
501
        else:
502
            model[path][2] = not model[path][2]
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
503
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
504
    def _toggle_commit_selection(self, button):
505
        all_files = self._commit_all_files_radio.get_active()
506
        if self._commit_all_changes != all_files:
507
            checked_col = self._treeview_files.get_column(0)
508
            self._commit_all_changes = all_files
509
            if all_files:
510
                checked_col.set_visible(False)
511
            else:
512
                checked_col.set_visible(True)
734.1.20 by Curtis Hovey
col.get_cell_renderers => col.get_cells.
513
            renderer = checked_col.get_cells()[0]
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
514
            renderer.set_property('activatable', not all_files)
515
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
516
    def _construct_pending_list(self):
517
        # Pending information defaults to hidden, we put it all in 1 box, so
518
        # that we can show/hide all of them at once
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
519
        self._pending_box = Gtk.VBox()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
520
        self._pending_box.hide()
521
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
522
        pending_message = Gtk.Label()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
523
        pending_message.set_markup(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
524
            _i18n('<i>* Cannot select specific files when merging</i>'))
734.1.4 by Curtis Hovey
Updated commit to gtk3.
525
        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.
526
        pending_message.show()
527
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
528
        pending_label = Gtk.Label(label=_i18n('Pending Revisions'))
734.1.4 by Curtis Hovey
Updated commit to gtk3.
529
        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.
530
        pending_label.show()
531
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
532
        scroller = Gtk.ScrolledWindow()
533
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
534
        self._treeview_pending = Gtk.TreeView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
535
        scroller.add(self._treeview_pending)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
536
        scroller.set_shadow_type(Gtk.ShadowType.IN)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
537
        scroller.show()
734.1.4 by Curtis Hovey
Updated commit to gtk3.
538
        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.
539
        self._treeview_pending.show()
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
540
        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.
541
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
542
        liststore = Gtk.ListStore(GObject.TYPE_STRING, # revision_id
543
                                  GObject.TYPE_STRING, # date
544
                                  GObject.TYPE_STRING, # committer
545
                                  GObject.TYPE_STRING, # summary
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
546
                                 )
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
547
        self._pending_store = liststore
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
548
        self._treeview_pending.set_model(liststore)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
549
        self._treeview_pending.append_column(Gtk.TreeViewColumn(_i18n('Date'),
550
                                             Gtk.CellRendererText(), text=1))
551
        self._treeview_pending.append_column(Gtk.TreeViewColumn(_i18n('Committer'),
552
                                             Gtk.CellRendererText(), text=2))
553
        self._treeview_pending.append_column(Gtk.TreeViewColumn(_i18n('Summary'),
554
                                             Gtk.CellRendererText(), text=3))
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
555
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
556
    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.
557
        from bzrlib.plugins.gtk.diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
558
278.1.41 by John Arbash Meinel
For the moment, just hide the section headings Files and Diff for *.
559
        # TODO: jam 2007-10-30 The diff label is currently disabled. If we
560
        #       decide that we really don't ever want to display it, we should
561
        #       actually remove it, and other references to it, along with the
562
        #       tests that it is set properly.
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
563
        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.
564
        self._diff_label.set_alignment(0, 0)
565
        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.
566
        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 *.
567
        # self._diff_label.show()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
568
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
569
        self._diff_view = DiffView()
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_view, 4, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
571
        self._diff_view.show()
572
573
    def _construct_file_message(self):
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
574
        scroller = Gtk.ScrolledWindow()
575
        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.
576
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
577
        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.
578
        scroller.add(self._file_message_text_view)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
579
        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.
580
        scroller.show()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
581
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
582
        self._file_message_text_view.modify_font(Pango.FontDescription("Monospace"))
583
        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.
584
        self._file_message_text_view.set_accepts_tab(False)
585
        self._file_message_text_view.show()
586
734.1.4 by Curtis Hovey
Updated commit to gtk3.
587
        self._file_message_expander = Gtk.Expander(
588
            label=_i18n('File commit message'))
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
589
        self._file_message_expander.set_expanded(True)
590
        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.
591
        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.
592
        self._file_message_expander.show()
593
594
    def _construct_global_message(self):
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
595
        self._global_message_label = Gtk.Label(label=_i18n('Global Commit Message'))
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
596
        self._global_message_label.set_markup(
597
            _i18n('<b>Global Commit Message</b>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
598
        self._global_message_label.set_alignment(0, 0)
599
        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.
600
        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.
601
        # 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.
602
        self._global_message_label.show()
603
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
604
        scroller = Gtk.ScrolledWindow()
605
        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.
606
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
607
        self._global_message_text_view = Gtk.TextView()
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
608
        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.
609
        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.
610
        scroller.add(self._global_message_text_view)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
611
        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.
612
        scroller.show()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
613
        self._add_to_right_table(scroller, 2, True)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
614
        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.
615
        self._file_message_text_view.set_accepts_tab(False)
616
        self._global_message_text_view.show()
617
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
618
    def _on_treeview_files_cursor_changed(self, treeview):
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
619
        treeselection = treeview.get_selection()
769.2.2 by Curtis Hovey
Added guard to avoid accessing a non-existent treeselection.
620
        if treeselection is None:
621
            # The treeview was probably destroyed as the dialog closes.
622
            return
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
623
        (model, selection) = treeselection.get_selected()
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
624
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
625
        if selection is not None:
626
            path, display_path = model.get(selection, 1, 3)
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
627
            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.
628
            if path == "":
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
629
                self._diff_view.show_diff(None)
630
            else:
769 by Jelmer Vernooij
Cope with some strings being unicode when returned by some versions of gtk.
631
                self._diff_view.show_diff([osutils.safe_unicode(path)])
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
632
            self._update_per_file_info(selection)
633
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
634
    def _on_accel_next(self, accel_group, window, keyval, modifier):
635
        # We don't really care about any of the parameters, because we know
636
        # where this message came from
637
        tree_selection = self._treeview_files.get_selection()
638
        (model, selection) = tree_selection.get_selected()
639
        if selection is None:
640
            next = None
641
        else:
642
            next = model.iter_next(selection)
643
644
        if next is None:
645
            # We have either made it to the end of the list, or nothing was
646
            # selected. Either way, select All Files, and jump to the global
647
            # commit message.
734.1.23 by Curtis Hovey
Always pass a Gtk.TreePath instead of an int or tuple.
648
            self._treeview_files.set_cursor(
769 by Jelmer Vernooij
Cope with some strings being unicode when returned by some versions of gtk.
649
                Gtk.TreePath(path=0), "", False)
278.1.32 by John Arbash Meinel
Add the Ctrl+n accelerator to jump through the commit messages.
650
            self._global_message_text_view.grab_focus()
651
        else:
652
            # Set the cursor to this entry, and jump to the per-file commit
653
            # message
734.1.13 by Curtis Hovey
Fixed search in diff.
654
            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.
655
            self._file_message_text_view.grab_focus()
656
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
657
    def _save_current_file_message(self):
658
        if self._last_selected_file is None:
659
            return # Nothing to save
660
        text_buffer = self._file_message_text_view.get_buffer()
661
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
734.1.4 by Curtis Hovey
Updated commit to gtk3.
662
                                        text_buffer.get_end_iter(), True)
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
663
        last_selected = self._files_store.get_iter(self._last_selected_file)
664
        self._files_store.set_value(last_selected, 5, cur_text)
665
666
    def _update_per_file_info(self, selection):
667
        # 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.
668
        if not self._enable_per_file_commits:
669
            return
670
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
671
        self._save_current_file_message()
672
        text_buffer = self._file_message_text_view.get_buffer()
673
        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.
674
        if file_id == "": # Whole tree
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
675
            self._file_message_expander.set_label(_i18n('File commit message'))
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
676
            self._file_message_expander.set_expanded(False)
677
            self._file_message_expander.set_sensitive(False)
678
            text_buffer.set_text('')
679
            self._last_selected_file = None
680
        else:
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
681
            self._file_message_expander.set_label(_i18n('Commit message for ')
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
682
                                                  + display_path)
683
            self._file_message_expander.set_expanded(True)
684
            self._file_message_expander.set_sensitive(True)
685
            text_buffer.set_text(message)
686
            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.
687
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
688
    def _get_specific_files(self):
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
689
        """Return the list of selected paths, and file info.
690
691
        :return: ([unicode paths], [{utf-8 file info}]
692
        """
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
693
        self._save_current_file_message()
694
        files = []
695
        records = iter(self._files_store)
696
        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.
697
        assert rec[0] == "", "Are we skipping the wrong record?"
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
698
699
        file_info = []
700
        for record in records:
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
701
            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.
702
                file_id = osutils.safe_utf8(record[0]) # [0] file_id
703
                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.
704
                # [5] commit message
705
                file_message = _sanitize_and_decode_message(record[5])
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
706
                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.
707
                if self._enable_per_file_commits and file_message:
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
708
                    # 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.
709
                    file_message = file_message.encode('UTF-8')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
710
                    file_info.append({'path':path, 'file_id':file_id,
711
                                     'message':file_message})
712
        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.
713
        if self._enable_per_file_commits:
714
            return files, file_info
715
        else:
716
            return files, []
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
717
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
718
    @show_bzr_error
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
719
    def _on_cancel_clicked(self, button):
720
        """ Cancel button clicked handler. """
721
        self._do_cancel()
722
723
    @show_bzr_error
724
    def _on_delete_window(self, source, event):
725
        """ Delete window handler. """
726
        self._do_cancel()
727
728
    def _do_cancel(self):
729
        """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.
730
        mgr = SavedCommitMessagesManager()
731
        self._saved_commit_messages_manager = mgr
732
        mgr.insert(self._get_global_commit_message(),
733
                   self._get_specific_files()[1])
734
        if mgr.is_not_empty(): # maybe worth saving
735
            response = self._question_dialog(
736
                _i18n('Commit cancelled'),
737
                _i18n('Do you want to save your commit messages ?'),
738
                parent=self)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
739
            if response == Gtk.ResponseType.NO:
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
740
                 # save nothing and destroy old comments if any
741
                mgr = SavedCommitMessagesManager()
742
        mgr.save(self._wt, self._wt.branch)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
743
        self.response(Gtk.ResponseType.CANCEL) # close window
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
744
745
    @show_bzr_error
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
746
    def _on_commit_clicked(self, button):
747
        """ Commit button clicked handler. """
748
        self._do_commit()
749
750
    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.
751
        message = self._get_global_commit_message()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
752
753
        if message == '':
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
754
            response = self._question_dialog(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
755
                _i18n('Commit with an empty message?'),
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
756
                _i18n('You can describe your commit intent in the message.'),
757
                parent=self)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
758
            if response == Gtk.ResponseType.NO:
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
759
                # Kindly give focus to message area
760
                self._global_message_text_view.grab_focus()
761
                return
762
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
763
        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.
764
        if self._pending:
765
            specific_files = None
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
766
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
767
        local = self._check_local.get_active()
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
768
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
769
        # All we care about is if there is a single unknown, so if this loop is
770
        # entered, then there are unknown files.
771
        # TODO: jam 20071002 It seems like this should cancel the dialog
772
        #       entirely, since there isn't a way for them to add the unknown
773
        #       files at this point.
774
        for path in self._wt.unknowns():
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
775
            response = self._question_dialog(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
776
                _i18n("Commit with unknowns?"),
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
777
                _i18n("Unknown files exist in the working tree. Commit anyway?"),
778
                parent=self)
779
                # Doesn't set a parent for the dialog..
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
780
            if response == Gtk.ResponseType.NO:
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
781
                return
782
            break
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
783
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
784
        rev_id = None
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
785
        revprops = {}
786
        if file_info:
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
787
            revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
788
        try:
789
            rev_id = self._wt.commit(message,
790
                       allow_pointless=False,
791
                       strict=False,
792
                       local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
793
                       specific_files=specific_files,
794
                       revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
795
        except errors.PointlessCommit:
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
796
            response = self._question_dialog(
475.1.2 by Vincent Ladeuil
Fix bug #187283 fix replacing _() by _i18n().
797
                _i18n('Commit with no changes?'),
798
                _i18n('There are no changes in the working tree.'
576.1.1 by Jasper Groenewegen
Add parent setting to dialogs and implement in gcommit
799
                      ' Do you want to commit anyway?'),
800
                parent=self)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
801
            if response == Gtk.ResponseType.YES:
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
802
                rev_id = self._wt.commit(message,
803
                               allow_pointless=True,
804
                               strict=False,
805
                               local=local,
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
806
                               specific_files=specific_files,
807
                               revprops=revprops)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
808
        self.committed_revision_id = rev_id
635.2.7 by Vincent Ladeuil
Land Anne Mohsen's patch for bug 215674.
809
        # destroy old comments if any
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
810
        SavedCommitMessagesManager().save(self._wt, self._wt.branch)
734.1.1 by Curtis Hovey
Mechanical changes made by pygi.convert.sh.
811
        self.response(Gtk.ResponseType.OK)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
812
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
813
    def _get_global_commit_message(self):
814
        buf = self._global_message_text_view.get_buffer()
815
        start, end = buf.get_bounds()
734.1.4 by Curtis Hovey
Updated commit to gtk3.
816
        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.
817
        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.
818
819
    def _set_global_commit_message(self, message):
820
        """Just a helper for the test suite."""
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
821
        if isinstance(message, unicode):
822
            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.
823
        self._global_message_text_view.get_buffer().set_text(message)
824
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
825
    def _set_file_commit_message(self, message):
826
        """Helper for the test suite."""
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
827
        if isinstance(message, unicode):
828
            message = message.encode('UTF-8')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
829
        self._file_message_text_view.get_buffer().set_text(message)
830
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
831
    @staticmethod
832
    def _rev_to_pending_info(rev):
833
        """Get the information from a pending merge."""
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
834
        from bzrlib.osutils import format_date
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
835
        rev_dict = {}
836
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
837
        rev_dict['summary'] = rev.get_summary()
838
        rev_dict['date'] = format_date(rev.timestamp,
839
                                       rev.timezone or 0,
840
                                       'original', date_fmt="%Y-%m-%d",
841
                                       show_offset=False)
842
        rev_dict['revision_id'] = rev.revision_id
843
        return rev_dict
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
844