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