[meld] Split out DiffMap functionality into new widget
- From: Kai Willadsen <kaiw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [meld] Split out DiffMap functionality into new widget
- Date: Tue, 23 Mar 2010 22:43:55 +0000 (UTC)
commit 48b455adfc3ee40ec7a274f96eea53a59064a6db
Author: Kai Willadsen <kai willadsen gmail com>
Date: Sun Mar 14 19:19:05 2010 +1000
Split out DiffMap functionality into new widget
The DiffMap widget (the bars showing a coloured summary of changes at
the left and right of the main file comparison window) has always been
a purely custom creation based on gtk.DrawingArea. This commit breaks it
out into a separate file and makes it a DrawingArea subclass.
This also changes the drawing done, syncing the colours used with the
LinkMap and TextView, and allowing thinner lines for insert chunks. In
addition, our style-dependent calculations should now be more robust for
unusual themes.
data/ui/filediff.glade | 14 +---
meld/diffmap.py | 151 ++++++++++++++++++++++++++++++++++++++++++++++++
meld/filediff.py | 53 ++---------------
3 files changed, 161 insertions(+), 57 deletions(-)
---
diff --git a/data/ui/filediff.glade b/data/ui/filediff.glade
index 9c5f7f5..91b455d 100644
--- a/data/ui/filediff.glade
+++ b/data/ui/filediff.glade
@@ -216,12 +216,9 @@
</packing>
</child>
<child>
- <widget class="GtkDrawingArea" id="diffmap1">
- <property name="width_request">20</property>
+ <widget class="Custom" id="diffmap1">
<property name="visible">True</property>
- <property name="events">GDK_BUTTON_PRESS_MASK</property>
- <signal name="expose_event" handler="on_diffmap_expose_event"/>
- <signal name="button_press_event" handler="on_diffmap_button_press_event"/>
+ <property name="creation_function">meld.diffmap.create_diffmap</property>
</widget>
<packing>
<property name="left_attach">6</property>
@@ -256,12 +253,9 @@
</packing>
</child>
<child>
- <widget class="GtkDrawingArea" id="diffmap0">
- <property name="width_request">20</property>
+ <widget class="Custom" id="diffmap0">
<property name="visible">True</property>
- <property name="events">GDK_BUTTON_PRESS_MASK</property>
- <signal name="expose_event" handler="on_diffmap_expose_event"/>
- <signal name="button_press_event" handler="on_diffmap_button_press_event"/>
+ <property name="creation_function">meld.diffmap.create_diffmap</property>
</widget>
<packing>
<property name="top_attach">1</property>
diff --git a/meld/diffmap.py b/meld/diffmap.py
new file mode 100644
index 0000000..3b31b60
--- /dev/null
+++ b/meld/diffmap.py
@@ -0,0 +1,151 @@
+### Copyright (C) 2002-2009 Stephen Kennedy <stevek gnome org>
+### Copyright (C) 2009-2010 Kai Willadsen <kai willadsen gmail com>
+
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import gobject
+import gtk
+
+
+class DiffMap(gtk.DrawingArea):
+
+ __gtype_name__ = "DiffMap"
+
+ __gsignals__ = {
+ 'expose-event': 'override',
+ 'button-press-event': 'override',
+ 'size-request': 'override',
+ }
+
+ def __init__(self):
+ gtk.DrawingArea.__init__(self)
+ self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ self._scrolladj = None
+ self._difffunc = lambda: None
+ self._handlers = []
+ self._y_offset = 0
+ self._h_offset = 0
+ self._scroll_y = 0
+ self._scroll_height = 0
+ self._num_lines = 0
+
+ def setup(self, scrollbar, textbuffer, change_chunk_fn):
+ for (o, h) in self._handlers:
+ o.disconnect(h)
+
+ self._scrolladj = scrollbar.get_adjustment()
+ self.on_scrollbar_style_set(scrollbar, None)
+ self.on_scrollbar_size_allocate(scrollbar, scrollbar.allocation)
+ self.on_textbuffer_changed(textbuffer)
+ scroll_style_hid = scrollbar.connect("style-set",
+ self.on_scrollbar_style_set)
+ scroll_size_hid = scrollbar.connect("size-allocate",
+ self.on_scrollbar_size_allocate)
+ buffer_changed_hid = textbuffer.connect("changed",
+ self.on_textbuffer_changed)
+ self._handlers = [(scrollbar, scroll_style_hid),
+ (scrollbar, scroll_size_hid),
+ (textbuffer, buffer_changed_hid)]
+ self._difffunc = change_chunk_fn
+ self.queue_draw()
+
+ def on_scrollbar_style_set(self, scrollbar, previous_style):
+ stepper_size = scrollbar.style_get_property("stepper-size")
+ steppers = [scrollbar.style_get_property(x) for x in
+ ("has-backward-stepper", "has-secondary-forward-stepper",
+ "has-secondary-backward-stepper", "has-forward-stepper")]
+ stepper_spacing = scrollbar.style_get_property("stepper-spacing")
+
+ offset = stepper_size * steppers[0:2].count(True)
+ shorter = stepper_size * steppers.count(True)
+ if steppers[0] or steppers[1]:
+ offset += stepper_spacing
+ shorter += stepper_spacing
+ if steppers[2] or steppers[3]:
+ shorter += stepper_spacing
+ self._y_offset = offset
+ self._h_offset = shorter
+ self.queue_draw()
+
+ def on_scrollbar_size_allocate(self, scrollbar, allocation):
+ self._scroll_y = allocation.y
+ self._scroll_height = allocation.height
+ self.queue_draw()
+
+ def on_textbuffer_changed(self, textbuffer):
+ num_lines = textbuffer.get_line_count()
+ if num_lines != self._num_lines:
+ self._num_lines = num_lines
+ self.queue_draw()
+
+ def do_expose_event(self, event):
+ scale = float(self._scroll_height - self._h_offset) / self._num_lines
+ y_start = self._scroll_y - self.allocation.y + self._y_offset
+
+ context = self.window.cairo_create()
+ context.translate(0, y_start)
+ context.set_line_width(1)
+ ctab = {"conflict": (1.0, 0.75294117647058822, 0.79607843137254897),
+ "insert": (0.75686274509803919, 1.0, 0.75686274509803919),
+ "replace": (0.8666666666666667, 0.93333333333333335, 1.0),
+ "delete": (0.75686274509803919, 1.0, 0.75686274509803919)}
+ darken = lambda color: [x * 0.8 for x in color]
+
+ xpad = self.style_get_property('x-padding')
+ x0 = xpad
+ x1 = self.allocation.width - 2 * xpad
+ for c in self._difffunc():
+ color = ctab[c[0]]
+ y0 = round(scale * c[1]) - 0.5
+ y1 = round(scale * c[2]) - 0.5
+ context.set_source_rgb(*color)
+ context.rectangle(x0, y0, x1, int(y1 - y0))
+ context.fill_preserve()
+ context.set_source_rgb(*darken(color))
+ context.stroke()
+
+ def do_button_press_event(self, event):
+ if event.button == 1:
+ y_start = self.allocation.y - self._scroll_y - self._y_offset
+ total_height = self._scroll_height - self._h_offset
+ fraction = (event.y + y_start) / total_height
+
+ adj = self._scrolladj
+ val = fraction * adj.upper - adj.page_size / 2
+ upper = adj.upper - adj.page_size
+ adj.set_value(max(min(upper, val), adj.lower))
+ return True
+ return False
+
+ def do_size_request(self, request):
+ request.width = self.style_get_property('width')
+
+gtk.widget_class_install_style_property(DiffMap,
+ ('width', float,
+ 'Width',
+ 'Width of the bar',
+ 0.0, gobject.G_MAXFLOAT, 20,
+ gobject.PARAM_READABLE))
+gtk.widget_class_install_style_property(DiffMap,
+ ('x-padding', float,
+ 'Width-wise padding',
+ 'Padding to be left between left and '
+ 'right edges and change blocks',
+ 0.0, gobject.G_MAXFLOAT, 2.5,
+ gobject.PARAM_READABLE))
+
+
+def create_diffmap(str1, str2, int1, int2):
+ return DiffMap()
diff --git a/meld/filediff.py b/meld/filediff.py
index 1f875ab..3eb4ead 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -33,7 +33,6 @@ from ui import gnomeglade
import misc
import melddoc
import paths
-import cairo
from util.sourceviewer import srcviewer
@@ -1142,52 +1141,6 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
lm.window.invalidate_rect(None, True)
lm.window.process_updates(True)
- #
- # scrollbar drawing
- #
- def on_diffmap__expose_event(self, area, event):
- def rect(ctx, color, y0,y1, xpad=2.5,width=area.get_allocation().width):
- ctx.set_source(color)
- context.rectangle(xpad, y0, width-2*xpad, max(2, y1-y0))
- ctx.fill_preserve()
- ctx.set_source_rgba(0, 0, 0, 1.0)
- ctx.stroke()
-
- diffmapindex = self.diffmap.index(area)
- textindex = (0, self.num_panes-1)[diffmapindex]
- scroll = self.scrolledwindow[textindex].get_vscrollbar()
- stepper_size = scroll.style_get_property("stepper-size")
-
- context = area.window.cairo_create() # setup cairo
- context.translate( 0, stepper_size )
- scale = float(scroll.get_allocation().height - 2*stepper_size) / self.textbuffer[textindex].get_line_count()
-
- context.set_line_width(0.5)
- solid_green = cairo.SolidPattern(.5, 1, .5, 0.25)
- solid_red = cairo.SolidPattern(1, .5, .5, 0.75)
- solid_blue = cairo.SolidPattern(.5, 1, 1, 0.25)
- ctab = {"conflict":solid_red,
- "insert":solid_green,
- "replace":solid_blue,
- "delete":solid_green}
- for c in self.linediffer.single_changes(textindex):
- assert c[0] != "equal"
- rect(context, ctab[c[0]], scale*c[1], scale*c[2])
-
- def on_diffmap_button_press_event(self, area, event):
- if event.button == 1:
- textindex = (0, self.num_panes-1)[self.diffmap.index(area)]
- scroll = self.scrolledwindow[textindex].get_vscrollbar()
- stepper_size = scroll.style_get_property("stepper-size")
- alloc = scroll.get_allocation()
- fraction = (event.y - (stepper_size + alloc.y) + area.get_allocation().y ) / (alloc.height - 2*stepper_size)
- adj = self.scrolledwindow[textindex].get_vadjustment()
- val = fraction * adj.upper - adj.page_size/2
- upper = adj.upper - adj.page_size
- adj.set_value( max( min(upper, val), 0) )
- return 1
- return 0
-
def set_num_panes(self, n):
if n != self.num_panes and n in (1,2,3):
self.num_panes = n
@@ -1201,6 +1154,12 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
tohide += self.linkmap[n-1:] + self.diffmap[n:]
map( lambda x: x.hide(), tohide )
+ def chunk_change_fn(i):
+ return lambda: self.linediffer.single_changes(i)
+ for (w, i) in zip(self.diffmap, (0, self.num_panes - 1)):
+ scroll = self.scrolledwindow[i].get_vscrollbar()
+ w.setup(scroll, self.textbuffer[i], chunk_change_fn(i))
+
for i in range(self.num_panes):
if self.bufferdata[i].modified:
self.statusimage[i].show()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]