/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-02-16 17:17:28 UTC
  • mto: (157.1.9 trunk) (170.1.3 trunk)
  • mto: This revision was merged to the branch mainline in revision 160.
  • Revision ID: szilveszter.farkas@gmail.com-20070216171728-i97utnlbukrkvzli
Updated genpot.sh. Regenerated POT file. Old Hungarian translation removed.

We have to encourage people to translate bzr-gtk/Olive through Rosetta. (The Olive product is already translatable, but bzr-gtk is still not.)

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