/b-gtk/fix-viz

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

« back to all changes in this revision

Viewing changes to commit.py

  • Committer: Jelmer Vernooij
  • Date: 2007-03-20 21:08:58 UTC
  • Revision ID: jelmer@samba.org-20070320210858-dj3gawaadvje001l
RemoveĀ unneededĀ imports.

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