/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 viz/diffwin.py

  • Committer: Jelmer Vernooij
  • Date: 2006-05-19 16:56:46 UTC
  • mfrom: (0.1.25 gannotate)
  • Revision ID: jelmer@samba.org-20060519165646-0d867938fdbc9097
Merge in Dan Loda's gannotate plugin and put it in annotate/

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
1
2
# -*- coding: UTF-8 -*-
2
3
"""Difference window.
3
4
 
13
14
 
14
15
import gtk
15
16
import pango
16
 
import os
17
 
import re
18
 
import sys
19
17
 
20
18
try:
21
19
    import gtksourceview
22
20
    have_gtksourceview = True
23
21
except ImportError:
24
22
    have_gtksourceview = False
25
 
try:
26
 
    import gconf
27
 
    have_gconf = True
28
 
except ImportError:
29
 
    have_gconf = False
30
 
 
31
 
import bzrlib
32
 
 
 
23
 
 
24
from bzrlib.delta import compare_trees
33
25
from bzrlib.diff import show_diff_trees
34
 
from bzrlib.errors import NoSuchFile
35
 
from bzrlib.trace import warning
36
26
 
37
27
 
38
28
class DiffWindow(gtk.Window):
42
32
    differences between two revisions on a branch.
43
33
    """
44
34
 
45
 
    def __init__(self):
 
35
    def __init__(self, app=None):
46
36
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
47
37
        self.set_border_width(0)
48
38
        self.set_title("bzrk diff")
49
39
 
 
40
        self.app = app
 
41
 
50
42
        # Use two thirds of the screen by default
51
43
        screen = self.get_screen()
52
44
        monitor = screen.get_monitor_geometry(0)
58
50
 
59
51
    def construct(self):
60
52
        """Construct the window contents."""
61
 
        # The   window  consists  of   a  pane   containing:  the
62
 
        # hierarchical list  of files on  the left, and  the diff
63
 
        # for the currently selected file on the right.
64
 
        pane = gtk.HPaned()
65
 
        self.add(pane)
66
 
        pane.show()
 
53
        hbox = gtk.HBox(spacing=6)
 
54
        hbox.set_border_width(12)
 
55
        self.add(hbox)
 
56
        hbox.show()
67
57
 
68
 
        # The file hierarchy: a scrollable treeview
69
58
        scrollwin = gtk.ScrolledWindow()
70
59
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
71
60
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
72
 
        pane.pack1(scrollwin)
 
61
        hbox.pack_start(scrollwin, expand=False, fill=True)
73
62
        scrollwin.show()
74
63
 
75
64
        self.model = gtk.TreeStore(str, str)
87
76
        column.add_attribute(cell, "text", 0)
88
77
        self.treeview.append_column(column)
89
78
 
90
 
        # The diffs of the  selected file: a scrollable source or
91
 
        # text view
 
79
 
92
80
        scrollwin = gtk.ScrolledWindow()
93
81
        scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
94
82
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
95
 
        pane.pack2(scrollwin)
 
83
        hbox.pack_start(scrollwin, expand=True, fill=True)
96
84
        scrollwin.show()
97
85
 
98
86
        if have_gtksourceview:
99
87
            self.buffer = gtksourceview.SourceBuffer()
100
88
            slm = gtksourceview.SourceLanguagesManager()
101
89
            gsl = slm.get_language_from_mime_type("text/x-patch")
102
 
            if have_gconf:
103
 
                self.apply_gedit_colors(gsl)
104
 
            self.apply_colordiff_colors(gsl)
105
90
            self.buffer.set_language(gsl)
106
91
            self.buffer.set_highlight(True)
107
92
 
115
100
        scrollwin.add(sourceview)
116
101
        sourceview.show()
117
102
 
118
 
    def set_diff(self, description, rev_tree, parent_tree):
 
103
    def set_diff(self, branch, revid, parentid):
119
104
        """Set the differences showed by this window.
120
105
 
121
106
        Compares the two trees and populates the window with the
122
107
        differences.
123
108
        """
124
 
        self.rev_tree = rev_tree
125
 
        self.parent_tree = parent_tree
 
109
        self.rev_tree = branch.repository.revision_tree(revid)
 
110
        self.parent_tree = branch.repository.revision_tree(parentid)
126
111
 
127
112
        self.model.clear()
128
 
        delta = self.rev_tree.changes_from(self.parent_tree)
 
113
        delta = compare_trees(self.parent_tree, self.rev_tree)
129
114
 
130
115
        self.model.append(None, [ "Complete Diff", "" ])
131
116
 
143
128
            titer = self.model.append(None, [ "Renamed", None ])
144
129
            for oldpath, newpath, id, kind, text_modified, meta_modified \
145
130
                    in delta.renamed:
146
 
                self.model.append(titer, [ oldpath, newpath ])
 
131
                self.model.append(titer, [ oldpath, oldpath ])
147
132
 
148
133
        if len(delta.modified):
149
134
            titer = self.model.append(None, [ "Modified", None ])
151
136
                self.model.append(titer, [ path, path ])
152
137
 
153
138
        self.treeview.expand_all()
154
 
        self.set_title(description + " - bzrk diff")
155
 
 
156
 
    def set_file(self, file_path):
157
 
        tv_path = None
158
 
        for data in self.model:
159
 
            for child in data.iterchildren():
160
 
                if child[0] == file_path or child[1] == file_path:
161
 
                    tv_path = child.path
162
 
                    break
163
 
        if tv_path is None:
164
 
            raise NoSuchFile(file_path)
165
 
        self.treeview.set_cursor(tv_path)
166
 
        self.treeview.scroll_to_cell(tv_path)
 
139
        self.set_title(revid + " - " + branch.nick + " - bzrk diff")
167
140
 
168
141
    def _treeview_cursor_cb(self, *args):
169
142
        """Callback for when the treeview cursor changes."""
176
149
 
177
150
        s = StringIO()
178
151
        show_diff_trees(self.parent_tree, self.rev_tree, s, specific_files)
179
 
        self.buffer.set_text(s.getvalue().decode(sys.getdefaultencoding(), 'replace'))
180
 
 
181
 
    @staticmethod
182
 
    def apply_gedit_colors(lang):
183
 
        """Set style for lang to that specified in gedit configuration.
184
 
 
185
 
        This method needs the gconf module.
186
 
        
187
 
        :param lang: a gtksourceview.SourceLanguage object.
188
 
        """
189
 
        GEDIT_SYNTAX_PATH = '/apps/gedit-2/preferences/syntax_highlighting'
190
 
        GEDIT_LANG_PATH = GEDIT_SYNTAX_PATH + '/' + lang.get_id()
191
 
 
192
 
        client = gconf.client_get_default()
193
 
        client.add_dir(GEDIT_LANG_PATH, gconf.CLIENT_PRELOAD_NONE)
194
 
 
195
 
        for tag in lang.get_tags():
196
 
            tag_id = tag.get_id()
197
 
            gconf_key = GEDIT_LANG_PATH + '/' + tag_id
198
 
            style_string = client.get_string(gconf_key)
199
 
 
200
 
            if style_string is None:
201
 
                continue
202
 
 
203
 
            # function to get a bool from a string that's either '0' or '1'
204
 
            string_bool = lambda x: bool(int(x))
205
 
 
206
 
            # style_string is a string like "2/#FFCCAA/#000000/0/1/0/0"
207
 
            # values are: mask, fg, bg, italic, bold, underline, strike
208
 
            # this packs them into (str_value, attr_name, conv_func) tuples
209
 
            items = zip(style_string.split('/'), ['mask', 'foreground',
210
 
                'background', 'italic', 'bold', 'underline', 'strikethrough' ],
211
 
                [ int, gtk.gdk.color_parse, gtk.gdk.color_parse, string_bool,
212
 
                    string_bool, string_bool, string_bool ]
213
 
            )
214
 
 
215
 
            style = gtksourceview.SourceTagStyle()
216
 
 
217
 
            # XXX The mask attribute controls whether the present values of
218
 
            # foreground and background color should in fact be used. Ideally
219
 
            # (and that's what gedit does), one could set all three attributes,
220
 
            # and let the TagStyle object figure out which colors to use.
221
 
            # However, in the GtkSourceview python bindings, the mask attribute
222
 
            # is read-only, and it's derived instead from the colors being
223
 
            # set or not. This means that we have to sometimes refrain from
224
 
            # setting fg or bg colors, depending on the value of the mask.
225
 
            # This code could go away if mask were writable.
226
 
            mask = int(items[0][0])
227
 
            if not (mask & 1): # GTK_SOURCE_TAG_STYLE_USE_BACKGROUND
228
 
                items[2:3] = []
229
 
            if not (mask & 2): # GTK_SOURCE_TAG_STYLE_USE_FOREGROUND
230
 
                items[1:2] = []
231
 
            items[0:1] = [] # skip the mask unconditionally
232
 
 
233
 
            for value, attr, func in items:
234
 
                try:
235
 
                    value = func(value)
236
 
                except ValueError:
237
 
                    warning('gconf key %s contains an invalid value: %s'
238
 
                            % gconf_key, value)
239
 
                else:
240
 
                    setattr(style, attr, value)
241
 
 
242
 
            lang.set_tag_style(tag_id, style)
243
 
 
244
 
    @staticmethod
245
 
    def apply_colordiff_colors(lang):
246
 
        """Set style colors for lang using the colordiff configuration file.
247
 
 
248
 
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
249
 
 
250
 
        :param lang: a "Diff" gtksourceview.SourceLanguage object.
251
 
        """
252
 
        colors = {}
253
 
 
254
 
        for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
255
 
            f = os.path.expanduser(f)
256
 
            if os.path.exists(f):
257
 
                try:
258
 
                    f = file(f)
259
 
                except IOError, e:
260
 
                    warning('could not open file %s: %s' % (f, str(e)))
261
 
                else:
262
 
                    colors.update(DiffWindow.parse_colordiffrc(f))
263
 
                    f.close()
264
 
 
265
 
        if not colors:
266
 
            # ~/.colordiffrc does not exist
267
 
            return
268
 
 
269
 
        mapping = {
270
 
                # map GtkSourceView tags to colordiff names
271
 
                # since GSV is richer, accept new names for extra bits,
272
 
                # defaulting to old names if they're not present
273
 
                'Added@32@line': ['newtext'],
274
 
                'Removed@32@line': ['oldtext'],
275
 
                'Location': ['location', 'diffstuff'],
276
 
                'Diff@32@file': ['file', 'diffstuff'],
277
 
                'Special@32@case': ['specialcase', 'diffstuff'],
278
 
        }
279
 
 
280
 
        for tag in lang.get_tags():
281
 
            tag_id = tag.get_id()
282
 
            keys = mapping.get(tag_id, [])
283
 
            color = None
284
 
 
285
 
            for key in keys:
286
 
                color = colors.get(key, None)
287
 
                if color is not None:
288
 
                    break
289
 
 
290
 
            if color is None:
291
 
                continue
292
 
 
293
 
            style = gtksourceview.SourceTagStyle()
294
 
            try:
295
 
                style.foreground = gtk.gdk.color_parse(color)
296
 
            except ValueError:
297
 
                warning('not a valid color: %s' % color)
298
 
            else:
299
 
                lang.set_tag_style(tag_id, style)
300
 
 
301
 
    @staticmethod
302
 
    def parse_colordiffrc(fileobj):
303
 
        """Parse fileobj as a colordiff configuration file.
304
 
        
305
 
        :return: A dict with the key -> value pairs.
306
 
        """
307
 
        colors = {}
308
 
        for line in fileobj:
309
 
            if re.match(r'^\s*#', line):
310
 
                continue
311
 
            if '=' not in line:
312
 
                continue
313
 
            key, val = line.split('=', 1)
314
 
            colors[key.strip()] = val.strip()
315
 
        return colors
316
 
 
 
152
        self.buffer.set_text(s.getvalue())