View difference between Paste ID: f63c6aaa9 and
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/env ruby
2
3
require 'gtk2'
4
require 'rational'
5
6
=begin
7
A drawing area, which can be panned and zoomed.
8
(requires rational)
9
Panning is done with middle mouse drag, zooming is with the scrollwheel.
10
To zoom only into x or y axis, hold down the shift or ctrl keys respectively.
11
You must create the following method:
12
expose_event event, cairo, coordinates
13
where
14
event is the expose event
15
cairo is the cairo context
16
coordinates is an array with the [xstart, ystart, xend, yend] coordinates
17
=end
18
class DragZoomDrawingArea < Gtk::DrawingArea
19
	type_register
20
21
	def initialize
22
		super
23
24
		@xscale = @yscale = 1.to_r
25
		@xoffset = @yoffset = 0.to_r
26
		@xscalestep = @yscalestep = 2.to_r
27
		@drag_sig_handlers = []
28
29
		signal_connect('expose-event') {|widget, event|
30
			cairo = window.create_cairo_context
31
			cairo.scale(@xscale, @yscale)
32
			cairo.translate(@xoffset, @yoffset)
33
34
			alloc = allocation
35
			width, height = alloc.width, alloc.height
36
37
			xstart = (0.to_r - @xoffset) / @xscale
38
			xend = (width.to_r - @xoffset) / @xscale
39
40
			ystart = (0.to_r - @yoffset) / @yscale
41
			yend = (height.to_r - @yoffset) / @yscale
42
43
			expose_event widget, event, cairo, [xstart, ystart, xend, yend]
44
			true
45
		}
46
47
		signal_connect('scroll-event') {|widget, event|
48
			do_xscale = do_yscale = true
49
50
			if event.state & Gdk::Window::ModifierType::SHIFT_MASK != 0
51
				do_yscale = false
52
			end
53
			if event.state & Gdk::Window::ModifierType::CONTROL_MASK != 0
54
				do_xscale = false
55
			end
56
57
			xscale_old, yscale_old = @xscale, @yscale
58
			case event.direction
59
			when Gdk::EventScroll::DOWN
60
				@xscale /= @xscalestep if do_xscale
61
				@yscale /= @yscalestep if do_yscale
62
			when Gdk::EventScroll::UP
63
				@xscale *= @xscalestep if do_xscale
64
				@yscale *= @yscalestep if do_yscale
65
			end
66
67
			@xoffset = event.x.to_i.to_r / @xscale - (event.x.to_i / xscale_old - @xoffset)
68
			@yoffset = event.y.to_i.to_r / @yscale - (event.y.to_i / yscale_old - @yoffset)
69
			queue_draw
70
		}
71
72
		@motion_cb = proc {|widget, event|
73
			deltax, deltay = event.x.to_i.to_r - @startx.to_i, event.y.to_i.to_r - @starty.to_i
74
			@xoffset += deltax / @xscale
75
			@yoffset += deltay / @yscale
76
			@startx, @starty = event.x, event.y
77
			queue_draw
78
		}
79
80
		signal_connect('button-press-event') {|widget, event|
81
			if(event.button == 2)
82
				@startx, @starty = event.x, event.y
83
				@drag_sig_handlers << signal_connect('motion-notify-event', &@motion_cb)
84
			end
85
		}
86
87
		signal_connect('button-release-event') {|widget, event|
88
			if(event.button == 2)
89
				@drag_sig_handlers.reject!{|s| signal_handler_disconnect(s); true}
90
			end
91
		}
92
93
		add_events(Gdk::Event::SCROLL_MASK |
94
			Gdk::Event::BUTTON_PRESS_MASK |
95
			Gdk::Event::BUTTON_RELEASE_MASK |
96
			Gdk::Event::POINTER_MOTION_MASK)
97
	end
98
end
99
100
class MyWindow < Gtk::Window
101
	def initialize
102
		super('interactive graph')
103
		set_size_request(500,500)
104
		signal_connect("delete-event") { Gtk.main_quit;true }
105
106
107
		drawing_area = DragZoomDrawingArea.new
108
		def drawing_area.expose_event widget, event, cairo, disp
109
			cairo.set_source_rgb(1,0,0)
110
			cairo.rectangle(100, 100, 50, 50)
111
			cairo.stroke
112
		end
113
		add(drawing_area)
114
		show_all
115
	end
116
end
117
118
app = MyWindow.new
119
120
Gtk.main
121
122