/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: 2011-07-31 16:50:29 UTC
  • mto: This revision was merged to the branch mainline in revision 741.
  • Revision ID: sinzui.is@verizon.net-20110731165029-9gixuqypi3lwapzm
Removed import_pygtk because gi does not impicitly call Main(). Inlined checks for gtk availablility.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import re
18
18
 
19
 
from gi.repository import Gdk
20
19
from gi.repository import Gtk
21
20
from gi.repository import GObject
22
21
from gi.repository import Pango
23
22
 
24
 
try:
25
 
    from bzrlib import bencode
26
 
except ImportError:
27
 
    from bzrlib.util import bencode
28
 
 
29
23
from bzrlib import (
30
24
    errors,
 
25
    osutils,
31
26
    trace,
32
27
    )
 
28
try:
 
29
    from bzrlib import bencode
 
30
except ImportError:
 
31
    from bzrlib.util import bencode
 
32
 
33
33
from bzrlib.plugins.gtk.dialog import question_dialog
34
34
from bzrlib.plugins.gtk.errors import show_bzr_error
35
35
from bzrlib.plugins.gtk.i18n import _i18n
36
 
from bzrlib.plugins.gtk.commitmsgs import SavedCommitMessagesManager
37
36
 
38
37
try:
39
38
    import dbus
62
61
    last_revision = parents[0]
63
62
 
64
63
    if last_revision is not None:
65
 
        graph = branch.repository.get_graph()
66
 
        ignore = set([r for r,ps in graph.iter_ancestry([last_revision])])
 
64
        try:
 
65
            ignore = set(branch.repository.get_ancestry(last_revision,
 
66
                                                        topo_sorted=False))
 
67
        except errors.NoSuchRevision:
 
68
            # the last revision is a ghost : assume everything is new
 
69
            # except for it
 
70
            ignore = set([None, last_revision])
67
71
    else:
68
 
        ignore = set([])
 
72
        ignore = set([None])
69
73
 
70
74
    pm = []
71
75
    for merge in pending:
103
107
    """Implementation of Commit."""
104
108
 
105
109
    def __init__(self, wt, selected=None, parent=None):
106
 
        super(CommitDialog, self).__init__(
107
 
            title="Commit to %s" % wt.basedir, parent=parent, flags=0)
 
110
        GObject.GObject.__init__(self, title="Commit to %s" % wt.basedir,
 
111
                            parent=parent, flags=0,)
108
112
        self.connect('delete-event', self._on_delete_window)
109
113
        self._question_dialog = question_dialog
110
114
 
117
121
        self._enable_per_file_commits = True
118
122
        self._commit_all_changes = True
119
123
        self.committed_revision_id = None # Nothing has been committed yet
120
 
        self._saved_commit_messages_manager = SavedCommitMessagesManager(
121
 
            self._wt, self._wt.branch)
 
124
        self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
122
125
 
123
126
        self.setup_params()
124
127
        self.construct()
128
131
        """Setup the member variables for state."""
129
132
        self._basis_tree = self._wt.basis_tree()
130
133
        self._delta = None
131
 
        self._wt.lock_read()
132
 
        try:
133
 
            self._pending = pending_revisions(self._wt)
134
 
        finally:
135
 
            self._wt.unlock()
 
134
        self._pending = pending_revisions(self._wt)
136
135
 
137
136
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
138
137
 
227
226
        # This sets the cursor, which causes the expander to close, which
228
227
        # causes the _file_message_text_view to never get realized. So we have
229
228
        # to give it a little kick, or it warns when we try to grab the focus
230
 
        self._treeview_files.set_cursor(initial_cursor, None, False)
 
229
        self._treeview_files.set_cursor(initial_cursor)
231
230
 
232
231
        def _realize_file_message_tree_view(*args):
233
232
            self._file_message_text_view.realize()
287
286
        self._construct_right_pane()
288
287
        self._construct_action_pane()
289
288
 
290
 
        self.get_content_area().pack_start(self._hpane, True, True, 0)
 
289
        self.vbox.pack_start(self._hpane, True, True, 0)
291
290
        self._hpane.show()
292
291
        self.set_focus(self._global_message_text_view)
293
292
 
313
312
 
314
313
    def _construct_accelerators(self):
315
314
        group = Gtk.AccelGroup()
316
 
        group.connect(Gdk.keyval_from_name('N'),
317
 
                      Gdk.ModifierType.CONTROL_MASK, 0, self._on_accel_next)
 
315
        group.connect_group(Gdk.keyval_from_name('N'),
 
316
                            Gdk.EventMask.CONTROL_MASK, 0, self._on_accel_next)
318
317
        self.add_accel_group(group)
319
318
 
320
319
        # ignore the escape key (avoid closing the window)
327
326
 
328
327
        self._check_local = Gtk.CheckButton(_i18n("_Only commit locally"),
329
328
                                            use_underline=True)
330
 
        self._left_pane_box.pack_end(self._check_local, False, False, 0)
 
329
        self._left_pane_box.pack_end(self._check_local, False, False)
331
330
        self._check_local.set_active(False)
332
331
 
333
332
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=False)
355
354
        self._button_cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
356
355
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
357
356
        self._button_cancel.show()
358
 
        self.get_action_area().pack_end(
359
 
            self._button_cancel, True, True, 0)
 
357
        self.action_area.pack_end(self._button_cancel)
360
358
        self._button_commit = Gtk.Button(_i18n("Comm_it"), use_underline=True)
361
359
        self._button_commit.connect('clicked', self._on_commit_clicked)
362
360
        self._button_commit.set_can_default(True)
363
361
        self._button_commit.show()
364
 
        self.get_action_area().pack_end(
365
 
            self._button_commit, True, True, 0)
 
362
        self.action_area.pack_end(self._button_commit)
366
363
        self._button_commit.grab_default()
367
364
 
368
365
    def _add_to_right_table(self, widget, weight, expanding=False):
388
385
        # file_label.show()
389
386
        self._files_box.pack_start(file_label, False, True, 0)
390
387
 
391
 
        self._commit_all_files_radio = Gtk.RadioButton.new_with_label(
 
388
        self._commit_all_files_radio = Gtk.RadioButton(
392
389
            None, _i18n("Commit all changes"))
393
390
        self._files_box.pack_start(self._commit_all_files_radio, False, True, 0)
394
391
        self._commit_all_files_radio.show()
395
392
        self._commit_all_files_radio.connect('toggled',
396
393
            self._toggle_commit_selection)
397
 
        self._commit_selected_radio = Gtk.RadioButton.new_with_label_from_widget(
 
394
        self._commit_selected_radio = Gtk.RadioButton(
398
395
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
399
396
        self._files_box.pack_start(self._commit_selected_radio, False, True, 0)
400
397
        self._commit_selected_radio.show()
412
409
        scroller.add(self._treeview_files)
413
410
        scroller.set_shadow_type(Gtk.ShadowType.IN)
414
411
        scroller.show()
415
 
        self._files_box.pack_start(scroller, True, True, 0)
 
412
        self._files_box.pack_start(scroller,
 
413
                                   expand=True, fill=True)
416
414
        self._files_box.show()
417
415
        self._left_pane_box.pack_start(self._files_box, True, True, 0)
418
416
 
463
461
                checked_col.set_visible(False)
464
462
            else:
465
463
                checked_col.set_visible(True)
466
 
            renderer = checked_col.get_cells()[0]
 
464
            renderer = checked_col.get_cell_renderers()[0]
467
465
            renderer.set_property('activatable', not all_files)
468
466
 
469
467
    def _construct_pending_list(self):
475
473
        pending_message = Gtk.Label()
476
474
        pending_message.set_markup(
477
475
            _i18n('<i>* Cannot select specific files when merging</i>'))
478
 
        self._pending_box.pack_start(pending_message, False, True, 5)
 
476
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
479
477
        pending_message.show()
480
478
 
481
479
        pending_label = Gtk.Label(label=_i18n('Pending Revisions'))
482
 
        self._pending_box.pack_start(pending_label, False, True, 0)
 
480
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
483
481
        pending_label.show()
484
482
 
485
483
        scroller = Gtk.ScrolledWindow()
488
486
        scroller.add(self._treeview_pending)
489
487
        scroller.set_shadow_type(Gtk.ShadowType.IN)
490
488
        scroller.show()
491
 
        self._pending_box.pack_start(scroller, True, True, 5)
 
489
        self._pending_box.pack_start(scroller,
 
490
                                     expand=True, fill=True, padding=5)
492
491
        self._treeview_pending.show()
493
492
        self._left_pane_box.pack_start(self._pending_box, True, True, 0)
494
493
 
537
536
        self._file_message_text_view.set_accepts_tab(False)
538
537
        self._file_message_text_view.show()
539
538
 
540
 
        self._file_message_expander = Gtk.Expander(
541
 
            label=_i18n('File commit message'))
 
539
        self._file_message_expander = Gtk.Expander(_i18n('File commit message'))
542
540
        self._file_message_expander.set_expanded(True)
543
541
        self._file_message_expander.add(scroller)
544
542
        self._add_to_right_table(self._file_message_expander, 1, False)
595
593
            # We have either made it to the end of the list, or nothing was
596
594
            # selected. Either way, select All Files, and jump to the global
597
595
            # commit message.
598
 
            self._treeview_files.set_cursor(
599
 
                Gtk.TreePath(path=0), None, False)
 
596
            self._treeview_files.set_cursor((0,))
600
597
            self._global_message_text_view.grab_focus()
601
598
        else:
602
599
            # Set the cursor to this entry, and jump to the per-file commit
603
600
            # message
604
 
            self._treeview_files.set_cursor(model.get_path(next), None, False)
 
601
            self._treeview_files.set_cursor(model.get_path(next))
605
602
            self._file_message_text_view.grab_focus()
606
603
 
607
604
    def _save_current_file_message(self):
609
606
            return # Nothing to save
610
607
        text_buffer = self._file_message_text_view.get_buffer()
611
608
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
612
 
                                        text_buffer.get_end_iter(), True)
 
609
                                        text_buffer.get_end_iter())
613
610
        last_selected = self._files_store.get_iter(self._last_selected_file)
614
611
        self._files_store.set_value(last_selected, 5, cur_text)
615
612
 
763
760
    def _get_global_commit_message(self):
764
761
        buf = self._global_message_text_view.get_buffer()
765
762
        start, end = buf.get_bounds()
766
 
        text = buf.get_text(start, end, True)
 
763
        text = buf.get_text(start, end)
767
764
        return _sanitize_and_decode_message(text)
768
765
 
769
766
    def _set_global_commit_message(self, message):
792
789
        rev_dict['revision_id'] = rev.revision_id
793
790
        return rev_dict
794
791
 
 
792
 
 
793
class SavedCommitMessagesManager:
 
794
    """Save glogal and per-file commit messages.
 
795
 
 
796
    Saves global commit message and utf-8 file_id->message dictionary
 
797
    of per-file commit messages on disk. Re-reads them later for re-using.
 
798
    """
 
799
 
 
800
    def __init__(self, tree=None, branch=None):
 
801
        """If branch is None, builds empty messages, otherwise reads them
 
802
        from branch's disk storage. 'tree' argument is for the future."""
 
803
        if branch is None:
 
804
            self.global_message = u''
 
805
            self.file_messages = {}
 
806
        else:
 
807
            config = branch.get_config()
 
808
            self.global_message = config.get_user_option(
 
809
                'gtk_global_commit_message')
 
810
            if self.global_message is None:
 
811
                self.global_message = u''
 
812
            file_messages = config.get_user_option('gtk_file_commit_messages')
 
813
            if file_messages: # unicode and B-encoded:
 
814
                self.file_messages = bencode.bdecode(
 
815
                    file_messages.encode('UTF-8'))
 
816
            else:
 
817
                self.file_messages = {}
 
818
 
 
819
    def get(self):
 
820
        return self.global_message, self.file_messages
 
821
 
 
822
    def is_not_empty(self):
 
823
        return bool(self.global_message or self.file_messages)
 
824
 
 
825
    def insert(self, global_message, file_info):
 
826
        """Formats per-file commit messages (list of dictionaries, one per file)
 
827
        into one utf-8 file_id->message dictionary and merges this with
 
828
        previously existing dictionary. Merges global commit message too."""
 
829
        file_messages = {}
 
830
        for fi in file_info:
 
831
            file_message = fi['message']
 
832
            if file_message:
 
833
                file_messages[fi['file_id']] = file_message # utf-8 strings
 
834
        for k,v in file_messages.iteritems():
 
835
            try:
 
836
                self.file_messages[k] = v + '\n******\n' + self.file_messages[k]
 
837
            except KeyError:
 
838
                self.file_messages[k] = v
 
839
        if self.global_message:
 
840
            self.global_message = global_message + '\n******\n' \
 
841
                + self.global_message
 
842
        else:
 
843
            self.global_message = global_message
 
844
 
 
845
    def save(self, tree, branch):
 
846
        # We store in branch's config, which can be a problem if two gcommit
 
847
        # are done in two checkouts of one single branch (comments overwrite
 
848
        # each other). Ideally should be in working tree. But uncommit does
 
849
        # not always have a working tree, though it always has a branch.
 
850
        # 'tree' argument is for the future
 
851
        config = branch.get_config()
 
852
        # should it be named "gtk_" or some more neutral name ("gui_" ?) to
 
853
        # be compatible with qbzr in the future?
 
854
        config.set_user_option('gtk_global_commit_message', self.global_message)
 
855
        # bencode() does not know unicode objects but set_user_option()
 
856
        # requires one:
 
857
        config.set_user_option(
 
858
            'gtk_file_commit_messages',
 
859
            bencode.bencode(self.file_messages).decode('UTF-8'))
 
860
 
 
861
 
 
862
def save_commit_messages(local, master, old_revno, old_revid,
 
863
                         new_revno, new_revid):
 
864
    b = local
 
865
    if b is None:
 
866
        b = master
 
867
    mgr = SavedCommitMessagesManager(None, b)
 
868
    revid_iterator = b.repository.iter_reverse_revision_history(old_revid)
 
869
    cur_revno = old_revno
 
870
    new_revision_id = old_revid
 
871
    graph = b.repository.get_graph()
 
872
    for rev_id in revid_iterator:
 
873
        if cur_revno == new_revno:
 
874
            break
 
875
        cur_revno -= 1
 
876
        rev = b.repository.get_revision(rev_id)
 
877
        file_info = rev.properties.get('file-info', None)
 
878
        if file_info is None:
 
879
            file_info = {}
 
880
        else:
 
881
            file_info = bencode.bdecode(file_info.encode('UTF-8'))
 
882
        global_message = osutils.safe_unicode(rev.message)
 
883
        # Concatenate comment of the uncommitted revision
 
884
        mgr.insert(global_message, file_info)
 
885
 
 
886
        parents = graph.get_parent_map([rev_id]).get(rev_id, None)
 
887
        if not parents:
 
888
            continue
 
889
    mgr.save(None, b)