/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: Jelmer Vernooij
  • Date: 2006-09-30 10:21:43 UTC
  • Revision ID: jelmer@samba.org-20060930102143-c0ef64d6ca860c21
Merge some files from Olive and bzr-gtk.

Show diffs side-by-side

added added

removed removed

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