/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: Jelmer Vernooij
  • Date: 2012-03-23 13:45:02 UTC
  • Revision ID: jelmer@samba.org-20120323134502-fsxyhpb0ilbabozp
Ignore bzr-handle-patch in software center.

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