-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlinkmap.py
More file actions
140 lines (110 loc) · 5.29 KB
/
linkmap.py
File metadata and controls
140 lines (110 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org>
# Copyright (C) 2009-2013 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, see <http://www.gnu.org/licenses/>.
import math
from gi.repository import Gdk, Gtk
from meld.settings import get_meld_settings
from meld.style import get_common_theme
# Rounded rectangle corner radius for culled changes display
RADIUS = 3
class LinkMap(Gtk.DrawingArea):
__gtype_name__ = "LinkMap"
def __init__(self):
self.filediff = None
self.views = []
def associate(self, filediff, left_view, right_view):
self.filediff = filediff
self.views = [left_view, right_view]
if self.get_direction() == Gtk.TextDirection.RTL:
self.views.reverse()
self.view_indices = [filediff.textview.index(t) for t in self.views]
meld_settings = get_meld_settings()
self.on_setting_changed(meld_settings, 'style-scheme')
meld_settings.connect('changed', self.on_setting_changed)
def on_setting_changed(self, settings, key):
if key == 'style-scheme':
self.fill_colors, self.line_colors = get_common_theme()
def do_draw(self, context):
if not self.views:
return
pix_start = [t.get_visible_rect().y for t in self.views]
y_offset = [
t.translate_coordinates(self, 0, 0)[1] + 1 for t in self.views]
clip_y = min(y_offset) - 1
clip_height = max(t.get_visible_rect().height for t in self.views) + 2
allocation = self.get_allocation()
stylecontext = self.get_style_context()
Gtk.render_background(
stylecontext, context, 0, clip_y, allocation.width, clip_height)
context.set_line_width(1.0)
height = allocation.height
visible = [
self.views[0].get_line_num_for_y(pix_start[0]),
self.views[0].get_line_num_for_y(pix_start[0] + height),
self.views[1].get_line_num_for_y(pix_start[1]),
self.views[1].get_line_num_for_y(pix_start[1] + height),
]
# For bezier control points
x_steps = [-0.5, allocation.width / 2, allocation.width + 0.5]
q_rad = math.pi / 2
left, right = self.view_indices
def view_offset_line(view_idx, line_num):
line_start = self.views[view_idx].get_y_for_line_num(line_num)
return line_start - pix_start[view_idx] + y_offset[view_idx]
for c in self.filediff.linediffer.pair_changes(left, right, visible):
# f and t are short for "from" and "to"
f0, f1 = [view_offset_line(0, l) for l in c[1:3]]
t0, t1 = [view_offset_line(1, l) for l in c[3:5]]
# We want the last pixel of the previous line
f1 = f1 if f1 == f0 else f1 - 1
t1 = t1 if t1 == t0 else t1 - 1
# If either endpoint is completely off-screen, we cull for clarity
if (t0 < 0 and t1 < 0) or (t0 > height and t1 > height):
if f0 == f1:
continue
context.arc(
x_steps[0], f0 - 0.5 + RADIUS, RADIUS, q_rad * 3, 0)
context.arc(x_steps[0], f1 - 0.5 - RADIUS, RADIUS, 0, q_rad)
context.close_path()
elif (f0 < 0 and f1 < 0) or (f0 > height and f1 > height):
if t0 == t1:
continue
context.arc_negative(x_steps[2], t0 - 0.5 + RADIUS, RADIUS,
q_rad * 3, q_rad * 2)
context.arc_negative(x_steps[2], t1 - 0.5 - RADIUS, RADIUS,
q_rad * 2, q_rad)
context.close_path()
else:
context.move_to(x_steps[0], f0 - 0.5)
context.curve_to(x_steps[1], f0 - 0.5,
x_steps[1], t0 - 0.5,
x_steps[2], t0 - 0.5)
context.line_to(x_steps[2], t1 - 0.5)
context.curve_to(x_steps[1], t1 - 0.5,
x_steps[1], f1 - 0.5,
x_steps[0], f1 - 0.5)
context.close_path()
Gdk.cairo_set_source_rgba(context, self.fill_colors[c[0]])
context.fill_preserve()
chunk_idx = self.filediff.linediffer.locate_chunk(left, c[1])[0]
if chunk_idx == self.filediff.cursor.chunk:
highlight = self.fill_colors['current-chunk-highlight']
Gdk.cairo_set_source_rgba(context, highlight)
context.fill_preserve()
Gdk.cairo_set_source_rgba(context, self.line_colors[c[0]])
context.stroke()
LinkMap.set_css_name("link-map")
class ScrollLinkMap(Gtk.DrawingArea):
__gtype_name__ = "ScrollLinkMap"