/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to commit.py

  • Committer: Curtis Hovey
  • Date: 2012-02-09 03:43:01 UTC
  • mto: This revision was merged to the branch mainline in revision 776.
  • Revision ID: sinzui.is@verizon.net-20120209034301-0uwkobbb0d64ugrw
Switched from Gtk.VPaned to Gtk.Paned.new(Gtk.Orientation.VERTICAL).

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import re
18
18
 
19
 
try:
20
 
    import pygtk
21
 
    pygtk.require("2.0")
22
 
except:
23
 
    pass
24
 
 
25
 
import gtk
26
 
import gobject
27
 
import pango
 
19
from gi.repository import Gdk
 
20
from gi.repository import Gtk
 
21
from gi.repository import GObject
 
22
from gi.repository import Pango
28
23
 
29
24
from bzrlib import (
 
25
    bencode,
30
26
    errors,
31
27
    osutils,
32
28
    trace,
33
29
    )
34
 
try:
35
 
    from bzrlib import bencode
36
 
except ImportError:
37
 
    from bzrlib.util import bencode
38
 
 
39
 
from bzrlib.plugins.gtk import _i18n
40
30
from bzrlib.plugins.gtk.dialog import question_dialog
41
31
from bzrlib.plugins.gtk.errors import show_bzr_error
 
32
from bzrlib.plugins.gtk.i18n import _i18n
 
33
from bzrlib.plugins.gtk.commitmsgs import SavedCommitMessagesManager
42
34
 
43
35
try:
44
36
    import dbus
67
59
    last_revision = parents[0]
68
60
 
69
61
    if last_revision is not None:
70
 
        try:
71
 
            ignore = set(branch.repository.get_ancestry(last_revision,
72
 
                                                        topo_sorted=False))
73
 
        except errors.NoSuchRevision:
74
 
            # the last revision is a ghost : assume everything is new
75
 
            # except for it
76
 
            ignore = set([None, last_revision])
 
62
        graph = branch.repository.get_graph()
 
63
        ignore = set([r for r,ps in graph.iter_ancestry([last_revision])])
77
64
    else:
78
 
        ignore = set([None])
 
65
        ignore = set([])
79
66
 
80
67
    pm = []
81
68
    for merge in pending:
106
93
def _sanitize_and_decode_message(utf8_message):
107
94
    """Turn a utf-8 message into a sanitized Unicode message."""
108
95
    fixed_newline = _newline_variants_re.sub('\n', utf8_message)
109
 
    return fixed_newline.decode('utf-8')
110
 
 
111
 
 
112
 
class CommitDialog(gtk.Dialog):
 
96
    return osutils.safe_unicode(fixed_newline)
 
97
 
 
98
 
 
99
class CommitDialog(Gtk.Dialog):
113
100
    """Implementation of Commit."""
114
101
 
115
102
    def __init__(self, wt, selected=None, parent=None):
116
 
        gtk.Dialog.__init__(self, title="Commit to %s" % wt.basedir,
117
 
                            parent=parent, flags=0,)
 
103
        super(CommitDialog, self).__init__(
 
104
            title="Commit to %s" % wt.basedir, parent=parent, flags=0)
118
105
        self.connect('delete-event', self._on_delete_window)
119
106
        self._question_dialog = question_dialog
120
107
 
121
 
        self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
 
108
        self.set_type_hint(Gdk.WindowTypeHint.NORMAL)
122
109
 
123
110
        self._wt = wt
124
111
        # TODO: Do something with this value, it is used by Olive
127
114
        self._enable_per_file_commits = True
128
115
        self._commit_all_changes = True
129
116
        self.committed_revision_id = None # Nothing has been committed yet
130
 
        self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
 
117
        self._last_selected_file = None
 
118
        self._saved_commit_messages_manager = SavedCommitMessagesManager(
 
119
            self._wt, self._wt.branch)
131
120
 
132
121
        self.setup_params()
133
122
        self.construct()
137
126
        """Setup the member variables for state."""
138
127
        self._basis_tree = self._wt.basis_tree()
139
128
        self._delta = None
140
 
        self._pending = pending_revisions(self._wt)
 
129
        self._wt.lock_read()
 
130
        try:
 
131
            self._pending = pending_revisions(self._wt)
 
132
        finally:
 
133
            self._wt.unlock()
141
134
 
142
135
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
143
136
 
195
188
 
196
189
        all_enabled = (self._selected is None)
197
190
        # The first entry is always the 'whole tree'
198
 
        all_iter = store.append([None, None, all_enabled, 'All Files', '', ''])
 
191
        all_iter = store.append(["", "", all_enabled, 'All Files', '', ''])
199
192
        initial_cursor = store.get_path(all_iter)
200
193
        # should we pass specific_files?
201
194
        self._wt.lock_read()
232
225
        # This sets the cursor, which causes the expander to close, which
233
226
        # causes the _file_message_text_view to never get realized. So we have
234
227
        # to give it a little kick, or it warns when we try to grab the focus
235
 
        self._treeview_files.set_cursor(initial_cursor)
 
228
        self._treeview_files.set_cursor(initial_cursor, None, False)
236
229
 
237
230
        def _realize_file_message_tree_view(*args):
238
231
            self._file_message_text_view.realize()
286
279
        """Build up the dialog widgets."""
287
280
        # The primary pane which splits it into left and right (adjustable)
288
281
        # sections.
289
 
        self._hpane = gtk.HPaned()
 
282
        self._hpane = Gtk.HPaned()
290
283
 
291
284
        self._construct_left_pane()
292
285
        self._construct_right_pane()
293
286
        self._construct_action_pane()
294
287
 
295
 
        self.vbox.pack_start(self._hpane)
 
288
        self.get_content_area().pack_start(self._hpane, True, True, 0)
296
289
        self._hpane.show()
297
290
        self.set_focus(self._global_message_text_view)
298
291
 
317
310
        self._hpane.set_position(300)
318
311
 
319
312
    def _construct_accelerators(self):
320
 
        group = gtk.AccelGroup()
321
 
        group.connect_group(gtk.gdk.keyval_from_name('N'),
322
 
                            gtk.gdk.CONTROL_MASK, 0, self._on_accel_next)
 
313
        group = Gtk.AccelGroup()
 
314
        group.connect(Gdk.keyval_from_name('N'),
 
315
                      Gdk.ModifierType.CONTROL_MASK, 0, self._on_accel_next)
323
316
        self.add_accel_group(group)
324
317
 
325
318
        # ignore the escape key (avoid closing the window)
326
319
        self.connect_object('close', self.emit_stop_by_name, 'close')
327
320
 
328
321
    def _construct_left_pane(self):
329
 
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=5)
 
322
        self._left_pane_box = Gtk.VBox(homogeneous=False, spacing=5)
330
323
        self._construct_file_list()
331
324
        self._construct_pending_list()
332
325
 
333
 
        self._check_local = gtk.CheckButton(_i18n("_Only commit locally"),
 
326
        self._check_local = Gtk.CheckButton(_i18n("_Only commit locally"),
334
327
                                            use_underline=True)
335
 
        self._left_pane_box.pack_end(self._check_local, False, False)
 
328
        self._left_pane_box.pack_end(self._check_local, False, False, 0)
336
329
        self._check_local.set_active(False)
337
330
 
338
331
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=False)
345
338
        # commit, and 1 for file commit, and it looked good. But I don't seem
346
339
        # to have a way to do that with the gtk boxes... :( (Which is extra
347
340
        # weird since wx uses gtk on Linux...)
348
 
        self._right_pane_table = gtk.Table(rows=10, columns=1, homogeneous=False)
 
341
        self._right_pane_table = Gtk.Table(rows=10, columns=1, homogeneous=False)
349
342
        self._right_pane_table.set_row_spacings(5)
350
343
        self._right_pane_table.set_col_spacings(5)
351
344
        self._right_pane_table_row = 0
357
350
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
358
351
 
359
352
    def _construct_action_pane(self):
360
 
        self._button_cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
 
353
        self._button_cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
361
354
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
362
355
        self._button_cancel.show()
363
 
        self.action_area.pack_end(self._button_cancel)
364
 
        self._button_commit = gtk.Button(_i18n("Comm_it"), use_underline=True)
 
356
        self.get_action_area().pack_end(
 
357
            self._button_cancel, True, True, 0)
 
358
        self._button_commit = Gtk.Button(_i18n("Comm_it"), use_underline=True)
365
359
        self._button_commit.connect('clicked', self._on_commit_clicked)
366
 
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
 
360
        self._button_commit.set_can_default(True)
367
361
        self._button_commit.show()
368
 
        self.action_area.pack_end(self._button_commit)
 
362
        self.get_action_area().pack_end(
 
363
            self._button_commit, True, True, 0)
369
364
        self._button_commit.grab_default()
370
365
 
371
366
    def _add_to_right_table(self, widget, weight, expanding=False):
377
372
        """
378
373
        end_row = self._right_pane_table_row + weight
379
374
        options = 0
380
 
        expand_opts = gtk.EXPAND | gtk.FILL | gtk.SHRINK
 
375
        expand_opts = Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK
381
376
        if expanding:
382
377
            options = expand_opts
383
378
        self._right_pane_table.attach(widget, 0, 1,
386
381
        self._right_pane_table_row = end_row
387
382
 
388
383
    def _construct_file_list(self):
389
 
        self._files_box = gtk.VBox(homogeneous=False, spacing=0)
390
 
        file_label = gtk.Label(_i18n('Files'))
 
384
        self._files_box = Gtk.VBox(homogeneous=False, spacing=0)
 
385
        file_label = Gtk.Label(label=_i18n('Files'))
391
386
        # file_label.show()
392
 
        self._files_box.pack_start(file_label, expand=False)
 
387
        self._files_box.pack_start(file_label, False, True, 0)
393
388
 
394
 
        self._commit_all_files_radio = gtk.RadioButton(
 
389
        self._commit_all_files_radio = Gtk.RadioButton.new_with_label(
395
390
            None, _i18n("Commit all changes"))
396
 
        self._files_box.pack_start(self._commit_all_files_radio, expand=False)
 
391
        self._files_box.pack_start(self._commit_all_files_radio, False, True, 0)
397
392
        self._commit_all_files_radio.show()
398
393
        self._commit_all_files_radio.connect('toggled',
399
394
            self._toggle_commit_selection)
400
 
        self._commit_selected_radio = gtk.RadioButton(
 
395
        self._commit_selected_radio = Gtk.RadioButton.new_with_label_from_widget(
401
396
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
402
 
        self._files_box.pack_start(self._commit_selected_radio, expand=False)
 
397
        self._files_box.pack_start(self._commit_selected_radio, False, True, 0)
403
398
        self._commit_selected_radio.show()
404
399
        self._commit_selected_radio.connect('toggled',
405
400
            self._toggle_commit_selection)
408
403
            self._commit_all_files_radio.set_sensitive(False)
409
404
            self._commit_selected_radio.set_sensitive(False)
410
405
 
411
 
        scroller = gtk.ScrolledWindow()
412
 
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
413
 
        self._treeview_files = gtk.TreeView()
 
406
        scroller = Gtk.ScrolledWindow()
 
407
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
 
408
        self._treeview_files = Gtk.TreeView()
414
409
        self._treeview_files.show()
415
410
        scroller.add(self._treeview_files)
416
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
 
411
        scroller.set_shadow_type(Gtk.ShadowType.IN)
417
412
        scroller.show()
418
 
        self._files_box.pack_start(scroller,
419
 
                                   expand=True, fill=True)
 
413
        self._files_box.pack_start(scroller, True, True, 0)
420
414
        self._files_box.show()
421
 
        self._left_pane_box.pack_start(self._files_box)
 
415
        self._left_pane_box.pack_start(self._files_box, True, True, 0)
422
416
 
423
417
        # Keep note that all strings stored in a ListStore must be UTF-8
424
418
        # strings. GTK does not support directly setting and restoring Unicode
425
419
        # objects.
426
 
        liststore = gtk.ListStore(
427
 
            gobject.TYPE_STRING,  # [0] file_id
428
 
            gobject.TYPE_STRING,  # [1] real path
429
 
            gobject.TYPE_BOOLEAN, # [2] checkbox
430
 
            gobject.TYPE_STRING,  # [3] display path
431
 
            gobject.TYPE_STRING,  # [4] changes type
432
 
            gobject.TYPE_STRING,  # [5] commit message
 
420
        liststore = Gtk.ListStore(
 
421
            GObject.TYPE_STRING,  # [0] file_id
 
422
            GObject.TYPE_STRING,  # [1] real path
 
423
            GObject.TYPE_BOOLEAN, # [2] checkbox
 
424
            GObject.TYPE_STRING,  # [3] display path
 
425
            GObject.TYPE_STRING,  # [4] changes type
 
426
            GObject.TYPE_STRING,  # [5] commit message
433
427
            )
434
428
        self._files_store = liststore
435
429
        self._treeview_files.set_model(liststore)
436
 
        crt = gtk.CellRendererToggle()
 
430
        crt = Gtk.CellRendererToggle()
437
431
        crt.set_property('activatable', not bool(self._pending))
438
432
        crt.connect("toggled", self._toggle_commit, self._files_store)
439
433
        if self._pending:
440
434
            name = _i18n('Commit*')
441
435
        else:
442
436
            name = _i18n('Commit')
443
 
        commit_col = gtk.TreeViewColumn(name, crt, active=2)
 
437
        commit_col = Gtk.TreeViewColumn(name, crt, active=2)
444
438
        commit_col.set_visible(False)
445
439
        self._treeview_files.append_column(commit_col)
446
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Path'),
447
 
                                           gtk.CellRendererText(), text=3))
448
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Type'),
449
 
                                           gtk.CellRendererText(), text=4))
 
440
        self._treeview_files.append_column(Gtk.TreeViewColumn(_i18n('Path'),
 
441
                                           Gtk.CellRendererText(), text=3))
 
442
        self._treeview_files.append_column(Gtk.TreeViewColumn(_i18n('Type'),
 
443
                                           Gtk.CellRendererText(), text=4))
450
444
        self._treeview_files.connect('cursor-changed',
451
445
                                     self._on_treeview_files_cursor_changed)
452
446
 
453
447
    def _toggle_commit(self, cell, path, model):
454
 
        if model[path][0] is None: # No file_id means 'All Files'
 
448
        if model[path][0] == "": # No file_id means 'All Files'
455
449
            new_val = not model[path][2]
456
450
            for node in model:
457
451
                node[2] = new_val
467
461
                checked_col.set_visible(False)
468
462
            else:
469
463
                checked_col.set_visible(True)
470
 
            renderer = checked_col.get_cell_renderers()[0]
 
464
            renderer = checked_col.get_cells()[0]
471
465
            renderer.set_property('activatable', not all_files)
472
466
 
473
467
    def _construct_pending_list(self):
474
468
        # Pending information defaults to hidden, we put it all in 1 box, so
475
469
        # that we can show/hide all of them at once
476
 
        self._pending_box = gtk.VBox()
 
470
        self._pending_box = Gtk.VBox()
477
471
        self._pending_box.hide()
478
472
 
479
 
        pending_message = gtk.Label()
 
473
        pending_message = Gtk.Label()
480
474
        pending_message.set_markup(
481
475
            _i18n('<i>* Cannot select specific files when merging</i>'))
482
 
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
 
476
        self._pending_box.pack_start(pending_message, False, True, 5)
483
477
        pending_message.show()
484
478
 
485
 
        pending_label = gtk.Label(_i18n('Pending Revisions'))
486
 
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
 
479
        pending_label = Gtk.Label(label=_i18n('Pending Revisions'))
 
480
        self._pending_box.pack_start(pending_label, False, True, 0)
487
481
        pending_label.show()
488
482
 
489
 
        scroller = gtk.ScrolledWindow()
490
 
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
491
 
        self._treeview_pending = gtk.TreeView()
 
483
        scroller = Gtk.ScrolledWindow()
 
484
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
 
485
        self._treeview_pending = Gtk.TreeView()
492
486
        scroller.add(self._treeview_pending)
493
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
 
487
        scroller.set_shadow_type(Gtk.ShadowType.IN)
494
488
        scroller.show()
495
 
        self._pending_box.pack_start(scroller,
496
 
                                     expand=True, fill=True, padding=5)
 
489
        self._pending_box.pack_start(scroller, True, True, 5)
497
490
        self._treeview_pending.show()
498
 
        self._left_pane_box.pack_start(self._pending_box)
 
491
        self._left_pane_box.pack_start(self._pending_box, True, True, 0)
499
492
 
500
 
        liststore = gtk.ListStore(gobject.TYPE_STRING, # revision_id
501
 
                                  gobject.TYPE_STRING, # date
502
 
                                  gobject.TYPE_STRING, # committer
503
 
                                  gobject.TYPE_STRING, # summary
 
493
        liststore = Gtk.ListStore(GObject.TYPE_STRING, # revision_id
 
494
                                  GObject.TYPE_STRING, # date
 
495
                                  GObject.TYPE_STRING, # committer
 
496
                                  GObject.TYPE_STRING, # summary
504
497
                                 )
505
498
        self._pending_store = liststore
506
499
        self._treeview_pending.set_model(liststore)
507
 
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Date'),
508
 
                                             gtk.CellRendererText(), text=1))
509
 
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Committer'),
510
 
                                             gtk.CellRendererText(), text=2))
511
 
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Summary'),
512
 
                                             gtk.CellRendererText(), text=3))
 
500
        self._treeview_pending.append_column(Gtk.TreeViewColumn(_i18n('Date'),
 
501
                                             Gtk.CellRendererText(), text=1))
 
502
        self._treeview_pending.append_column(Gtk.TreeViewColumn(_i18n('Committer'),
 
503
                                             Gtk.CellRendererText(), text=2))
 
504
        self._treeview_pending.append_column(Gtk.TreeViewColumn(_i18n('Summary'),
 
505
                                             Gtk.CellRendererText(), text=3))
513
506
 
514
507
    def _construct_diff_view(self):
515
 
        from diff import DiffView
 
508
        from bzrlib.plugins.gtk.diff import DiffView
516
509
 
517
510
        # TODO: jam 2007-10-30 The diff label is currently disabled. If we
518
511
        #       decide that we really don't ever want to display it, we should
519
512
        #       actually remove it, and other references to it, along with the
520
513
        #       tests that it is set properly.
521
 
        self._diff_label = gtk.Label(_i18n('Diff for whole tree'))
 
514
        self._diff_label = Gtk.Label(label=_i18n('Diff for whole tree'))
522
515
        self._diff_label.set_alignment(0, 0)
523
516
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
524
517
        self._add_to_right_table(self._diff_label, 1, False)
529
522
        self._diff_view.show()
530
523
 
531
524
    def _construct_file_message(self):
532
 
        scroller = gtk.ScrolledWindow()
533
 
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
525
        scroller = Gtk.ScrolledWindow()
 
526
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
534
527
 
535
 
        self._file_message_text_view = gtk.TextView()
 
528
        self._file_message_text_view = Gtk.TextView()
536
529
        scroller.add(self._file_message_text_view)
537
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
 
530
        scroller.set_shadow_type(Gtk.ShadowType.IN)
538
531
        scroller.show()
539
532
 
540
 
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
541
 
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
 
533
        self._file_message_text_view.modify_font(Pango.FontDescription("Monospace"))
 
534
        self._file_message_text_view.set_wrap_mode(Gtk.WrapMode.WORD)
542
535
        self._file_message_text_view.set_accepts_tab(False)
543
536
        self._file_message_text_view.show()
544
537
 
545
 
        self._file_message_expander = gtk.Expander(_i18n('File commit message'))
 
538
        self._file_message_expander = Gtk.Expander(
 
539
            label=_i18n('File commit message'))
546
540
        self._file_message_expander.set_expanded(True)
547
541
        self._file_message_expander.add(scroller)
548
542
        self._add_to_right_table(self._file_message_expander, 1, False)
549
543
        self._file_message_expander.show()
550
544
 
551
545
    def _construct_global_message(self):
552
 
        self._global_message_label = gtk.Label(_i18n('Global Commit Message'))
 
546
        self._global_message_label = Gtk.Label(label=_i18n('Global Commit Message'))
553
547
        self._global_message_label.set_markup(
554
548
            _i18n('<b>Global Commit Message</b>'))
555
549
        self._global_message_label.set_alignment(0, 0)
558
552
        # Can we remove the spacing between the label and the box?
559
553
        self._global_message_label.show()
560
554
 
561
 
        scroller = gtk.ScrolledWindow()
562
 
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
555
        scroller = Gtk.ScrolledWindow()
 
556
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
563
557
 
564
 
        self._global_message_text_view = gtk.TextView()
 
558
        self._global_message_text_view = Gtk.TextView()
565
559
        self._set_global_commit_message(self._saved_commit_messages_manager.get()[0])
566
 
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
 
560
        self._global_message_text_view.modify_font(Pango.FontDescription("Monospace"))
567
561
        scroller.add(self._global_message_text_view)
568
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
 
562
        scroller.set_shadow_type(Gtk.ShadowType.IN)
569
563
        scroller.show()
570
564
        self._add_to_right_table(scroller, 2, True)
571
 
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
 
565
        self._file_message_text_view.set_wrap_mode(Gtk.WrapMode.WORD)
572
566
        self._file_message_text_view.set_accepts_tab(False)
573
567
        self._global_message_text_view.show()
574
568
 
575
569
    def _on_treeview_files_cursor_changed(self, treeview):
576
570
        treeselection = treeview.get_selection()
 
571
        if treeselection is None:
 
572
            # The treeview was probably destroyed as the dialog closes.
 
573
            return
577
574
        (model, selection) = treeselection.get_selected()
578
575
 
579
576
        if selection is not None:
580
577
            path, display_path = model.get(selection, 1, 3)
581
578
            self._diff_label.set_text(_i18n('Diff for ') + display_path)
582
 
            if path is None:
 
579
            if path == "":
583
580
                self._diff_view.show_diff(None)
584
581
            else:
585
 
                self._diff_view.show_diff([path.decode('UTF-8')])
 
582
                self._diff_view.show_diff([osutils.safe_unicode(path)])
586
583
            self._update_per_file_info(selection)
587
584
 
588
585
    def _on_accel_next(self, accel_group, window, keyval, modifier):
599
596
            # We have either made it to the end of the list, or nothing was
600
597
            # selected. Either way, select All Files, and jump to the global
601
598
            # commit message.
602
 
            self._treeview_files.set_cursor((0,))
 
599
            self._treeview_files.set_cursor(
 
600
                Gtk.TreePath(path=0), "", False)
603
601
            self._global_message_text_view.grab_focus()
604
602
        else:
605
603
            # Set the cursor to this entry, and jump to the per-file commit
606
604
            # message
607
 
            self._treeview_files.set_cursor(model.get_path(next))
 
605
            self._treeview_files.set_cursor(model.get_path(next), None, False)
608
606
            self._file_message_text_view.grab_focus()
609
607
 
610
608
    def _save_current_file_message(self):
612
610
            return # Nothing to save
613
611
        text_buffer = self._file_message_text_view.get_buffer()
614
612
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
615
 
                                        text_buffer.get_end_iter())
 
613
                                        text_buffer.get_end_iter(), True)
616
614
        last_selected = self._files_store.get_iter(self._last_selected_file)
617
615
        self._files_store.set_value(last_selected, 5, cur_text)
618
616
 
624
622
        self._save_current_file_message()
625
623
        text_buffer = self._file_message_text_view.get_buffer()
626
624
        file_id, display_path, message = self._files_store.get(selection, 0, 3, 5)
627
 
        if file_id is None: # Whole tree
 
625
        if file_id == "": # Whole tree
628
626
            self._file_message_expander.set_label(_i18n('File commit message'))
629
627
            self._file_message_expander.set_expanded(False)
630
628
            self._file_message_expander.set_sensitive(False)
647
645
        files = []
648
646
        records = iter(self._files_store)
649
647
        rec = records.next() # Skip the All Files record
650
 
        assert rec[0] is None, "Are we skipping the wrong record?"
 
648
        assert rec[0] == "", "Are we skipping the wrong record?"
651
649
 
652
650
        file_info = []
653
651
        for record in records:
654
652
            if self._commit_all_changes or record[2]:# [2] checkbox
655
 
                file_id = record[0] # [0] file_id
656
 
                path = record[1]    # [1] real path
 
653
                file_id = osutils.safe_utf8(record[0]) # [0] file_id
 
654
                path = osutils.safe_utf8(record[1])    # [1] real path
657
655
                # [5] commit message
658
656
                file_message = _sanitize_and_decode_message(record[5])
659
657
                files.append(path.decode('UTF-8'))
689
687
                _i18n('Commit cancelled'),
690
688
                _i18n('Do you want to save your commit messages ?'),
691
689
                parent=self)
692
 
            if response == gtk.RESPONSE_NO:
 
690
            if response == Gtk.ResponseType.NO:
693
691
                 # save nothing and destroy old comments if any
694
692
                mgr = SavedCommitMessagesManager()
695
693
        mgr.save(self._wt, self._wt.branch)
696
 
        self.response(gtk.RESPONSE_CANCEL) # close window
 
694
        self.response(Gtk.ResponseType.CANCEL) # close window
697
695
 
698
696
    @show_bzr_error
699
697
    def _on_commit_clicked(self, button):
708
706
                _i18n('Commit with an empty message?'),
709
707
                _i18n('You can describe your commit intent in the message.'),
710
708
                parent=self)
711
 
            if response == gtk.RESPONSE_NO:
 
709
            if response == Gtk.ResponseType.NO:
712
710
                # Kindly give focus to message area
713
711
                self._global_message_text_view.grab_focus()
714
712
                return
730
728
                _i18n("Unknown files exist in the working tree. Commit anyway?"),
731
729
                parent=self)
732
730
                # Doesn't set a parent for the dialog..
733
 
            if response == gtk.RESPONSE_NO:
 
731
            if response == Gtk.ResponseType.NO:
734
732
                return
735
733
            break
736
734
 
751
749
                _i18n('There are no changes in the working tree.'
752
750
                      ' Do you want to commit anyway?'),
753
751
                parent=self)
754
 
            if response == gtk.RESPONSE_YES:
 
752
            if response == Gtk.ResponseType.YES:
755
753
                rev_id = self._wt.commit(message,
756
754
                               allow_pointless=True,
757
755
                               strict=False,
761
759
        self.committed_revision_id = rev_id
762
760
        # destroy old comments if any
763
761
        SavedCommitMessagesManager().save(self._wt, self._wt.branch)
764
 
        self.response(gtk.RESPONSE_OK)
 
762
        self.response(Gtk.ResponseType.OK)
765
763
 
766
764
    def _get_global_commit_message(self):
767
765
        buf = self._global_message_text_view.get_buffer()
768
766
        start, end = buf.get_bounds()
769
 
        text = buf.get_text(start, end)
 
767
        text = buf.get_text(start, end, True)
770
768
        return _sanitize_and_decode_message(text)
771
769
 
772
770
    def _set_global_commit_message(self, message):
795
793
        rev_dict['revision_id'] = rev.revision_id
796
794
        return rev_dict
797
795
 
798
 
 
799
 
class SavedCommitMessagesManager:
800
 
    """Save glogal and per-file commit messages.
801
 
 
802
 
    Saves global commit message and utf-8 file_id->message dictionary
803
 
    of per-file commit messages on disk. Re-reads them later for re-using.
804
 
    """
805
 
 
806
 
    def __init__(self, tree=None, branch=None):
807
 
        """If branch is None, builds empty messages, otherwise reads them
808
 
        from branch's disk storage. 'tree' argument is for the future."""
809
 
        if branch is None:
810
 
            self.global_message = u''
811
 
            self.file_messages = {}
812
 
        else:
813
 
            config = branch.get_config()
814
 
            self.global_message = config.get_user_option(
815
 
                'gtk_global_commit_message')
816
 
            if self.global_message is None:
817
 
                self.global_message = u''
818
 
            file_messages = config.get_user_option('gtk_file_commit_messages')
819
 
            if file_messages: # unicode and B-encoded:
820
 
                self.file_messages = bencode.bdecode(
821
 
                    file_messages.encode('UTF-8'))
822
 
            else:
823
 
                self.file_messages = {}
824
 
 
825
 
    def get(self):
826
 
        return self.global_message, self.file_messages
827
 
 
828
 
    def is_not_empty(self):
829
 
        return bool(self.global_message or self.file_messages)
830
 
 
831
 
    def insert(self, global_message, file_info):
832
 
        """Formats per-file commit messages (list of dictionaries, one per file)
833
 
        into one utf-8 file_id->message dictionary and merges this with
834
 
        previously existing dictionary. Merges global commit message too."""
835
 
        file_messages = {}
836
 
        for fi in file_info:
837
 
            file_message = fi['message']
838
 
            if file_message:
839
 
                file_messages[fi['file_id']] = file_message # utf-8 strings
840
 
        for k,v in file_messages.iteritems():
841
 
            try:
842
 
                self.file_messages[k] = v + '\n******\n' + self.file_messages[k]
843
 
            except KeyError:
844
 
                self.file_messages[k] = v
845
 
        if self.global_message:
846
 
            self.global_message = global_message + '\n******\n' \
847
 
                + self.global_message
848
 
        else:
849
 
            self.global_message = global_message
850
 
 
851
 
    def save(self, tree, branch):
852
 
        # We store in branch's config, which can be a problem if two gcommit
853
 
        # are done in two checkouts of one single branch (comments overwrite
854
 
        # each other). Ideally should be in working tree. But uncommit does
855
 
        # not always have a working tree, though it always has a branch.
856
 
        # 'tree' argument is for the future
857
 
        config = branch.get_config()
858
 
        # should it be named "gtk_" or some more neutral name ("gui_" ?) to
859
 
        # be compatible with qbzr in the future?
860
 
        config.set_user_option('gtk_global_commit_message', self.global_message)
861
 
        # bencode() does not know unicode objects but set_user_option()
862
 
        # requires one:
863
 
        config.set_user_option(
864
 
            'gtk_file_commit_messages',
865
 
            bencode.bencode(self.file_messages).decode('UTF-8'))
866
 
 
867
 
 
868
 
def save_commit_messages(local, master, old_revno, old_revid,
869
 
                         new_revno, new_revid):
870
 
    b = local
871
 
    if b is None:
872
 
        b = master
873
 
    mgr = SavedCommitMessagesManager(None, b)
874
 
    revid_iterator = b.repository.iter_reverse_revision_history(old_revid)
875
 
    cur_revno = old_revno
876
 
    new_revision_id = old_revid
877
 
    graph = b.repository.get_graph()
878
 
    for rev_id in revid_iterator:
879
 
        if cur_revno == new_revno:
880
 
            break
881
 
        cur_revno -= 1
882
 
        rev = b.repository.get_revision(rev_id)
883
 
        file_info = rev.properties.get('file-info', None)
884
 
        if file_info is None:
885
 
            file_info = {}
886
 
        else:
887
 
            file_info = bencode.bdecode(file_info.encode('UTF-8'))
888
 
        global_message = osutils.safe_unicode(rev.message)
889
 
        # Concatenate comment of the uncommitted revision
890
 
        mgr.insert(global_message, file_info)
891
 
 
892
 
        parents = graph.get_parent_map([rev_id]).get(rev_id, None)
893
 
        if not parents:
894
 
            continue
895
 
    mgr.save(None, b)