/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: Szilveszter Farkas (Phanatic)
  • Date: 2007-03-15 16:23:15 UTC
  • mfrom: (170 trunk)
  • mto: (170.1.3 trunk)
  • mto: This revision was merged to the branch mainline in revision 172.
  • Revision ID: szilveszter.farkas@gmail.com-20070315162315-rs1sbxjh31n314zc
MergeĀ fromĀ trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
import pango
26
26
 
27
27
import os.path
28
 
import re
29
28
 
30
 
from bzrlib import errors, osutils
31
 
from bzrlib.trace import mutter
 
29
import bzrlib.errors as errors
 
30
from bzrlib import osutils
32
31
 
33
32
from dialog import error_dialog, question_dialog
34
33
from errors import show_bzr_error
36
35
try:
37
36
    import dbus
38
37
    import dbus.glib
39
 
    have_dbus = True
 
38
    bus = dbus.SystemBus()
 
39
    proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
 
40
                              '/org/freedesktop/NetworkManager')
 
41
    dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.NetworkManager')
 
42
    have_nm = True
40
43
except ImportError:
41
 
    have_dbus = False
42
 
 
43
 
 
44
 
def pending_revisions(wt):
45
 
    """Return a list of pending merges or None if there are none of them.
46
 
 
47
 
    Arguably this should be a core function, and
48
 
    ``bzrlib.status.show_pending_merges`` should be built on top of it.
49
 
 
50
 
    :return: [(rev, [children])]
51
 
    """
52
 
    parents = wt.get_parent_ids()
53
 
    if len(parents) < 2:
54
 
        return None
55
 
 
56
 
    # The basic pending merge algorithm uses the same algorithm as
57
 
    # bzrlib.status.show_pending_merges
58
 
    pending = parents[1:]
59
 
    branch = wt.branch
60
 
    last_revision = parents[0]
61
 
 
62
 
    if last_revision is not None:
63
 
        try:
64
 
            ignore = set(branch.repository.get_ancestry(last_revision,
65
 
                                                        topo_sorted=False))
66
 
        except errors.NoSuchRevision:
67
 
            # the last revision is a ghost : assume everything is new
68
 
            # except for it
69
 
            ignore = set([None, last_revision])
70
 
    else:
71
 
        ignore = set([None])
72
 
 
73
 
    pm = []
74
 
    for merge in pending:
75
 
        ignore.add(merge)
76
 
        try:
77
 
            rev = branch.repository.get_revision(merge)
78
 
            children = []
79
 
            pm.append((rev, children))
80
 
 
81
 
            # This does need to be topo sorted, so we search backwards
82
 
            inner_merges = branch.repository.get_ancestry(merge)
83
 
            assert inner_merges[0] is None
84
 
            inner_merges.pop(0)
85
 
            for mmerge in reversed(inner_merges):
86
 
                if mmerge in ignore:
87
 
                    continue
88
 
                rev = branch.repository.get_revision(mmerge)
89
 
                children.append(rev)
90
 
 
91
 
                ignore.add(mmerge)
92
 
        except errors.NoSuchRevision:
93
 
            print "DEBUG: NoSuchRevision:", merge
94
 
 
95
 
    return pm
96
 
 
 
44
    have_nm = False
97
45
 
98
46
class CommitDialog(gtk.Dialog):
99
 
    """Implementation of Commit."""
100
 
 
101
 
    def __init__(self, wt, selected=None, parent=None):
102
 
         gtk.Dialog.__init__(self, title="Commit - Olive",
103
 
                                   parent=parent,
104
 
                                   flags=0,
105
 
                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
106
 
         self._wt = wt
107
 
         self._selected = selected
108
 
 
109
 
         self.setup_params()
110
 
         self.construct()
111
 
         self.set_default_size(800, 600)
112
 
 
113
 
    def setup_params(self):
114
 
        """Setup the member variables for state."""
115
 
        self._basis_tree = self._wt.basis_tree()
116
 
        self._delta = self._wt.changes_from(self._basis_tree)
117
 
 
118
 
        self._pending = pending_revisions(self._wt)
119
 
 
120
 
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
121
 
 
122
 
    def construct(self):
123
 
        """Build up the dialog widgets."""
124
 
        # The primary pane which splits it into left and right (adjustable)
125
 
        # sections.
126
 
        self._hpane = gtk.HPaned()
127
 
 
128
 
        self._construct_left_pane()
129
 
        self._construct_right_pane()
130
 
 
131
 
        self.vbox.pack_start(self._hpane)
132
 
        self._hpane.show()
133
 
        self.set_focus(self._global_message_text_view)
134
 
 
135
 
    def _construct_left_pane(self):
136
 
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=3)
137
 
        self._construct_file_list()
138
 
        self._construct_pending_list()
139
 
 
140
 
        self._pending_box.show()
141
 
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=True)
142
 
        self._left_pane_box.show()
143
 
 
144
 
    def _construct_right_pane(self):
145
 
        # TODO: I really want to make it so the diff view gets more space than
146
 
        # the global commit message, and the per-file commit message gets even
147
 
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
148
 
        # commit, and 1 for file commit, and it looked good. But I don't seem
149
 
        # to have a way to do that with the gtk boxes... :( (Which is extra
150
 
        # weird since wx uses gtk on Linux...)
151
 
        self._right_pane_box = gtk.VBox(homogeneous=False, spacing=3)
152
 
        self._construct_diff_view()
153
 
        self._construct_file_message()
154
 
        self._construct_global_message()
155
 
 
156
 
        self._right_pane_box.show()
157
 
        self._hpane.pack2(self._right_pane_box, resize=True, shrink=True)
158
 
 
159
 
    def _construct_file_list(self):
160
 
        self._files_box = gtk.VBox()
161
 
        file_label = gtk.Label()
162
 
        file_label.set_markup(_('<b>Files</b>'))
163
 
        file_label.show()
164
 
        self._files_box.pack_start(file_label, expand=False)
165
 
 
166
 
        scroller = gtk.ScrolledWindow()
167
 
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
47
    """ New implementation of the Commit dialog. """
 
48
    def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
 
49
        """ Initialize the Commit Dialog. """
 
50
        gtk.Dialog.__init__(self, title="Commit - Olive",
 
51
                                  parent=parent,
 
52
                                  flags=0,
 
53
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
 
54
        
 
55
        # Get arguments
 
56
        self.wt = wt
 
57
        self.wtpath = wtpath
 
58
        self.notbranch = notbranch
 
59
        self.selected = selected
 
60
        
 
61
        # Set the delta
 
62
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
63
        self.delta = self.wt.changes_from(self.old_tree)
 
64
        
 
65
        # Get pending merges
 
66
        self.pending = self._pending_merges(self.wt)
 
67
        
 
68
        # Do some preliminary checks
 
69
        self._is_checkout = False
 
70
        self._is_pending = False
 
71
        if self.wt is None and not self.notbranch:
 
72
            error_dialog(_('Directory does not have a working tree'),
 
73
                         _('Operation aborted.'))
 
74
            self.close()
 
75
            return
 
76
 
 
77
        if self.notbranch:
 
78
            error_dialog(_('Directory is not a branch'),
 
79
                         _('You can perform this action only in a branch.'))
 
80
            self.close()
 
81
            return
 
82
        else:
 
83
            if self.wt.branch.get_bound_location() is not None:
 
84
                # we have a checkout, so the local commit checkbox must appear
 
85
                self._is_checkout = True
 
86
            
 
87
            if self.pending:
 
88
                # There are pending merges, file selection not supported
 
89
                self._is_pending = True
 
90
        
 
91
        # Create the widgets
 
92
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
 
93
        self._check_strict = gtk.CheckButton(_("_Allow unknown files"),
 
94
                                             use_underline=True)
 
95
        self._expander_files = gtk.Expander(_("File(s) to commit"))
 
96
        self._vpaned_main = gtk.VPaned()
 
97
        self._scrolledwindow_files = gtk.ScrolledWindow()
 
98
        self._scrolledwindow_message = gtk.ScrolledWindow()
168
99
        self._treeview_files = gtk.TreeView()
169
 
        self._treeview_files.show()
170
 
        scroller.add(self._treeview_files)
171
 
        scroller.show()
172
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
173
 
        self._files_box.pack_start(scroller,
174
 
                                   expand=True, fill=True)
175
 
        self._files_box.show()
176
 
        self._left_pane_box.pack_start(self._files_box)
177
 
 
178
 
    def _construct_pending_list(self):
179
 
        # Pending information defaults to hidden, we put it all in 1 box, so
180
 
        # that we can show/hide all of them at once
181
 
        self._pending_box = gtk.VBox()
182
 
        self._pending_box.hide()
183
 
 
184
 
        # TODO: This message should be centered
185
 
        pending_message = gtk.Label()
186
 
        pending_message.set_markup(
187
 
            _('<i>Cannot select specific files when merging</i>'))
188
 
        self._pending_box.pack_start(pending_message, expand=False)
189
 
        pending_message.show()
190
 
 
191
 
        pending_label = gtk.Label()
192
 
        pending_label.set_markup(_('<b>Pending Revisions</b>'))
193
 
        self._pending_box.pack_start(pending_label, expand=False)
194
 
        pending_label.show()
195
 
 
196
 
        scroller = gtk.ScrolledWindow()
197
 
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
198
 
        self._treeview_pending = gtk.TreeView()
199
 
        scroller.add(self._treeview_pending)
200
 
        scroller.show()
201
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
202
 
        self._pending_box.pack_start(scroller,
203
 
                                     expand=True, fill=True)
204
 
        self._treeview_pending.show()
205
 
        self._left_pane_box.pack_start(self._pending_box)
206
 
 
207
 
    def _construct_diff_view(self):
208
 
        from diff import DiffDisplay
209
 
 
210
 
        self._diff_label = gtk.Label(_('Diff for whole tree'))
211
 
        self._right_pane_box.pack_start(self._diff_label, expand=False)
212
 
        self._diff_label.show()
213
 
 
214
 
        self._diff_view = DiffDisplay()
215
 
        # self._diff_display.set_trees(self.wt, self.wt.basis_tree())
216
 
        # self._diff_display.show_diff(None)
217
 
        self._right_pane_box.pack_start(self._diff_view,
218
 
                                        expand=True, fill=True)
219
 
        self._diff_view.show()
220
 
 
221
 
    def _construct_file_message(self):
222
 
        file_message_box = gtk.VBox()
223
 
        self._file_message_text_view =  gtk.TextView()
224
 
        # There should be some way to get at the TextView's ScrolledWindow
225
 
        file_message_box.pack_start(self._file_message_text_view, expand=True, fill=True)
226
 
 
227
 
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
228
 
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
229
 
        self._file_message_text_view.set_accepts_tab(False)
230
 
        self._file_message_text_view.show()
231
 
 
232
 
        self._file_message_expander = gtk.Expander(_('Message for XXX'))
233
 
        self._file_message_expander.add(file_message_box)
234
 
        file_message_box.show()
235
 
        # TODO: When expanded, we want to change expand=True
236
 
        self._right_pane_box.pack_start(self._file_message_expander,
237
 
                                        expand=False, fill=True)
238
 
        self._file_message_expander.connect('notify::expanded',
239
 
                                            self._expand_file_message_callback)
240
 
        self._file_message_expander.show()
241
 
 
242
 
    def _expand_file_message_callback(self, expander, param_spec):
243
 
        if expander.get_expanded():
244
 
            self._right_pane_box.set_child_packing(expander,
245
 
                expand=True, fill=True, padding=0, pack_type=gtk.PACK_START)
246
 
        else:
247
 
            self._right_pane_box.set_child_packing(expander,
248
 
                expand=False, fill=True, padding=0, pack_type=gtk.PACK_START)
249
 
 
250
 
    def _construct_global_message(self):
251
 
        self._global_message_label = gtk.Label(_('Global Commit Message'))
252
 
        self._right_pane_box.pack_start(self._global_message_label, expand=False)
253
 
        self._global_message_label.show()
254
 
 
255
 
        self._global_message_text_view =  gtk.TextView()
256
 
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
257
 
        self._right_pane_box.pack_start(self._global_message_text_view,
258
 
                                        expand=True, fill=True)
259
 
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
260
 
        self._file_message_text_view.set_accepts_tab(False)
261
 
        self._global_message_text_view.show()
262
 
 
263
 
    @staticmethod
264
 
    def _rev_to_pending_info(rev):
265
 
        """Get the information from a pending merge."""
 
100
        self._vbox_message = gtk.VBox()
 
101
        self._label_message = gtk.Label(_("Commit message:"))
 
102
        self._textview_message = gtk.TextView()
 
103
        
 
104
        if self._is_pending:
 
105
            self._expander_merges = gtk.Expander(_("Pending merges"))
 
106
            self._vpaned_list = gtk.VPaned()
 
107
            self._scrolledwindow_merges = gtk.ScrolledWindow()
 
108
            self._treeview_merges = gtk.TreeView()
 
109
 
 
110
        # Set callbacks
 
111
        self._button_commit.connect('clicked', self._on_commit_clicked)
 
112
        self._treeview_files.connect('row_activated', self._on_treeview_files_row_activated)
 
113
        
 
114
        # Set properties
 
115
        self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
 
116
                                              gtk.POLICY_AUTOMATIC)
 
117
        self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
 
118
                                                gtk.POLICY_AUTOMATIC)
 
119
        self._textview_message.modify_font(pango.FontDescription("Monospace"))
 
120
        self.set_default_size(500, 500)
 
121
        self._vpaned_main.set_position(200)
 
122
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
 
123
 
 
124
        if self._is_pending:
 
125
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
 
126
                                                   gtk.POLICY_AUTOMATIC)
 
127
            self._treeview_files.set_sensitive(False)
 
128
        
 
129
        # Construct the dialog
 
130
        self.action_area.pack_end(self._button_commit)
 
131
        
 
132
        self._scrolledwindow_files.add(self._treeview_files)
 
133
        self._scrolledwindow_message.add(self._textview_message)
 
134
        
 
135
        self._expander_files.add(self._scrolledwindow_files)
 
136
        
 
137
        self._vbox_message.pack_start(self._label_message, False, False)
 
138
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
 
139
        
 
140
        if self._is_pending:        
 
141
            self._expander_merges.add(self._scrolledwindow_merges)
 
142
            self._scrolledwindow_merges.add(self._treeview_merges)
 
143
            self._vpaned_list.add1(self._expander_files)
 
144
            self._vpaned_list.add2(self._expander_merges)
 
145
            self._vpaned_main.add1(self._vpaned_list)
 
146
        else:
 
147
            self._vpaned_main.add1(self._expander_files)
 
148
 
 
149
        self._vpaned_main.add2(self._vbox_message)
 
150
        
 
151
        self.vbox.pack_start(self._vpaned_main, True, True)
 
152
        if self._is_checkout: 
 
153
            self._check_local = gtk.CheckButton(_("_Only commit locally"),
 
154
                                                use_underline=True)
 
155
            self.vbox.pack_start(self._check_local, False, False)
 
156
            if have_nm:
 
157
                # 3 is the enum value for STATE_CONNECTED
 
158
                self._check_local.set_active(dbus_iface.state() != 3)
 
159
        self.vbox.pack_start(self._check_strict, False, False)
 
160
        
 
161
        # Create the file list
 
162
        self._create_file_view()
 
163
        # Create the pending merges
 
164
        self._create_pending_merges()
 
165
        
 
166
        # Expand the corresponding expander
 
167
        if self._is_pending:
 
168
            self._expander_merges.set_expanded(True)
 
169
        else:
 
170
            self._expander_files.set_expanded(True)
 
171
        
 
172
        # Display dialog
 
173
        self.vbox.show_all()
 
174
        
 
175
        # Default to Commit button
 
176
        self._button_commit.grab_default()
 
177
    
 
178
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
 
179
        # FIXME: the diff window freezes for some reason
 
180
        treeselection = treeview.get_selection()
 
181
        (model, iter) = treeselection.get_selected()
 
182
        
 
183
        if iter is not None:
 
184
            from diff import DiffWindow
 
185
            
 
186
            _selected = model.get_value(iter, 1)
 
187
            
 
188
            diff = DiffWindow()
 
189
            diff.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
 
190
            diff.set_modal(True)
 
191
            parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
192
            diff.set_diff(self.wt.branch.nick, self.wt, parent_tree)
 
193
            try:
 
194
                diff.set_file(_selected)
 
195
            except errors.NoSuchFile:
 
196
                pass
 
197
            diff.show()
 
198
    
 
199
    @show_bzr_error
 
200
    def _on_commit_clicked(self, button):
 
201
        """ Commit button clicked handler. """
 
202
        textbuffer = self._textview_message.get_buffer()
 
203
        start, end = textbuffer.get_bounds()
 
204
        message = textbuffer.get_text(start, end).decode('utf-8')
 
205
        
 
206
        if not self.pending:
 
207
            specific_files = self._get_specific_files()
 
208
        else:
 
209
            specific_files = None
 
210
 
 
211
        if message == '':
 
212
            response = question_dialog(_('Commit with an empty message?'),
 
213
                                       _('You can describe your commit intent in the message.'))
 
214
            if response == gtk.RESPONSE_NO:
 
215
                # Kindly give focus to message area
 
216
                self._textview_message.grab_focus()
 
217
                return
 
218
 
 
219
        if self._is_checkout:
 
220
            local = self._check_local.get_active()
 
221
        else:
 
222
            local = False
 
223
        
 
224
        try:
 
225
            self.wt.commit(message,
 
226
                       allow_pointless=False,
 
227
                       strict=self._check_strict.get_active(),
 
228
                       local=local,
 
229
                       specific_files=specific_files)
 
230
        except errors.PointlessCommit:
 
231
            response = question_dialog(_('Commit with no changes?'),
 
232
                                       _('There are no changes in the working tree.'))
 
233
            if response == gtk.RESPONSE_YES:
 
234
                self.wt.commit(message,
 
235
                               allow_pointless=True,
 
236
                               strict=self._check_strict.get_active(),
 
237
                               local=local,
 
238
                               specific_files=specific_files)
 
239
        self.response(gtk.RESPONSE_OK)
 
240
 
 
241
    def _pending_merges(self, wt):
 
242
        """ Return a list of pending merges or None if there are none of them. """
 
243
        parents = wt.get_parent_ids()
 
244
        if len(parents) < 2:
 
245
            return None
 
246
        
 
247
        import re
266
248
        from bzrlib.osutils import format_date
267
 
 
268
 
        rev_dict = {}
269
 
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
270
 
        rev_dict['summary'] = rev.get_summary()
271
 
        rev_dict['date'] = format_date(rev.timestamp,
272
 
                                       rev.timezone or 0,
273
 
                                       'original', date_fmt="%Y-%m-%d",
274
 
                                       show_offset=False)
275
 
        rev_dict['revision_id'] = rev.revision_id
276
 
        return rev_dict
277
 
 
278
 
 
279
 
# class CommitDialog(gtk.Dialog):
280
 
#     """ New implementation of the Commit dialog. """
281
 
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
282
 
#         """ Initialize the Commit Dialog. """
283
 
#         gtk.Dialog.__init__(self, title="Commit - Olive",
284
 
#                                   parent=parent,
285
 
#                                   flags=0,
286
 
#                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
287
 
#         
288
 
#         # Get arguments
289
 
#         self.wt = wt
290
 
#         self.wtpath = wtpath
291
 
#         self.notbranch = notbranch
292
 
#         self.selected = selected
293
 
#         
294
 
#         # Set the delta
295
 
#         self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
296
 
#         self.delta = self.wt.changes_from(self.old_tree)
297
 
#         
298
 
#         # Get pending merges
299
 
#         self.pending = self._pending_merges(self.wt)
300
 
#         
301
 
#         # Do some preliminary checks
302
 
#         self._is_checkout = False
303
 
#         self._is_pending = False
304
 
#         if self.wt is None and not self.notbranch:
305
 
#             error_dialog(_('Directory does not have a working tree'),
306
 
#                          _('Operation aborted.'))
307
 
#             self.close()
308
 
#             return
309
 
310
 
#         if self.notbranch:
311
 
#             error_dialog(_('Directory is not a branch'),
312
 
#                          _('You can perform this action only in a branch.'))
313
 
#             self.close()
314
 
#             return
315
 
#         else:
316
 
#             if self.wt.branch.get_bound_location() is not None:
317
 
#                 # we have a checkout, so the local commit checkbox must appear
318
 
#                 self._is_checkout = True
319
 
#             
320
 
#             if self.pending:
321
 
#                 # There are pending merges, file selection not supported
322
 
#                 self._is_pending = True
323
 
#         
324
 
#         # Create the widgets
325
 
#         # This is the main horizontal box, which is used to separate the commit
326
 
#         # info from the diff window.
327
 
#         self._hpane = gtk.HPaned()
328
 
#         self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
329
 
#         self._expander_files = gtk.Expander(_("File(s) to commit"))
330
 
#         self._vpaned_main = gtk.VPaned()
331
 
#         self._scrolledwindow_files = gtk.ScrolledWindow()
332
 
#         self._scrolledwindow_message = gtk.ScrolledWindow()
333
 
#         self._treeview_files = gtk.TreeView()
334
 
#         self._vbox_message = gtk.VBox()
335
 
#         self._label_message = gtk.Label(_("Commit message:"))
336
 
#         self._textview_message = gtk.TextView()
337
 
#         
338
 
#         if self._is_pending:
339
 
#             self._expander_merges = gtk.Expander(_("Pending merges"))
340
 
#             self._vpaned_list = gtk.VPaned()
341
 
#             self._scrolledwindow_merges = gtk.ScrolledWindow()
342
 
#             self._treeview_merges = gtk.TreeView()
343
 
344
 
#         # Set callbacks
345
 
#         self._button_commit.connect('clicked', self._on_commit_clicked)
346
 
#         self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
347
 
#         self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
348
 
#         
349
 
#         # Set properties
350
 
#         self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
351
 
#                                               gtk.POLICY_AUTOMATIC)
352
 
#         self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
353
 
#                                                 gtk.POLICY_AUTOMATIC)
354
 
#         self._textview_message.modify_font(pango.FontDescription("Monospace"))
355
 
#         self.set_default_size(500, 500)
356
 
#         self._vpaned_main.set_position(200)
357
 
#         self._button_commit.set_flags(gtk.CAN_DEFAULT)
358
 
359
 
#         if self._is_pending:
360
 
#             self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
361
 
#                                                    gtk.POLICY_AUTOMATIC)
362
 
#             self._treeview_files.set_sensitive(False)
363
 
#         
364
 
#         # Construct the dialog
365
 
#         self.action_area.pack_end(self._button_commit)
366
 
#         
367
 
#         self._scrolledwindow_files.add(self._treeview_files)
368
 
#         self._scrolledwindow_message.add(self._textview_message)
369
 
#         
370
 
#         self._expander_files.add(self._scrolledwindow_files)
371
 
#         
372
 
#         self._vbox_message.pack_start(self._label_message, False, False)
373
 
#         self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
374
 
#         
375
 
#         if self._is_pending:        
376
 
#             self._expander_merges.add(self._scrolledwindow_merges)
377
 
#             self._scrolledwindow_merges.add(self._treeview_merges)
378
 
#             self._vpaned_list.add1(self._expander_files)
379
 
#             self._vpaned_list.add2(self._expander_merges)
380
 
#             self._vpaned_main.add1(self._vpaned_list)
381
 
#         else:
382
 
#             self._vpaned_main.add1(self._expander_files)
383
 
384
 
#         self._vpaned_main.add2(self._vbox_message)
385
 
#         
386
 
#         self._hpane.pack1(self._vpaned_main)
387
 
#         self.vbox.pack_start(self._hpane, expand=True, fill=True)
388
 
#         if self._is_checkout: 
389
 
#             self._check_local = gtk.CheckButton(_("_Only commit locally"),
390
 
#                                                 use_underline=True)
391
 
#             self.vbox.pack_start(self._check_local, False, False)
392
 
#             if have_dbus:
393
 
#                 bus = dbus.SystemBus()
394
 
#                 proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
395
 
#                               '/org/freedesktop/NetworkManager')
396
 
#                 dbus_iface = dbus.Interface(
397
 
#                         proxy_obj, 'org.freedesktop.NetworkManager')
398
 
#                 try:
399
 
#                     # 3 is the enum value for STATE_CONNECTED
400
 
#                     self._check_local.set_active(dbus_iface.state() != 3)
401
 
#                 except dbus.DBusException, e:
402
 
#                     # Silently drop errors. While DBus may be 
403
 
#                     # available, NetworkManager doesn't necessarily have to be
404
 
#                     mutter("unable to get networkmanager state: %r" % e)
405
 
#                 
406
 
#         # Create the file list
407
 
#         self._create_file_view()
408
 
#         # Create the pending merges
409
 
#         self._create_pending_merges()
410
 
#         self._create_diff_view()
411
 
#         
412
 
#         # Expand the corresponding expander
413
 
#         if self._is_pending:
414
 
#             self._expander_merges.set_expanded(True)
415
 
#         else:
416
 
#             self._expander_files.set_expanded(True)
417
 
#         
418
 
#         # Display dialog
419
 
#         self.vbox.show_all()
420
 
#         
421
 
#         # Default to Commit button
422
 
#         self._button_commit.grab_default()
423
 
#     
424
 
#     def _show_diff_view(self, treeview):
425
 
#         # FIXME: the diff window freezes for some reason
426
 
#         treeselection = treeview.get_selection()
427
 
#         (model, iter) = treeselection.get_selected()
428
 
429
 
#         if iter is not None:
430
 
#             selected = model.get_value(iter, 3) # Get the real_path attribute
431
 
#             self._diff_display.show_diff([selected])
432
 
433
 
#     def _on_treeview_files_cursor_changed(self, treeview):
434
 
#         self._show_diff_view(treeview)
435
 
#         
436
 
#     def _on_treeview_files_row_activated(self, treeview, path, view_column):
437
 
#         self._show_diff_view(treeview)
438
 
#     
439
 
#     @show_bzr_error
440
 
#     def _on_commit_clicked(self, button):
441
 
#         """ Commit button clicked handler. """
442
 
#         textbuffer = self._textview_message.get_buffer()
443
 
#         start, end = textbuffer.get_bounds()
444
 
#         message = textbuffer.get_text(start, end).decode('utf-8')
445
 
#         
446
 
#         if not self.pending:
447
 
#             specific_files = self._get_specific_files()
448
 
#         else:
449
 
#             specific_files = None
450
 
451
 
#         if message == '':
452
 
#             response = question_dialog(_('Commit with an empty message?'),
453
 
#                                        _('You can describe your commit intent in the message.'))
454
 
#             if response == gtk.RESPONSE_NO:
455
 
#                 # Kindly give focus to message area
456
 
#                 self._textview_message.grab_focus()
457
 
#                 return
458
 
459
 
#         if self._is_checkout:
460
 
#             local = self._check_local.get_active()
461
 
#         else:
462
 
#             local = False
463
 
464
 
#         if list(self.wt.unknowns()) != []:
465
 
#             response = question_dialog(_("Commit with unknowns?"),
466
 
#                _("Unknown files exist in the working tree. Commit anyway?"))
467
 
#             if response == gtk.RESPONSE_NO:
468
 
#                 return
469
 
#         
470
 
#         try:
471
 
#             self.wt.commit(message,
472
 
#                        allow_pointless=False,
473
 
#                        strict=False,
474
 
#                        local=local,
475
 
#                        specific_files=specific_files)
476
 
#         except errors.PointlessCommit:
477
 
#             response = question_dialog(_('Commit with no changes?'),
478
 
#                                        _('There are no changes in the working tree.'))
479
 
#             if response == gtk.RESPONSE_YES:
480
 
#                 self.wt.commit(message,
481
 
#                                allow_pointless=True,
482
 
#                                strict=False,
483
 
#                                local=local,
484
 
#                                specific_files=specific_files)
485
 
#         self.response(gtk.RESPONSE_OK)
486
 
487
 
#     def _pending_merges(self, wt):
488
 
#         """ Return a list of pending merges or None if there are none of them. """
489
 
#         parents = wt.get_parent_ids()
490
 
#         if len(parents) < 2:
491
 
#             return None
492
 
#         
493
 
#         import re
494
 
#         from bzrlib.osutils import format_date
495
 
#         
496
 
#         pending = parents[1:]
497
 
#         branch = wt.branch
498
 
#         last_revision = parents[0]
499
 
#         
500
 
#         if last_revision is not None:
501
 
#             try:
502
 
#                 ignore = set(branch.repository.get_ancestry(last_revision))
503
 
#             except errors.NoSuchRevision:
504
 
#                 # the last revision is a ghost : assume everything is new 
505
 
#                 # except for it
506
 
#                 ignore = set([None, last_revision])
507
 
#         else:
508
 
#             ignore = set([None])
509
 
#         
510
 
#         pm = []
511
 
#         for merge in pending:
512
 
#             ignore.add(merge)
513
 
#             try:
514
 
#                 m_revision = branch.repository.get_revision(merge)
515
 
#                 
516
 
#                 rev = {}
517
 
#                 rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
518
 
#                 rev['summary'] = m_revision.get_summary()
519
 
#                 rev['date'] = format_date(m_revision.timestamp,
520
 
#                                           m_revision.timezone or 0, 
521
 
#                                           'original', date_fmt="%Y-%m-%d",
522
 
#                                           show_offset=False)
523
 
#                 
524
 
#                 pm.append(rev)
525
 
#                 
526
 
#                 inner_merges = branch.repository.get_ancestry(merge)
527
 
#                 assert inner_merges[0] is None
528
 
#                 inner_merges.pop(0)
529
 
#                 inner_merges.reverse()
530
 
#                 for mmerge in inner_merges:
531
 
#                     if mmerge in ignore:
532
 
#                         continue
533
 
#                     mm_revision = branch.repository.get_revision(mmerge)
534
 
#                     
535
 
#                     rev = {}
536
 
#                     rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
537
 
#                     rev['summary'] = mm_revision.get_summary()
538
 
#                     rev['date'] = format_date(mm_revision.timestamp,
539
 
#                                               mm_revision.timezone or 0, 
540
 
#                                               'original', date_fmt="%Y-%m-%d",
541
 
#                                               show_offset=False)
542
 
#                 
543
 
#                     pm.append(rev)
544
 
#                     
545
 
#                     ignore.add(mmerge)
546
 
#             except errors.NoSuchRevision:
547
 
#                 print "DEBUG: NoSuchRevision:", merge
548
 
#         
549
 
#         return pm
550
 
551
 
#     def _create_file_view(self):
552
 
#         self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
553
 
#                                          gobject.TYPE_STRING,    # [1] path to display
554
 
#                                          gobject.TYPE_STRING,    # [2] changes type
555
 
#                                          gobject.TYPE_STRING)    # [3] real path
556
 
#         self._treeview_files.set_model(self._file_store)
557
 
#         crt = gtk.CellRendererToggle()
558
 
#         crt.set_property("activatable", True)
559
 
#         crt.connect("toggled", self._toggle_commit, self._file_store)
560
 
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
561
 
#                                      crt, active=0))
562
 
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
563
 
#                                      gtk.CellRendererText(), text=1))
564
 
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
565
 
#                                      gtk.CellRendererText(), text=2))
566
 
567
 
#         for path, id, kind in self.delta.added:
568
 
#             marker = osutils.kind_marker(kind)
569
 
#             if self.selected is not None:
570
 
#                 if path == os.path.join(self.wtpath, self.selected):
571
 
#                     self._file_store.append([ True, path+marker, _('added'), path ])
572
 
#                 else:
573
 
#                     self._file_store.append([ False, path+marker, _('added'), path ])
574
 
#             else:
575
 
#                 self._file_store.append([ True, path+marker, _('added'), path ])
576
 
577
 
#         for path, id, kind in self.delta.removed:
578
 
#             marker = osutils.kind_marker(kind)
579
 
#             if self.selected is not None:
580
 
#                 if path == os.path.join(self.wtpath, self.selected):
581
 
#                     self._file_store.append([ True, path+marker, _('removed'), path ])
582
 
#                 else:
583
 
#                     self._file_store.append([ False, path+marker, _('removed'), path ])
584
 
#             else:
585
 
#                 self._file_store.append([ True, path+marker, _('removed'), path ])
586
 
587
 
#         for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
588
 
#             marker = osutils.kind_marker(kind)
589
 
#             if text_modified or meta_modified:
590
 
#                 changes = _('renamed and modified')
591
 
#             else:
592
 
#                 changes = _('renamed')
593
 
#             if self.selected is not None:
594
 
#                 if newpath == os.path.join(self.wtpath, self.selected):
595
 
#                     self._file_store.append([ True,
596
 
#                                               oldpath+marker + '  =>  ' + newpath+marker,
597
 
#                                               changes,
598
 
#                                               newpath
599
 
#                                             ])
600
 
#                 else:
601
 
#                     self._file_store.append([ False,
602
 
#                                               oldpath+marker + '  =>  ' + newpath+marker,
603
 
#                                               changes,
604
 
#                                               newpath
605
 
#                                             ])
606
 
#             else:
607
 
#                 self._file_store.append([ True,
608
 
#                                           oldpath+marker + '  =>  ' + newpath+marker,
609
 
#                                           changes,
610
 
#                                           newpath
611
 
#                                         ])
612
 
613
 
#         for path, id, kind, text_modified, meta_modified in self.delta.modified:
614
 
#             marker = osutils.kind_marker(kind)
615
 
#             if self.selected is not None:
616
 
#                 if path == os.path.join(self.wtpath, self.selected):
617
 
#                     self._file_store.append([ True, path+marker, _('modified'), path ])
618
 
#                 else:
619
 
#                     self._file_store.append([ False, path+marker, _('modified'), path ])
620
 
#             else:
621
 
#                 self._file_store.append([ True, path+marker, _('modified'), path ])
622
 
#     
623
 
#     def _create_pending_merges(self):
624
 
#         if not self.pending:
625
 
#             return
626
 
#         
627
 
#         liststore = gtk.ListStore(gobject.TYPE_STRING,
628
 
#                                   gobject.TYPE_STRING,
629
 
#                                   gobject.TYPE_STRING)
630
 
#         self._treeview_merges.set_model(liststore)
631
 
#         
632
 
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
633
 
#                                             gtk.CellRendererText(), text=0))
634
 
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
635
 
#                                             gtk.CellRendererText(), text=1))
636
 
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
637
 
#                                             gtk.CellRendererText(), text=2))
638
 
#         
639
 
#         for item in self.pending:
640
 
#             liststore.append([ item['date'],
641
 
#                                item['committer'],
642
 
#                                item['summary'] ])
643
 
#     
644
 
645
 
#     def _create_diff_view(self):
646
 
#         from diff import DiffDisplay
647
 
648
 
#         self._diff_display = DiffDisplay()
649
 
#         self._diff_display.set_trees(self.wt, self.wt.basis_tree())
650
 
#         self._diff_display.show_diff(None)
651
 
#         self._diff_display.show()
652
 
#         self._hpane.pack2(self._diff_display)
653
 
654
 
#     def _get_specific_files(self):
655
 
#         ret = []
656
 
#         it = self._file_store.get_iter_first()
657
 
#         while it:
658
 
#             if self._file_store.get_value(it, 0):
659
 
#                 # get real path from hidden column 3
660
 
#                 ret.append(self._file_store.get_value(it, 3))
661
 
#             it = self._file_store.iter_next(it)
662
 
663
 
#         return ret
664
 
#     
665
 
#     def _toggle_commit(self, cell, path, model):
666
 
#         model[path][0] = not model[path][0]
667
 
#         return
 
249
        
 
250
        pending = parents[1:]
 
251
        branch = wt.branch
 
252
        last_revision = parents[0]
 
253
        
 
254
        if last_revision is not None:
 
255
            try:
 
256
                ignore = set(branch.repository.get_ancestry(last_revision))
 
257
            except errors.NoSuchRevision:
 
258
                # the last revision is a ghost : assume everything is new 
 
259
                # except for it
 
260
                ignore = set([None, last_revision])
 
261
        else:
 
262
            ignore = set([None])
 
263
        
 
264
        pm = []
 
265
        for merge in pending:
 
266
            ignore.add(merge)
 
267
            try:
 
268
                m_revision = branch.repository.get_revision(merge)
 
269
                
 
270
                rev = {}
 
271
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
 
272
                rev['summary'] = m_revision.get_summary()
 
273
                rev['date'] = format_date(m_revision.timestamp,
 
274
                                          m_revision.timezone or 0, 
 
275
                                          'original', date_fmt="%Y-%m-%d",
 
276
                                          show_offset=False)
 
277
                
 
278
                pm.append(rev)
 
279
                
 
280
                inner_merges = branch.repository.get_ancestry(merge)
 
281
                assert inner_merges[0] is None
 
282
                inner_merges.pop(0)
 
283
                inner_merges.reverse()
 
284
                for mmerge in inner_merges:
 
285
                    if mmerge in ignore:
 
286
                        continue
 
287
                    mm_revision = branch.repository.get_revision(mmerge)
 
288
                    
 
289
                    rev = {}
 
290
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
 
291
                    rev['summary'] = mm_revision.get_summary()
 
292
                    rev['date'] = format_date(mm_revision.timestamp,
 
293
                                              mm_revision.timezone or 0, 
 
294
                                              'original', date_fmt="%Y-%m-%d",
 
295
                                              show_offset=False)
 
296
                
 
297
                    pm.append(rev)
 
298
                    
 
299
                    ignore.add(mmerge)
 
300
            except errors.NoSuchRevision:
 
301
                print "DEBUG: NoSuchRevision:", merge
 
302
        
 
303
        return pm
 
304
 
 
305
    def _create_file_view(self):
 
306
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
 
307
                                         gobject.TYPE_STRING,    # [1] path to display
 
308
                                         gobject.TYPE_STRING,    # [2] changes type
 
309
                                         gobject.TYPE_STRING)    # [3] real path
 
310
        self._treeview_files.set_model(self._file_store)
 
311
        crt = gtk.CellRendererToggle()
 
312
        crt.set_property("activatable", True)
 
313
        crt.connect("toggled", self._toggle_commit, self._file_store)
 
314
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
 
315
                                     crt, active=0))
 
316
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
 
317
                                     gtk.CellRendererText(), text=1))
 
318
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
 
319
                                     gtk.CellRendererText(), text=2))
 
320
 
 
321
        for path, id, kind in self.delta.added:
 
322
            marker = osutils.kind_marker(kind)
 
323
            if self.selected is not None:
 
324
                if path == os.path.join(self.wtpath, self.selected):
 
325
                    self._file_store.append([ True, path+marker, _('added'), path ])
 
326
                else:
 
327
                    self._file_store.append([ False, path+marker, _('added'), path ])
 
328
            else:
 
329
                self._file_store.append([ True, path+marker, _('added'), path ])
 
330
 
 
331
        for path, id, kind in self.delta.removed:
 
332
            marker = osutils.kind_marker(kind)
 
333
            if self.selected is not None:
 
334
                if path == os.path.join(self.wtpath, self.selected):
 
335
                    self._file_store.append([ True, path+marker, _('removed'), path ])
 
336
                else:
 
337
                    self._file_store.append([ False, path+marker, _('removed'), path ])
 
338
            else:
 
339
                self._file_store.append([ True, path+marker, _('removed'), path ])
 
340
 
 
341
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
 
342
            marker = osutils.kind_marker(kind)
 
343
            if text_modified or meta_modified:
 
344
                changes = _('renamed and modified')
 
345
            else:
 
346
                changes = _('renamed')
 
347
            if self.selected is not None:
 
348
                if newpath == os.path.join(self.wtpath, self.selected):
 
349
                    self._file_store.append([ True,
 
350
                                              oldpath+marker + '  =>  ' + newpath+marker,
 
351
                                              changes,
 
352
                                              newpath
 
353
                                            ])
 
354
                else:
 
355
                    self._file_store.append([ False,
 
356
                                              oldpath+marker + '  =>  ' + newpath+marker,
 
357
                                              changes,
 
358
                                              newpath
 
359
                                            ])
 
360
            else:
 
361
                self._file_store.append([ True,
 
362
                                          oldpath+marker + '  =>  ' + newpath+marker,
 
363
                                          changes,
 
364
                                          newpath
 
365
                                        ])
 
366
 
 
367
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
 
368
            marker = osutils.kind_marker(kind)
 
369
            if self.selected is not None:
 
370
                if path == os.path.join(self.wtpath, self.selected):
 
371
                    self._file_store.append([ True, path+marker, _('modified'), path ])
 
372
                else:
 
373
                    self._file_store.append([ False, path+marker, _('modified'), path ])
 
374
            else:
 
375
                self._file_store.append([ True, path+marker, _('modified'), path ])
 
376
    
 
377
    def _create_pending_merges(self):
 
378
        if not self.pending:
 
379
            return
 
380
        
 
381
        liststore = gtk.ListStore(gobject.TYPE_STRING,
 
382
                                  gobject.TYPE_STRING,
 
383
                                  gobject.TYPE_STRING)
 
384
        self._treeview_merges.set_model(liststore)
 
385
        
 
386
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
 
387
                                            gtk.CellRendererText(), text=0))
 
388
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
 
389
                                            gtk.CellRendererText(), text=1))
 
390
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
 
391
                                            gtk.CellRendererText(), text=2))
 
392
        
 
393
        for item in self.pending:
 
394
            liststore.append([ item['date'],
 
395
                               item['committer'],
 
396
                               item['summary'] ])
 
397
    
 
398
    def _get_specific_files(self):
 
399
        ret = []
 
400
        it = self._file_store.get_iter_first()
 
401
        while it:
 
402
            if self._file_store.get_value(it, 0):
 
403
                # get real path from hidden column 3
 
404
                ret.append(self._file_store.get_value(it, 3))
 
405
            it = self._file_store.iter_next(it)
 
406
 
 
407
        return ret
 
408
    
 
409
    def _toggle_commit(self, cell, path, model):
 
410
        model[path][0] = not model[path][0]
 
411
        return