Commit 48b455ad authored by Kai Willadsen's avatar Kai Willadsen

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.
parent 0a66e232
......@@ -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>
......
### 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()
......@@ -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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment