.
1 /*
2 Copyright (C) 2012, 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16
17 namespace BirdFont {
18
19 public class Tool : Widget {
20
21 public double x = 0;
22 public double y = 0;
23 public double w;
24 public double h;
25
26 double scale;
27
28 public bool active = false;
29 public bool selected = false;
30
31 public Text icon_font;
32
33 public signal void select_action (Tool selected);
34 public signal void deselect_action (Tool selected);
35
36 public signal void press_action (Tool selected, int button, int x, int y);
37 public signal void double_click_action (Tool selected, int button, int x, int y);
38 public signal void move_action (Tool selected, int x, int y);
39 public signal void move_out_action (Tool selected);
40 public signal void release_action (Tool selected, int button, int x, int y);
41
42 /** Returns true if tool is listening for scroll wheel actions. */
43 public signal bool scroll_wheel_up_action (Tool selected);
44 public signal bool scroll_wheel_down_action (Tool selected);
45
46 public signal void key_press_action (Tool selected, uint32 keyval);
47 public signal void key_release_action (Tool selected, uint32 keyval);
48
49 public signal void panel_press_action (Tool selected, uint button, double x, double y);
50 public signal void panel_release_action (Tool selected, uint button, double x, double y);
51 public signal void panel_double_click_action (Tool selected, uint button, double x, double y);
52
53 /** @return true is event is consumed. */
54 public signal bool panel_move_action (Tool selected, double x, double y);
55
56 public signal void draw_action (Tool selected, Context cr, Glyph glyph);
57
58 public string name = "";
59
60 static int next_id = 1;
61
62 int id;
63
64 public bool new_selection = false;
65
66 bool show_bg = true;
67
68 public string tip = "";
69
70 public bool persistent = false;
71 public bool editor_events = false;
72
73 bool waiting_for_tooltip = false;
74 bool showing_this_tooltip = false;
75 static Tool active_tooltip = new Tool ();
76
77 public bool visible = true;
78 public bool is_tool_modifier = false;
79
80 public string icon_color = "";
81
82 public signal void redraw_tool ();
83
84 /** Create tool with a certain name and load icon "name".png */
85 public Tool (string? name = null, string tip = "") {
86 this.tip = tip;
87
88 icon_font = new Text ();
89
90 scale = MainWindow.units;
91 w = 33 * Toolbox.get_scale ();
92 h = (33 / 1.11) * Toolbox.get_scale ();
93
94 if (name != null) {
95 set_icon ((!) name);
96 this.name = (!) name;
97 }
98
99 id = next_id;
100 next_id++;
101
102 panel_press_action.connect ((self, button, x, y) => {
103 if (is_active ()) {
104 redraw ();
105 }
106 });
107
108 select_action.connect ((self) => {
109 redraw ();
110 });
111
112 deselect_action.connect ((self) => {
113 redraw ();
114 });
115
116 move_out_action.connect ((self) => {
117 MainWindow.get_toolbox ().hide_tooltip ();
118 active_tooltip.showing_this_tooltip = false;
119 redraw ();
120 });
121
122 panel_move_action.connect ((self, x, y) => {
123 if (is_active ()) {
124 wait_for_tooltip ();
125 redraw ();
126 }
127 return false;
128 });
129 }
130
131 public virtual string get_tip () {
132 return tip;
133 }
134
135 public override void draw (Context cr) {
136 draw_tool (cr, 0, 0);
137 }
138
139 public void redraw () {
140 redraw_tool ();
141 Toolbox.redraw_tool_box ();
142 }
143
144 public override double get_height () {
145 return 33 * scale;
146 }
147
148 public override double get_width () {
149 return 33 * scale;
150 }
151
152 public void set_tool_visibility (bool v) {
153 visible = v;
154 }
155
156 public bool tool_is_visible () {
157 return visible;
158 }
159
160 void wait_for_tooltip () {
161 TimeoutSource timer_show;
162 int timeout_interval = 1500;
163
164 if (active_tooltip != this) {
165 if (active_tooltip.showing_this_tooltip) {
166 timeout_interval = 1;
167 }
168
169 active_tooltip.showing_this_tooltip = false;
170 showing_this_tooltip = false;
171 active_tooltip = this;
172
173 if (!waiting_for_tooltip) {
174 waiting_for_tooltip = true;
175 timer_show = new TimeoutSource (timeout_interval);
176 timer_show.set_callback (() => {
177 if (get_tip () != "" && active_tooltip.is_active () && !active_tooltip.showing_this_tooltip) {
178 show_tooltip ();
179 }
180 waiting_for_tooltip = false;
181 return waiting_for_tooltip;
182 });
183 timer_show.attach (null);
184 }
185 }
186 }
187
188 public static void show_tooltip () {
189 TimeoutSource timer_hide;
190 Toolbox toolbox;
191 string tip;
192 string key_binding;
193
194 toolbox = MainWindow.get_toolbox ();
195
196 // hide tooltip label later
197 if (!active_tooltip.showing_this_tooltip) {
198 timer_hide = new TimeoutSource (1500);
199 timer_hide.set_callback (() => {
200 if (!active_tooltip.is_active ()) {
201 toolbox.hide_tooltip ();
202 active_tooltip.showing_this_tooltip = false;
203 active_tooltip = new Tool ();
204 }
205 return active_tooltip.showing_this_tooltip;
206 });
207 timer_hide.attach (null);
208 }
209
210 active_tooltip.showing_this_tooltip = true;
211
212 tip = @"$(active_tooltip.get_tip ())";
213 key_binding = active_tooltip.get_key_binding ();
214
215 if (key_binding != "") {
216 tip += " (" + key_binding + ")";
217 }
218
219 toolbox.hide_tooltip ();
220 toolbox.show_tooltip (tip, (int) active_tooltip.x, (int) active_tooltip.y);
221 }
222
223 public string get_key_binding () {
224 ToolItem? ti = MainWindow.get_menu ().get_item_for_tool (this);
225 ToolItem t;
226
227 if (ti == null) {
228 warning ("No key binding for tool.");
229 return "";
230 }
231
232 t = (!) ti;
233 return t.get_key_binding ();
234 }
235
236 public void set_icon (string name) {
237 bool found;
238 string icon_file;
239
240 icon_file = Theme.get_icon_file ();
241 icon_font = new Text ((!) name);
242 found = icon_font.load_font (icon_file);
243 icon_font.use_cache (true);
244 icon_font.set_font_size (40 * Toolbox.get_scale ());
245
246 if (!found) {
247 warning (@"Icon font for toolbox was not found. ($(icon_file))");
248 }
249 }
250
251 public bool is_active () {
252 return active;
253 }
254
255 public void set_show_background (bool bg) {
256 show_bg = bg;
257 }
258
259 public int get_id () {
260 return id;
261 }
262
263 public string get_name () {
264 return name;
265 }
266
267 public bool is_selected () {
268 return selected;
269 }
270
271 public new bool is_over (double xp, double yp) {
272 bool r = (x <= xp <= x + w && y <= yp <= y + h);
273 return r;
274 }
275
276 public bool set_selected (bool a) {
277 new_selection = true;
278 selected = a;
279 set_active (a);
280
281 if (!a) {
282 deselect_action (this);
283 }
284
285 return true;
286 }
287
288 /** @return true if this tool changes state, */
289 public bool set_active (bool ac) {
290 bool ret = (active != ac);
291 active = ac;
292 return ret;
293 }
294
295 public virtual void draw_tool (Context cr, double px, double py) {
296 double xt = x - px;
297 double yt = y - py;
298
299 double bgx, bgy;
300 double iconx, icony;
301
302 string border = "Button Border 3";
303 string background = "Button Border 3";
304
305 double scale = Toolbox.get_scale ();
306
307 cr.save ();
308
309 bgx = xt;
310 bgy = yt;
311
312 // Button in four states
313 if (selected) {
314 border = "Button Border 1";
315 background = "Button Background 1";
316 }
317
318 if (selected && active) {
319 border = "Button Border 2";
320 background = "Button Background 2";
321 }
322
323 if (!selected) {
324 border = "Button Border 3";
325 background = "Button Background 3";
326 }
327
328 if (!selected && active) {
329 border = "Button Border 4";
330 background = "Button Background 4";
331 }
332
333 Theme.color (cr, background);
334 draw_rounded_rectangle (cr, bgx, bgy, 34 * scale, 28 * scale, 4 * scale);
335 cr.fill ();
336
337 cr.set_line_width (1);
338 Theme.color (cr, border);
339 draw_rounded_rectangle (cr, bgx, bgy, 34 * scale, 28 * scale, 4 * scale);
340 cr.stroke ();
341
342 iconx = bgx + 1 + (w - 1) / 2 - icon_font.get_sidebearing_extent () / 2;
343 icony = bgy + 1 + (h - 1) / 2 - icon_font.get_height () / 2;
344
345 if (icon_color == "") {
346 if (!selected) {
347 Theme.text_color (icon_font, "Tool Foreground");
348 } else {
349 Theme.text_color (icon_font, "Selected Tool Foreground");
350 }
351 } else {
352 Theme.text_color (icon_font, icon_color);
353 }
354
355 icon_font.widget_x = iconx;
356 icon_font.widget_y = icony;
357
358 icon_font.draw (cr);
359
360 cr.restore ();
361 }
362
363 /** Run pending events in main loop before continuing. */
364 public static void @yield () {
365 int t = 0;
366 TimeoutSource time = new TimeoutSource (500);
367 bool timeout;
368 unowned MainContext context;
369 bool acquired;
370
371 if (TestBirdFont.is_slow_test ()) {
372 timeout = false;
373
374 time.set_callback (() => {
375 timeout = true;
376 return false;
377 });
378
379 time.attach (null);
380 } else {
381 timeout = true;
382 }
383
384 context = MainContext.default ();
385 acquired = context.acquire ();
386
387 if (unlikely (!acquired)) {
388 warning ("Failed to acquire main loop.\n");
389 return;
390 }
391
392 while (context.pending () || TestBirdFont.is_slow_test ()) {
393 context.iteration (true);
394 t++;
395
396 if (!context.pending () && TestBirdFont.is_slow_test ()) {
397 if (timeout) break;
398 }
399 }
400
401 context.release ();
402 }
403
404 public void set_persistent (bool p) {
405 persistent = p;
406 }
407
408 public virtual void before_undo () {
409 }
410
411 public virtual void after_undo () {
412 }
413
414 }
415
416 }
417