/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to commit.py

  • Committer: Jelmer Vernooij
  • Date: 2006-05-19 16:37:13 UTC
  • Revision ID: jelmer@samba.org-20060519163713-be77b31c72cbc7e8
Move visualisation code to a separate directory, preparing for bundling 
the GTK+ plugins for bzr.

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 gobject
25
 
import pango
26
 
 
27
 
import os.path
28
 
 
29
 
import bzrlib.errors as errors
30
 
from bzrlib import osutils
31
 
 
32
 
from olive.dialog import error_dialog, question_dialog
33
 
from errors import show_bzr_error
34
 
 
35
 
class CommitDialog(gtk.Dialog):
36
 
    """ New implementation of the Commit dialog. """
37
 
    def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
38
 
        """ Initialize the Commit Dialog. """
39
 
        gtk.Dialog.__init__(self, title="Commit - Olive",
40
 
                                  parent=parent,
41
 
                                  flags=0,
42
 
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
43
 
        
44
 
        # Get arguments
45
 
        self.wt = wt
46
 
        self.wtpath = wtpath
47
 
        self.notbranch = notbranch
48
 
        self.selected = selected
49
 
        
50
 
        # Set the delta
51
 
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
52
 
        self.delta = self.wt.changes_from(self.old_tree)
53
 
        
54
 
        # Get pending merges
55
 
        self.pending = self._pending_merges(self.wt)
56
 
        
57
 
        # Do some preliminary checks
58
 
        self._is_checkout = False
59
 
        self._is_pending = False
60
 
        if self.wt is None and not self.notbranch:
61
 
            error_dialog(_('Directory does not have a working tree'),
62
 
                         _('Operation aborted.'))
63
 
            self.close()
64
 
            return
65
 
 
66
 
        if self.notbranch:
67
 
            error_dialog(_('Directory is not a branch'),
68
 
                         _('You can perform this action only in a branch.'))
69
 
            self.close()
70
 
            return
71
 
        else:
72
 
            if self.wt.branch.get_bound_location() is not None:
73
 
                # we have a checkout, so the local commit checkbox must appear
74
 
                self._is_checkout = True
75
 
            
76
 
            if self.pending:
77
 
                # There are pending merges, file selection not supported
78
 
                self._is_pending = True
79
 
        
80
 
        # Create the widgets
81
 
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
82
 
        if self._is_checkout:
83
 
            self._check_local = gtk.CheckButton(_("_Only commit locally"),
84
 
                                                use_underline=True)
85
 
        self._check_strict = gtk.CheckButton(_("_Allow unknown files"),
86
 
                                             use_underline=True)
87
 
        self._expander_files = gtk.Expander(_("File(s) to commit"))
88
 
        self._vpaned_main = gtk.VPaned()
89
 
        self._scrolledwindow_files = gtk.ScrolledWindow()
90
 
        self._scrolledwindow_message = gtk.ScrolledWindow()
91
 
        self._treeview_files = gtk.TreeView()
92
 
        self._vbox_message = gtk.VBox()
93
 
        self._label_message = gtk.Label(_("Commit message:"))
94
 
        self._textview_message = gtk.TextView()
95
 
        
96
 
        if self._is_pending:
97
 
            self._expander_merges = gtk.Expander(_("Pending merges"))
98
 
            self._vpaned_list = gtk.VPaned()
99
 
            self._scrolledwindow_merges = gtk.ScrolledWindow()
100
 
            self._treeview_merges = gtk.TreeView()
101
 
 
102
 
        # Set callbacks
103
 
        self._button_commit.connect('clicked', self._on_commit_clicked)
104
 
        self._treeview_files.connect('row_activated', self._on_treeview_files_row_activated)
105
 
        
106
 
        # Set properties
107
 
        self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
108
 
                                              gtk.POLICY_AUTOMATIC)
109
 
        self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
110
 
                                                gtk.POLICY_AUTOMATIC)
111
 
        self._textview_message.modify_font(pango.FontDescription("Monospace"))
112
 
        self.set_default_size(500, 500)
113
 
        self._vpaned_main.set_position(200)
114
 
 
115
 
        if self._is_pending:
116
 
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
117
 
                                                   gtk.POLICY_AUTOMATIC)
118
 
            self._treeview_files.set_sensitive(False)
119
 
        
120
 
        # Construct the dialog
121
 
        self.action_area.pack_end(self._button_commit)
122
 
        
123
 
        self._scrolledwindow_files.add(self._treeview_files)
124
 
        self._scrolledwindow_message.add(self._textview_message)
125
 
        
126
 
        self._expander_files.add(self._scrolledwindow_files)
127
 
        
128
 
        self._vbox_message.pack_start(self._label_message, False, False)
129
 
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
130
 
        
131
 
        if self._is_pending:        
132
 
            self._expander_merges.add(self._scrolledwindow_merges)
133
 
            self._scrolledwindow_merges.add(self._treeview_merges)
134
 
            self._vpaned_list.add1(self._expander_files)
135
 
            self._vpaned_list.add2(self._expander_merges)
136
 
            self._vpaned_main.add1(self._vpaned_list)
137
 
        else:
138
 
            self._vpaned_main.add1(self._expander_files)
139
 
 
140
 
        self._vpaned_main.add2(self._vbox_message)
141
 
        
142
 
        self.vbox.pack_start(self._vpaned_main, True, True)
143
 
        if self._is_checkout:
144
 
            self.vbox.pack_start(self._check_local, False, False)
145
 
        self.vbox.pack_start(self._check_strict, False, False)
146
 
        
147
 
        # Create the file list
148
 
        self._create_file_view()
149
 
        # Create the pending merges
150
 
        self._create_pending_merges()
151
 
        
152
 
        # Expand the corresponding expander
153
 
        if self._is_pending:
154
 
            self._expander_merges.set_expanded(True)
155
 
        else:
156
 
            self._expander_files.set_expanded(True)
157
 
        
158
 
        # Display dialog
159
 
        self.vbox.show_all()
160
 
    
161
 
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
162
 
        # FIXME: the diff window freezes for some reason
163
 
        treeselection = treeview.get_selection()
164
 
        (model, iter) = treeselection.get_selected()
165
 
        
166
 
        if iter is not None:
167
 
            from olive import DiffWindow
168
 
            
169
 
            _selected = model.get_value(iter, 1)
170
 
            
171
 
            diff = DiffWindow()
172
 
            parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
173
 
            diff.set_diff(self.wt.branch.nick, self.wt, parent_tree)
174
 
            try:
175
 
                diff.set_file(_selected)
176
 
            except errors.NoSuchFile:
177
 
                pass
178
 
            diff.show()
179
 
    
180
 
    @show_bzr_error
181
 
    def _on_commit_clicked(self, button):
182
 
        """ Commit button clicked handler. """
183
 
        textbuffer = self._textview_message.get_buffer()
184
 
        start, end = textbuffer.get_bounds()
185
 
        message = textbuffer.get_text(start, end).decode('utf-8')
186
 
        
187
 
        if not self.pending:
188
 
            specific_files = self._get_specific_files()
189
 
        else:
190
 
            specific_files = None
191
 
 
192
 
        if message == '':
193
 
            response = question_dialog(_('Commit with an empty message?'),
194
 
                                       _('You can describe your commit intent in the message.'))
195
 
            if response == gtk.RESPONSE_NO:
196
 
                # Kindly give focus to message area
197
 
                self._textview_message.grab_focus()
198
 
                return
199
 
 
200
 
        if self._is_checkout:
201
 
            local = self._check_local.get_active()
202
 
        else:
203
 
            local = False
204
 
        
205
 
        try:
206
 
            self.wt.commit(message,
207
 
                       allow_pointless=False,
208
 
                       strict=self._check_strict.get_active(),
209
 
                       local=local,
210
 
                       specific_files=specific_files)
211
 
        except errors.PointlessCommit:
212
 
            response = question_dialog(_('Commit with no changes?'),
213
 
                                       _('There are no changes in the working tree.'))
214
 
            if response == gtk.RESPONSE_YES:
215
 
                self.wt.commit(message,
216
 
                               allow_pointless=True,
217
 
                               strict=self._check_strict.get_active(),
218
 
                               local=local,
219
 
                               specific_files=specific_files)
220
 
        self.response(gtk.RESPONSE_OK)
221
 
 
222
 
    def _pending_merges(self, wt):
223
 
        """ Return a list of pending merges or None if there are none of them. """
224
 
        parents = wt.get_parent_ids()
225
 
        if len(parents) < 2:
226
 
            return None
227
 
        
228
 
        import re
229
 
        from bzrlib.osutils import format_date
230
 
        
231
 
        pending = parents[1:]
232
 
        branch = wt.branch
233
 
        last_revision = parents[0]
234
 
        
235
 
        if last_revision is not None:
236
 
            try:
237
 
                ignore = set(branch.repository.get_ancestry(last_revision))
238
 
            except errors.NoSuchRevision:
239
 
                # the last revision is a ghost : assume everything is new 
240
 
                # except for it
241
 
                ignore = set([None, last_revision])
242
 
        else:
243
 
            ignore = set([None])
244
 
        
245
 
        pm = []
246
 
        for merge in pending:
247
 
            ignore.add(merge)
248
 
            try:
249
 
                m_revision = branch.repository.get_revision(merge)
250
 
                
251
 
                rev = {}
252
 
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
253
 
                rev['summary'] = m_revision.get_summary()
254
 
                rev['date'] = format_date(m_revision.timestamp,
255
 
                                          m_revision.timezone or 0, 
256
 
                                          'original', date_fmt="%Y-%m-%d",
257
 
                                          show_offset=False)
258
 
                
259
 
                pm.append(rev)
260
 
                
261
 
                inner_merges = branch.repository.get_ancestry(merge)
262
 
                assert inner_merges[0] is None
263
 
                inner_merges.pop(0)
264
 
                inner_merges.reverse()
265
 
                for mmerge in inner_merges:
266
 
                    if mmerge in ignore:
267
 
                        continue
268
 
                    mm_revision = branch.repository.get_revision(mmerge)
269
 
                    
270
 
                    rev = {}
271
 
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
272
 
                    rev['summary'] = mm_revision.get_summary()
273
 
                    rev['date'] = format_date(mm_revision.timestamp,
274
 
                                              mm_revision.timezone or 0, 
275
 
                                              'original', date_fmt="%Y-%m-%d",
276
 
                                              show_offset=False)
277
 
                
278
 
                    pm.append(rev)
279
 
                    
280
 
                    ignore.add(mmerge)
281
 
            except errors.NoSuchRevision:
282
 
                print "DEBUG: NoSuchRevision:", merge
283
 
        
284
 
        return pm
285
 
 
286
 
    def _create_file_view(self):
287
 
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
288
 
                                         gobject.TYPE_STRING,    # [1] path to display
289
 
                                         gobject.TYPE_STRING,    # [2] changes type
290
 
                                         gobject.TYPE_STRING)    # [3] real path
291
 
        self._treeview_files.set_model(self._file_store)
292
 
        crt = gtk.CellRendererToggle()
293
 
        crt.set_property("activatable", True)
294
 
        crt.connect("toggled", self._toggle_commit, self._file_store)
295
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
296
 
                                     crt, active=0))
297
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
298
 
                                     gtk.CellRendererText(), text=1))
299
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
300
 
                                     gtk.CellRendererText(), text=2))
301
 
 
302
 
        for path, id, kind in self.delta.added:
303
 
            marker = osutils.kind_marker(kind)
304
 
            if self.selected is not None:
305
 
                if path == os.path.join(self.wtpath, self.selected):
306
 
                    self._file_store.append([ True, path+marker, _('added'), path ])
307
 
                else:
308
 
                    self._file_store.append([ False, path+marker, _('added'), path ])
309
 
            else:
310
 
                self._file_store.append([ True, path+marker, _('added'), path ])
311
 
 
312
 
        for path, id, kind in self.delta.removed:
313
 
            marker = osutils.kind_marker(kind)
314
 
            if self.selected is not None:
315
 
                if path == os.path.join(self.wtpath, self.selected):
316
 
                    self._file_store.append([ True, path+marker, _('removed'), path ])
317
 
                else:
318
 
                    self._file_store.append([ False, path+marker, _('removed'), path ])
319
 
            else:
320
 
                self._file_store.append([ True, path+marker, _('removed'), path ])
321
 
 
322
 
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
323
 
            marker = osutils.kind_marker(kind)
324
 
            if text_modified or meta_modified:
325
 
                changes = _('renamed and modified')
326
 
            else:
327
 
                changes = _('renamed')
328
 
            if self.selected is not None:
329
 
                if newpath == os.path.join(self.wtpath, self.selected):
330
 
                    self._file_store.append([ True,
331
 
                                              oldpath+marker + '  =>  ' + newpath+marker,
332
 
                                              changes,
333
 
                                              newpath
334
 
                                            ])
335
 
                else:
336
 
                    self._file_store.append([ False,
337
 
                                              oldpath+marker + '  =>  ' + newpath+marker,
338
 
                                              changes,
339
 
                                              newpath
340
 
                                            ])
341
 
            else:
342
 
                self._file_store.append([ True,
343
 
                                          oldpath+marker + '  =>  ' + newpath+marker,
344
 
                                          changes,
345
 
                                          newpath
346
 
                                        ])
347
 
 
348
 
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
349
 
            marker = osutils.kind_marker(kind)
350
 
            if self.selected is not None:
351
 
                if path == os.path.join(self.wtpath, self.selected):
352
 
                    self._file_store.append([ True, path+marker, _('modified'), path ])
353
 
                else:
354
 
                    self._file_store.append([ False, path+marker, _('modified'), path ])
355
 
            else:
356
 
                self._file_store.append([ True, path+marker, _('modified'), path ])
357
 
    
358
 
    def _create_pending_merges(self):
359
 
        if not self.pending:
360
 
            return
361
 
        
362
 
        liststore = gtk.ListStore(gobject.TYPE_STRING,
363
 
                                  gobject.TYPE_STRING,
364
 
                                  gobject.TYPE_STRING)
365
 
        self._treeview_merges.set_model(liststore)
366
 
        
367
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
368
 
                                            gtk.CellRendererText(), text=0))
369
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
370
 
                                            gtk.CellRendererText(), text=1))
371
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
372
 
                                            gtk.CellRendererText(), text=2))
373
 
        
374
 
        for item in self.pending:
375
 
            liststore.append([ item['date'],
376
 
                               item['committer'],
377
 
                               item['summary'] ])
378
 
    
379
 
    def _get_specific_files(self):
380
 
        ret = []
381
 
        it = self._file_store.get_iter_first()
382
 
        while it:
383
 
            if self._file_store.get_value(it, 0):
384
 
                # get real path from hidden column 3
385
 
                ret.append(self._file_store.get_value(it, 3))
386
 
            it = self._file_store.iter_next(it)
387
 
 
388
 
        return ret
389
 
    
390
 
    def _toggle_commit(self, cell, path, model):
391
 
        model[path][0] = not model[path][0]
392
 
        return