.
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 B;
17
18 namespace BirdFont {
19
20 /** Interface for events from native window to the current tab. */
21 public class AbstractMenu : GLib.Object {
22
23 public bool show_menu {
24 get {
25 return menu_visibility;
26 }
27
28 set {
29 string tab_name;
30
31 menu_visibility = value;
32 current_menu = top_menu;
33
34 if (menu_visibility) {
35 tab_name = MainWindow.get_tab_bar ().get_selected_tab ().get_display ().get_name ();
36 if (tab_name == "Preview") {
37 MenuTab.select_overview ();
38 }
39 }
40 }
41 }
42
43 public bool menu_visibility = false;
44 public SubMenu top_menu;
45
46 SubMenu current_menu;
47 WidgetAllocation allocation = new WidgetAllocation ();
48
49 double width = 250 * MainWindow.units;
50 double height = 25 * MainWindow.units;
51
52 public Gee.HashMap<string, MenuItem> menu_items = new Gee.HashMap<string, MenuItem> ();
53 public Gee.ArrayList<MenuItem> sorted_menu_items = new Gee.ArrayList<MenuItem> ();
54
55 public AbstractMenu () {
56 }
57
58 public ToolItem? get_item_for_tool (Tool t) {
59 ToolItem tm;
60
61 foreach (MenuItem item in sorted_menu_items) {
62 if (item is ToolItem) {
63 tm = (ToolItem) item;
64
65 if (tm.tool == t) {
66 return tm;
67 }
68 }
69 }
70
71 return null;
72 }
73
74 public void process_key_binding_events (uint keyval) {
75 string display;
76 FontDisplay current_display = MainWindow.get_current_display ();
77 ToolItem tm;
78 unichar lower_keyval = ((unichar) keyval).tolower ();
79
80 foreach (MenuItem item in sorted_menu_items) {
81 if (item.key.tolower () == lower_keyval && item.modifiers == KeyBindings.modifier) {
82
83 display = current_display.get_name ();
84
85 if (current_display is Glyph) {
86 display = "Glyph";
87 }
88
89 if (!current_display.needs_modifier () || item.modifiers != NONE) {
90 if (!SettingsDisplay.update_key_bindings
91 && item.in_display (display)
92 && !(item is ToolItem)) {
93 item.action ();
94 return;
95 }
96
97 if (item is ToolItem) {
98 tm = (ToolItem) item;
99
100 if (tm.in_display (display)) {
101 if (tm.tool.editor_events) {
102 MainWindow.get_toolbox ().set_current_tool (tm.tool);
103 tm.tool.select_action (tm.tool);
104 return;
105 } else {
106 tm.tool.select_action (tm.tool);
107 return;
108 }
109 }
110 }
111 }
112 }
113 }
114 }
115
116 public void load_key_bindings () {
117 File default_key_bindings = SearchPaths.find_file (null, "key_bindings.xml");
118 File user_key_bindings = get_child (BirdFont.get_settings_directory (), "key_bindings.xml");
119
120 if (default_key_bindings.query_exists ()) {
121 parse_key_bindings (default_key_bindings);
122 }
123
124 if (user_key_bindings.query_exists ()) {
125 parse_key_bindings (user_key_bindings);
126 }
127 }
128
129 public void parse_key_bindings (File f) {
130 string xml_data;
131 XmlParser parser;
132
133 try {
134 FileUtils.get_contents((!) f.get_path (), out xml_data);
135 parser = new XmlParser (xml_data);
136 parse_bindings (parser.get_root_tag ());
137 } catch (GLib.Error e) {
138 warning (e.message);
139 }
140 }
141
142 public void parse_bindings (Tag tag) {
143 foreach (Tag t in tag) {
144 if (t.get_name () == "action") {
145 parse_binding (t.get_attributes ());
146 }
147 }
148 }
149
150 public void parse_binding (Attributes attr) {
151 uint modifier = NONE;
152 unichar key = '\0';
153 string action = "";
154 MenuItem menu_action;
155 MenuItem? ma;
156
157 foreach (Attribute a in attr) {
158 if (a.get_name () == "key") {
159 key = a.get_content ().get_char (0);
160 }
161
162 if (a.get_name () == "ctrl" && a.get_content () == "true") {
163 modifier |= CTRL;
164 }
165
166 if (a.get_name () == "alt" && a.get_content () == "true") {
167 modifier |= ALT;
168 }
169
170 if (a.get_name () == "command" && a.get_content () == "true") {
171 modifier |= LOGO;
172 }
173
174 if (a.get_name () == "shift" && a.get_content () == "true") {
175 modifier |= SHIFT;
176 }
177
178 if (a.get_name () == "action") {
179 action = a.get_content ();
180 }
181 }
182
183 ma = menu_items.get (action);
184 if (ma != null) {
185 menu_action = (!) ma;
186 menu_action.modifiers = modifier;
187 menu_action.key = key;
188 }
189 }
190
191 public MenuItem add_menu_item (string label, string description = "", string display = "") {
192 MenuItem i = new MenuItem (label, description);
193
194 if (description != "") {
195 menu_items.set (description, i);
196 sorted_menu_items.add (i);
197 }
198
199 if (display != "") {
200 i.add_display (display);
201 }
202
203 return i;
204 }
205
206 public void button_release (int button, double ex, double ey) {
207 double y = 0;
208 double x = allocation.width - width;
209
210 if (button == 1) {
211 foreach (MenuItem item in current_menu.items) {
212 if (x <= ex < allocation.width && y <= ey <= y + height) {
213 item.action ();
214 GlyphCanvas.redraw ();
215 return;
216 }
217
218 y += height;
219 }
220
221 menu_visibility = false;
222 current_menu = (!) top_menu;
223 GlyphCanvas.redraw ();
224 }
225 }
226
227 public void add_tool_key_bindings () {
228 ToolItem tool_item;
229 foreach (ToolCollection tool_set in MainWindow.get_toolbox ().tool_sets) {
230 foreach (Expander e in tool_set.get_expanders ()) {
231 foreach (Tool t in e.tool) {
232 tool_item = new ToolItem (t);
233 if (tool_item.identifier != "" && !has_menu_item (tool_item.identifier)) {
234 menu_items.set (tool_item.identifier, tool_item);
235 sorted_menu_items.add (tool_item);
236 }
237
238 foreach (string d in tool_set.get_displays ()) {
239 tool_item.add_display (d);
240 }
241 }
242 }
243 }
244 }
245
246 public bool has_menu_item (string identifier) {
247 foreach (MenuItem mi in sorted_menu_items) {
248 if (mi.identifier == identifier) {
249 return true;
250 }
251 }
252
253 return false;
254 }
255
256 public void set_menu (SubMenu m) {
257 current_menu = m;
258 GlyphCanvas.redraw ();
259 }
260
261 public double layout_width () {
262 Text key_binding = new Text ();
263 double font_size = 17 * MainWindow.units;;
264 double w;
265
266 width = 0;
267 foreach (MenuItem item in current_menu.items) {
268 key_binding.set_text (item.get_key_bindings ());
269
270 item.label.set_font_size (font_size);
271 key_binding.set_font_size (font_size);
272
273 w = item.label.get_extent ();
274 w += key_binding.get_extent ();
275 w += 3 * height * MainWindow.units;
276
277 if (w > width) {
278 width = w;
279 }
280 }
281
282 return width;
283 }
284
285 public void draw (WidgetAllocation allocation, Context cr) {
286 double y;
287 double x;
288 double label_x;
289 double label_y;
290 double font_size;
291 Text key_binding;
292 double binding_extent;
293
294 width = layout_width ();
295
296 key_binding = new Text ();
297
298 x = allocation.width - width;
299 y = 0;
300 font_size = 17 * MainWindow.units;
301 this.allocation = allocation;
302
303 foreach (MenuItem item in current_menu.items) {
304 cr.save ();
305 Theme.color (cr, "Menu Background");
306 cr.rectangle (x, y, width, height);
307 cr.fill ();
308 cr.restore ();
309
310 cr.save ();
311 label_x = allocation.width - width + 0.7 * height * MainWindow.units;
312 label_y = y + font_size - 1 * MainWindow.units;
313 Theme.text_color (item.label, "Menu Foreground");
314 item.label.draw_at_baseline (cr, label_x, label_y);
315
316 key_binding.set_text (item.get_key_bindings ());
317 key_binding.set_font_size (font_size);
318 binding_extent = key_binding.get_extent ();
319 label_x = x + width - binding_extent - 0.6 * height * MainWindow.units;
320 key_binding.set_font_size (font_size);
321 Theme.text_color (key_binding, "Menu Foreground");
322 key_binding.draw_at_baseline (cr, label_x, label_y);
323
324 y += height;
325 }
326 }
327
328 public void write_key_bindings () {
329 DataOutputStream os;
330 File file;
331 bool has_ctrl, has_alt, has_command, has_shift;
332
333 file = get_child (BirdFont.get_settings_directory (), "key_bindings.xml");
334
335 try {
336 if (file.query_exists ()) {
337 file.delete ();
338 }
339 } catch (GLib.Error e) {
340 warning (e.message);
341 }
342
343 try {
344 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));
345 os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");
346 os.put_string ("\n");
347
348 os.put_string ("<bindings>\n");
349
350 foreach (MenuItem item in sorted_menu_items) {
351 os.put_string ("\t<action ");
352
353 os.put_string (@"key=\"$((!)item.key.to_string ())\" ");
354
355 has_ctrl = (item.modifiers & CTRL) > 0;
356 os.put_string (@"ctrl=\"$(has_ctrl.to_string ())\" ");
357
358 has_alt = (item.modifiers & ALT) > 0;
359 os.put_string (@"alt=\"$(has_alt.to_string ())\" ");
360
361 has_command = (item.modifiers & LOGO) > 0;
362 os.put_string (@"command=\"$(has_command.to_string ())\" ");
363
364 has_shift = (item.modifiers & SHIFT) > 0;
365 os.put_string (@"shift=\"$(has_shift.to_string ())\" ");
366
367 os.put_string (@"action=\"$(item.identifier)\" ");
368
369 os.put_string ("/>\n");
370 }
371 os.put_string ("</bindings>\n");
372
373 os.close ();
374 } catch (GLib.Error e) {
375 warning (e.message);
376 }
377 }
378
379 public void set_current_menu (SubMenu menu) {
380 current_menu = menu;
381 }
382 }
383
384 }
385