291
261
self.buffer.set_text(decoded.encode('UTF-8'))
294
class DiffWidget(gtk.HPaned):
264
class DiffWindow(Window):
267
This object represents and manages a single window containing the
268
differences between two revisions on a branch.
299
super(DiffWidget, self).__init__()
271
def __init__(self, parent=None):
272
Window.__init__(self, parent)
273
self.set_border_width(0)
274
self.set_title("bzrk diff")
276
# Use two thirds of the screen by default
277
screen = self.get_screen()
278
monitor = screen.get_monitor_geometry(0)
279
width = int(monitor.width * 0.66)
280
height = int(monitor.height * 0.66)
281
self.set_default_size(width, height)
286
"""Construct the window contents."""
287
# The window consists of a pane containing: the
288
# hierarchical list of files on the left, and the diff
289
# for the currently selected file on the right.
301
294
# The file hierarchy: a scrollable treeview
302
295
scrollwin = gtk.ScrolledWindow()
303
296
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
304
297
scrollwin.set_shadow_type(gtk.SHADOW_IN)
305
self.pack1(scrollwin)
298
pane.pack1(scrollwin)
308
301
self.model = gtk.TreeStore(str, str)
309
302
self.treeview = gtk.TreeView(self.model)
310
303
self.treeview.set_headers_visible(False)
320
313
column.add_attribute(cell, "text", 0)
321
314
self.treeview.append_column(column)
323
def set_diff_text(self, lines):
324
"""Set the current diff from a list of lines
326
:param lines: The diff to show, in unified diff format
328
316
# The diffs of the selected file: a scrollable source or
331
def set_diff_text_sections(self, sections):
332
if getattr(self, 'diff_view', None) is None:
333
self.diff_view = DiffFileView()
334
self.pack2(self.diff_view)
318
self.diff_view = DiffView()
319
pane.pack2(self.diff_view)
335
320
self.diff_view.show()
336
for oldname, newname, patch in sections:
337
self.diff_view._diffs[newname] = str(patch)
340
self.model.append(None, [oldname, newname])
341
self.diff_view.show_diff(None)
343
def set_diff(self, rev_tree, parent_tree):
322
def set_diff(self, description, rev_tree, parent_tree):
344
323
"""Set the differences showed by this window.
346
325
Compares the two trees and populates the window with the
349
if getattr(self, 'diff_view', None) is None:
350
self.diff_view = DiffView()
351
self.pack2(self.diff_view)
352
self.diff_view.show()
353
328
self.diff_view.set_trees(rev_tree, parent_tree)
354
329
self.rev_tree = rev_tree
355
330
self.parent_tree = parent_tree
405
379
elif specific_files == [ "" ]:
406
380
specific_files = None
408
382
self.diff_view.show_diff(specific_files)
410
def _on_wraplines_toggled(self, widget=None, wrap=False):
411
"""Callback for when the wrap lines checkbutton is toggled"""
412
if wrap or widget.get_active():
413
self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_WORD)
415
self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_NONE)
417
class DiffWindow(Window):
420
This object represents and manages a single window containing the
421
differences between two revisions on a branch.
424
def __init__(self, parent=None, operations=None):
425
Window.__init__(self, parent)
426
self.set_border_width(0)
427
self.set_title("bzrk diff")
429
# Use two thirds of the screen by default
430
screen = self.get_screen()
431
monitor = screen.get_monitor_geometry(0)
432
width = int(monitor.width * 0.66)
433
height = int(monitor.height * 0.66)
434
self.set_default_size(width, height)
435
self.construct(operations)
437
def construct(self, operations):
438
"""Construct the window contents."""
439
self.vbox = gtk.VBox()
442
self.diff = DiffWidget()
443
self.vbox.pack_end(self.diff, True, True, 0)
445
# Build after DiffWidget to connect signals
446
menubar = self._get_menu_bar()
447
self.vbox.pack_start(menubar, False, False, 0)
448
hbox = self._get_button_bar(operations)
450
self.vbox.pack_start(hbox, False, True, 0)
453
def _get_menu_bar(self):
454
menubar = gtk.MenuBar()
456
mb_view = gtk.MenuItem(_i18n("_View"))
457
mb_view_menu = gtk.Menu()
458
mb_view_wrapsource = gtk.CheckMenuItem(_i18n("Wrap _Long Lines"))
459
mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
460
mb_view_wrapsource.show()
461
mb_view_menu.append(mb_view_wrapsource)
463
mb_view.set_submenu(mb_view_menu)
465
menubar.append(mb_view)
469
def _get_button_bar(self, operations):
470
"""Return a button bar to use.
472
:return: None, meaning that no button bar will be used.
474
if operations is None:
476
hbox = gtk.HButtonBox()
477
hbox.set_layout(gtk.BUTTONBOX_START)
478
for title, method in operations:
479
merge_button = gtk.Button(title)
481
merge_button.set_relief(gtk.RELIEF_NONE)
482
merge_button.connect("clicked", method)
483
hbox.pack_start(merge_button, expand=False, fill=True)
487
def _get_merge_target(self):
488
d = gtk.FileChooserDialog('Merge branch', self,
489
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
490
buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
492
gtk.RESPONSE_CANCEL,))
495
if result != gtk.RESPONSE_OK:
496
raise SelectCancelled()
497
return d.get_current_folder_uri()
501
def _merge_successful(self):
502
# No conflicts found.
503
info_dialog(_i18n('Merge successful'),
504
_i18n('All changes applied successfully.'))
506
def _conflicts(self):
507
warning_dialog(_i18n('Conflicts encountered'),
508
_i18n('Please resolve the conflicts manually'
509
' before committing.'))
511
def _handle_error(self, e):
512
error_dialog('Error', str(e))
514
def _get_save_path(self, basename):
515
d = gtk.FileChooserDialog('Save As', self,
516
gtk.FILE_CHOOSER_ACTION_SAVE,
517
buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
519
gtk.RESPONSE_CANCEL,))
520
d.set_current_name(basename)
523
if result != gtk.RESPONSE_OK:
524
raise SelectCancelled()
525
return urlutils.local_path_from_url(d.get_uri())
529
def set_diff(self, description, rev_tree, parent_tree):
530
"""Set the differences showed by this window.
532
Compares the two trees and populates the window with the
535
self.diff.set_diff(rev_tree, parent_tree)
536
self.set_title(description + " - bzrk diff")
538
def set_file(self, file_path):
539
self.diff.set_file(file_path)
542
class DiffController(object):
544
def __init__(self, path, patch, window=None):
548
window = DiffWindow(operations=self._provide_operations())
549
self.initialize_window(window)
552
def initialize_window(self, window):
553
window.diff.set_diff_text_sections(self.get_diff_sections())
554
window.set_title(self.path + " - diff")
556
def get_diff_sections(self):
557
yield "Complete Diff", None, ''.join(self.patch)
558
for patch in parse_patches(self.patch):
559
oldname = patch.oldname.split('\t')[0]
560
newname = patch.newname.split('\t')[0]
561
yield oldname, newname, str(patch)
563
def perform_save(self, window):
565
save_path = self.window._get_save_path(osutils.basename(self.path))
566
except SelectCancelled:
568
source = open(self.path, 'rb')
570
target = open(save_path, 'wb')
572
osutils.pumpfile(source, target)
578
def _provide_operations(self):
579
return [('Save', self.perform_save)]
582
class MergeDirectiveController(DiffController):
584
def __init__(self, path, directive, window=None):
585
DiffController.__init__(self, path, directive.patch.splitlines(True),
587
self.directive = directive
588
self.merge_target = None
590
def _provide_operations(self):
591
return [('Merge', self.perform_merge), ('Save', self.perform_save)]
593
def perform_merge(self, window):
594
if self.merge_target is None:
596
self.merge_target = self.window._get_merge_target()
597
except SelectCancelled:
599
tree = workingtree.WorkingTree.open(self.merge_target)
603
merger, verified = _mod_merge.Merger.from_mergeable(tree,
604
self.directive, progress.DummyProgress())
605
merger.check_basis(True)
606
merger.merge_type = _mod_merge.Merge3Merger
607
conflict_count = merger.do_merge()
609
if conflict_count == 0:
610
self.window._merge_successful()
612
self.window._conflicts()
613
# There are conflicts to be resolved.
614
self.window.destroy()
616
self.window._handle_error(e)
621
def iter_changes_to_status(source, target):
385
def _iter_changes_to_status(source, target):
622
386
"""Determine the differences between trees.
624
This is a wrapper around iter_changes which just yields more
388
This is a wrapper around _iter_changes which just yields more
625
389
understandable results.
627
391
:param source: The source tree (basis tree)