7
7
__copyright__ = "Copyright 2005 Canonical Ltd."
8
__author__ = "Scott James Remnant <scott@ubuntu.com>"
8
__author__ = "Scott James Remnant <scott@ubuntu.com>"
11
11
from cStringIO import StringIO
13
from gi.repository import Gtk
14
from gi.repository import Pango
20
from xml.etree.ElementTree import Element, SubElement, tostring
22
from elementtree.ElementTree import Element, SubElement, tostring
16
from gi.repository import Gtk
17
from gi.repository import Pango
19
25
from gi.repository import GtkSource
20
26
have_gtksourceview = True
21
27
except ImportError:
22
28
have_gtksourceview = False
30
from gi.repository import GConf
24
35
from bzrlib import (
70
80
self.buffer = GtkSource.Buffer()
71
81
lang_manager = GtkSource.LanguageManager.get_default()
72
82
language = lang_manager.guess_language(None, "text/x-patch")
84
self.apply_gedit_colors(self.buffer)
85
self.apply_colordiff_colors(self.buffer)
73
86
self.buffer.set_language(language)
74
87
self.buffer.set_highlight_syntax(True)
75
89
self.sourceview = GtkSource.View(buffer=self.buffer)
77
91
self.buffer = Gtk.TextBuffer()
78
92
self.sourceview = Gtk.TextView(self.buffer)
80
94
self.sourceview.set_editable(False)
81
self.sourceview.override_font(Pango.FontDescription("Monospace"))
95
self.sourceview.modify_font(Pango.FontDescription("Monospace"))
82
96
self.add(self.sourceview)
84
self.sourceview.show()
97
self.sourceview.show()
100
def apply_gedit_colors(buf):
101
"""Set style to that specified in gedit configuration.
103
This method needs the gconf module.
105
:param buf: a GtkSource.Buffer object.
107
GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
108
GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
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)
116
style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
118
if style_scheme is not None:
119
buf.set_style_scheme(style_scheme)
122
def apply_colordiff_colors(klass, buf):
123
"""Set style colors for lang using the colordiff configuration file.
125
Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
127
:param buf: a "Diff" GtkSource.Buffer object.
129
scheme_manager = GtkSource.StyleSchemeManager()
130
style_scheme = scheme_manager.get_scheme('colordiff')
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:
137
for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
138
f = os.path.expanduser(f)
139
if os.path.exists(f):
143
warning('could not open file %s: %s' % (f, str(e)))
145
colors.update(klass.parse_colordiffrc(f))
149
# ~/.colordiffrc does not exist
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'],
163
converted_colors = {}
164
for name, values in mapping.items():
167
color = colors.get(value, None)
168
if color is not None:
172
converted_colors[name] = color
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)
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)
189
scheme_manager.force_rescan()
190
style_scheme = scheme_manager.get_scheme('colordiff')
192
buf.set_style_scheme(style_scheme)
195
def parse_colordiffrc(fileobj):
196
"""Parse fileobj as a colordiff configuration file.
198
:return: A dict with the key -> value pairs.
202
if re.match(r'^\s*#', line):
206
key, val = line.split('=', 1)
207
colors[key.strip()] = val.strip()
86
210
def set_trees(self, rev_tree, parent_tree):
87
211
self.rev_tree = rev_tree
92
216
# self.parent_tree.lock_read()
93
217
# self.rev_tree.lock_read()
95
# self.delta = iter_changes_to_status(
96
# self.parent_tree, self.rev_tree)
219
# self.delta = iter_changes_to_status(self.parent_tree, self.rev_tree)
97
220
# self.path_to_status = {}
98
221
# self.path_to_diff = {}
99
222
# source_inv = self.parent_tree.inventory
100
223
# target_inv = self.rev_tree.inventory
101
224
# for (file_id, real_path, change_type, display_path) in self.delta:
102
# self.path_to_status[real_path] = u'=== %s %s' % (
103
# change_type, display_path)
225
# self.path_to_status[real_path] = u'=== %s %s' % (change_type, display_path)
104
226
# if change_type in ('modified', 'renamed and modified'):
105
227
# source_ie = source_inv[file_id]
106
228
# target_ie = target_inv[file_id]
231
346
self.model.clear()
232
347
delta = self.rev_tree.changes_from(self.parent_tree)
234
self.model.append(None, ["Complete Diff", ""])
349
self.model.append(None, [ "Complete Diff", "" ])
236
351
if len(delta.added):
237
titer = self.model.append(None, ["Added", None])
352
titer = self.model.append(None, [ "Added", None ])
238
353
for path, id, kind in delta.added:
239
self.model.append(titer, [path, path])
354
self.model.append(titer, [ path, path ])
241
356
if len(delta.removed):
242
titer = self.model.append(None, ["Removed", None])
357
titer = self.model.append(None, [ "Removed", None ])
243
358
for path, id, kind in delta.removed:
244
self.model.append(titer, [path, path])
359
self.model.append(titer, [ path, path ])
246
361
if len(delta.renamed):
247
titer = self.model.append(None, ["Renamed", None])
362
titer = self.model.append(None, [ "Renamed", None ])
248
363
for oldpath, newpath, id, kind, text_modified, meta_modified \
249
364
in delta.renamed:
250
self.model.append(titer, [oldpath, newpath])
365
self.model.append(titer, [ oldpath, newpath ])
252
367
if len(delta.modified):
253
titer = self.model.append(None, ["Modified", None])
368
titer = self.model.append(None, [ "Modified", None ])
254
369
for path, id, kind, text_modified, meta_modified in delta.modified:
255
self.model.append(titer, [path, path])
370
self.model.append(titer, [ path, path ])
257
372
self.treeview.expand_all()
258
373
self.diff_view.show_diff(None)
273
388
def _treeview_cursor_cb(self, *args):
274
389
"""Callback for when the treeview cursor changes."""
275
390
(path, col) = self.treeview.get_cursor()
278
specific_files = [self.model[path][1]]
279
if specific_files == [None]:
281
elif specific_files == [""]:
391
specific_files = [ self.model[path][1] ]
392
if specific_files == [ None ]:
394
elif specific_files == [ "" ]:
282
395
specific_files = None
284
397
self.diff_view.show_diff(specific_files)
286
399
def _on_wraplines_toggled(self, widget=None, wrap=False):
287
400
"""Callback for when the wrap lines checkbutton is toggled"""
288
401
if wrap or widget.get_active():