/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: Szilveszter Farkas (Phanatic)
  • Date: 2007-03-15 16:23:15 UTC
  • mfrom: (170 trunk)
  • mto: (170.1.3 trunk)
  • mto: This revision was merged to the branch mainline in revision 172.
  • Revision ID: szilveszter.farkas@gmail.com-20070315162315-rs1sbxjh31n314zc
MergeĀ fromĀ trunk.

Show diffs side-by-side

added added

removed removed

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