/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 olive/commit.py

  • Committer: Szilveszter Farkas (Phanatic)
  • Date: 2006-07-16 23:51:20 UTC
  • mto: (0.14.1 main) (93.1.1 win32.bialix)
  • mto: This revision was merged to the branch mainline in revision 83.
  • Revision ID: Szilveszter.Farkas@gmail.com-20060716235120-0ea07ea7fc6e9330
2006-07-17  Szilveszter Farkas <Szilveszter.Farkas@gmail.com>

    * setup.py: some tweaks
    * olive-gtk: added main executable
    * olive/frontend/gtk/handler.py: signal handler class added (OliveHandler)
    * olive/frontend/gtk/__init__.py: main GTK class added (OliveGtk)
    * olive.glade: GTK UI description added (generated by Glade)
    * oliveicon2.png: icon added

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
try:
18
 
    import pygtk
19
 
    pygtk.require("2.0")
20
 
except:
21
 
    pass
22
 
 
23
 
import gtk
24
 
import gtk.glade
25
 
import gobject
26
 
import pango
27
 
 
28
 
import bzrlib.errors as errors
29
 
from bzrlib import osutils
30
 
 
31
 
from dialog import error_dialog, question_dialog
32
 
from guifiles import GLADEFILENAME
33
 
 
34
 
 
35
 
class CommitDialog:
36
 
    """ Display Commit dialog and perform the needed actions. """
37
 
    def __init__(self, wt, wtpath, notbranch):
38
 
        """ Initialize the Commit dialog.
39
 
        :param  wt:         bzr working tree object
40
 
        :param  wtpath:     path to working tree root
41
 
        :param  notbranch:  flag that path is not a brach
42
 
        :type   notbranch:  bool
43
 
        """
44
 
        self.glade = gtk.glade.XML(GLADEFILENAME, 'window_commit', 'olive-gtk')
45
 
        
46
 
        self.wt = wt
47
 
        self.wtpath = wtpath
48
 
        self.notbranch = notbranch
49
 
 
50
 
        # Get some important widgets
51
 
        self.window = self.glade.get_widget('window_commit')
52
 
        self.checkbutton_local = self.glade.get_widget('checkbutton_commit_local')
53
 
        self.textview = self.glade.get_widget('textview_commit')
54
 
        self.file_expander = self.glade.get_widget('expander_commit_select')
55
 
        self.file_view = self.glade.get_widget('treeview_commit_select')
56
 
        self.pending_expander = self.glade.get_widget('expander_commit_pending')
57
 
        self.pending_label = self.glade.get_widget('label_commit_pending')
58
 
        self.pending_view = self.glade.get_widget('treeview_commit_pending')
59
 
 
60
 
        if wt is None or notbranch:
61
 
            return
62
 
        
63
 
        # Set the delta
64
 
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
65
 
        self.delta = self.wt.changes_from(self.old_tree)
66
 
        
67
 
        # Get pending merges
68
 
        self.pending = self._pending_merges(self.wt)
69
 
        
70
 
        # Dictionary for signal_autoconnect
71
 
        dic = { "on_button_commit_commit_clicked": self.commit,
72
 
                "on_button_commit_cancel_clicked": self.close }
73
 
 
74
 
        # Connect the signals to the handlers
75
 
        self.glade.signal_autoconnect(dic)
76
 
        
77
 
        # Create the file list
78
 
        self._create_file_view()
79
 
        # Create the pending merges
80
 
        self._create_pending_merges()
81
 
    
82
 
    def display(self):
83
 
        """ Display the Push dialog.
84
 
        @return:    True if dialog is shown.
85
 
        """
86
 
        if self.wt is None and not self.notbranch:
87
 
            error_dialog(_('Directory does not have a working tree'),
88
 
                         _('Operation aborted.'))
89
 
            self.close()
90
 
            return False
91
 
        if self.notbranch:
92
 
            error_dialog(_('Directory is not a branch'),
93
 
                         _('You can perform this action only in a branch.'))
94
 
            self.close()
95
 
            return False
96
 
        else:
97
 
            if self.wt.branch.get_bound_location() is not None:
98
 
                # we have a checkout, so the local commit checkbox must appear
99
 
                self.checkbutton_local.show()
100
 
            
101
 
            if self.pending:
102
 
                # There are pending merges, file selection not supported
103
 
                self.file_expander.set_expanded(False)
104
 
                self.file_view.set_sensitive(False)
105
 
            else:
106
 
                # No pending merges
107
 
                self.pending_expander.hide()
108
 
            
109
 
            self.textview.modify_font(pango.FontDescription("Monospace"))
110
 
            self.window.show()
111
 
            return True
112
 
    
113
 
    def _create_file_view(self):
114
 
        self.file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
115
 
                                        gobject.TYPE_STRING,    # [1] path to display
116
 
                                        gobject.TYPE_STRING,    # [2] changes type
117
 
                                        gobject.TYPE_STRING)    # [3] real path
118
 
        self.file_view.set_model(self.file_store)
119
 
        crt = gtk.CellRendererToggle()
120
 
        crt.set_property("activatable", True)
121
 
        crt.connect("toggled", self._toggle_commit, self.file_store)
122
 
        self.file_view.append_column(gtk.TreeViewColumn(_('Commit'),
123
 
                                     crt, active=0))
124
 
        self.file_view.append_column(gtk.TreeViewColumn(_('Path'),
125
 
                                     gtk.CellRendererText(), text=1))
126
 
        self.file_view.append_column(gtk.TreeViewColumn(_('Type'),
127
 
                                     gtk.CellRendererText(), text=2))
128
 
 
129
 
        for path, id, kind in self.delta.added:
130
 
            marker = osutils.kind_marker(kind)
131
 
            self.file_store.append([ True, path+marker, _('added'), path ])
132
 
 
133
 
        for path, id, kind in self.delta.removed:
134
 
            marker = osutils.kind_marker(kind)
135
 
            self.file_store.append([ True, path+marker, _('removed'), path ])
136
 
 
137
 
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
138
 
            marker = osutils.kind_marker(kind)
139
 
            if text_modified or meta_modified:
140
 
                changes = _('renamed and modified')
141
 
            else:
142
 
                changes = _('renamed')
143
 
            self.file_store.append([ True,
144
 
                                     oldpath+marker + '  =>  ' + newpath+marker,
145
 
                                     changes,
146
 
                                     newpath
147
 
                                   ])
148
 
 
149
 
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
150
 
            marker = osutils.kind_marker(kind)
151
 
            self.file_store.append([ True, path+marker, _('modified'), path ])
152
 
    
153
 
    def _create_pending_merges(self):
154
 
        if not self.pending:
155
 
            # hide unused pending merge part
156
 
            scrolled_window = self.glade.get_widget('scrolledwindow_commit_pending')
157
 
            parent = scrolled_window.get_parent()
158
 
            parent.remove(scrolled_window)
159
 
            parent = self.pending_label.get_parent()
160
 
            parent.remove(self.pending_label)
161
 
            return
162
 
        
163
 
        liststore = gtk.ListStore(gobject.TYPE_STRING,
164
 
                                  gobject.TYPE_STRING,
165
 
                                  gobject.TYPE_STRING)
166
 
        self.pending_view.set_model(liststore)
167
 
        
168
 
        self.pending_view.append_column(gtk.TreeViewColumn(_('Date'),
169
 
                                        gtk.CellRendererText(), text=0))
170
 
        self.pending_view.append_column(gtk.TreeViewColumn(_('Committer'),
171
 
                                        gtk.CellRendererText(), text=1))
172
 
        self.pending_view.append_column(gtk.TreeViewColumn(_('Summary'),
173
 
                                        gtk.CellRendererText(), text=2))
174
 
        
175
 
        for item in self.pending:
176
 
            liststore.append([ item['date'],
177
 
                               item['committer'],
178
 
                               item['summary'] ])
179
 
    
180
 
    def _get_specific_files(self):
181
 
        ret = []
182
 
        it = self.file_store.get_iter_first()
183
 
        while it:
184
 
            if self.file_store.get_value(it, 0):
185
 
                # get real path from hidden column 3
186
 
                ret.append(self.file_store.get_value(it, 3))
187
 
            it = self.file_store.iter_next(it)
188
 
 
189
 
        return ret
190
 
    
191
 
    def _toggle_commit(self, cell, path, model):
192
 
        model[path][0] = not model[path][0]
193
 
        return
194
 
    
195
 
    def _pending_merges(self, wt):
196
 
        """ Return a list of pending merges or None if there are none of them. """
197
 
        parents = wt.get_parent_ids()
198
 
        if len(parents) < 2:
199
 
            return None
200
 
        
201
 
        import re
202
 
        from bzrlib.osutils import format_date
203
 
        
204
 
        pending = parents[1:]
205
 
        branch = wt.branch
206
 
        last_revision = parents[0]
207
 
        
208
 
        if last_revision is not None:
209
 
            try:
210
 
                ignore = set(branch.repository.get_ancestry(last_revision))
211
 
            except errors.NoSuchRevision:
212
 
                # the last revision is a ghost : assume everything is new 
213
 
                # except for it
214
 
                ignore = set([None, last_revision])
215
 
        else:
216
 
            ignore = set([None])
217
 
        
218
 
        pm = []
219
 
        for merge in pending:
220
 
            ignore.add(merge)
221
 
            try:
222
 
                m_revision = branch.repository.get_revision(merge)
223
 
                
224
 
                rev = {}
225
 
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
226
 
                rev['summary'] = m_revision.get_summary()
227
 
                rev['date'] = format_date(m_revision.timestamp,
228
 
                                          m_revision.timezone or 0, 
229
 
                                          'original', date_fmt="%Y-%m-%d",
230
 
                                          show_offset=False)
231
 
                
232
 
                pm.append(rev)
233
 
                
234
 
                inner_merges = branch.repository.get_ancestry(merge)
235
 
                assert inner_merges[0] is None
236
 
                inner_merges.pop(0)
237
 
                inner_merges.reverse()
238
 
                for mmerge in inner_merges:
239
 
                    if mmerge in ignore:
240
 
                        continue
241
 
                    mm_revision = branch.repository.get_revision(mmerge)
242
 
                    
243
 
                    rev = {}
244
 
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
245
 
                    rev['summary'] = mm_revision.get_summary()
246
 
                    rev['date'] = format_date(mm_revision.timestamp,
247
 
                                              mm_revision.timezone or 0, 
248
 
                                              'original', date_fmt="%Y-%m-%d",
249
 
                                              show_offset=False)
250
 
                
251
 
                    pm.append(rev)
252
 
                    
253
 
                    ignore.add(mmerge)
254
 
            except errors.NoSuchRevision:
255
 
                print "DEBUG: NoSuchRevision:", merge
256
 
        
257
 
        return pm
258
 
 
259
 
    def commit(self, widget):
260
 
        textbuffer = self.textview.get_buffer()
261
 
        start, end = textbuffer.get_bounds()
262
 
        message = textbuffer.get_text(start, end).decode('utf-8')
263
 
        
264
 
        checkbutton_strict = self.glade.get_widget('checkbutton_commit_strict')
265
 
        checkbutton_force = self.glade.get_widget('checkbutton_commit_force')
266
 
        
267
 
        if not self.pending:
268
 
            specific_files = self._get_specific_files()
269
 
        else:
270
 
            specific_files = None
271
 
 
272
 
        if message == '':
273
 
            response = question_dialog('Commit with an empty message ?',
274
 
                                       'You can describe your commit intent'
275
 
                                       +' in the message')
276
 
            if response == gtk.RESPONSE_NO:
277
 
                # Kindly give focus to message area
278
 
                self.textview.grab_focus()
279
 
                return
280
 
 
281
 
        try:
282
 
            self.wt.commit(message,
283
 
                           allow_pointless=checkbutton_force.get_active(),
284
 
                           strict=checkbutton_strict.get_active(),
285
 
                           local=self.checkbutton_local.get_active(),
286
 
                           specific_files=specific_files)
287
 
        except errors.NotBranchError:
288
 
            error_dialog(_('Directory is not a branch'),
289
 
                         _('You can perform this action only in a branch.'))
290
 
            return
291
 
        except errors.LocalRequiresBoundBranch:
292
 
            error_dialog(_('Directory is not a checkout'),
293
 
                         _('You can perform local commit only on checkouts.'))
294
 
            return
295
 
        except errors.PointlessCommit:
296
 
            error_dialog(_('No changes to commit'),
297
 
                         _('Try force commit if you want to commit anyway.'))
298
 
            return
299
 
        except errors.ConflictsInTree:
300
 
            error_dialog(_('Conflicts in tree'),
301
 
                         _('You need to resolve the conflicts before committing.'))
302
 
            return
303
 
        except errors.StrictCommitFailed:
304
 
            error_dialog(_('Strict commit failed'),
305
 
                         _('There are unknown files in the working tree.\nPlease add or delete them.'))
306
 
            return
307
 
        except errors.BoundBranchOutOfDate, errmsg:
308
 
            error_dialog(_('Bound branch is out of date'),
309
 
                         _('%s') % errmsg)
310
 
            return
311
 
        except errors.BzrError, msg:
312
 
            error_dialog(_('Unknown bzr error'), str(msg))
313
 
            return
314
 
        except Exception, msg:
315
 
            error_dialog(_('Unknown error'), str(msg))
316
 
            return
317
 
 
318
 
        self.close()
319
 
 
320
 
    def close(self, widget=None):
321
 
        self.window.destroy()
322
 
 
323
 
class CommitDialogNew(gtk.Dialog):
324
 
    """ New implementation of the Commit dialog. """
325
 
    def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
326
 
        """ Initialize the Commit Dialog. """
327
 
        gtk.Dialog.__init__(self, title="Commit - Olive",
328
 
                                  parent=parent,
329
 
                                  flags=0,
330
 
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
331
 
        
332
 
        # Get arguments
333
 
        self.wt = wt
334
 
        self.wtpath = wtpath
335
 
        self.notbranch = notbranch
336
 
        self.selected = selected
337
 
        
338
 
        # Set the delta
339
 
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
340
 
        self.delta = self.wt.changes_from(self.old_tree)
341
 
        
342
 
        # Get pending merges
343
 
        self.pending = self._pending_merges(self.wt)
344
 
        
345
 
        # Do some preliminary checks
346
 
        self._is_checkout = False
347
 
        self._is_pending = False
348
 
        if self.wt is None and not self.notbranch:
349
 
            error_dialog(_('Directory does not have a working tree'),
350
 
                         _('Operation aborted.'))
351
 
            self.close()
352
 
            return
353
 
 
354
 
        if self.notbranch:
355
 
            error_dialog(_('Directory is not a branch'),
356
 
                         _('You can perform this action only in a branch.'))
357
 
            self.close()
358
 
            return
359
 
        else:
360
 
            if self.wt.branch.get_bound_location() is not None:
361
 
                # we have a checkout, so the local commit checkbox must appear
362
 
                self._is_checkout = True
363
 
            
364
 
            if self.pending:
365
 
                # There are pending merges, file selection not supported
366
 
                self._is_pending = True
367
 
        
368
 
        # Create the widgets
369
 
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
370
 
        if self._is_checkout:
371
 
            self._check_local = gtk.CheckButton(_("_Local only commit (works in checkouts)"),
372
 
                                                use_underline=True)
373
 
        self._check_strict = gtk.CheckButton(_("_Strict commit (fails if unknown files are present)"),
374
 
                                             use_underline=True)
375
 
        self._expander_files = gtk.Expander(_("Please select the file(s) to commit"))
376
 
        self._vpaned_main = gtk.VPaned()
377
 
        self._scrolledwindow_files = gtk.ScrolledWindow()
378
 
        self._scrolledwindow_message = gtk.ScrolledWindow()
379
 
        self._treeview_files = gtk.TreeView()
380
 
        self._vbox_message = gtk.VBox()
381
 
        self._label_message = gtk.Label(_("Please specify a commit message:"))
382
 
        self._textview_message = gtk.TextView()
383
 
        
384
 
        if self._is_pending:
385
 
            self._expander_merges = gtk.Expander(_("Pending merges"))
386
 
            self._vpaned_list = gtk.VPaned()
387
 
            self._scrolledwindow_merges = gtk.ScrolledWindow()
388
 
            self._treeview_merges = gtk.TreeView()
389
 
 
390
 
        # Set callbacks
391
 
        self._button_commit.connect('clicked', self._on_commit_clicked)
392
 
        self._treeview_files.connect('row_activated', self._on_treeview_files_row_activated)
393
 
        
394
 
        # Set properties
395
 
        self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
396
 
                                              gtk.POLICY_AUTOMATIC)
397
 
        self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
398
 
                                                gtk.POLICY_AUTOMATIC)
399
 
        self._textview_message.modify_font(pango.FontDescription("Monospace"))
400
 
        self.set_default_size(500, 500)
401
 
        self._vpaned_main.set_position(200)
402
 
 
403
 
        if self._is_pending:
404
 
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
405
 
                                                   gtk.POLICY_AUTOMATIC)
406
 
            self._treeview_files.set_sensitive(False)
407
 
        
408
 
        # Construct the dialog
409
 
        self.action_area.pack_end(self._button_commit)
410
 
        
411
 
        self._scrolledwindow_files.add(self._treeview_files)
412
 
        self._scrolledwindow_message.add(self._textview_message)
413
 
        
414
 
        self._expander_files.add(self._scrolledwindow_files)
415
 
        
416
 
        self._vbox_message.pack_start(self._label_message, False, False)
417
 
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
418
 
        
419
 
        if self._is_pending:        
420
 
            self._expander_merges.add(self._scrolledwindow_merges)
421
 
            self._scrolledwindow_merges.add(self._treeview_merges)
422
 
            self._vpaned_list.add1(self._expander_files)
423
 
            self._vpaned_list.add2(self._expander_merges)
424
 
            self._vpaned_main.add1(self._vpaned_list)
425
 
        else:
426
 
            self._vpaned_main.add1(self._expander_files)
427
 
 
428
 
        self._vpaned_main.add2(self._vbox_message)
429
 
        
430
 
        self.vbox.pack_start(self._vpaned_main, True, True)
431
 
        if self._is_checkout:
432
 
            self.vbox.pack_start(self._check_local, False, False)
433
 
        self.vbox.pack_start(self._check_strict, False, False)
434
 
        
435
 
        # Create the file list
436
 
        self._create_file_view()
437
 
        # Create the pending merges
438
 
        self._create_pending_merges()
439
 
        
440
 
        # Expand the corresponding expander
441
 
        if self._is_pending:
442
 
            self._expander_merges.set_expanded(True)
443
 
        else:
444
 
            self._expander_files.set_expanded(True)
445
 
        
446
 
        # Display dialog
447
 
        self.vbox.show_all()
448
 
    
449
 
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
450
 
        # FIXME: the diff window freezes for some reason
451
 
        treeselection = treeview.get_selection()
452
 
        (model, iter) = treeselection.get_selected()
453
 
        
454
 
        if iter is not None:
455
 
            from olive import DiffWindow
456
 
            
457
 
            _selected = model.get_value(iter, 1)
458
 
            
459
 
            diff = DiffWindow()
460
 
            parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
461
 
            diff.set_diff(self.wt.branch.nick, self.wt, parent_tree)
462
 
            try:
463
 
                diff.set_file(_selected)
464
 
            except errors.NoSuchFile:
465
 
                pass
466
 
            diff.show()
467
 
    
468
 
    def _on_commit_clicked(self, button):
469
 
        """ Commit button clicked handler. """
470
 
        textbuffer = self._textview_message.get_buffer()
471
 
        start, end = textbuffer.get_bounds()
472
 
        message = textbuffer.get_text(start, end).decode('utf-8')
473
 
        
474
 
        if not self.pending:
475
 
            specific_files = self._get_specific_files()
476
 
        else:
477
 
            specific_files = None
478
 
 
479
 
        if message == '':
480
 
            response = question_dialog(_('Commit with an empty message?'),
481
 
                                       _('You can describe your commit intent in the message.'))
482
 
            if response == gtk.RESPONSE_NO:
483
 
                # Kindly give focus to message area
484
 
                self._textview_message.grab_focus()
485
 
                return
486
 
 
487
 
        if self._is_checkout:
488
 
            local = self._check_local.get_active()
489
 
        else:
490
 
            local = False
491
 
        
492
 
        try:
493
 
            self.wt.commit(message,
494
 
                           allow_pointless=False,
495
 
                           strict=self._check_strict.get_active(),
496
 
                           local=local,
497
 
                           specific_files=specific_files)
498
 
        except errors.NotBranchError:
499
 
            error_dialog(_('Directory is not a branch'),
500
 
                         _('You can perform this action only in a branch.'))
501
 
            return
502
 
        except errors.LocalRequiresBoundBranch:
503
 
            error_dialog(_('Directory is not a checkout'),
504
 
                         _('You can perform local commit only on checkouts.'))
505
 
            return
506
 
        except errors.ConflictsInTree:
507
 
            error_dialog(_('Conflicts in tree'),
508
 
                         _('You need to resolve the conflicts before committing.'))
509
 
            return
510
 
        except errors.StrictCommitFailed:
511
 
            error_dialog(_('Strict commit failed'),
512
 
                         _('There are unknown files in the working tree.\nPlease add or delete them.'))
513
 
            return
514
 
        except errors.BoundBranchOutOfDate, errmsg:
515
 
            error_dialog(_('Bound branch is out of date'),
516
 
                         _('%s') % errmsg)
517
 
            return
518
 
        except errors.PointlessCommit:
519
 
            response = question_dialog(_('Commit with no changes?'),
520
 
                                       _('There are no changes in the working tree.'))
521
 
            if response == gtk.RESPONSE_YES:
522
 
                # Try to commit again
523
 
                try:
524
 
                    self.wt.commit(message,
525
 
                                   allow_pointless=True,
526
 
                                   strict=self._check_strict.get_active(),
527
 
                                   local=local,
528
 
                                   specific_files=specific_files)
529
 
                except errors.BzrError, msg:
530
 
                    error_dialog(_('Unknown bzr error'), str(msg))
531
 
                    return
532
 
                except Exception, msg:
533
 
                    error_dialog(_('Unknown error'), str(msg))
534
 
                    return
535
 
        except errors.BzrError, msg:
536
 
            error_dialog(_('Unknown bzr error'), str(msg))
537
 
            return
538
 
        except Exception, msg:
539
 
            error_dialog(_('Unknown error'), str(msg))
540
 
            return
541
 
        
542
 
        self.response(gtk.RESPONSE_OK)
543
 
 
544
 
    def _pending_merges(self, wt):
545
 
        """ Return a list of pending merges or None if there are none of them. """
546
 
        parents = wt.get_parent_ids()
547
 
        if len(parents) < 2:
548
 
            return None
549
 
        
550
 
        import re
551
 
        from bzrlib.osutils import format_date
552
 
        
553
 
        pending = parents[1:]
554
 
        branch = wt.branch
555
 
        last_revision = parents[0]
556
 
        
557
 
        if last_revision is not None:
558
 
            try:
559
 
                ignore = set(branch.repository.get_ancestry(last_revision))
560
 
            except errors.NoSuchRevision:
561
 
                # the last revision is a ghost : assume everything is new 
562
 
                # except for it
563
 
                ignore = set([None, last_revision])
564
 
        else:
565
 
            ignore = set([None])
566
 
        
567
 
        pm = []
568
 
        for merge in pending:
569
 
            ignore.add(merge)
570
 
            try:
571
 
                m_revision = branch.repository.get_revision(merge)
572
 
                
573
 
                rev = {}
574
 
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
575
 
                rev['summary'] = m_revision.get_summary()
576
 
                rev['date'] = format_date(m_revision.timestamp,
577
 
                                          m_revision.timezone or 0, 
578
 
                                          'original', date_fmt="%Y-%m-%d",
579
 
                                          show_offset=False)
580
 
                
581
 
                pm.append(rev)
582
 
                
583
 
                inner_merges = branch.repository.get_ancestry(merge)
584
 
                assert inner_merges[0] is None
585
 
                inner_merges.pop(0)
586
 
                inner_merges.reverse()
587
 
                for mmerge in inner_merges:
588
 
                    if mmerge in ignore:
589
 
                        continue
590
 
                    mm_revision = branch.repository.get_revision(mmerge)
591
 
                    
592
 
                    rev = {}
593
 
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
594
 
                    rev['summary'] = mm_revision.get_summary()
595
 
                    rev['date'] = format_date(mm_revision.timestamp,
596
 
                                              mm_revision.timezone or 0, 
597
 
                                              'original', date_fmt="%Y-%m-%d",
598
 
                                              show_offset=False)
599
 
                
600
 
                    pm.append(rev)
601
 
                    
602
 
                    ignore.add(mmerge)
603
 
            except errors.NoSuchRevision:
604
 
                print "DEBUG: NoSuchRevision:", merge
605
 
        
606
 
        return pm
607
 
 
608
 
    def _create_file_view(self):
609
 
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
610
 
                                         gobject.TYPE_STRING,    # [1] path to display
611
 
                                         gobject.TYPE_STRING,    # [2] changes type
612
 
                                         gobject.TYPE_STRING)    # [3] real path
613
 
        self._treeview_files.set_model(self._file_store)
614
 
        crt = gtk.CellRendererToggle()
615
 
        crt.set_property("activatable", True)
616
 
        crt.connect("toggled", self._toggle_commit, self._file_store)
617
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
618
 
                                     crt, active=0))
619
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
620
 
                                     gtk.CellRendererText(), text=1))
621
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
622
 
                                     gtk.CellRendererText(), text=2))
623
 
 
624
 
        for path, id, kind in self.delta.added:
625
 
            marker = osutils.kind_marker(kind)
626
 
            self._file_store.append([ True, path+marker, _('added'), path ])
627
 
 
628
 
        for path, id, kind in self.delta.removed:
629
 
            marker = osutils.kind_marker(kind)
630
 
            self._file_store.append([ True, path+marker, _('removed'), path ])
631
 
 
632
 
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
633
 
            marker = osutils.kind_marker(kind)
634
 
            if text_modified or meta_modified:
635
 
                changes = _('renamed and modified')
636
 
            else:
637
 
                changes = _('renamed')
638
 
            self._file_store.append([ True,
639
 
                                      oldpath+marker + '  =>  ' + newpath+marker,
640
 
                                      changes,
641
 
                                      newpath
642
 
                                    ])
643
 
 
644
 
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
645
 
            marker = osutils.kind_marker(kind)
646
 
            self._file_store.append([ True, path+marker, _('modified'), path ])
647
 
    
648
 
    def _create_pending_merges(self):
649
 
        if not self.pending:
650
 
            return
651
 
        
652
 
        liststore = gtk.ListStore(gobject.TYPE_STRING,
653
 
                                  gobject.TYPE_STRING,
654
 
                                  gobject.TYPE_STRING)
655
 
        self._treeview_merges.set_model(liststore)
656
 
        
657
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
658
 
                                            gtk.CellRendererText(), text=0))
659
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
660
 
                                            gtk.CellRendererText(), text=1))
661
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
662
 
                                            gtk.CellRendererText(), text=2))
663
 
        
664
 
        for item in self.pending:
665
 
            liststore.append([ item['date'],
666
 
                               item['committer'],
667
 
                               item['summary'] ])
668
 
    
669
 
    def _get_specific_files(self):
670
 
        ret = []
671
 
        it = self._file_store.get_iter_first()
672
 
        while it:
673
 
            if self._file_store.get_value(it, 0):
674
 
                # get real path from hidden column 3
675
 
                ret.append(self._file_store.get_value(it, 3))
676
 
            it = self._file_store.iter_next(it)
677
 
 
678
 
        return ret
679
 
    
680
 
    def _toggle_commit(self, cell, path, model):
681
 
        model[path][0] = not model[path][0]
682
 
        return