.
1 /*
2 Copyright (C) 2014 2015 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 using Math;
17
18 namespace BirdFont {
19
20 public class SettingsDisplay : FontDisplay {
21
22 double scroll = 0;
23 double content_height = 1;
24 WidgetAllocation allocation;
25 Gee.ArrayList<SettingsItem> tools;
26
27 public static SpinButton precision;
28
29 SettingsItem new_key_bindings = new SettingsItem.head_line ("");
30 public static bool update_key_bindings = false;
31
32 public SettingsDisplay () {
33 allocation = new WidgetAllocation ();
34 tools = new Gee.ArrayList<SettingsItem> ();
35 content_height = 200;
36 precision = new SpinButton ("precision");
37 create_setting_items ();
38 }
39
40 public void create_setting_items () {
41 tools.clear ();
42 // setting items
43 tools.add (new SettingsItem.head_line (t_("Settings")));
44
45 SpinButton stroke_width = new SpinButton ("stroke_width");
46 tools.add (new SettingsItem (stroke_width, t_("Stroke width")));
47
48 stroke_width.set_max (4);
49 stroke_width.set_min (0.002);
50 stroke_width.set_value_round (1);
51
52 if (Preferences.get ("stroke_width_for_open_paths") != "") {
53 stroke_width.set_value (Preferences.get ("stroke_width_for_open_paths"));
54 }
55
56 stroke_width.new_value_action.connect ((self) => {
57 Glyph g = MainWindow.get_current_glyph ();
58 Path.stroke_width = stroke_width.get_value ();
59 g.redraw_area (0, 0, g.allocation.width, g.allocation.height);
60 Preferences.set ("stroke_width_for_open_paths", stroke_width.get_display_value ());
61 MainWindow.get_toolbox ().redraw ((int) stroke_width.x, (int) stroke_width.y, 70, 70);
62 });
63
64 Path.stroke_width = stroke_width.get_value ();
65
66 // adjust precision
67 string precision_value = Preferences.get ("precision");
68
69 if (precision_value != "") {
70 precision.set_value (precision_value);
71 } else {
72 #if ANDROID
73 precision.set_value_round (0.5);
74 #else
75 precision.set_value_round (1);
76 #endif
77 }
78
79 precision.new_value_action.connect ((self) => {
80 MainWindow.get_toolbox ().select_tool (precision);
81 Preferences.set ("precision", self.get_display_value ());
82 MainWindow.get_toolbox ().redraw ((int) precision.x, (int) precision.y, 70, 70);
83 });
84
85 precision.select_action.connect((self) => {
86 DrawingTools.pen_tool.set_precision (((SpinButton)self).get_value ());
87 });
88
89 precision.set_min (0.001);
90 precision.set_max (1);
91
92 tools.add (new SettingsItem (precision, t_("Precision for pen tool")));
93
94 Tool show_all_line_handles = new Tool ("show_all_line_handles");
95 show_all_line_handles.select_action.connect((self) => {
96 Path.show_all_line_handles = !Path.show_all_line_handles;
97 Glyph g = MainWindow.get_current_glyph ();
98 g.redraw_area (0, 0, g.allocation.width, g.allocation.height);
99 });
100 tools.add (new SettingsItem (show_all_line_handles, t_("Show or hide control point handles")));
101
102 Tool fill_open_path = new Tool ("fill_open_path");
103 fill_open_path.select_action.connect((self) => {
104 Path.fill_open_path = !Path.fill_open_path;
105 Glyph g = MainWindow.get_current_glyph ();
106 g.redraw_area (0, 0, g.allocation.width, g.allocation.height);
107 });
108 tools.add (new SettingsItem (fill_open_path, t_("Fill open paths.")));
109
110 Tool ttf_units = new Tool ("ttf_units");
111 ttf_units.select_action.connect((self) => {
112 GridTool.ttf_units = !GridTool.ttf_units;
113 Preferences.set ("ttf_units", @"$(GridTool.ttf_units)");
114 });
115 tools.add (new SettingsItem (ttf_units, t_("Use TTF units.")));
116
117 SpinButton freehand_samples = new SpinButton ("freehand_samples_per_point");
118 tools.add (new SettingsItem (freehand_samples, t_("Number of points added by the freehand tool")));
119
120 freehand_samples.set_max (9);
121 freehand_samples.set_min (0.002);
122
123 if (BirdFont.android) {
124 freehand_samples.set_value_round (2.5);
125 } else {
126 freehand_samples.set_value_round (1);
127 }
128
129 if (Preferences.get ("freehand_samples") != "") {
130 freehand_samples.set_value (Preferences.get ("freehand_samples"));
131 DrawingTools.track_tool.set_samples_per_point (freehand_samples.get_value ());
132 }
133
134 freehand_samples.new_value_action.connect ((self) => {
135 DrawingTools.track_tool.set_samples_per_point (freehand_samples.get_value ());
136 });
137
138 SpinButton simplification_threshold = new SpinButton ("simplification_threshold");
139 simplification_threshold.set_value_round (0.5);
140 tools.add (new SettingsItem (simplification_threshold, t_("Path simplification threshold")));
141
142 simplification_threshold.set_max (5);
143 freehand_samples.set_min (0.002);
144
145 if (Preferences.get ("simplification_threshold") != "") {
146 freehand_samples.set_value (Preferences.get ("simplification_threshold"));
147 DrawingTools.pen_tool.set_simplification_threshold (simplification_threshold.get_value ());
148 }
149
150 freehand_samples.new_value_action.connect ((self) => {
151 DrawingTools.pen_tool.set_simplification_threshold (simplification_threshold.get_value ());
152 });
153
154 tools.add (new SettingsItem.head_line (t_("Key Bindings")));
155
156 foreach (MenuItem menu_item in MainWindow.get_menu ().sorted_menu_items) {
157 tools.add (new SettingsItem.key_binding (menu_item));
158 }
159
160 tools.add (new SettingsItem.head_line (t_("Themes")));
161
162 Gee.ArrayList<Tool> theme_buttons = new Gee.ArrayList<Tool> ();
163
164 foreach (string theme in Theme.themes) {
165 string label;
166 Tool select_theme = new Tool (theme);
167
168 select_theme.deselect_action.connect((self) => {
169 self.set_active (false);
170 });
171
172 select_theme.select_action.connect((self) => {
173 string theme_file = self.get_name ();
174 TabBar tb;
175
176 Preferences.set ("theme", theme_file);
177 Theme.load_theme (theme_file);
178
179 foreach (Tool t in theme_buttons) {
180 t.set_selected (false);
181 t.set_active (false);
182 }
183
184 self.set_selected (true);
185 create_setting_items ();
186
187 Toolbox.redraw_tool_box ();
188 GlyphCanvas.redraw ();
189
190 tb = MainWindow.get_tab_bar ();
191 tb.redraw (0, 0, tb.width, tb.height);
192 });
193
194 select_theme.set_icon ("theme");
195
196 if (theme == "default.theme") {
197 label = t_("Default theme");
198 } else if (theme == "high_contrast.theme") {
199 label = t_("High contrast theme");
200 } else if (theme == "custom.theme") {
201 label = t_("Custom theme");
202 } else {
203 label = theme.replace (".theme", "");
204 }
205
206 tools.add (new SettingsItem (select_theme, label));
207 theme_buttons.add (select_theme);
208
209 if (select_theme.get_name () == Theme.current_theme) {
210 select_theme.set_selected (true);
211 }
212 }
213
214 foreach (Tool t in theme_buttons) {
215 t.set_selected (t.name == Theme.current_theme);
216 }
217
218 Tool add_theme = new Tool ("add_new_theme");
219 add_theme.select_action.connect((self) => {
220 foreach (Tool t in theme_buttons) {
221 t.set_selected (false);
222 }
223
224 self.set_selected (false);
225 Theme.add_new_theme (this);
226 GlyphCanvas.redraw ();
227 });
228 tools.add (new SettingsItem (add_theme, t_("Add new theme")));
229
230 tools.add (new SettingsItem.head_line (t_("Colors")));
231
232 foreach (string color in Theme.color_list) {
233 SettingsItem s = new SettingsItem.color (color);
234 ColorTool c = (ColorTool) ((!) s.button);
235
236 tools.add (s);
237
238 c.color_updated.connect (() => {
239 create_setting_items ();
240 GlyphCanvas.redraw ();
241 });
242 }
243 }
244
245 public override void draw (WidgetAllocation allocation, Context cr) {
246 this.allocation = allocation;
247
248 layout ();
249
250 // background
251 cr.save ();
252 cr.rectangle (0, 0, allocation.width, allocation.height);
253 cr.set_line_width (0);
254 Theme.color (cr, "Background 4");
255 cr.fill ();
256 cr.stroke ();
257 cr.restore ();
258
259 foreach (SettingsItem s in tools) {
260 if (-20 * MainWindow.units <= s.y <= allocation.height + 20 * MainWindow.units) {
261 s.draw (allocation, cr);
262 }
263 }
264 }
265
266 public void layout () {
267 double y = -scroll;
268 bool first = true;
269 foreach (SettingsItem s in tools) {
270
271 if (!first && s.headline) {
272 y += 30 * MainWindow.units;
273 }
274
275 s.y = y;
276
277 if (s.button != null) {
278 ((!) s.button).y = y;
279 ((!) s.button).x = 20 * MainWindow.units;
280 }
281
282 if (s.headline) {
283 y += 50 * MainWindow.units;
284 } else {
285 y += 40 * MainWindow.units;
286 }
287
288 first = false;
289 }
290
291 content_height = y + scroll;
292 }
293
294 void set_key_bindings (SettingsItem item) {
295 if (new_key_bindings.active) {
296 new_key_bindings.active = false;
297 update_key_bindings = false;
298 } else {
299 new_key_bindings.active = false;
300 new_key_bindings = item;
301 update_key_bindings = true;
302 new_key_bindings.active = true;
303 }
304 }
305
306 public override void key_release (uint keyval) {
307 SettingsItem old_key_binding;
308
309 if (update_key_bindings) {
310 if (keyval == Key.BACK_SPACE) {
311 update_key_bindings = false;
312 new_key_bindings.active = false;
313 new_key_bindings.menu_item.modifiers = NONE;
314 new_key_bindings.menu_item.key = '\0';
315 } else if (KeyBindings.get_mod_from_key (keyval) == NONE) {
316
317 if (has_key_binding (KeyBindings.modifier, (unichar) keyval)) {
318 old_key_binding = (!) get_key_binding (KeyBindings.modifier, (unichar) keyval);
319 old_key_binding.menu_item.modifiers = NONE;
320 old_key_binding.menu_item.key = '\0';
321 }
322
323 new_key_bindings.menu_item.modifiers = KeyBindings.modifier;
324 new_key_bindings.menu_item.key = (unichar) keyval;
325 update_key_bindings = false;
326 new_key_bindings.active = false;
327 }
328
329 MainWindow.get_menu ().write_key_bindings ();
330 GlyphCanvas.redraw ();
331 }
332 }
333
334 bool has_key_binding (uint modifier, unichar key) {
335 return get_key_binding (modifier, key) != null;
336 }
337
338 SettingsItem? get_key_binding (uint modifier, unichar key) {
339 foreach (SettingsItem i in tools) {
340 if (i.menu_item.modifiers == modifier && i.menu_item.key == key) {
341 return i;
342 }
343 }
344
345 return null;
346 }
347
348 public override void button_press (uint button, double x, double y) {
349 foreach (SettingsItem s in tools) {
350 if (s.handle_events && s.button != null) {
351 if (((!) s.button).is_over (x, y)) {
352
353 ((!) s.button).set_selected (! ((!) s.button).selected);
354
355 if (((!) s.button).selected) {
356 ((!) s.button).select_action ((!) s.button);
357 }
358
359 ((!) s.button).panel_press_action ((!) s.button, button, x, y);
360 }
361 }
362 }
363 GlyphCanvas.redraw ();
364 }
365
366 public override void button_release (int button, double x, double y) {
367 foreach (SettingsItem s in tools) {
368 if (s.handle_events && s.button != null) {
369 ((!) s.button).panel_release_action (((!) s.button), button, x, y);
370 }
371
372 if (s.key_bindings && s.y <= y < s.y + 40 * MainWindow.units && button == 1) {
373 set_key_bindings (s);
374 }
375 }
376 GlyphCanvas.redraw ();
377 }
378
379 public override void motion_notify (double x, double y) {
380 bool consumed = false;
381 bool active;
382 bool update = false;
383
384 foreach (SettingsItem si in tools) {
385
386 if (si.handle_events && si.button != null) {
387 active = ((!) si.button).is_over (x, y);
388
389 if (!active && ((!) si.button).is_active ()) {
390 ((!) si.button).move_out_action ((!) si.button);
391 }
392
393 if (((!) si.button).set_active (active)) {
394 update = true;
395 }
396 }
397 }
398
399 foreach (SettingsItem s in tools) {
400 if (s.handle_events && s.button != null) {
401 if (((!) s.button).panel_move_action ((!) s.button, x, y)) {
402 consumed = true;
403 }
404 }
405 }
406
407 if (consumed || update) {
408 GlyphCanvas.redraw ();
409 }
410 }
411
412 public override string get_label () {
413 return t_("Settings");
414 }
415
416 public override string get_name () {
417 return "Settings";
418 }
419
420 public override bool has_scrollbar () {
421 return true;
422 }
423
424 public override void scroll_wheel_down (double x, double y) {
425 foreach (SettingsItem s in tools) {
426 if (s.handle_events && s.button != null) {
427 if (((!) s.button).is_over (x, y)) {
428 ((!) s.button).scroll_wheel_down_action ((!) s.button);
429 return;
430 }
431 }
432 }
433
434 scroll += 25 * MainWindow.units;
435
436 if (scroll + allocation.height >= content_height) {
437 scroll = content_height - allocation.height;
438 }
439
440 update_scrollbar ();
441 GlyphCanvas.redraw ();
442 }
443
444 public override void scroll_wheel_up (double x, double y) {
445 foreach (SettingsItem s in tools) {
446 if (s.handle_events && s.button != null) {
447 if (((!) s.button).is_over (x, y)) {
448 ((!) s.button).scroll_wheel_up_action ((!) s.button);
449 return;
450 }
451 }
452 }
453
454 scroll -= 25 * MainWindow.units;
455
456 if (scroll < 0) {
457 scroll = 0;
458 }
459
460 update_scrollbar ();
461 GlyphCanvas.redraw ();
462 }
463
464 public override void selected_canvas () {
465 MainWindow.get_toolbox ().set_default_tool_size ();
466 update_scrollbar ();
467 GlyphCanvas.redraw ();
468 }
469
470 public override void update_scrollbar () {
471 double h = content_height - allocation.height;
472 MainWindow.set_scrollbar_size (allocation.height / content_height);
473 MainWindow.set_scrollbar_position (scroll / h);
474 }
475
476 public override void scroll_to (double percent) {
477 double h = content_height - allocation.height;
478 scroll = percent * h;
479 GlyphCanvas.redraw ();
480 }
481 }
482
483 }
484