/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 diff.py

  • Committer: David Planella
  • Date: 2010-08-21 09:32:13 UTC
  • mto: This revision was merged to the branch mainline in revision 719.
  • Revision ID: david.planella@ubuntu.com-20100821093213-njpqd5sploa8n71m
Adapted desktop entries for translation

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
"""
6
6
 
7
7
__copyright__ = "Copyright 2005 Canonical Ltd."
8
 
__author__ = "Scott James Remnant <scott@ubuntu.com>"
 
8
__author__    = "Scott James Remnant <scott@ubuntu.com>"
9
9
 
10
10
 
11
11
from cStringIO import StringIO
12
12
 
 
13
import pygtk
 
14
pygtk.require("2.0")
 
15
import gtk
 
16
import pango
13
17
import os
14
18
import re
15
19
import sys
16
20
import inspect
17
21
try:
18
 
    from xml.etree.ElementTree import (
19
 
        Element,
20
 
        SubElement,
21
 
        tostring,
22
 
        )
23
 
    Element, SubElement, tostring  # Hush PEP8 redefinition.
 
22
    from xml.etree.ElementTree import Element, SubElement, tostring
24
23
except ImportError:
25
 
    from elementtree.ElementTree import (
26
 
        Element,
27
 
        SubElement,
28
 
        tostring,
29
 
        )
 
24
    from elementtree.ElementTree import Element, SubElement, tostring
30
25
 
31
 
from gi.repository import Gtk
32
 
from gi.repository import Pango
33
26
try:
34
 
    from gi.repository import GtkSource
 
27
    import gtksourceview2
35
28
    have_gtksourceview = True
36
29
except ImportError:
37
30
    have_gtksourceview = False
38
31
try:
39
 
    from gi.repository import GConf
 
32
    import gconf
40
33
    have_gconf = True
41
34
except ImportError:
42
35
    have_gconf = False
47
40
    osutils,
48
41
    urlutils,
49
42
    workingtree,
50
 
    )
51
 
from bzrlib.diff import show_diff_trees
 
43
)
 
44
from bzrlib.diff import show_diff_trees, internal_diff
52
45
from bzrlib.patches import parse_patches
53
46
from bzrlib.trace import warning
54
 
from bzrlib.plugins.gtk.dialog import (
55
 
    error_dialog,
56
 
    info_dialog,
57
 
    warning_dialog,
58
 
    )
59
 
from bzrlib.plugins.gtk.i18n import _i18n
 
47
from bzrlib.plugins.gtk import _i18n
60
48
from bzrlib.plugins.gtk.window import Window
 
49
from dialog import error_dialog, info_dialog, warning_dialog
61
50
 
62
51
 
63
52
def fallback_guess_language(slm, content_type):
73
62
    pass
74
63
 
75
64
 
76
 
class DiffFileView(Gtk.ScrolledWindow):
 
65
class DiffFileView(gtk.ScrolledWindow):
77
66
    """Window for displaying diffs from a diff file"""
78
67
 
79
68
    def __init__(self):
80
 
        super(DiffFileView, self).__init__()
 
69
        gtk.ScrolledWindow.__init__(self)
81
70
        self.construct()
82
71
        self._diffs = {}
83
72
 
84
73
    def construct(self):
85
 
        self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
86
 
        self.set_shadow_type(Gtk.ShadowType.IN)
 
74
        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
75
        self.set_shadow_type(gtk.SHADOW_IN)
87
76
 
88
77
        if have_gtksourceview:
89
 
            self.buffer = GtkSource.Buffer()
90
 
            lang_manager = GtkSource.LanguageManager.get_default()
91
 
            language = lang_manager.guess_language(None, "text/x-patch")
 
78
            self.buffer = gtksourceview2.Buffer()
 
79
            slm = gtksourceview2.LanguageManager()
 
80
            guess_language = getattr(gtksourceview2.LanguageManager, 
 
81
                "guess_language", fallback_guess_language)
 
82
            gsl = guess_language(slm, content_type="text/x-patch")
92
83
            if have_gconf:
93
84
                self.apply_gedit_colors(self.buffer)
94
85
            self.apply_colordiff_colors(self.buffer)
95
 
            self.buffer.set_language(language)
 
86
            self.buffer.set_language(gsl)
96
87
            self.buffer.set_highlight_syntax(True)
97
88
 
98
 
            self.sourceview = GtkSource.View(buffer=self.buffer)
 
89
            self.sourceview = gtksourceview2.View(self.buffer)
99
90
        else:
100
 
            self.buffer = Gtk.TextBuffer()
101
 
            self.sourceview = Gtk.TextView(self.buffer)
 
91
            self.buffer = gtk.TextBuffer()
 
92
            self.sourceview = gtk.TextView(self.buffer)
102
93
 
103
94
        self.sourceview.set_editable(False)
104
 
        self.sourceview.modify_font(Pango.FontDescription("Monospace"))
 
95
        self.sourceview.modify_font(pango.FontDescription("Monospace"))
105
96
        self.add(self.sourceview)
106
97
        self.sourceview.show()
107
98
 
111
102
 
112
103
        This method needs the gconf module.
113
104
 
114
 
        :param buf: a GtkSource.Buffer object.
 
105
        :param buf: a gtksourceview2.Buffer object.
115
106
        """
116
107
        GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
117
 
        GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
118
108
 
119
 
        client = GConf.Client.get_default()
 
109
        client = gconf.client_get_default()
120
110
        style_scheme_name = client.get_string(GEDIT_SCHEME_PATH)
121
111
        if style_scheme_name is not None:
122
 
            style_scheme_mgr = GtkSource.StyleSchemeManager()
123
 
            style_scheme_mgr.append_search_path(GEDIT_USER_STYLES_PATH)
124
 
 
125
 
            style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
126
 
 
127
 
            if style_scheme is not None:
128
 
                buf.set_style_scheme(style_scheme)
 
112
            style_scheme = gtksourceview2.StyleSchemeManager().get_scheme(style_scheme_name)
 
113
            
 
114
            buf.set_style_scheme(style_scheme)
129
115
 
130
116
    @classmethod
131
117
    def apply_colordiff_colors(klass, buf):
133
119
 
134
120
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
135
121
 
136
 
        :param buf: a "Diff" GtkSource.Buffer object.
 
122
        :param buf: a "Diff" gtksourceview2.Buffer object.
137
123
        """
138
 
        scheme_manager = GtkSource.StyleSchemeManager()
 
124
        scheme_manager = gtksourceview2.StyleSchemeManager()
139
125
        style_scheme = scheme_manager.get_scheme('colordiff')
140
 
 
 
126
        
141
127
        # if style scheme not found, we'll generate it from colordiffrc
142
128
        # TODO: reload if colordiffrc has changed.
143
129
        if style_scheme is None:
157
143
            if not colors:
158
144
                # ~/.colordiffrc does not exist
159
145
                return
160
 
 
 
146
            
161
147
            mapping = {
162
148
                # map GtkSourceView2 scheme styles to colordiff names
163
149
                # since GSV is richer, accept new names for extra bits,
168
154
                'diff:file': ['file', 'diffstuff'],
169
155
                'diff:special-case': ['specialcase', 'diffstuff'],
170
156
            }
171
 
 
 
157
            
172
158
            converted_colors = {}
173
159
            for name, values in mapping.items():
174
160
                color = None
179
165
                if color is None:
180
166
                    continue
181
167
                converted_colors[name] = color
182
 
 
 
168
            
183
169
            # some xml magic to produce needed style scheme description
184
170
            e_style_scheme = Element('style-scheme')
185
171
            e_style_scheme.set('id', 'colordiff')
189
175
                style = SubElement(e_style_scheme, 'style')
190
176
                style.set('name', name)
191
177
                style.set('foreground', '#%s' % color)
192
 
 
 
178
            
193
179
            scheme_xml = tostring(e_style_scheme, 'UTF-8')
194
 
            if not os.path.exists(os.path.expanduser(
195
 
                '~/.local/share/gtksourceview-2.0/styles')):
196
 
                os.makedirs(os.path.expanduser(
197
 
                    '~/.local/share/gtksourceview-2.0/styles'))
198
 
            file(os.path.expanduser(
199
 
                '~/.local/share/gtksourceview-2.0/styles/colordiff.xml'),
200
 
                'w').write(scheme_xml)
201
 
 
 
180
            if not os.path.exists(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles')):
 
181
                os.makedirs(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles'))
 
182
            file(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles/colordiff.xml'), 'w').write(scheme_xml)
 
183
            
202
184
            scheme_manager.force_rescan()
203
185
            style_scheme = scheme_manager.get_scheme('colordiff')
204
 
 
 
186
        
205
187
        buf.set_style_scheme(style_scheme)
206
188
 
207
189
    @staticmethod
229
211
#        self.parent_tree.lock_read()
230
212
#        self.rev_tree.lock_read()
231
213
#        try:
232
 
#            self.delta = iter_changes_to_status(
233
 
#               self.parent_tree, self.rev_tree)
 
214
#            self.delta = iter_changes_to_status(self.parent_tree, self.rev_tree)
234
215
#            self.path_to_status = {}
235
216
#            self.path_to_diff = {}
236
217
#            source_inv = self.parent_tree.inventory
237
218
#            target_inv = self.rev_tree.inventory
238
219
#            for (file_id, real_path, change_type, display_path) in self.delta:
239
 
#                self.path_to_status[real_path] = u'=== %s %s' % (
240
 
#                    change_type, display_path)
 
220
#                self.path_to_status[real_path] = u'=== %s %s' % (change_type, display_path)
241
221
#                if change_type in ('modified', 'renamed and modified'):
242
222
#                    source_ie = source_inv[file_id]
243
223
#                    target_ie = target_inv[file_id]
245
225
#                    source_ie.diff(internal_diff, *old path, *old_tree,
246
226
#                                   *new_path, target_ie, self.rev_tree,
247
227
#                                   sio)
248
 
#                    self.path_to_diff[real_path] =
 
228
#                    self.path_to_diff[real_path] = 
249
229
#
250
230
#        finally:
251
231
#            self.rev_tree.unlock()
265
245
    """This is the soft and chewy filling for a DiffWindow."""
266
246
 
267
247
    def __init__(self):
268
 
        super(DiffView, self).__init__()
 
248
        DiffFileView.__init__(self)
269
249
        self.rev_tree = None
270
250
        self.parent_tree = None
271
251
 
295
275
        self.buffer.set_text(decoded.encode('UTF-8'))
296
276
 
297
277
 
298
 
class DiffWidget(Gtk.HPaned):
 
278
class DiffWidget(gtk.HPaned):
299
279
    """Diff widget
300
280
 
301
281
    """
302
 
 
303
 
    SHOW_WIDGETS = True
304
 
 
305
282
    def __init__(self):
306
283
        super(DiffWidget, self).__init__()
307
284
 
308
285
        # The file hierarchy: a scrollable treeview
309
 
        scrollwin = Gtk.ScrolledWindow()
310
 
        scrollwin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
311
 
        scrollwin.set_shadow_type(Gtk.ShadowType.IN)
 
286
        scrollwin = gtk.ScrolledWindow()
 
287
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
288
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
312
289
        self.pack1(scrollwin)
313
 
        if self.SHOW_WIDGETS:
314
 
            scrollwin.show()
315
 
 
316
 
        self.model = Gtk.TreeStore(str, str)
317
 
        self.treeview = Gtk.TreeView(model=self.model)
 
290
        scrollwin.show()
 
291
        
 
292
        self.model = gtk.TreeStore(str, str)
 
293
        self.treeview = gtk.TreeView(self.model)
318
294
        self.treeview.set_headers_visible(False)
319
295
        self.treeview.set_search_column(1)
320
296
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
321
297
        scrollwin.add(self.treeview)
322
 
        if self.SHOW_WIDGETS:
323
 
            self.treeview.show()
 
298
        self.treeview.show()
324
299
 
325
 
        cell = Gtk.CellRendererText()
 
300
        cell = gtk.CellRendererText()
326
301
        cell.set_property("width-chars", 20)
327
 
        column = Gtk.TreeViewColumn()
328
 
        column.pack_start(cell, True)
 
302
        column = gtk.TreeViewColumn()
 
303
        column.pack_start(cell, expand=True)
329
304
        column.add_attribute(cell, "text", 0)
330
305
        self.treeview.append_column(column)
331
306
 
341
316
        if getattr(self, 'diff_view', None) is None:
342
317
            self.diff_view = DiffFileView()
343
318
            self.pack2(self.diff_view)
344
 
        if self.SHOW_WIDGETS:
345
 
            self.diff_view.show()
 
319
        self.diff_view.show()
346
320
        for oldname, newname, patch in sections:
347
321
            self.diff_view._diffs[newname] = str(patch)
348
322
            if newname is None:
359
333
        if getattr(self, 'diff_view', None) is None:
360
334
            self.diff_view = DiffView()
361
335
            self.pack2(self.diff_view)
362
 
        if self.SHOW_WIDGETS:
363
 
            self.diff_view.show()
 
336
        self.diff_view.show()
364
337
        self.diff_view.set_trees(rev_tree, parent_tree)
365
338
        self.rev_tree = rev_tree
366
339
        self.parent_tree = parent_tree
368
341
        self.model.clear()
369
342
        delta = self.rev_tree.changes_from(self.parent_tree)
370
343
 
371
 
        self.model.append(None, ["Complete Diff", ""])
 
344
        self.model.append(None, [ "Complete Diff", "" ])
372
345
 
373
346
        if len(delta.added):
374
 
            titer = self.model.append(None, ["Added", None])
 
347
            titer = self.model.append(None, [ "Added", None ])
375
348
            for path, id, kind in delta.added:
376
 
                self.model.append(titer, [path, path])
 
349
                self.model.append(titer, [ path, path ])
377
350
 
378
351
        if len(delta.removed):
379
 
            titer = self.model.append(None, ["Removed", None])
 
352
            titer = self.model.append(None, [ "Removed", None ])
380
353
            for path, id, kind in delta.removed:
381
 
                self.model.append(titer, [path, path])
 
354
                self.model.append(titer, [ path, path ])
382
355
 
383
356
        if len(delta.renamed):
384
 
            titer = self.model.append(None, ["Renamed", None])
 
357
            titer = self.model.append(None, [ "Renamed", None ])
385
358
            for oldpath, newpath, id, kind, text_modified, meta_modified \
386
359
                    in delta.renamed:
387
 
                self.model.append(titer, [oldpath, newpath])
 
360
                self.model.append(titer, [ oldpath, newpath ])
388
361
 
389
362
        if len(delta.modified):
390
 
            titer = self.model.append(None, ["Modified", None])
 
363
            titer = self.model.append(None, [ "Modified", None ])
391
364
            for path, id, kind, text_modified, meta_modified in delta.modified:
392
 
                self.model.append(titer, [path, path])
 
365
                self.model.append(titer, [ path, path ])
393
366
 
394
367
        self.treeview.expand_all()
395
368
        self.diff_view.show_diff(None)
404
377
                    break
405
378
        if tv_path is None:
406
379
            raise errors.NoSuchFile(file_path)
407
 
        self.treeview.set_cursor(tv_path, None, False)
 
380
        self.treeview.set_cursor(tv_path)
408
381
        self.treeview.scroll_to_cell(tv_path)
409
382
 
410
383
    def _treeview_cursor_cb(self, *args):
411
384
        """Callback for when the treeview cursor changes."""
412
385
        (path, col) = self.treeview.get_cursor()
413
 
        if path is None:
414
 
            return
415
 
        specific_files = [self.model[path][1]]
416
 
        if specific_files == [None]:
417
 
            return
418
 
        elif specific_files == [""]:
 
386
        specific_files = [ self.model[path][1] ]
 
387
        if specific_files == [ None ]:
 
388
            return
 
389
        elif specific_files == [ "" ]:
419
390
            specific_files = None
420
 
 
 
391
        
421
392
        self.diff_view.show_diff(specific_files)
422
 
 
 
393
    
423
394
    def _on_wraplines_toggled(self, widget=None, wrap=False):
424
395
        """Callback for when the wrap lines checkbutton is toggled"""
425
396
        if wrap or widget.get_active():
426
 
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.WORD)
 
397
            self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_WORD)
427
398
        else:
428
 
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.NONE)
429
 
 
 
399
            self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_NONE)
430
400
 
431
401
class DiffWindow(Window):
432
402
    """Diff window.
435
405
    differences between two revisions on a branch.
436
406
    """
437
407
 
438
 
    SHOW_WIDGETS = True
439
 
 
440
408
    def __init__(self, parent=None, operations=None):
441
 
        super(DiffWindow, self).__init__(parent=parent)
 
409
        Window.__init__(self, parent)
442
410
        self.set_border_width(0)
443
 
        self.set_title("bzr diff")
 
411
        self.set_title("bzrk diff")
444
412
 
445
413
        # Use two thirds of the screen by default
446
414
        screen = self.get_screen()
452
420
 
453
421
    def construct(self, operations):
454
422
        """Construct the window contents."""
455
 
        self.vbox = Gtk.VBox()
 
423
        self.vbox = gtk.VBox()
456
424
        self.add(self.vbox)
457
 
        if self.SHOW_WIDGETS:
458
 
            self.vbox.show()
 
425
        self.vbox.show()
459
426
        self.diff = DiffWidget()
460
427
        self.vbox.pack_end(self.diff, True, True, 0)
461
 
        if self.SHOW_WIDGETS:
462
 
            self.diff.show_all()
 
428
        self.diff.show_all()
463
429
        # Build after DiffWidget to connect signals
464
430
        menubar = self._get_menu_bar()
465
431
        self.vbox.pack_start(menubar, False, False, 0)
466
432
        hbox = self._get_button_bar(operations)
467
433
        if hbox is not None:
468
434
            self.vbox.pack_start(hbox, False, True, 0)
469
 
 
 
435
        
 
436
    
470
437
    def _get_menu_bar(self):
471
 
        menubar = Gtk.MenuBar()
 
438
        menubar = gtk.MenuBar()
472
439
        # View menu
473
 
        mb_view = Gtk.MenuItem.new_with_mnemonic(_i18n("_View"))
474
 
        mb_view_menu = Gtk.Menu()
475
 
        mb_view_wrapsource = Gtk.CheckMenuItem.new_with_mnemonic(
476
 
            _i18n("Wrap _Long Lines"))
 
440
        mb_view = gtk.MenuItem(_i18n("_View"))
 
441
        mb_view_menu = gtk.Menu()
 
442
        mb_view_wrapsource = gtk.CheckMenuItem(_i18n("Wrap _Long Lines"))
477
443
        mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
 
444
        mb_view_wrapsource.show()
478
445
        mb_view_menu.append(mb_view_wrapsource)
 
446
        mb_view.show()
479
447
        mb_view.set_submenu(mb_view_menu)
 
448
        mb_view.show()
480
449
        menubar.append(mb_view)
481
 
        if self.SHOW_WIDGETS:
482
 
            menubar.show_all()
 
450
        menubar.show()
483
451
        return menubar
484
 
 
 
452
    
485
453
    def _get_button_bar(self, operations):
486
454
        """Return a button bar to use.
487
455
 
489
457
        """
490
458
        if operations is None:
491
459
            return None
492
 
        hbox = Gtk.HButtonBox()
493
 
        hbox.set_layout(Gtk.ButtonBoxStyle.START)
 
460
        hbox = gtk.HButtonBox()
 
461
        hbox.set_layout(gtk.BUTTONBOX_START)
494
462
        for title, method in operations:
495
 
            merge_button = Gtk.Button(title)
496
 
            if self.SHOW_WIDGETS:
497
 
                merge_button.show()
498
 
            merge_button.set_relief(Gtk.ReliefStyle.NONE)
 
463
            merge_button = gtk.Button(title)
 
464
            merge_button.show()
 
465
            merge_button.set_relief(gtk.RELIEF_NONE)
499
466
            merge_button.connect("clicked", method)
500
 
            hbox.pack_start(merge_button, False, True, 0)
501
 
        if self.SHOW_WIDGETS:
502
 
            hbox.show()
 
467
            hbox.pack_start(merge_button, expand=False, fill=True)
 
468
        hbox.show()
503
469
        return hbox
504
470
 
505
471
    def _get_merge_target(self):
506
 
        d = Gtk.FileChooserDialog('Merge branch', self,
507
 
                                  Gtk.FileChooserAction.SELECT_FOLDER,
508
 
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
509
 
                                           Gtk.STOCK_CANCEL,
510
 
                                           Gtk.ResponseType.CANCEL,))
 
472
        d = gtk.FileChooserDialog('Merge branch', self,
 
473
                                  gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
 
474
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
 
475
                                           gtk.STOCK_CANCEL,
 
476
                                           gtk.RESPONSE_CANCEL,))
511
477
        try:
512
478
            result = d.run()
513
 
            if result != Gtk.ResponseType.OK:
 
479
            if result != gtk.RESPONSE_OK:
514
480
                raise SelectCancelled()
515
481
            return d.get_current_folder_uri()
516
482
        finally:
530
496
        error_dialog('Error', str(e))
531
497
 
532
498
    def _get_save_path(self, basename):
533
 
        d = Gtk.FileChooserDialog('Save As', self,
534
 
                                  Gtk.FileChooserAction.SAVE,
535
 
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
536
 
                                           Gtk.STOCK_CANCEL,
537
 
                                           Gtk.ResponseType.CANCEL,))
 
499
        d = gtk.FileChooserDialog('Save As', self,
 
500
                                  gtk.FILE_CHOOSER_ACTION_SAVE,
 
501
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
 
502
                                           gtk.STOCK_CANCEL,
 
503
                                           gtk.RESPONSE_CANCEL,))
538
504
        d.set_current_name(basename)
539
505
        try:
540
506
            result = d.run()
541
 
            if result != Gtk.ResponseType.OK:
 
507
            if result != gtk.RESPONSE_OK:
542
508
                raise SelectCancelled()
543
509
            return urlutils.local_path_from_url(d.get_uri())
544
510
        finally:
606
572
class MergeDirectiveController(DiffController):
607
573
 
608
574
    def __init__(self, path, directive, window=None):
609
 
        super(MergeDirectiveController, self).__init__(
610
 
            path, directive.patch.splitlines(True), window)
 
575
        DiffController.__init__(self, path, directive.patch.splitlines(True),
 
576
                                window)
611
577
        self.directive = directive
612
578
        self.merge_target = None
613
579