/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
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
35
try:
36
    import dbus
37
    import dbus.glib
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
43
except ImportError:
44
    have_nm = False
45
135 by Jelmer Vernooij
Throw out the old CommitDialog code and use the new code instead, also for 'gcommit'.
46
class CommitDialog(gtk.Dialog):
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
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)
145 by Jelmer Vernooij
Fix some strings, import.
93
        self._check_strict = gtk.CheckButton(_("_Allow unknown files"),
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
94
                                             use_underline=True)
145 by Jelmer Vernooij
Fix some strings, import.
95
        self._expander_files = gtk.Expander(_("File(s) to commit"))
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
96
        self._vpaned_main = gtk.VPaned()
97
        self._scrolledwindow_files = gtk.ScrolledWindow()
98
        self._scrolledwindow_message = gtk.ScrolledWindow()
99
        self._treeview_files = gtk.TreeView()
100
        self._vbox_message = gtk.VBox()
145 by Jelmer Vernooij
Fix some strings, import.
101
        self._label_message = gtk.Label(_("Commit message:"))
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
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
123
        if self._is_pending:
124
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
125
                                                   gtk.POLICY_AUTOMATIC)
126
            self._treeview_files.set_sensitive(False)
127
        
128
        # Construct the dialog
129
        self.action_area.pack_end(self._button_commit)
130
        
131
        self._scrolledwindow_files.add(self._treeview_files)
132
        self._scrolledwindow_message.add(self._textview_message)
133
        
134
        self._expander_files.add(self._scrolledwindow_files)
135
        
136
        self._vbox_message.pack_start(self._label_message, False, False)
137
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
138
        
139
        if self._is_pending:        
140
            self._expander_merges.add(self._scrolledwindow_merges)
141
            self._scrolledwindow_merges.add(self._treeview_merges)
142
            self._vpaned_list.add1(self._expander_files)
143
            self._vpaned_list.add2(self._expander_merges)
144
            self._vpaned_main.add1(self._vpaned_list)
145
        else:
146
            self._vpaned_main.add1(self._expander_files)
147
148
        self._vpaned_main.add2(self._vbox_message)
149
        
150
        self.vbox.pack_start(self._vpaned_main, True, True)
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
151
        if self._is_checkout and not have_nm:
152
            self._check_local = gtk.CheckButton(_("_Only commit locally"),
153
                                                use_underline=True)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
154
            self.vbox.pack_start(self._check_local, False, False)
155
        self.vbox.pack_start(self._check_strict, False, False)
156
        
157
        # Create the file list
158
        self._create_file_view()
159
        # Create the pending merges
160
        self._create_pending_merges()
161
        
162
        # Expand the corresponding expander
163
        if self._is_pending:
164
            self._expander_merges.set_expanded(True)
165
        else:
166
            self._expander_files.set_expanded(True)
167
        
168
        # Display dialog
169
        self.vbox.show_all()
170
    
171
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
172
        # FIXME: the diff window freezes for some reason
173
        treeselection = treeview.get_selection()
174
        (model, iter) = treeselection.get_selected()
175
        
176
        if iter is not None:
153 by Jelmer Vernooij
Fix references to dialog.
177
            from diff import DiffWindow
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
178
            
179
            _selected = model.get_value(iter, 1)
180
            
181
            diff = DiffWindow()
182
            parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
183
            diff.set_diff(self.wt.branch.nick, self.wt, parent_tree)
184
            try:
185
                diff.set_file(_selected)
186
            except errors.NoSuchFile:
187
                pass
188
            diff.show()
189
    
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.
190
    @show_bzr_error
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
191
    def _on_commit_clicked(self, button):
192
        """ Commit button clicked handler. """
193
        textbuffer = self._textview_message.get_buffer()
194
        start, end = textbuffer.get_bounds()
195
        message = textbuffer.get_text(start, end).decode('utf-8')
196
        
197
        if not self.pending:
198
            specific_files = self._get_specific_files()
199
        else:
200
            specific_files = None
201
202
        if message == '':
203
            response = question_dialog(_('Commit with an empty message?'),
204
                                       _('You can describe your commit intent in the message.'))
205
            if response == gtk.RESPONSE_NO:
206
                # Kindly give focus to message area
207
                self._textview_message.grab_focus()
208
                return
209
210
        if self._is_checkout:
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
211
            if have_nm:
212
                local = (dbus_iface.state() != 3)
213
            else:
214
                local = self._check_local.get_active()
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
215
        else:
216
            local = False
217
        
218
        try:
219
            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.
220
                       allow_pointless=False,
221
                       strict=self._check_strict.get_active(),
222
                       local=local,
223
                       specific_files=specific_files)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
224
        except errors.PointlessCommit:
225
            response = question_dialog(_('Commit with no changes?'),
226
                                       _('There are no changes in the working tree.'))
227
            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.
228
                self.wt.commit(message,
229
                               allow_pointless=True,
230
                               strict=self._check_strict.get_active(),
231
                               local=local,
232
                               specific_files=specific_files)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
233
        self.response(gtk.RESPONSE_OK)
234
235
    def _pending_merges(self, wt):
236
        """ Return a list of pending merges or None if there are none of them. """
237
        parents = wt.get_parent_ids()
238
        if len(parents) < 2:
239
            return None
240
        
241
        import re
242
        from bzrlib.osutils import format_date
243
        
244
        pending = parents[1:]
245
        branch = wt.branch
246
        last_revision = parents[0]
247
        
248
        if last_revision is not None:
249
            try:
250
                ignore = set(branch.repository.get_ancestry(last_revision))
251
            except errors.NoSuchRevision:
252
                # the last revision is a ghost : assume everything is new 
253
                # except for it
254
                ignore = set([None, last_revision])
255
        else:
256
            ignore = set([None])
257
        
258
        pm = []
259
        for merge in pending:
260
            ignore.add(merge)
261
            try:
262
                m_revision = branch.repository.get_revision(merge)
263
                
264
                rev = {}
265
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
266
                rev['summary'] = m_revision.get_summary()
267
                rev['date'] = format_date(m_revision.timestamp,
268
                                          m_revision.timezone or 0, 
269
                                          'original', date_fmt="%Y-%m-%d",
270
                                          show_offset=False)
271
                
272
                pm.append(rev)
273
                
274
                inner_merges = branch.repository.get_ancestry(merge)
275
                assert inner_merges[0] is None
276
                inner_merges.pop(0)
277
                inner_merges.reverse()
278
                for mmerge in inner_merges:
279
                    if mmerge in ignore:
280
                        continue
281
                    mm_revision = branch.repository.get_revision(mmerge)
282
                    
283
                    rev = {}
284
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
285
                    rev['summary'] = mm_revision.get_summary()
286
                    rev['date'] = format_date(mm_revision.timestamp,
287
                                              mm_revision.timezone or 0, 
288
                                              'original', date_fmt="%Y-%m-%d",
289
                                              show_offset=False)
290
                
291
                    pm.append(rev)
292
                    
293
                    ignore.add(mmerge)
294
            except errors.NoSuchRevision:
295
                print "DEBUG: NoSuchRevision:", merge
296
        
297
        return pm
298
299
    def _create_file_view(self):
300
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
301
                                         gobject.TYPE_STRING,    # [1] path to display
302
                                         gobject.TYPE_STRING,    # [2] changes type
303
                                         gobject.TYPE_STRING)    # [3] real path
304
        self._treeview_files.set_model(self._file_store)
305
        crt = gtk.CellRendererToggle()
306
        crt.set_property("activatable", True)
307
        crt.connect("toggled", self._toggle_commit, self._file_store)
308
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
309
                                     crt, active=0))
310
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
311
                                     gtk.CellRendererText(), text=1))
312
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
313
                                     gtk.CellRendererText(), text=2))
314
315
        for path, id, kind in self.delta.added:
316
            marker = osutils.kind_marker(kind)
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
317
            if self.selected is not None:
318
                if path == os.path.join(self.wtpath, self.selected):
319
                    self._file_store.append([ True, path+marker, _('added'), path ])
320
                else:
321
                    self._file_store.append([ False, path+marker, _('added'), path ])
322
            else:
323
                self._file_store.append([ True, path+marker, _('added'), path ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
324
325
        for path, id, kind in self.delta.removed:
326
            marker = osutils.kind_marker(kind)
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
327
            if self.selected is not None:
328
                if path == os.path.join(self.wtpath, self.selected):
329
                    self._file_store.append([ True, path+marker, _('removed'), path ])
330
                else:
331
                    self._file_store.append([ False, path+marker, _('removed'), path ])
332
            else:
333
                self._file_store.append([ True, path+marker, _('removed'), path ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
334
335
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
336
            marker = osutils.kind_marker(kind)
337
            if text_modified or meta_modified:
338
                changes = _('renamed and modified')
339
            else:
340
                changes = _('renamed')
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
341
            if self.selected is not None:
342
                if newpath == os.path.join(self.wtpath, self.selected):
343
                    self._file_store.append([ True,
344
                                              oldpath+marker + '  =>  ' + newpath+marker,
345
                                              changes,
346
                                              newpath
347
                                            ])
348
                else:
349
                    self._file_store.append([ False,
350
                                              oldpath+marker + '  =>  ' + newpath+marker,
351
                                              changes,
352
                                              newpath
353
                                            ])
354
            else:
355
                self._file_store.append([ True,
356
                                          oldpath+marker + '  =>  ' + newpath+marker,
357
                                          changes,
358
                                          newpath
359
                                        ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
360
361
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
362
            marker = osutils.kind_marker(kind)
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
363
            if self.selected is not None:
364
                if path == os.path.join(self.wtpath, self.selected):
365
                    self._file_store.append([ True, path+marker, _('modified'), path ])
366
                else:
367
                    self._file_store.append([ False, path+marker, _('modified'), path ])
368
            else:
369
                self._file_store.append([ True, path+marker, _('modified'), path ])
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
370
    
371
    def _create_pending_merges(self):
372
        if not self.pending:
373
            return
374
        
375
        liststore = gtk.ListStore(gobject.TYPE_STRING,
376
                                  gobject.TYPE_STRING,
377
                                  gobject.TYPE_STRING)
378
        self._treeview_merges.set_model(liststore)
379
        
380
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
381
                                            gtk.CellRendererText(), text=0))
382
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
383
                                            gtk.CellRendererText(), text=1))
384
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
385
                                            gtk.CellRendererText(), text=2))
386
        
387
        for item in self.pending:
388
            liststore.append([ item['date'],
389
                               item['committer'],
390
                               item['summary'] ])
391
    
392
    def _get_specific_files(self):
393
        ret = []
394
        it = self._file_store.get_iter_first()
395
        while it:
396
            if self._file_store.get_value(it, 0):
397
                # get real path from hidden column 3
398
                ret.append(self._file_store.get_value(it, 3))
399
            it = self._file_store.iter_next(it)
400
401
        return ret
402
    
403
    def _toggle_commit(self, cell, path, model):
404
        model[path][0] = not model[path][0]
405
        return