/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: Gustav Hartvigsson
  • Date: 2014-11-25 15:34:07 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20141125153407-827k8g7qy9u5byd5
* Fixed bzr-gtk's viz util, using the new Gtk Inspector.

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 sys
 
14
import inspect
 
15
 
13
16
from gi.repository import Gtk
14
17
from gi.repository import Pango
15
 
import os
16
 
import re
17
 
import sys
18
 
import inspect
19
 
try:
20
 
    from xml.etree.ElementTree import Element, SubElement, tostring
21
 
except ImportError:
22
 
    from elementtree.ElementTree import Element, SubElement, tostring
23
 
 
24
18
try:
25
19
    from gi.repository import GtkSource
26
20
    have_gtksourceview = True
27
21
except ImportError:
28
22
    have_gtksourceview = False
29
 
try:
30
 
    from gi.repository import GConf
31
 
    have_gconf = True
32
 
except ImportError:
33
 
    have_gconf = False
34
23
 
35
24
from bzrlib import (
36
25
    errors,
38
27
    osutils,
39
28
    urlutils,
40
29
    workingtree,
41
 
)
 
30
    )
42
31
from bzrlib.diff import show_diff_trees
43
32
from bzrlib.patches import parse_patches
44
 
from bzrlib.trace import warning
45
33
from bzrlib.plugins.gtk.dialog import (
46
34
    error_dialog,
47
35
    info_dialog,
67
55
class DiffFileView(Gtk.ScrolledWindow):
68
56
    """Window for displaying diffs from a diff file"""
69
57
 
 
58
    SHOW_WIDGETS = True
 
59
 
70
60
    def __init__(self):
71
 
        GObject.GObject.__init__(self)
 
61
        super(DiffFileView, self).__init__()
72
62
        self.construct()
73
63
        self._diffs = {}
74
64
 
78
68
 
79
69
        if have_gtksourceview:
80
70
            self.buffer = GtkSource.Buffer()
81
 
            slm = GtkSource.LanguageManager()
82
 
            guess_language = getattr(GtkSource.LanguageManager, 
83
 
                "guess_language", fallback_guess_language)
84
 
            gsl = guess_language(slm, content_type="text/x-patch")
85
 
            if have_gconf:
86
 
                self.apply_gedit_colors(self.buffer)
87
 
            self.apply_colordiff_colors(self.buffer)
88
 
            self.buffer.set_language(gsl)
 
71
            lang_manager = GtkSource.LanguageManager.get_default()
 
72
            language = lang_manager.guess_language(None, "text/x-patch")
 
73
            self.buffer.set_language(language)
89
74
            self.buffer.set_highlight_syntax(True)
90
 
 
91
 
            self.sourceview = GtkSource.View(self.buffer)
 
75
            self.sourceview = GtkSource.View(buffer=self.buffer)
92
76
        else:
93
77
            self.buffer = Gtk.TextBuffer()
94
78
            self.sourceview = Gtk.TextView(self.buffer)
95
79
 
96
80
        self.sourceview.set_editable(False)
97
 
        self.sourceview.modify_font(Pango.FontDescription("Monospace"))
 
81
        self.sourceview.override_font(Pango.FontDescription("Monospace"))
98
82
        self.add(self.sourceview)
99
 
        self.sourceview.show()
100
 
 
101
 
    @staticmethod
102
 
    def apply_gedit_colors(buf):
103
 
        """Set style to that specified in gedit configuration.
104
 
 
105
 
        This method needs the gconf module.
106
 
 
107
 
        :param buf: a GtkSource.Buffer object.
108
 
        """
109
 
        GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
110
 
        GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
111
 
 
112
 
        client = GConf.Client.get_default()
113
 
        style_scheme_name = client.get_string(GEDIT_SCHEME_PATH)
114
 
        if style_scheme_name is not None:
115
 
            style_scheme_mgr = GtkSource.StyleSchemeManager()
116
 
            style_scheme_mgr.append_search_path(GEDIT_USER_STYLES_PATH)
117
 
            
118
 
            style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
119
 
            
120
 
            if style_scheme is not None:
121
 
                buf.set_style_scheme(style_scheme)
122
 
 
123
 
    @classmethod
124
 
    def apply_colordiff_colors(klass, buf):
125
 
        """Set style colors for lang using the colordiff configuration file.
126
 
 
127
 
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
128
 
 
129
 
        :param buf: a "Diff" GtkSource.Buffer object.
130
 
        """
131
 
        scheme_manager = GtkSource.StyleSchemeManager()
132
 
        style_scheme = scheme_manager.get_scheme('colordiff')
133
 
        
134
 
        # if style scheme not found, we'll generate it from colordiffrc
135
 
        # TODO: reload if colordiffrc has changed.
136
 
        if style_scheme is None:
137
 
            colors = {}
138
 
 
139
 
            for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
140
 
                f = os.path.expanduser(f)
141
 
                if os.path.exists(f):
142
 
                    try:
143
 
                        f = file(f)
144
 
                    except IOError, e:
145
 
                        warning('could not open file %s: %s' % (f, str(e)))
146
 
                    else:
147
 
                        colors.update(klass.parse_colordiffrc(f))
148
 
                        f.close()
149
 
 
150
 
            if not colors:
151
 
                # ~/.colordiffrc does not exist
152
 
                return
153
 
            
154
 
            mapping = {
155
 
                # map GtkSourceView2 scheme styles to colordiff names
156
 
                # since GSV is richer, accept new names for extra bits,
157
 
                # defaulting to old names if they're not present
158
 
                'diff:added-line': ['newtext'],
159
 
                'diff:removed-line': ['oldtext'],
160
 
                'diff:location': ['location', 'diffstuff'],
161
 
                'diff:file': ['file', 'diffstuff'],
162
 
                'diff:special-case': ['specialcase', 'diffstuff'],
163
 
            }
164
 
            
165
 
            converted_colors = {}
166
 
            for name, values in mapping.items():
167
 
                color = None
168
 
                for value in values:
169
 
                    color = colors.get(value, None)
170
 
                    if color is not None:
171
 
                        break
172
 
                if color is None:
173
 
                    continue
174
 
                converted_colors[name] = color
175
 
            
176
 
            # some xml magic to produce needed style scheme description
177
 
            e_style_scheme = Element('style-scheme')
178
 
            e_style_scheme.set('id', 'colordiff')
179
 
            e_style_scheme.set('_name', 'ColorDiff')
180
 
            e_style_scheme.set('version', '1.0')
181
 
            for name, color in converted_colors.items():
182
 
                style = SubElement(e_style_scheme, 'style')
183
 
                style.set('name', name)
184
 
                style.set('foreground', '#%s' % color)
185
 
            
186
 
            scheme_xml = tostring(e_style_scheme, 'UTF-8')
187
 
            if not os.path.exists(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles')):
188
 
                os.makedirs(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles'))
189
 
            file(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles/colordiff.xml'), 'w').write(scheme_xml)
190
 
            
191
 
            scheme_manager.force_rescan()
192
 
            style_scheme = scheme_manager.get_scheme('colordiff')
193
 
        
194
 
        buf.set_style_scheme(style_scheme)
195
 
 
196
 
    @staticmethod
197
 
    def parse_colordiffrc(fileobj):
198
 
        """Parse fileobj as a colordiff configuration file.
199
 
 
200
 
        :return: A dict with the key -> value pairs.
201
 
        """
202
 
        colors = {}
203
 
        for line in fileobj:
204
 
            if re.match(r'^\s*#', line):
205
 
                continue
206
 
            if '=' not in line:
207
 
                continue
208
 
            key, val = line.split('=', 1)
209
 
            colors[key.strip()] = val.strip()
210
 
        return colors
 
83
        if self.SHOW_WIDGETS:
 
84
            self.sourceview.show()
211
85
 
212
86
    def set_trees(self, rev_tree, parent_tree):
213
87
        self.rev_tree = rev_tree
218
92
#        self.parent_tree.lock_read()
219
93
#        self.rev_tree.lock_read()
220
94
#        try:
221
 
#            self.delta = iter_changes_to_status(self.parent_tree, self.rev_tree)
 
95
#            self.delta = iter_changes_to_status(
 
96
#               self.parent_tree, self.rev_tree)
222
97
#            self.path_to_status = {}
223
98
#            self.path_to_diff = {}
224
99
#            source_inv = self.parent_tree.inventory
225
100
#            target_inv = self.rev_tree.inventory
226
101
#            for (file_id, real_path, change_type, display_path) in self.delta:
227
 
#                self.path_to_status[real_path] = u'=== %s %s' % (change_type, display_path)
 
102
#                self.path_to_status[real_path] = u'=== %s %s' % (
 
103
#                    change_type, display_path)
228
104
#                if change_type in ('modified', 'renamed and modified'):
229
105
#                    source_ie = source_inv[file_id]
230
106
#                    target_ie = target_inv[file_id]
232
108
#                    source_ie.diff(internal_diff, *old path, *old_tree,
233
109
#                                   *new_path, target_ie, self.rev_tree,
234
110
#                                   sio)
235
 
#                    self.path_to_diff[real_path] = 
 
111
#                    self.path_to_diff[real_path] =
236
112
#
237
113
#        finally:
238
114
#            self.rev_tree.unlock()
252
128
    """This is the soft and chewy filling for a DiffWindow."""
253
129
 
254
130
    def __init__(self):
255
 
        DiffFileView.__init__(self)
 
131
        super(DiffView, self).__init__()
256
132
        self.rev_tree = None
257
133
        self.parent_tree = None
258
134
 
286
162
    """Diff widget
287
163
 
288
164
    """
 
165
 
 
166
    SHOW_WIDGETS = True
 
167
 
289
168
    def __init__(self):
290
169
        super(DiffWidget, self).__init__()
291
170
 
294
173
        scrollwin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
295
174
        scrollwin.set_shadow_type(Gtk.ShadowType.IN)
296
175
        self.pack1(scrollwin)
297
 
        scrollwin.show()
298
 
        
 
176
        if self.SHOW_WIDGETS:
 
177
            scrollwin.show()
 
178
 
299
179
        self.model = Gtk.TreeStore(str, str)
300
 
        self.treeview = Gtk.TreeView(self.model)
 
180
        self.treeview = Gtk.TreeView(model=self.model)
301
181
        self.treeview.set_headers_visible(False)
302
182
        self.treeview.set_search_column(1)
303
183
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
304
184
        scrollwin.add(self.treeview)
305
 
        self.treeview.show()
 
185
        if self.SHOW_WIDGETS:
 
186
            self.treeview.show()
306
187
 
307
188
        cell = Gtk.CellRendererText()
308
189
        cell.set_property("width-chars", 20)
309
190
        column = Gtk.TreeViewColumn()
310
 
        column.pack_start(cell, True, True, 0)
 
191
        column.pack_start(cell, True)
311
192
        column.add_attribute(cell, "text", 0)
312
193
        self.treeview.append_column(column)
313
194
 
323
204
        if getattr(self, 'diff_view', None) is None:
324
205
            self.diff_view = DiffFileView()
325
206
            self.pack2(self.diff_view)
326
 
        self.diff_view.show()
 
207
        if self.SHOW_WIDGETS:
 
208
            self.diff_view.show()
327
209
        for oldname, newname, patch in sections:
328
210
            self.diff_view._diffs[newname] = str(patch)
329
211
            if newname is None:
340
222
        if getattr(self, 'diff_view', None) is None:
341
223
            self.diff_view = DiffView()
342
224
            self.pack2(self.diff_view)
343
 
        self.diff_view.show()
 
225
        if self.SHOW_WIDGETS:
 
226
            self.diff_view.show()
344
227
        self.diff_view.set_trees(rev_tree, parent_tree)
345
228
        self.rev_tree = rev_tree
346
229
        self.parent_tree = parent_tree
348
231
        self.model.clear()
349
232
        delta = self.rev_tree.changes_from(self.parent_tree)
350
233
 
351
 
        self.model.append(None, [ "Complete Diff", "" ])
 
234
        self.model.append(None, ["Complete Diff", ""])
352
235
 
353
236
        if len(delta.added):
354
 
            titer = self.model.append(None, [ "Added", None ])
 
237
            titer = self.model.append(None, ["Added", None])
355
238
            for path, id, kind in delta.added:
356
 
                self.model.append(titer, [ path, path ])
 
239
                self.model.append(titer, [path, path])
357
240
 
358
241
        if len(delta.removed):
359
 
            titer = self.model.append(None, [ "Removed", None ])
 
242
            titer = self.model.append(None, ["Removed", None])
360
243
            for path, id, kind in delta.removed:
361
 
                self.model.append(titer, [ path, path ])
 
244
                self.model.append(titer, [path, path])
362
245
 
363
246
        if len(delta.renamed):
364
 
            titer = self.model.append(None, [ "Renamed", None ])
 
247
            titer = self.model.append(None, ["Renamed", None])
365
248
            for oldpath, newpath, id, kind, text_modified, meta_modified \
366
249
                    in delta.renamed:
367
 
                self.model.append(titer, [ oldpath, newpath ])
 
250
                self.model.append(titer, [oldpath, newpath])
368
251
 
369
252
        if len(delta.modified):
370
 
            titer = self.model.append(None, [ "Modified", None ])
 
253
            titer = self.model.append(None, ["Modified", None])
371
254
            for path, id, kind, text_modified, meta_modified in delta.modified:
372
 
                self.model.append(titer, [ path, path ])
 
255
                self.model.append(titer, [path, path])
373
256
 
374
257
        self.treeview.expand_all()
375
258
        self.diff_view.show_diff(None)
384
267
                    break
385
268
        if tv_path is None:
386
269
            raise errors.NoSuchFile(file_path)
387
 
        self.treeview.set_cursor(tv_path)
 
270
        self.treeview.set_cursor(tv_path, None, False)
388
271
        self.treeview.scroll_to_cell(tv_path)
389
272
 
390
273
    def _treeview_cursor_cb(self, *args):
391
274
        """Callback for when the treeview cursor changes."""
392
275
        (path, col) = self.treeview.get_cursor()
393
 
        specific_files = [ self.model[path][1] ]
394
 
        if specific_files == [ None ]:
395
 
            return
396
 
        elif specific_files == [ "" ]:
 
276
        if path is None:
 
277
            return
 
278
        specific_files = [self.model[path][1]]
 
279
        if specific_files == [None]:
 
280
            return
 
281
        elif specific_files == [""]:
397
282
            specific_files = None
398
 
        
 
283
 
399
284
        self.diff_view.show_diff(specific_files)
400
 
    
 
285
 
401
286
    def _on_wraplines_toggled(self, widget=None, wrap=False):
402
287
        """Callback for when the wrap lines checkbutton is toggled"""
403
288
        if wrap or widget.get_active():
405
290
        else:
406
291
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.NONE)
407
292
 
 
293
 
408
294
class DiffWindow(Window):
409
295
    """Diff window.
410
296
 
412
298
    differences between two revisions on a branch.
413
299
    """
414
300
 
 
301
    SHOW_WIDGETS = True
 
302
 
415
303
    def __init__(self, parent=None, operations=None):
416
 
        Window.__init__(self, parent)
 
304
        super(DiffWindow, self).__init__(parent=parent)
417
305
        self.set_border_width(0)
418
 
        self.set_title("bzrk diff")
 
306
        self.set_title("bzr diff")
419
307
 
420
308
        # Use two thirds of the screen by default
421
309
        screen = self.get_screen()
429
317
        """Construct the window contents."""
430
318
        self.vbox = Gtk.VBox()
431
319
        self.add(self.vbox)
432
 
        self.vbox.show()
 
320
        if self.SHOW_WIDGETS:
 
321
            self.vbox.show()
433
322
        self.diff = DiffWidget()
434
323
        self.vbox.pack_end(self.diff, True, True, 0)
435
 
        self.diff.show_all()
 
324
        if self.SHOW_WIDGETS:
 
325
            self.diff.show_all()
436
326
        # Build after DiffWidget to connect signals
437
327
        menubar = self._get_menu_bar()
438
328
        self.vbox.pack_start(menubar, False, False, 0)
439
329
        hbox = self._get_button_bar(operations)
440
330
        if hbox is not None:
441
331
            self.vbox.pack_start(hbox, False, True, 0)
442
 
        
443
 
    
 
332
 
444
333
    def _get_menu_bar(self):
445
334
        menubar = Gtk.MenuBar()
446
335
        # View menu
447
 
        mb_view = Gtk.MenuItem(_i18n("_View"))
 
336
        mb_view = Gtk.MenuItem.new_with_mnemonic(_i18n("_View"))
448
337
        mb_view_menu = Gtk.Menu()
449
 
        mb_view_wrapsource = Gtk.CheckMenuItem(_i18n("Wrap _Long Lines"))
 
338
        mb_view_wrapsource = Gtk.CheckMenuItem.new_with_mnemonic(
 
339
            _i18n("Wrap _Long Lines"))
450
340
        mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
451
 
        mb_view_wrapsource.show()
452
341
        mb_view_menu.append(mb_view_wrapsource)
453
 
        mb_view.show()
454
342
        mb_view.set_submenu(mb_view_menu)
455
 
        mb_view.show()
456
343
        menubar.append(mb_view)
457
 
        menubar.show()
 
344
        if self.SHOW_WIDGETS:
 
345
            menubar.show_all()
458
346
        return menubar
459
 
    
 
347
 
460
348
    def _get_button_bar(self, operations):
461
349
        """Return a button bar to use.
462
350
 
468
356
        hbox.set_layout(Gtk.ButtonBoxStyle.START)
469
357
        for title, method in operations:
470
358
            merge_button = Gtk.Button(title)
471
 
            merge_button.show()
 
359
            if self.SHOW_WIDGETS:
 
360
                merge_button.show()
472
361
            merge_button.set_relief(Gtk.ReliefStyle.NONE)
473
362
            merge_button.connect("clicked", method)
474
 
            hbox.pack_start(merge_button, expand=False, fill=True)
475
 
        hbox.show()
 
363
            hbox.pack_start(merge_button, False, True, 0)
 
364
        if self.SHOW_WIDGETS:
 
365
            hbox.show()
476
366
        return hbox
477
367
 
478
368
    def _get_merge_target(self):
579
469
class MergeDirectiveController(DiffController):
580
470
 
581
471
    def __init__(self, path, directive, window=None):
582
 
        DiffController.__init__(self, path, directive.patch.splitlines(True),
583
 
                                window)
 
472
        super(MergeDirectiveController, self).__init__(
 
473
            path, directive.patch.splitlines(True), window)
584
474
        self.directive = directive
585
475
        self.merge_target = None
586
476