/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: Jasper Groenewegen
  • Date: 2008-07-27 12:01:40 UTC
  • mfrom: (576.3.2 improve-merge)
  • mto: This revision was merged to the branch mainline in revision 579.
  • Revision ID: colbrac@xs4all.nl-20080727120140-1agdlzkc9fumjk5f
Merge merge dialog improvements

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 os.path
 
18
import re
 
19
 
17
20
try:
18
21
    import pygtk
19
22
    pygtk.require("2.0")
24
27
import gobject
25
28
import pango
26
29
 
27
 
import os.path
28
 
import re
29
 
 
30
30
from bzrlib import errors, osutils
31
31
from bzrlib.trace import mutter
32
32
from bzrlib.util import bencode
33
33
 
34
 
from dialog import error_dialog, question_dialog
35
 
from errors import show_bzr_error
 
34
from bzrlib.plugins.gtk import _i18n
 
35
from bzrlib.plugins.gtk.dialog import question_dialog
 
36
from bzrlib.plugins.gtk.errors import show_bzr_error
36
37
 
37
38
try:
38
39
    import dbus
104
105
                                  parent=parent,
105
106
                                  flags=0,
106
107
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
107
 
        self._question_dialog = question_dialog
108
108
 
109
109
        self._wt = wt
 
110
        # TODO: Do something with this value, it is used by Olive
 
111
        #       It used to set all changes but this one to False
110
112
        self._selected = selected
 
113
        self._enable_per_file_commits = True
 
114
        self._commit_all_changes = True
111
115
        self.committed_revision_id = None # Nothing has been committed yet
112
116
 
113
117
        self.setup_params()
128
132
        self._fill_in_diff()
129
133
        self._fill_in_files()
130
134
        self._fill_in_checkout()
 
135
        self._fill_in_per_file_info()
131
136
 
132
137
    def _fill_in_pending(self):
133
138
        if not self._pending:
159
164
        store = self._files_store
160
165
        self._treeview_files.set_model(None)
161
166
 
162
 
        added = _('added')
163
 
        removed = _('removed')
164
 
        renamed = _('renamed')
165
 
        renamed_and_modified = _('renamed and modified')
166
 
        modified = _('modified')
167
 
        kind_changed = _('kind changed')
 
167
        added = _i18n('added')
 
168
        removed = _i18n('removed')
 
169
        renamed = _i18n('renamed')
 
170
        renamed_and_modified = _i18n('renamed and modified')
 
171
        modified = _i18n('modified')
 
172
        kind_changed = _i18n('kind changed')
168
173
 
169
174
        # The store holds:
170
175
        # [file_id, real path, checkbox, display path, changes type, message]
171
 
        # _iter_changes returns:
 
176
        # iter_changes returns:
172
177
        # (file_id, (path_in_source, path_in_target),
173
178
        #  changed_content, versioned, parent, name, kind,
174
179
        #  executable)
175
180
 
 
181
        all_enabled = (self._selected is None)
176
182
        # The first entry is always the 'whole tree'
177
 
        store.append([None, None, True, 'All Files', '', ''])
 
183
        all_iter = store.append([None, None, all_enabled, 'All Files', '', ''])
 
184
        initial_cursor = store.get_path(all_iter)
178
185
        # should we pass specific_files?
179
186
        self._wt.lock_read()
180
187
        self._basis_tree.lock_read()
181
188
        try:
182
 
            from diff import _iter_changes_to_status
 
189
            from diff import iter_changes_to_status
183
190
            for (file_id, real_path, change_type, display_path
184
 
                ) in _iter_changes_to_status(self._basis_tree, self._wt):
185
 
                store.append([file_id, real_path.encode('UTF-8'),
186
 
                              True, display_path.encode('UTF-8'),
187
 
                              change_type, ''])
 
191
                ) in iter_changes_to_status(self._basis_tree, self._wt):
 
192
                if self._selected and real_path != self._selected:
 
193
                    enabled = False
 
194
                else:
 
195
                    enabled = True
 
196
                item_iter = store.append([
 
197
                    file_id,
 
198
                    real_path.encode('UTF-8'),
 
199
                    enabled,
 
200
                    display_path.encode('UTF-8'),
 
201
                    change_type,
 
202
                    '', # Initial comment
 
203
                    ])
 
204
                if self._selected and enabled:
 
205
                    initial_cursor = store.get_path(item_iter)
188
206
        finally:
189
207
            self._basis_tree.unlock()
190
208
            self._wt.unlock()
191
209
 
192
210
        self._treeview_files.set_model(store)
193
211
        self._last_selected_file = None
194
 
        self._treeview_files.set_cursor(0)
 
212
        # This sets the cursor, which causes the expander to close, which
 
213
        # causes the _file_message_text_view to never get realized. So we have
 
214
        # to give it a little kick, or it warns when we try to grab the focus
 
215
        self._treeview_files.set_cursor(initial_cursor)
 
216
 
 
217
        def _realize_file_message_tree_view(*args):
 
218
            self._file_message_text_view.realize()
 
219
        self.connect_after('realize', _realize_file_message_tree_view)
195
220
 
196
221
    def _fill_in_diff(self):
197
222
        self._diff_view.set_trees(self._wt, self._basis_tree)
202
227
            return
203
228
        if have_dbus:
204
229
            bus = dbus.SystemBus()
205
 
            proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
206
 
                                       '/org/freedesktop/NetworkManager')
 
230
            try:
 
231
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
 
232
                                           '/org/freedesktop/NetworkManager')
 
233
            except dbus.DBusException:
 
234
                mutter("networkmanager not available.")
 
235
                self._check_local.show()
 
236
                return
 
237
            
207
238
            dbus_iface = dbus.Interface(proxy_obj,
208
239
                                        'org.freedesktop.NetworkManager')
209
240
            try:
215
246
                mutter("unable to get networkmanager state: %r" % e)
216
247
        self._check_local.show()
217
248
 
 
249
    def _fill_in_per_file_info(self):
 
250
        config = self._wt.branch.get_config()
 
251
        enable_per_file_commits = config.get_user_option('per_file_commits')
 
252
        if (enable_per_file_commits is None
 
253
            or enable_per_file_commits.lower()
 
254
                not in ('y', 'yes', 'on', 'enable', '1', 't', 'true')):
 
255
            self._enable_per_file_commits = False
 
256
        else:
 
257
            self._enable_per_file_commits = True
 
258
        if not self._enable_per_file_commits:
 
259
            self._file_message_expander.hide()
 
260
            self._global_message_label.set_markup(_i18n('<b>Commit Message</b>'))
 
261
 
218
262
    def _compute_delta(self):
219
263
        self._delta = self._wt.changes_from(self._basis_tree)
220
264
 
232
276
        self._hpane.show()
233
277
        self.set_focus(self._global_message_text_view)
234
278
 
 
279
        self._construct_accelerators()
 
280
        self._set_sizes()
 
281
 
 
282
    def _set_sizes(self):
235
283
        # This seems like a reasonable default, we might like it to
236
284
        # be a bit wider, so that by default we can fit an 80-line diff in the
237
285
        # diff window.
248
296
        self.set_default_size(width, height)
249
297
        self._hpane.set_position(300)
250
298
 
 
299
    def _construct_accelerators(self):
 
300
        group = gtk.AccelGroup()
 
301
        group.connect_group(gtk.gdk.keyval_from_name('N'),
 
302
                            gtk.gdk.CONTROL_MASK, 0, self._on_accel_next)
 
303
        self.add_accel_group(group)
 
304
 
 
305
        # ignore the escape key (avoid closing the window)
 
306
        self.connect_object('close', self.emit_stop_by_name, 'close')
 
307
 
251
308
    def _construct_left_pane(self):
252
309
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=5)
253
310
        self._construct_file_list()
254
311
        self._construct_pending_list()
255
312
 
256
 
        self._check_local = gtk.CheckButton(_("_Only commit locally"),
 
313
        self._check_local = gtk.CheckButton(_i18n("_Only commit locally"),
257
314
                                            use_underline=True)
258
315
        self._left_pane_box.pack_end(self._check_local, False, False)
259
316
        self._check_local.set_active(False)
280
337
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
281
338
 
282
339
    def _construct_action_pane(self):
283
 
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
 
340
        self._button_commit = gtk.Button(_i18n("Comm_it"), use_underline=True)
284
341
        self._button_commit.connect('clicked', self._on_commit_clicked)
285
342
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
286
343
        self._button_commit.show()
306
363
 
307
364
    def _construct_file_list(self):
308
365
        self._files_box = gtk.VBox(homogeneous=False, spacing=0)
309
 
        file_label = gtk.Label(_('Files'))
310
 
        file_label.show()
 
366
        file_label = gtk.Label(_i18n('Files'))
 
367
        # file_label.show()
311
368
        self._files_box.pack_start(file_label, expand=False)
312
369
 
 
370
        self._commit_all_files_radio = gtk.RadioButton(
 
371
            None, _i18n("Commit all changes"))
 
372
        self._files_box.pack_start(self._commit_all_files_radio, expand=False)
 
373
        self._commit_all_files_radio.show()
 
374
        self._commit_all_files_radio.connect('toggled',
 
375
            self._toggle_commit_selection)
 
376
        self._commit_selected_radio = gtk.RadioButton(
 
377
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
 
378
        self._files_box.pack_start(self._commit_selected_radio, expand=False)
 
379
        self._commit_selected_radio.show()
 
380
        self._commit_selected_radio.connect('toggled',
 
381
            self._toggle_commit_selection)
 
382
        if self._pending:
 
383
            self._commit_all_files_radio.set_label(_i18n('Commit all changes*'))
 
384
            self._commit_all_files_radio.set_sensitive(False)
 
385
            self._commit_selected_radio.set_sensitive(False)
 
386
 
313
387
        scroller = gtk.ScrolledWindow()
314
388
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
315
389
        self._treeview_files = gtk.TreeView()
316
390
        self._treeview_files.show()
317
391
        scroller.add(self._treeview_files)
 
392
        scroller.set_shadow_type(gtk.SHADOW_IN)
318
393
        scroller.show()
319
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
320
394
        self._files_box.pack_start(scroller,
321
395
                                   expand=True, fill=True)
322
396
        self._files_box.show()
323
397
        self._left_pane_box.pack_start(self._files_box)
324
398
 
 
399
        # Keep note that all strings stored in a ListStore must be UTF-8
 
400
        # strings. GTK does not support directly setting and restoring Unicode
 
401
        # objects.
325
402
        liststore = gtk.ListStore(
326
403
            gobject.TYPE_STRING,  # [0] file_id
327
404
            gobject.TYPE_STRING,  # [1] real path
333
410
        self._files_store = liststore
334
411
        self._treeview_files.set_model(liststore)
335
412
        crt = gtk.CellRendererToggle()
336
 
        crt.set_active(not bool(self._pending))
 
413
        crt.set_property('activatable', not bool(self._pending))
337
414
        crt.connect("toggled", self._toggle_commit, self._files_store)
338
415
        if self._pending:
339
 
            name = _('Commit*')
 
416
            name = _i18n('Commit*')
340
417
        else:
341
 
            name = _('Commit')
342
 
        self._treeview_files.append_column(gtk.TreeViewColumn(name,
343
 
                                           crt, active=2))
344
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
 
418
            name = _i18n('Commit')
 
419
        commit_col = gtk.TreeViewColumn(name, crt, active=2)
 
420
        commit_col.set_visible(False)
 
421
        self._treeview_files.append_column(commit_col)
 
422
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Path'),
345
423
                                           gtk.CellRendererText(), text=3))
346
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
 
424
        self._treeview_files.append_column(gtk.TreeViewColumn(_i18n('Type'),
347
425
                                           gtk.CellRendererText(), text=4))
348
426
        self._treeview_files.connect('cursor-changed',
349
427
                                     self._on_treeview_files_cursor_changed)
356
434
        else:
357
435
            model[path][2] = not model[path][2]
358
436
 
 
437
    def _toggle_commit_selection(self, button):
 
438
        all_files = self._commit_all_files_radio.get_active()
 
439
        if self._commit_all_changes != all_files:
 
440
            checked_col = self._treeview_files.get_column(0)
 
441
            self._commit_all_changes = all_files
 
442
            if all_files:
 
443
                checked_col.set_visible(False)
 
444
            else:
 
445
                checked_col.set_visible(True)
 
446
            renderer = checked_col.get_cell_renderers()[0]
 
447
            renderer.set_property('activatable', not all_files)
 
448
 
359
449
    def _construct_pending_list(self):
360
450
        # Pending information defaults to hidden, we put it all in 1 box, so
361
451
        # that we can show/hide all of them at once
364
454
 
365
455
        pending_message = gtk.Label()
366
456
        pending_message.set_markup(
367
 
            _('<i>* Cannot select specific files when merging</i>'))
 
457
            _i18n('<i>* Cannot select specific files when merging</i>'))
368
458
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
369
459
        pending_message.show()
370
460
 
371
 
        pending_label = gtk.Label(_('Pending Revisions'))
 
461
        pending_label = gtk.Label(_i18n('Pending Revisions'))
372
462
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
373
463
        pending_label.show()
374
464
 
376
466
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
377
467
        self._treeview_pending = gtk.TreeView()
378
468
        scroller.add(self._treeview_pending)
 
469
        scroller.set_shadow_type(gtk.SHADOW_IN)
379
470
        scroller.show()
380
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
381
471
        self._pending_box.pack_start(scroller,
382
472
                                     expand=True, fill=True, padding=5)
383
473
        self._treeview_pending.show()
390
480
                                 )
391
481
        self._pending_store = liststore
392
482
        self._treeview_pending.set_model(liststore)
393
 
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Date'),
 
483
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Date'),
394
484
                                             gtk.CellRendererText(), text=1))
395
 
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Committer'),
 
485
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Committer'),
396
486
                                             gtk.CellRendererText(), text=2))
397
 
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Summary'),
 
487
        self._treeview_pending.append_column(gtk.TreeViewColumn(_i18n('Summary'),
398
488
                                             gtk.CellRendererText(), text=3))
399
489
 
400
490
    def _construct_diff_view(self):
401
491
        from diff import DiffView
402
492
 
403
 
        self._diff_label = gtk.Label(_('Diff for whole tree'))
 
493
        # TODO: jam 2007-10-30 The diff label is currently disabled. If we
 
494
        #       decide that we really don't ever want to display it, we should
 
495
        #       actually remove it, and other references to it, along with the
 
496
        #       tests that it is set properly.
 
497
        self._diff_label = gtk.Label(_i18n('Diff for whole tree'))
404
498
        self._diff_label.set_alignment(0, 0)
405
499
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
406
500
        self._add_to_right_table(self._diff_label, 1, False)
407
 
        self._diff_label.show()
 
501
        # self._diff_label.show()
408
502
 
409
503
        self._diff_view = DiffView()
410
504
        self._add_to_right_table(self._diff_view, 4, True)
411
505
        self._diff_view.show()
412
506
 
413
507
    def _construct_file_message(self):
414
 
        file_message_box = gtk.VBox()
415
508
        scroller = gtk.ScrolledWindow()
416
509
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
417
510
 
418
511
        self._file_message_text_view = gtk.TextView()
419
512
        scroller.add(self._file_message_text_view)
 
513
        scroller.set_shadow_type(gtk.SHADOW_IN)
420
514
        scroller.show()
421
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
422
 
        file_message_box.pack_start(scroller, expand=True, fill=True)
423
515
 
424
516
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
425
517
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
426
518
        self._file_message_text_view.set_accepts_tab(False)
427
519
        self._file_message_text_view.show()
428
520
 
429
 
        self._file_message_expander = gtk.Expander(_('File commit message'))
430
 
        self._file_message_expander.add(file_message_box)
431
 
        file_message_box.show()
 
521
        self._file_message_expander = gtk.Expander(_i18n('File commit message'))
 
522
        self._file_message_expander.set_expanded(True)
 
523
        self._file_message_expander.add(scroller)
432
524
        self._add_to_right_table(self._file_message_expander, 1, False)
433
525
        self._file_message_expander.show()
434
526
 
435
527
    def _construct_global_message(self):
436
 
        self._global_message_label = gtk.Label(_('Global Commit Message'))
 
528
        self._global_message_label = gtk.Label(_i18n('Global Commit Message'))
 
529
        self._global_message_label.set_markup(
 
530
            _i18n('<b>Global Commit Message</b>'))
437
531
        self._global_message_label.set_alignment(0, 0)
438
532
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
439
533
        self._add_to_right_table(self._global_message_label, 1, False)
446
540
        self._global_message_text_view = gtk.TextView()
447
541
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
448
542
        scroller.add(self._global_message_text_view)
 
543
        scroller.set_shadow_type(gtk.SHADOW_IN)
449
544
        scroller.show()
450
 
        scroller.set_shadow_type(gtk.SHADOW_IN)
451
545
        self._add_to_right_table(scroller, 2, True)
452
546
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
453
547
        self._file_message_text_view.set_accepts_tab(False)
459
553
 
460
554
        if selection is not None:
461
555
            path, display_path = model.get(selection, 1, 3)
462
 
            self._diff_label.set_text(_('Diff for ') + display_path)
 
556
            self._diff_label.set_text(_i18n('Diff for ') + display_path)
463
557
            if path is None:
464
558
                self._diff_view.show_diff(None)
465
559
            else:
466
560
                self._diff_view.show_diff([path.decode('UTF-8')])
467
561
            self._update_per_file_info(selection)
468
562
 
 
563
    def _on_accel_next(self, accel_group, window, keyval, modifier):
 
564
        # We don't really care about any of the parameters, because we know
 
565
        # where this message came from
 
566
        tree_selection = self._treeview_files.get_selection()
 
567
        (model, selection) = tree_selection.get_selected()
 
568
        if selection is None:
 
569
            next = None
 
570
        else:
 
571
            next = model.iter_next(selection)
 
572
 
 
573
        if next is None:
 
574
            # We have either made it to the end of the list, or nothing was
 
575
            # selected. Either way, select All Files, and jump to the global
 
576
            # commit message.
 
577
            self._treeview_files.set_cursor((0,))
 
578
            self._global_message_text_view.grab_focus()
 
579
        else:
 
580
            # Set the cursor to this entry, and jump to the per-file commit
 
581
            # message
 
582
            self._treeview_files.set_cursor(model.get_path(next))
 
583
            self._file_message_text_view.grab_focus()
 
584
 
469
585
    def _save_current_file_message(self):
470
586
        if self._last_selected_file is None:
471
587
            return # Nothing to save
477
593
 
478
594
    def _update_per_file_info(self, selection):
479
595
        # The node is changing, so cache the current message
 
596
        if not self._enable_per_file_commits:
 
597
            return
 
598
 
480
599
        self._save_current_file_message()
481
600
        text_buffer = self._file_message_text_view.get_buffer()
482
601
        file_id, display_path, message = self._files_store.get(selection, 0, 3, 5)
483
602
        if file_id is None: # Whole tree
484
 
            self._file_message_expander.set_label(_('File commit message'))
 
603
            self._file_message_expander.set_label(_i18n('File commit message'))
485
604
            self._file_message_expander.set_expanded(False)
486
605
            self._file_message_expander.set_sensitive(False)
487
606
            text_buffer.set_text('')
488
607
            self._last_selected_file = None
489
608
        else:
490
 
            self._file_message_expander.set_label(_('Commit message for ')
 
609
            self._file_message_expander.set_label(_i18n('Commit message for ')
491
610
                                                  + display_path)
492
611
            self._file_message_expander.set_expanded(True)
493
612
            self._file_message_expander.set_sensitive(True)
495
614
            self._last_selected_file = self._files_store.get_path(selection)
496
615
 
497
616
    def _get_specific_files(self):
 
617
        """Return the list of selected paths, and file info.
 
618
 
 
619
        :return: ([unicode paths], [{utf-8 file info}]
 
620
        """
498
621
        self._save_current_file_message()
499
622
        files = []
500
623
        records = iter(self._files_store)
503
626
 
504
627
        file_info = []
505
628
        for record in records:
506
 
            if record[2]: # [2] checkbox
 
629
            if self._commit_all_changes or record[2]:# [2] checkbox
507
630
                file_id = record[0] # [0] file_id
508
 
                path = record[1] # [1] real path
 
631
                path = record[1]    # [1] real path
509
632
                file_message = record[5] # [5] commit message
510
633
                files.append(path.decode('UTF-8'))
511
 
                if file_message:
 
634
                if self._enable_per_file_commits and file_message:
512
635
                    # All of this needs to be utf-8 information
513
636
                    file_info.append({'path':path, 'file_id':file_id,
514
637
                                     'message':file_message})
515
638
        file_info.sort(key=lambda x:(x['path'], x['file_id']))
516
 
        return files, file_info
 
639
        if self._enable_per_file_commits:
 
640
            return files, file_info
 
641
        else:
 
642
            return files, []
517
643
 
518
644
    @show_bzr_error
519
645
    def _on_commit_clicked(self, button):
524
650
        message = self._get_global_commit_message()
525
651
 
526
652
        if message == '':
527
 
            response = self._question_dialog(
528
 
                            _('Commit with an empty message?'),
529
 
                            _('You can describe your commit intent in the message.'))
 
653
            response = question_dialog(
 
654
                _i18n('Commit with an empty message?'),
 
655
                _i18n('You can describe your commit intent in the message.'),
 
656
                parent=self)
530
657
            if response == gtk.RESPONSE_NO:
531
658
                # Kindly give focus to message area
532
659
                self._global_message_text_view.grab_focus()
544
671
        #       entirely, since there isn't a way for them to add the unknown
545
672
        #       files at this point.
546
673
        for path in self._wt.unknowns():
547
 
            response = self._question_dialog(
548
 
                _("Commit with unknowns?"),
549
 
                _("Unknown files exist in the working tree. Commit anyway?"))
 
674
            response = question_dialog(
 
675
                _i18n("Commit with unknowns?"),
 
676
                _i18n("Unknown files exist in the working tree. Commit anyway?"),
 
677
                parent=self)
 
678
                # Doesn't set a parent for the dialog..
550
679
            if response == gtk.RESPONSE_NO:
551
680
                return
552
681
            break
554
683
        rev_id = None
555
684
        revprops = {}
556
685
        if file_info:
557
 
            revprops['file-info'] = bencode.bencode(file_info)
 
686
            revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')
558
687
        try:
559
688
            rev_id = self._wt.commit(message,
560
689
                       allow_pointless=False,
563
692
                       specific_files=specific_files,
564
693
                       revprops=revprops)
565
694
        except errors.PointlessCommit:
566
 
            response = self._question_dialog(
567
 
                                _('Commit with no changes?'),
568
 
                                _('There are no changes in the working tree.'
569
 
                                  ' Do you want to commit anyway?'))
 
695
            response = question_dialog(
 
696
                _i18n('Commit with no changes?'),
 
697
                _i18n('There are no changes in the working tree.'
 
698
                      ' Do you want to commit anyway?'),
 
699
                parent=self)
570
700
            if response == gtk.RESPONSE_YES:
571
701
                rev_id = self._wt.commit(message,
572
702
                               allow_pointless=True,
598
728
    def _rev_to_pending_info(rev):
599
729
        """Get the information from a pending merge."""
600
730
        from bzrlib.osutils import format_date
601
 
 
602
731
        rev_dict = {}
603
732
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
604
733
        rev_dict['summary'] = rev.get_summary()
608
737
                                       show_offset=False)
609
738
        rev_dict['revision_id'] = rev.revision_id
610
739
        return rev_dict
611
 
 
612
 
 
613
 
# class CommitDialog(gtk.Dialog):
614
 
#     """ New implementation of the Commit dialog. """
615
 
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
616
 
#         """ Initialize the Commit Dialog. """
617
 
#         gtk.Dialog.__init__(self, title="Commit - Olive",
618
 
#                                   parent=parent,
619
 
#                                   flags=0,
620
 
#                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
621
 
#         
622
 
#         # Get arguments
623
 
#         self.wt = wt
624
 
#         self.wtpath = wtpath
625
 
#         self.notbranch = notbranch
626
 
#         self.selected = selected
627
 
#         
628
 
#         # Set the delta
629
 
#         self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
630
 
#         self.delta = self.wt.changes_from(self.old_tree)
631
 
#         
632
 
#         # Get pending merges
633
 
#         self.pending = self._pending_merges(self.wt)
634
 
#         
635
 
#         # Do some preliminary checks
636
 
#         self._is_checkout = False
637
 
#         self._is_pending = False
638
 
#         if self.wt is None and not self.notbranch:
639
 
#             error_dialog(_('Directory does not have a working tree'),
640
 
#                          _('Operation aborted.'))
641
 
#             self.close()
642
 
#             return
643
 
644
 
#         if self.notbranch:
645
 
#             error_dialog(_('Directory is not a branch'),
646
 
#                          _('You can perform this action only in a branch.'))
647
 
#             self.close()
648
 
#             return
649
 
#         else:
650
 
#             if self.wt.branch.get_bound_location() is not None:
651
 
#                 # we have a checkout, so the local commit checkbox must appear
652
 
#                 self._is_checkout = True
653
 
#             
654
 
#             if self.pending:
655
 
#                 # There are pending merges, file selection not supported
656
 
#                 self._is_pending = True
657
 
#         
658
 
#         # Create the widgets
659
 
#         # This is the main horizontal box, which is used to separate the commit
660
 
#         # info from the diff window.
661
 
#         self._hpane = gtk.HPaned()
662
 
#         self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
663
 
#         self._expander_files = gtk.Expander(_("File(s) to commit"))
664
 
#         self._vpaned_main = gtk.VPaned()
665
 
#         self._scrolledwindow_files = gtk.ScrolledWindow()
666
 
#         self._scrolledwindow_message = gtk.ScrolledWindow()
667
 
#         self._treeview_files = gtk.TreeView()
668
 
#         self._vbox_message = gtk.VBox()
669
 
#         self._label_message = gtk.Label(_("Commit message:"))
670
 
#         self._textview_message = gtk.TextView()
671
 
#         
672
 
#         if self._is_pending:
673
 
#             self._expander_merges = gtk.Expander(_("Pending merges"))
674
 
#             self._vpaned_list = gtk.VPaned()
675
 
#             self._scrolledwindow_merges = gtk.ScrolledWindow()
676
 
#             self._treeview_merges = gtk.TreeView()
677
 
678
 
#         # Set callbacks
679
 
#         self._button_commit.connect('clicked', self._on_commit_clicked)
680
 
#         self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
681
 
#         self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
682
 
#         
683
 
#         # Set properties
684
 
#         self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
685
 
#                                               gtk.POLICY_AUTOMATIC)
686
 
#         self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
687
 
#                                                 gtk.POLICY_AUTOMATIC)
688
 
#         self._textview_message.modify_font(pango.FontDescription("Monospace"))
689
 
#         self.set_default_size(500, 500)
690
 
#         self._vpaned_main.set_position(200)
691
 
#         self._button_commit.set_flags(gtk.CAN_DEFAULT)
692
 
693
 
#         if self._is_pending:
694
 
#             self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
695
 
#                                                    gtk.POLICY_AUTOMATIC)
696
 
#             self._treeview_files.set_sensitive(False)
697
 
#         
698
 
#         # Construct the dialog
699
 
#         self.action_area.pack_end(self._button_commit)
700
 
#         
701
 
#         self._scrolledwindow_files.add(self._treeview_files)
702
 
#         self._scrolledwindow_message.add(self._textview_message)
703
 
#         
704
 
#         self._expander_files.add(self._scrolledwindow_files)
705
 
#         
706
 
#         self._vbox_message.pack_start(self._label_message, False, False)
707
 
#         self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
708
 
#         
709
 
#         if self._is_pending:        
710
 
#             self._expander_merges.add(self._scrolledwindow_merges)
711
 
#             self._scrolledwindow_merges.add(self._treeview_merges)
712
 
#             self._vpaned_list.add1(self._expander_files)
713
 
#             self._vpaned_list.add2(self._expander_merges)
714
 
#             self._vpaned_main.add1(self._vpaned_list)
715
 
#         else:
716
 
#             self._vpaned_main.add1(self._expander_files)
717
 
718
 
#         self._vpaned_main.add2(self._vbox_message)
719
 
#         
720
 
#         self._hpane.pack1(self._vpaned_main)
721
 
#         self.vbox.pack_start(self._hpane, expand=True, fill=True)
722
 
#         if self._is_checkout: 
723
 
#             self._check_local = gtk.CheckButton(_("_Only commit locally"),
724
 
#                                                 use_underline=True)
725
 
#             self.vbox.pack_start(self._check_local, False, False)
726
 
#             if have_dbus:
727
 
#                 bus = dbus.SystemBus()
728
 
#                 proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
729
 
#                               '/org/freedesktop/NetworkManager')
730
 
#                 dbus_iface = dbus.Interface(
731
 
#                         proxy_obj, 'org.freedesktop.NetworkManager')
732
 
#                 try:
733
 
#                     # 3 is the enum value for STATE_CONNECTED
734
 
#                     self._check_local.set_active(dbus_iface.state() != 3)
735
 
#                 except dbus.DBusException, e:
736
 
#                     # Silently drop errors. While DBus may be 
737
 
#                     # available, NetworkManager doesn't necessarily have to be
738
 
#                     mutter("unable to get networkmanager state: %r" % e)
739
 
#                 
740
 
#         # Create the file list
741
 
#         self._create_file_view()
742
 
#         # Create the pending merges
743
 
#         self._create_pending_merges()
744
 
#         self._create_diff_view()
745
 
#         
746
 
#         # Expand the corresponding expander
747
 
#         if self._is_pending:
748
 
#             self._expander_merges.set_expanded(True)
749
 
#         else:
750
 
#             self._expander_files.set_expanded(True)
751
 
#         
752
 
#         # Display dialog
753
 
#         self.vbox.show_all()
754
 
#         
755
 
#         # Default to Commit button
756
 
#         self._button_commit.grab_default()
757
 
#     
758
 
#     def _show_diff_view(self, treeview):
759
 
#         # FIXME: the diff window freezes for some reason
760
 
#         treeselection = treeview.get_selection()
761
 
#         (model, iter) = treeselection.get_selected()
762
 
763
 
#         if iter is not None:
764
 
#             selected = model.get_value(iter, 3) # Get the real_path attribute
765
 
#             self._diff_display.show_diff([selected])
766
 
767
 
#     def _on_treeview_files_cursor_changed(self, treeview):
768
 
#         self._show_diff_view(treeview)
769
 
#         
770
 
#     def _on_treeview_files_row_activated(self, treeview, path, view_column):
771
 
#         self._show_diff_view(treeview)
772
 
#     
773
 
#     @show_bzr_error
774
 
#     def _on_commit_clicked(self, button):
775
 
#         """ Commit button clicked handler. """
776
 
#         textbuffer = self._textview_message.get_buffer()
777
 
#         start, end = textbuffer.get_bounds()
778
 
#         message = textbuffer.get_text(start, end).decode('utf-8')
779
 
#         
780
 
#         if not self.pending:
781
 
#             specific_files = self._get_specific_files()
782
 
#         else:
783
 
#             specific_files = None
784
 
785
 
#         if message == '':
786
 
#             response = question_dialog(_('Commit with an empty message?'),
787
 
#                                        _('You can describe your commit intent in the message.'))
788
 
#             if response == gtk.RESPONSE_NO:
789
 
#                 # Kindly give focus to message area
790
 
#                 self._textview_message.grab_focus()
791
 
#                 return
792
 
793
 
#         if self._is_checkout:
794
 
#             local = self._check_local.get_active()
795
 
#         else:
796
 
#             local = False
797
 
798
 
#         if list(self.wt.unknowns()) != []:
799
 
#             response = question_dialog(_("Commit with unknowns?"),
800
 
#                _("Unknown files exist in the working tree. Commit anyway?"))
801
 
#             if response == gtk.RESPONSE_NO:
802
 
#                 return
803
 
#         
804
 
#         try:
805
 
#             self.wt.commit(message,
806
 
#                        allow_pointless=False,
807
 
#                        strict=False,
808
 
#                        local=local,
809
 
#                        specific_files=specific_files)
810
 
#         except errors.PointlessCommit:
811
 
#             response = question_dialog(_('Commit with no changes?'),
812
 
#                                        _('There are no changes in the working tree.'))
813
 
#             if response == gtk.RESPONSE_YES:
814
 
#                 self.wt.commit(message,
815
 
#                                allow_pointless=True,
816
 
#                                strict=False,
817
 
#                                local=local,
818
 
#                                specific_files=specific_files)
819
 
#         self.response(gtk.RESPONSE_OK)
820
 
821
 
#     def _pending_merges(self, wt):
822
 
#         """ Return a list of pending merges or None if there are none of them. """
823
 
#         parents = wt.get_parent_ids()
824
 
#         if len(parents) < 2:
825
 
#             return None
826
 
#         
827
 
#         import re
828
 
#         from bzrlib.osutils import format_date
829
 
#         
830
 
#         pending = parents[1:]
831
 
#         branch = wt.branch
832
 
#         last_revision = parents[0]
833
 
#         
834
 
#         if last_revision is not None:
835
 
#             try:
836
 
#                 ignore = set(branch.repository.get_ancestry(last_revision))
837
 
#             except errors.NoSuchRevision:
838
 
#                 # the last revision is a ghost : assume everything is new 
839
 
#                 # except for it
840
 
#                 ignore = set([None, last_revision])
841
 
#         else:
842
 
#             ignore = set([None])
843
 
#         
844
 
#         pm = []
845
 
#         for merge in pending:
846
 
#             ignore.add(merge)
847
 
#             try:
848
 
#                 m_revision = branch.repository.get_revision(merge)
849
 
#                 
850
 
#                 rev = {}
851
 
#                 rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
852
 
#                 rev['summary'] = m_revision.get_summary()
853
 
#                 rev['date'] = format_date(m_revision.timestamp,
854
 
#                                           m_revision.timezone or 0, 
855
 
#                                           'original', date_fmt="%Y-%m-%d",
856
 
#                                           show_offset=False)
857
 
#                 
858
 
#                 pm.append(rev)
859
 
#                 
860
 
#                 inner_merges = branch.repository.get_ancestry(merge)
861
 
#                 assert inner_merges[0] is None
862
 
#                 inner_merges.pop(0)
863
 
#                 inner_merges.reverse()
864
 
#                 for mmerge in inner_merges:
865
 
#                     if mmerge in ignore:
866
 
#                         continue
867
 
#                     mm_revision = branch.repository.get_revision(mmerge)
868
 
#                     
869
 
#                     rev = {}
870
 
#                     rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
871
 
#                     rev['summary'] = mm_revision.get_summary()
872
 
#                     rev['date'] = format_date(mm_revision.timestamp,
873
 
#                                               mm_revision.timezone or 0, 
874
 
#                                               'original', date_fmt="%Y-%m-%d",
875
 
#                                               show_offset=False)
876
 
#                 
877
 
#                     pm.append(rev)
878
 
#                     
879
 
#                     ignore.add(mmerge)
880
 
#             except errors.NoSuchRevision:
881
 
#                 print "DEBUG: NoSuchRevision:", merge
882
 
#         
883
 
#         return pm
884
 
885
 
#     def _create_file_view(self):
886
 
#         self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
887
 
#                                          gobject.TYPE_STRING,    # [1] path to display
888
 
#                                          gobject.TYPE_STRING,    # [2] changes type
889
 
#                                          gobject.TYPE_STRING)    # [3] real path
890
 
#         self._treeview_files.set_model(self._file_store)
891
 
#         crt = gtk.CellRendererToggle()
892
 
#         crt.set_property("activatable", True)
893
 
#         crt.connect("toggled", self._toggle_commit, self._file_store)
894
 
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
895
 
#                                      crt, active=0))
896
 
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
897
 
#                                      gtk.CellRendererText(), text=1))
898
 
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
899
 
#                                      gtk.CellRendererText(), text=2))
900
 
901
 
#         for path, id, kind in self.delta.added:
902
 
#             marker = osutils.kind_marker(kind)
903
 
#             if self.selected is not None:
904
 
#                 if path == os.path.join(self.wtpath, self.selected):
905
 
#                     self._file_store.append([ True, path+marker, _('added'), path ])
906
 
#                 else:
907
 
#                     self._file_store.append([ False, path+marker, _('added'), path ])
908
 
#             else:
909
 
#                 self._file_store.append([ True, path+marker, _('added'), path ])
910
 
911
 
#         for path, id, kind in self.delta.removed:
912
 
#             marker = osutils.kind_marker(kind)
913
 
#             if self.selected is not None:
914
 
#                 if path == os.path.join(self.wtpath, self.selected):
915
 
#                     self._file_store.append([ True, path+marker, _('removed'), path ])
916
 
#                 else:
917
 
#                     self._file_store.append([ False, path+marker, _('removed'), path ])
918
 
#             else:
919
 
#                 self._file_store.append([ True, path+marker, _('removed'), path ])
920
 
921
 
#         for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
922
 
#             marker = osutils.kind_marker(kind)
923
 
#             if text_modified or meta_modified:
924
 
#                 changes = _('renamed and modified')
925
 
#             else:
926
 
#                 changes = _('renamed')
927
 
#             if self.selected is not None:
928
 
#                 if newpath == os.path.join(self.wtpath, self.selected):
929
 
#                     self._file_store.append([ True,
930
 
#                                               oldpath+marker + '  =>  ' + newpath+marker,
931
 
#                                               changes,
932
 
#                                               newpath
933
 
#                                             ])
934
 
#                 else:
935
 
#                     self._file_store.append([ False,
936
 
#                                               oldpath+marker + '  =>  ' + newpath+marker,
937
 
#                                               changes,
938
 
#                                               newpath
939
 
#                                             ])
940
 
#             else:
941
 
#                 self._file_store.append([ True,
942
 
#                                           oldpath+marker + '  =>  ' + newpath+marker,
943
 
#                                           changes,
944
 
#                                           newpath
945
 
#                                         ])
946
 
947
 
#         for path, id, kind, text_modified, meta_modified in self.delta.modified:
948
 
#             marker = osutils.kind_marker(kind)
949
 
#             if self.selected is not None:
950
 
#                 if path == os.path.join(self.wtpath, self.selected):
951
 
#                     self._file_store.append([ True, path+marker, _('modified'), path ])
952
 
#                 else:
953
 
#                     self._file_store.append([ False, path+marker, _('modified'), path ])
954
 
#             else:
955
 
#                 self._file_store.append([ True, path+marker, _('modified'), path ])
956
 
#     
957
 
#     def _create_pending_merges(self):
958
 
#         if not self.pending:
959
 
#             return
960
 
#         
961
 
#         liststore = gtk.ListStore(gobject.TYPE_STRING,
962
 
#                                   gobject.TYPE_STRING,
963
 
#                                   gobject.TYPE_STRING)
964
 
#         self._treeview_merges.set_model(liststore)
965
 
#         
966
 
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
967
 
#                                             gtk.CellRendererText(), text=0))
968
 
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
969
 
#                                             gtk.CellRendererText(), text=1))
970
 
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
971
 
#                                             gtk.CellRendererText(), text=2))
972
 
#         
973
 
#         for item in self.pending:
974
 
#             liststore.append([ item['date'],
975
 
#                                item['committer'],
976
 
#                                item['summary'] ])
977
 
#     
978
 
979
 
#     def _create_diff_view(self):
980
 
#         from diff import DiffView
981
 
982
 
#         self._diff_display = DiffView()
983
 
#         self._diff_display.set_trees(self.wt, self.wt.basis_tree())
984
 
#         self._diff_display.show_diff(None)
985
 
#         self._diff_display.show()
986
 
#         self._hpane.pack2(self._diff_display)
987
 
988
 
#     def _get_specific_files(self):
989
 
#         ret = []
990
 
#         it = self._file_store.get_iter_first()
991
 
#         while it:
992
 
#             if self._file_store.get_value(it, 0):
993
 
#                 # get real path from hidden column 3
994
 
#                 ret.append(self._file_store.get_value(it, 3))
995
 
#             it = self._file_store.iter_next(it)
996
 
997
 
#         return ret
998
 
#     
999
 
#     def _toggle_commit(self, cell, path, model):
1000
 
#         model[path][0] = not model[path][0]
1001
 
#         return