/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
2
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
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.
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
7
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
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.
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
12
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
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
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
22
0.13.11 by Jelmer Vernooij
Bunch of small fixes, cleanups and simplifications.
23
import gtk
24
import gobject
25
import pango
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
26
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
27
import os.path
28
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
29
import bzrlib.errors as errors
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
30
from bzrlib import osutils
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
31
153 by Jelmer Vernooij
Fix references to dialog.
32
from dialog import error_dialog, question_dialog
132 by Jelmer Vernooij
Use decorator for catching and showing bzr-gtk errors graphically. Eventually, this should go away and should be handled by the ui factory.
33
from errors import show_bzr_error
93.1.6 by Alexander Belchenko
detecting name of glade file doing in separate module (olive.gladefile)
34
135 by Jelmer Vernooij
Throw out the old CommitDialog code and use the new code instead, also for 'gcommit'.
35
class CommitDialog(gtk.Dialog):
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
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:
145 by Jelmer Vernooij
Fix some strings, import.
83
            self._check_local = gtk.CheckButton(_("_Only commit locally"),
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
84
                                                use_underline=True)
145 by Jelmer Vernooij
Fix some strings, import.
85
        self._check_strict = gtk.CheckButton(_("_Allow unknown files"),
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
86
                                             use_underline=True)
145 by Jelmer Vernooij
Fix some strings, import.
87
        self._expander_files = gtk.Expander(_("File(s) to commit"))
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
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()
145 by Jelmer Vernooij
Fix some strings, import.
93
        self._label_message = gtk.Label(_("Commit message:"))
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
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)
126.1.29 by Szilveszter Farkas (Phanatic)
Default button set on Commit dialog (Fixed: #83030)
114
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
115
116
        if self._is_pending:
117
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
118
                                                   gtk.POLICY_AUTOMATIC)
119
            self._treeview_files.set_sensitive(False)
120
        
121
        # Construct the dialog
122
        self.action_area.pack_end(self._button_commit)
123
        
124
        self._scrolledwindow_files.add(self._treeview_files)
125
        self._scrolledwindow_message.add(self._textview_message)
126
        
127
        self._expander_files.add(self._scrolledwindow_files)
128
        
129
        self._vbox_message.pack_start(self._label_message, False, False)
130
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
131
        
132
        if self._is_pending:        
133
            self._expander_merges.add(self._scrolledwindow_merges)
134
            self._scrolledwindow_merges.add(self._treeview_merges)
135
            self._vpaned_list.add1(self._expander_files)
136
            self._vpaned_list.add2(self._expander_merges)
137
            self._vpaned_main.add1(self._vpaned_list)
138
        else:
139
            self._vpaned_main.add1(self._expander_files)
140
141
        self._vpaned_main.add2(self._vbox_message)
142
        
143
        self.vbox.pack_start(self._vpaned_main, True, True)
144
        if self._is_checkout:
145
            self.vbox.pack_start(self._check_local, False, False)
146
        self.vbox.pack_start(self._check_strict, False, False)
147
        
148
        # Create the file list
149
        self._create_file_view()
150
        # Create the pending merges
151
        self._create_pending_merges()
152
        
153
        # Expand the corresponding expander
154
        if self._is_pending:
155
            self._expander_merges.set_expanded(True)
156
        else:
157
            self._expander_files.set_expanded(True)
158
        
159
        # Display dialog
160
        self.vbox.show_all()
126.1.29 by Szilveszter Farkas (Phanatic)
Default button set on Commit dialog (Fixed: #83030)
161
        
162
        # Default to Commit button
163
        self._button_commit.grab_default()
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
164
    
165
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
166
        # FIXME: the diff window freezes for some reason
167
        treeselection = treeview.get_selection()
168
        (model, iter) = treeselection.get_selected()
169
        
170
        if iter is not None:
153 by Jelmer Vernooij
Fix references to dialog.
171
            from diff import DiffWindow
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
172
            
173
            _selected = model.get_value(iter, 1)
174
            
175
            diff = DiffWindow()
126.1.28 by Szilveszter Farkas (Phanatic)
The Diff window won't freeze when calling from the Commit dialog.
176
            diff.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
177
            diff.set_modal(True)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
178
            parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
179
            diff.set_diff(self.wt.branch.nick, self.wt, parent_tree)
180
            try:
181
                diff.set_file(_selected)
182
            except errors.NoSuchFile:
183
                pass
184
            diff.show()
185
    
132 by Jelmer Vernooij
Use decorator for catching and showing bzr-gtk errors graphically. Eventually, this should go away and should be handled by the ui factory.
186
    @show_bzr_error
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
187
    def _on_commit_clicked(self, button):
188
        """ Commit button clicked handler. """
189
        textbuffer = self._textview_message.get_buffer()
190
        start, end = textbuffer.get_bounds()
191
        message = textbuffer.get_text(start, end).decode('utf-8')
192
        
193
        if not self.pending:
194
            specific_files = self._get_specific_files()
195
        else:
196
            specific_files = None
197
198
        if message == '':
199
            response = question_dialog(_('Commit with an empty message?'),
200
                                       _('You can describe your commit intent in the message.'))
201
            if response == gtk.RESPONSE_NO:
202
                # Kindly give focus to message area
203
                self._textview_message.grab_focus()
204
                return
205
206
        if self._is_checkout:
207
            local = self._check_local.get_active()
208
        else:
209
            local = False
210
        
211
        try:
212
            self.wt.commit(message,
132 by Jelmer Vernooij
Use decorator for catching and showing bzr-gtk errors graphically. Eventually, this should go away and should be handled by the ui factory.
213
                       allow_pointless=False,
214
                       strict=self._check_strict.get_active(),
215
                       local=local,
216
                       specific_files=specific_files)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
217
        except errors.PointlessCommit:
218
            response = question_dialog(_('Commit with no changes?'),
219
                                       _('There are no changes in the working tree.'))
220
            if response == gtk.RESPONSE_YES:
132 by Jelmer Vernooij
Use decorator for catching and showing bzr-gtk errors graphically. Eventually, this should go away and should be handled by the ui factory.
221
                self.wt.commit(message,
222
                               allow_pointless=True,
223
                               strict=self._check_strict.get_active(),
224
                               local=local,
225
                               specific_files=specific_files)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
226
        self.response(gtk.RESPONSE_OK)
227
228
    def _pending_merges(self, wt):
229
        """ Return a list of pending merges or None if there are none of them. """
230
        parents = wt.get_parent_ids()
231
        if len(parents) < 2:
232
            return None
233
        
234
        import re
235
        from bzrlib.osutils import format_date
236
        
237
        pending = parents[1:]
238
        branch = wt.branch
239
        last_revision = parents[0]
240
        
241
        if last_revision is not None:
242
            try:
243
                ignore = set(branch.repository.get_ancestry(last_revision))
244
            except errors.NoSuchRevision:
245
                # the last revision is a ghost : assume everything is new 
246
                # except for it
247
                ignore = set([None, last_revision])
248
        else:
249
            ignore = set([None])
250
        
251
        pm = []
252
        for merge in pending:
253
            ignore.add(merge)
254
            try:
255
                m_revision = branch.repository.get_revision(merge)
256
                
257
                rev = {}
258
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
259
                rev['summary'] = m_revision.get_summary()
260
                rev['date'] = format_date(m_revision.timestamp,
261
                                          m_revision.timezone or 0, 
262
                                          'original', date_fmt="%Y-%m-%d",
263
                                          show_offset=False)
264
                
265
                pm.append(rev)
266
                
267
                inner_merges = branch.repository.get_ancestry(merge)
268
                assert inner_merges[0] is None
269
                inner_merges.pop(0)
270
                inner_merges.reverse()
271
                for mmerge in inner_merges:
272
                    if mmerge in ignore:
273
                        continue
274
                    mm_revision = branch.repository.get_revision(mmerge)
275
                    
276
                    rev = {}
277
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
278
                    rev['summary'] = mm_revision.get_summary()
279
                    rev['date'] = format_date(mm_revision.timestamp,
280
                                              mm_revision.timezone or 0, 
281
                                              'original', date_fmt="%Y-%m-%d",
282
                                              show_offset=False)
283
                
284
                    pm.append(rev)
285
                    
286
                    ignore.add(mmerge)
287
            except errors.NoSuchRevision:
288
                print "DEBUG: NoSuchRevision:", merge
289
        
290
        return pm
291
292
    def _create_file_view(self):
293
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
294
                                         gobject.TYPE_STRING,    # [1] path to display
295
                                         gobject.TYPE_STRING,    # [2] changes type
296
                                         gobject.TYPE_STRING)    # [3] real path
297
        self._treeview_files.set_model(self._file_store)
298
        crt = gtk.CellRendererToggle()
299
        crt.set_property("activatable", True)
300
        crt.connect("toggled", self._toggle_commit, self._file_store)
301
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
302
                                     crt, active=0))
303
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
304
                                     gtk.CellRendererText(), text=1))
305
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
306
                                     gtk.CellRendererText(), text=2))
307
308
        for path, id, kind in self.delta.added:
309
            marker = osutils.kind_marker(kind)
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
310
            if self.selected is not None:
311
                if path == os.path.join(self.wtpath, self.selected):
312
                    self._file_store.append([ True, path+marker, _('added'), path ])
313
                else:
314
                    self._file_store.append([ False, path+marker, _('added'), path ])
315
            else:
316
                self._file_store.append([ True, path+marker, _('added'), path ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
317
318
        for path, id, kind in self.delta.removed:
319
            marker = osutils.kind_marker(kind)
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
320
            if self.selected is not None:
321
                if path == os.path.join(self.wtpath, self.selected):
322
                    self._file_store.append([ True, path+marker, _('removed'), path ])
323
                else:
324
                    self._file_store.append([ False, path+marker, _('removed'), path ])
325
            else:
326
                self._file_store.append([ True, path+marker, _('removed'), path ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
327
328
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
329
            marker = osutils.kind_marker(kind)
330
            if text_modified or meta_modified:
331
                changes = _('renamed and modified')
332
            else:
333
                changes = _('renamed')
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
334
            if self.selected is not None:
335
                if newpath == os.path.join(self.wtpath, self.selected):
336
                    self._file_store.append([ True,
337
                                              oldpath+marker + '  =>  ' + newpath+marker,
338
                                              changes,
339
                                              newpath
340
                                            ])
341
                else:
342
                    self._file_store.append([ False,
343
                                              oldpath+marker + '  =>  ' + newpath+marker,
344
                                              changes,
345
                                              newpath
346
                                            ])
347
            else:
348
                self._file_store.append([ True,
349
                                          oldpath+marker + '  =>  ' + newpath+marker,
350
                                          changes,
351
                                          newpath
352
                                        ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
353
354
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
355
            marker = osutils.kind_marker(kind)
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
356
            if self.selected is not None:
357
                if path == os.path.join(self.wtpath, self.selected):
358
                    self._file_store.append([ True, path+marker, _('modified'), path ])
359
                else:
360
                    self._file_store.append([ False, path+marker, _('modified'), path ])
361
            else:
362
                self._file_store.append([ True, path+marker, _('modified'), path ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
363
    
364
    def _create_pending_merges(self):
365
        if not self.pending:
366
            return
367
        
368
        liststore = gtk.ListStore(gobject.TYPE_STRING,
369
                                  gobject.TYPE_STRING,
370
                                  gobject.TYPE_STRING)
371
        self._treeview_merges.set_model(liststore)
372
        
373
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
374
                                            gtk.CellRendererText(), text=0))
375
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
376
                                            gtk.CellRendererText(), text=1))
377
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
378
                                            gtk.CellRendererText(), text=2))
379
        
380
        for item in self.pending:
381
            liststore.append([ item['date'],
382
                               item['committer'],
383
                               item['summary'] ])
384
    
385
    def _get_specific_files(self):
386
        ret = []
387
        it = self._file_store.get_iter_first()
388
        while it:
389
            if self._file_store.get_value(it, 0):
390
                # get real path from hidden column 3
391
                ret.append(self._file_store.get_value(it, 3))
392
            it = self._file_store.iter_next(it)
393
394
        return ret
395
    
396
    def _toggle_commit(self, cell, path, model):
397
        model[path][0] = not model[path][0]
398
        return