/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: Jelmer Vernooij
  • Date: 2008-06-29 16:20:15 UTC
  • mto: This revision was merged to the branch mainline in revision 519.
  • Revision ID: jelmer@samba.org-20080629162015-amhe7xj4cdmup4id
Rename GtkProgressBarStack to GtkWindowProgressBarStack

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 re
18
 
 
19
17
try:
20
18
    import pygtk
21
19
    pygtk.require("2.0")
22
20
except:
23
21
    pass
24
22
 
25
 
from gi.repository import Gtk
26
 
from gi.repository import GObject
27
 
from gi.repository import Pango
28
 
 
29
 
from bzrlib import (
30
 
    errors,
31
 
    osutils,
32
 
    trace,
33
 
    )
34
 
try:
35
 
    from bzrlib import bencode
36
 
except ImportError:
37
 
    from bzrlib.util import bencode
38
 
 
39
 
from bzrlib.plugins.gtk.dialog import question_dialog
40
 
from bzrlib.plugins.gtk.errors import show_bzr_error
41
 
from bzrlib.plugins.gtk.i18n import _i18n
 
23
import gtk
 
24
import gobject
 
25
import pango
 
26
 
 
27
import os.path
 
28
import re
 
29
 
 
30
from bzrlib import errors, osutils
 
31
from bzrlib.trace import mutter
 
32
from bzrlib.util import bencode
 
33
 
 
34
from bzrlib.plugins.gtk import _i18n
 
35
from dialog import error_dialog, question_dialog
 
36
from errors import show_bzr_error
42
37
 
43
38
try:
44
39
    import dbus
102
97
    return pm
103
98
 
104
99
 
105
 
_newline_variants_re = re.compile(r'\r\n?')
106
 
def _sanitize_and_decode_message(utf8_message):
107
 
    """Turn a utf-8 message into a sanitized Unicode message."""
108
 
    fixed_newline = _newline_variants_re.sub('\n', utf8_message)
109
 
    return fixed_newline.decode('utf-8')
110
 
 
111
 
 
112
 
class CommitDialog(Gtk.Dialog):
 
100
class CommitDialog(gtk.Dialog):
113
101
    """Implementation of Commit."""
114
102
 
115
103
    def __init__(self, wt, selected=None, parent=None):
116
 
        GObject.GObject.__init__(self, title="Commit to %s" % wt.basedir,
117
 
                            parent=parent, flags=0,)
118
 
        self.connect('delete-event', self._on_delete_window)
 
104
        gtk.Dialog.__init__(self, title="Commit - Olive",
 
105
                                  parent=parent,
 
106
                                  flags=0,
 
107
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
119
108
        self._question_dialog = question_dialog
120
109
 
121
 
        self.set_type_hint(Gdk.WindowTypeHint.NORMAL)
122
 
 
123
110
        self._wt = wt
124
111
        # TODO: Do something with this value, it is used by Olive
125
112
        #       It used to set all changes but this one to False
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)
131
117
 
132
118
        self.setup_params()
133
119
        self.construct()
202
188
        self._basis_tree.lock_read()
203
189
        try:
204
190
            from diff import iter_changes_to_status
205
 
            saved_file_messages = self._saved_commit_messages_manager.get()[1]
206
191
            for (file_id, real_path, change_type, display_path
207
192
                ) in iter_changes_to_status(self._basis_tree, self._wt):
208
193
                if self._selected and real_path != self._selected:
209
194
                    enabled = False
210
195
                else:
211
196
                    enabled = True
212
 
                try:
213
 
                    default_message = saved_file_messages[file_id]
214
 
                except KeyError:
215
 
                    default_message = ''
216
197
                item_iter = store.append([
217
198
                    file_id,
218
199
                    real_path.encode('UTF-8'),
219
200
                    enabled,
220
201
                    display_path.encode('UTF-8'),
221
202
                    change_type,
222
 
                    default_message, # Initial comment
 
203
                    '', # Initial comment
223
204
                    ])
224
205
                if self._selected and enabled:
225
206
                    initial_cursor = store.get_path(item_iter)
251
232
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
252
233
                                           '/org/freedesktop/NetworkManager')
253
234
            except dbus.DBusException:
254
 
                trace.mutter("networkmanager not available.")
 
235
                mutter("networkmanager not available.")
255
236
                self._check_local.show()
256
237
                return
257
238
            
263
244
            except dbus.DBusException, e:
264
245
                # Silently drop errors. While DBus may be
265
246
                # available, NetworkManager doesn't necessarily have to be
266
 
                trace.mutter("unable to get networkmanager state: %r" % e)
 
247
                mutter("unable to get networkmanager state: %r" % e)
267
248
        self._check_local.show()
268
249
 
269
250
    def _fill_in_per_file_info(self):
286
267
        """Build up the dialog widgets."""
287
268
        # The primary pane which splits it into left and right (adjustable)
288
269
        # sections.
289
 
        self._hpane = Gtk.HPaned()
 
270
        self._hpane = gtk.HPaned()
290
271
 
291
272
        self._construct_left_pane()
292
273
        self._construct_right_pane()
293
274
        self._construct_action_pane()
294
275
 
295
 
        self.vbox.pack_start(self._hpane, True, True, 0)
 
276
        self.vbox.pack_start(self._hpane)
296
277
        self._hpane.show()
297
278
        self.set_focus(self._global_message_text_view)
298
279
 
317
298
        self._hpane.set_position(300)
318
299
 
319
300
    def _construct_accelerators(self):
320
 
        group = Gtk.AccelGroup()
321
 
        group.connect_group(Gdk.keyval_from_name('N'),
322
 
                            Gdk.EventMask.CONTROL_MASK, 0, self._on_accel_next)
 
301
        group = gtk.AccelGroup()
 
302
        group.connect_group(gtk.gdk.keyval_from_name('N'),
 
303
                            gtk.gdk.CONTROL_MASK, 0, self._on_accel_next)
323
304
        self.add_accel_group(group)
324
305
 
325
306
        # ignore the escape key (avoid closing the window)
326
307
        self.connect_object('close', self.emit_stop_by_name, 'close')
327
308
 
328
309
    def _construct_left_pane(self):
329
 
        self._left_pane_box = Gtk.VBox(homogeneous=False, spacing=5)
 
310
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=5)
330
311
        self._construct_file_list()
331
312
        self._construct_pending_list()
332
313
 
333
 
        self._check_local = Gtk.CheckButton(_i18n("_Only commit locally"),
 
314
        self._check_local = gtk.CheckButton(_i18n("_Only commit locally"),
334
315
                                            use_underline=True)
335
316
        self._left_pane_box.pack_end(self._check_local, False, False)
336
317
        self._check_local.set_active(False)
345
326
        # commit, and 1 for file commit, and it looked good. But I don't seem
346
327
        # to have a way to do that with the gtk boxes... :( (Which is extra
347
328
        # weird since wx uses gtk on Linux...)
348
 
        self._right_pane_table = Gtk.Table(rows=10, columns=1, homogeneous=False)
 
329
        self._right_pane_table = gtk.Table(rows=10, columns=1, homogeneous=False)
349
330
        self._right_pane_table.set_row_spacings(5)
350
331
        self._right_pane_table.set_col_spacings(5)
351
332
        self._right_pane_table_row = 0
357
338
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
358
339
 
359
340
    def _construct_action_pane(self):
360
 
        self._button_cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
361
 
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
362
 
        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)
 
341
        self._button_commit = gtk.Button(_i18n("Comm_it"), use_underline=True)
365
342
        self._button_commit.connect('clicked', self._on_commit_clicked)
366
 
        self._button_commit.set_can_default(True)
 
343
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
367
344
        self._button_commit.show()
368
345
        self.action_area.pack_end(self._button_commit)
369
346
        self._button_commit.grab_default()
377
354
        """
378
355
        end_row = self._right_pane_table_row + weight
379
356
        options = 0
380
 
        expand_opts = Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK
 
357
        expand_opts = gtk.EXPAND | gtk.FILL | gtk.SHRINK
381
358
        if expanding:
382
359
            options = expand_opts
383
360
        self._right_pane_table.attach(widget, 0, 1,
386
363
        self._right_pane_table_row = end_row
387
364
 
388
365
    def _construct_file_list(self):
389
 
        self._files_box = Gtk.VBox(homogeneous=False, spacing=0)
390
 
        file_label = Gtk.Label(label=_i18n('Files'))
 
366
        self._files_box = gtk.VBox(homogeneous=False, spacing=0)
 
367
        file_label = gtk.Label(_i18n('Files'))
391
368
        # file_label.show()
392
 
        self._files_box.pack_start(file_label, False, True, 0)
 
369
        self._files_box.pack_start(file_label, expand=False)
393
370
 
394
 
        self._commit_all_files_radio = Gtk.RadioButton(
 
371
        self._commit_all_files_radio = gtk.RadioButton(
395
372
            None, _i18n("Commit all changes"))
396
 
        self._files_box.pack_start(self._commit_all_files_radio, False, True, 0)
 
373
        self._files_box.pack_start(self._commit_all_files_radio, expand=False)
397
374
        self._commit_all_files_radio.show()
398
375
        self._commit_all_files_radio.connect('toggled',
399
376
            self._toggle_commit_selection)
400
 
        self._commit_selected_radio = Gtk.RadioButton(
 
377
        self._commit_selected_radio = gtk.RadioButton(
401
378
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
402
 
        self._files_box.pack_start(self._commit_selected_radio, False, True, 0)
 
379
        self._files_box.pack_start(self._commit_selected_radio, expand=False)
403
380
        self._commit_selected_radio.show()
404
381
        self._commit_selected_radio.connect('toggled',
405
382
            self._toggle_commit_selection)
408
385
            self._commit_all_files_radio.set_sensitive(False)
409
386
            self._commit_selected_radio.set_sensitive(False)
410
387
 
411
 
        scroller = Gtk.ScrolledWindow()
412
 
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
413
 
        self._treeview_files = Gtk.TreeView()
 
388
        scroller = gtk.ScrolledWindow()
 
389
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
390
        self._treeview_files = gtk.TreeView()
414
391
        self._treeview_files.show()
415
392
        scroller.add(self._treeview_files)
416
 
        scroller.set_shadow_type(Gtk.ShadowType.IN)
 
393
        scroller.set_shadow_type(gtk.SHADOW_IN)
417
394
        scroller.show()
418
395
        self._files_box.pack_start(scroller,
419
396
                                   expand=True, fill=True)
420
397
        self._files_box.show()
421
 
        self._left_pane_box.pack_start(self._files_box, True, True, 0)
 
398
        self._left_pane_box.pack_start(self._files_box)
422
399
 
423
400
        # Keep note that all strings stored in a ListStore must be UTF-8
424
401
        # strings. GTK does not support directly setting and restoring Unicode
425
402
        # 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
 
403
        liststore = gtk.ListStore(
 
404
            gobject.TYPE_STRING,  # [0] file_id
 
405
            gobject.TYPE_STRING,  # [1] real path
 
406
            gobject.TYPE_BOOLEAN, # [2] checkbox
 
407
            gobject.TYPE_STRING,  # [3] display path
 
408
            gobject.TYPE_STRING,  # [4] changes type
 
409
            gobject.TYPE_STRING,  # [5] commit message
433
410
            )
434
411
        self._files_store = liststore
435
412
        self._treeview_files.set_model(liststore)
436
 
        crt = Gtk.CellRendererToggle()
 
413
        crt = gtk.CellRendererToggle()
437
414
        crt.set_property('activatable', not bool(self._pending))
438
415
        crt.connect("toggled", self._toggle_commit, self._files_store)
439
416
        if self._pending:
440
417
            name = _i18n('Commit*')
441
418
        else:
442
419
            name = _i18n('Commit')
443
 
        commit_col = Gtk.TreeViewColumn(name, crt, active=2)
 
420
        commit_col = gtk.TreeViewColumn(name, crt, active=2)
444
421
        commit_col.set_visible(False)
445
422
        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))
 
423
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Path'),
 
424
                                           gtk.CellRendererText(), text=3))
 
425
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Type'),
 
426
                                           gtk.CellRendererText(), text=4))
450
427
        self._treeview_files.connect('cursor-changed',
451
428
                                     self._on_treeview_files_cursor_changed)
452
429
 
473
450
    def _construct_pending_list(self):
474
451
        # Pending information defaults to hidden, we put it all in 1 box, so
475
452
        # that we can show/hide all of them at once
476
 
        self._pending_box = Gtk.VBox()
 
453
        self._pending_box = gtk.VBox()
477
454
        self._pending_box.hide()
478
455
 
479
 
        pending_message = Gtk.Label()
 
456
        pending_message = gtk.Label()
480
457
        pending_message.set_markup(
481
458
            _i18n('<i>* Cannot select specific files when merging</i>'))
482
459
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
483
460
        pending_message.show()
484
461
 
485
 
        pending_label = Gtk.Label(label=_i18n('Pending Revisions'))
 
462
        pending_label = gtk.Label(_i18n('Pending Revisions'))
486
463
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
487
464
        pending_label.show()
488
465
 
489
 
        scroller = Gtk.ScrolledWindow()
490
 
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
491
 
        self._treeview_pending = Gtk.TreeView()
 
466
        scroller = gtk.ScrolledWindow()
 
467
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
468
        self._treeview_pending = gtk.TreeView()
492
469
        scroller.add(self._treeview_pending)
493
 
        scroller.set_shadow_type(Gtk.ShadowType.IN)
 
470
        scroller.set_shadow_type(gtk.SHADOW_IN)
494
471
        scroller.show()
495
472
        self._pending_box.pack_start(scroller,
496
473
                                     expand=True, fill=True, padding=5)
497
474
        self._treeview_pending.show()
498
 
        self._left_pane_box.pack_start(self._pending_box, True, True, 0)
 
475
        self._left_pane_box.pack_start(self._pending_box)
499
476
 
500
 
        liststore = Gtk.ListStore(GObject.TYPE_STRING, # revision_id
501
 
                                  GObject.TYPE_STRING, # date
502
 
                                  GObject.TYPE_STRING, # committer
503
 
                                  GObject.TYPE_STRING, # summary
 
477
        liststore = gtk.ListStore(gobject.TYPE_STRING, # revision_id
 
478
                                  gobject.TYPE_STRING, # date
 
479
                                  gobject.TYPE_STRING, # committer
 
480
                                  gobject.TYPE_STRING, # summary
504
481
                                 )
505
482
        self._pending_store = liststore
506
483
        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))
 
484
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Date'),
 
485
                                             gtk.CellRendererText(), text=1))
 
486
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Committer'),
 
487
                                             gtk.CellRendererText(), text=2))
 
488
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Summary'),
 
489
                                             gtk.CellRendererText(), text=3))
513
490
 
514
491
    def _construct_diff_view(self):
515
 
        from bzrlib.plugins.gtk.diff import DiffView
 
492
        from diff import DiffView
516
493
 
517
494
        # TODO: jam 2007-10-30 The diff label is currently disabled. If we
518
495
        #       decide that we really don't ever want to display it, we should
519
496
        #       actually remove it, and other references to it, along with the
520
497
        #       tests that it is set properly.
521
 
        self._diff_label = Gtk.Label(label=_i18n('Diff for whole tree'))
 
498
        self._diff_label = gtk.Label(_i18n('Diff for whole tree'))
522
499
        self._diff_label.set_alignment(0, 0)
523
500
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
524
501
        self._add_to_right_table(self._diff_label, 1, False)
529
506
        self._diff_view.show()
530
507
 
531
508
    def _construct_file_message(self):
532
 
        scroller = Gtk.ScrolledWindow()
533
 
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
 
509
        scroller = gtk.ScrolledWindow()
 
510
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
534
511
 
535
 
        self._file_message_text_view = Gtk.TextView()
 
512
        self._file_message_text_view = gtk.TextView()
536
513
        scroller.add(self._file_message_text_view)
537
 
        scroller.set_shadow_type(Gtk.ShadowType.IN)
 
514
        scroller.set_shadow_type(gtk.SHADOW_IN)
538
515
        scroller.show()
539
516
 
540
 
        self._file_message_text_view.modify_font(Pango.FontDescription("Monospace"))
541
 
        self._file_message_text_view.set_wrap_mode(Gtk.WrapMode.WORD)
 
517
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
 
518
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
542
519
        self._file_message_text_view.set_accepts_tab(False)
543
520
        self._file_message_text_view.show()
544
521
 
545
 
        self._file_message_expander = Gtk.Expander(_i18n('File commit message'))
 
522
        self._file_message_expander = gtk.Expander(_i18n('File commit message'))
546
523
        self._file_message_expander.set_expanded(True)
547
524
        self._file_message_expander.add(scroller)
548
525
        self._add_to_right_table(self._file_message_expander, 1, False)
549
526
        self._file_message_expander.show()
550
527
 
551
528
    def _construct_global_message(self):
552
 
        self._global_message_label = Gtk.Label(label=_i18n('Global Commit Message'))
 
529
        self._global_message_label = gtk.Label(_i18n('Global Commit Message'))
553
530
        self._global_message_label.set_markup(
554
531
            _i18n('<b>Global Commit Message</b>'))
555
532
        self._global_message_label.set_alignment(0, 0)
558
535
        # Can we remove the spacing between the label and the box?
559
536
        self._global_message_label.show()
560
537
 
561
 
        scroller = Gtk.ScrolledWindow()
562
 
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
 
538
        scroller = gtk.ScrolledWindow()
 
539
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
563
540
 
564
 
        self._global_message_text_view = Gtk.TextView()
565
 
        self._set_global_commit_message(self._saved_commit_messages_manager.get()[0])
566
 
        self._global_message_text_view.modify_font(Pango.FontDescription("Monospace"))
 
541
        self._global_message_text_view = gtk.TextView()
 
542
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
567
543
        scroller.add(self._global_message_text_view)
568
 
        scroller.set_shadow_type(Gtk.ShadowType.IN)
 
544
        scroller.set_shadow_type(gtk.SHADOW_IN)
569
545
        scroller.show()
570
546
        self._add_to_right_table(scroller, 2, True)
571
 
        self._file_message_text_view.set_wrap_mode(Gtk.WrapMode.WORD)
 
547
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
572
548
        self._file_message_text_view.set_accepts_tab(False)
573
549
        self._global_message_text_view.show()
574
550
 
654
630
            if self._commit_all_changes or record[2]:# [2] checkbox
655
631
                file_id = record[0] # [0] file_id
656
632
                path = record[1]    # [1] real path
657
 
                # [5] commit message
658
 
                file_message = _sanitize_and_decode_message(record[5])
 
633
                file_message = record[5] # [5] commit message
659
634
                files.append(path.decode('UTF-8'))
660
635
                if self._enable_per_file_commits and file_message:
661
636
                    # All of this needs to be utf-8 information
662
 
                    file_message = file_message.encode('UTF-8')
663
637
                    file_info.append({'path':path, 'file_id':file_id,
664
638
                                     'message':file_message})
665
639
        file_info.sort(key=lambda x:(x['path'], x['file_id']))
669
643
            return files, []
670
644
 
671
645
    @show_bzr_error
672
 
    def _on_cancel_clicked(self, button):
673
 
        """ Cancel button clicked handler. """
674
 
        self._do_cancel()
675
 
 
676
 
    @show_bzr_error
677
 
    def _on_delete_window(self, source, event):
678
 
        """ Delete window handler. """
679
 
        self._do_cancel()
680
 
 
681
 
    def _do_cancel(self):
682
 
        """If requested, saves commit messages when cancelling gcommit; they are re-used by a next gcommit"""
683
 
        mgr = SavedCommitMessagesManager()
684
 
        self._saved_commit_messages_manager = mgr
685
 
        mgr.insert(self._get_global_commit_message(),
686
 
                   self._get_specific_files()[1])
687
 
        if mgr.is_not_empty(): # maybe worth saving
688
 
            response = self._question_dialog(
689
 
                _i18n('Commit cancelled'),
690
 
                _i18n('Do you want to save your commit messages ?'),
691
 
                parent=self)
692
 
            if response == Gtk.ResponseType.NO:
693
 
                 # save nothing and destroy old comments if any
694
 
                mgr = SavedCommitMessagesManager()
695
 
        mgr.save(self._wt, self._wt.branch)
696
 
        self.response(Gtk.ResponseType.CANCEL) # close window
697
 
 
698
 
    @show_bzr_error
699
646
    def _on_commit_clicked(self, button):
700
647
        """ Commit button clicked handler. """
701
648
        self._do_commit()
706
653
        if message == '':
707
654
            response = self._question_dialog(
708
655
                _i18n('Commit with an empty message?'),
709
 
                _i18n('You can describe your commit intent in the message.'),
710
 
                parent=self)
711
 
            if response == Gtk.ResponseType.NO:
 
656
                _i18n('You can describe your commit intent in the message.'))
 
657
            if response == gtk.RESPONSE_NO:
712
658
                # Kindly give focus to message area
713
659
                self._global_message_text_view.grab_focus()
714
660
                return
727
673
        for path in self._wt.unknowns():
728
674
            response = self._question_dialog(
729
675
                _i18n("Commit with unknowns?"),
730
 
                _i18n("Unknown files exist in the working tree. Commit anyway?"),
731
 
                parent=self)
732
 
                # Doesn't set a parent for the dialog..
733
 
            if response == Gtk.ResponseType.NO:
 
676
                _i18n("Unknown files exist in the working tree. Commit anyway?"))
 
677
            if response == gtk.RESPONSE_NO:
734
678
                return
735
679
            break
736
680
 
749
693
            response = self._question_dialog(
750
694
                _i18n('Commit with no changes?'),
751
695
                _i18n('There are no changes in the working tree.'
752
 
                      ' Do you want to commit anyway?'),
753
 
                parent=self)
754
 
            if response == Gtk.ResponseType.YES:
 
696
                      ' Do you want to commit anyway?'))
 
697
            if response == gtk.RESPONSE_YES:
755
698
                rev_id = self._wt.commit(message,
756
699
                               allow_pointless=True,
757
700
                               strict=False,
759
702
                               specific_files=specific_files,
760
703
                               revprops=revprops)
761
704
        self.committed_revision_id = rev_id
762
 
        # destroy old comments if any
763
 
        SavedCommitMessagesManager().save(self._wt, self._wt.branch)
764
 
        self.response(Gtk.ResponseType.OK)
 
705
        self.response(gtk.RESPONSE_OK)
765
706
 
766
707
    def _get_global_commit_message(self):
767
708
        buf = self._global_message_text_view.get_buffer()
768
709
        start, end = buf.get_bounds()
769
 
        text = buf.get_text(start, end)
770
 
        return _sanitize_and_decode_message(text)
 
710
        return buf.get_text(start, end).decode('utf-8')
771
711
 
772
712
    def _set_global_commit_message(self, message):
773
713
        """Just a helper for the test suite."""
794
734
                                       show_offset=False)
795
735
        rev_dict['revision_id'] = rev.revision_id
796
736
        return rev_dict
797
 
 
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)