/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: Mark Lee
  • Date: 2009-07-11 18:39:14 UTC
  • mto: This revision was merged to the branch mainline in revision 661.
  • Revision ID: bzr@lazymalevolence.com-20090711183914-zuii3et5skiv2njo
Re-ignore credits.pickle.

Show diffs side-by-side

added added

removed removed

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