The Birdfont Source Code


All Repositories / birdfont.git / commit – RSS feed

Fix memory leak

These changes was commited to the Birdfont repository Sat, 23 May 2015 13:13:29 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
author Johan Mattsson <johan.mattsson.m@gmail.com>
Sat, 23 May 2015 13:13:29 +0000 (15:13 +0200)
committer Johan Mattsson <johan.mattsson.m@gmail.com>
Sat, 23 May 2015 13:13:29 +0000 (15:13 +0200)
commit f319bf52ad7769ed3abb8001ca74a7b00673283b
tree c11d3e3da82b333b021e528c98942a5096085cf1
parent 70222d67fce8026eac387a5650525b1876cb3f27
Fix memory leak

17 files changed:
libbirdfont/BirdFont.vala
libbirdfont/BirdFontFile.vala
libbirdfont/BirdFontPart.vala
libbirdfont/DropMenu.vala [deleted ]
libbirdfont/Font.vala
libbirdfont/Glyph.vala
libbirdfont/GlyphCollection.vala
libbirdfont/KerningClasses.vala
libbirdfont/Ligatures.vala
libbirdfont/MenuAction.vala
libbirdfont/OverView.vala
libbirdfont/OverViewItem.vala
libbirdfont/Renderer/FallbackFont.vala
libbirdfont/Renderer/FontCache.vala
libbirdfont/Renderer/Text.vala
libbirdfont/VersionList.vala
--- a/libbirdfont/BirdFont.vala +++ b/libbirdfont/BirdFont.vala @@ -402,6 +402,7 @@ CodePageBits codepage_bits; args = new Argument.command_line (arg); + Font.empty = new Font (); #if ANDROID BirdFont.logging = true;
--- a/libbirdfont/BirdFontFile.vala +++ b/libbirdfont/BirdFontFile.vala @@ -146,7 +146,7 @@ try { string data; - foreach (Glyph g in gc.get_version_list ().glyphs) { + foreach (Glyph g in gc.glyphs) { if (g.get_background_image () != null) { bg = (!) g.get_background_image (); data = bg.get_png_base64 (); @@ -370,13 +370,13 @@ } public void write_selected (GlyphCollection gc, DataOutputStream os) throws GLib.Error { - os.put_string (@"\t<selected id=\"$(gc.get_selected_id ())\"/>\n"); + os.put_string (@"\t<selected id=\"$(gc.selected)\"/>\n"); } public void write_glyph_collection (GlyphCollection gc, DataOutputStream os) throws GLib.Error { write_glyph_collection_start (gc, os); write_selected (gc, os); - foreach (Glyph g in gc.get_version_list ().glyphs) { + foreach (Glyph g in gc.glyphs) { write_glyph (g, os); } write_glyph_collection_end (os); @@ -1124,8 +1124,13 @@ current_gc = font.get_glyph_collection_by_name (name); new_glyph_collection = (current_gc == null); - gc = (!new_glyph_collection) ? (!) current_gc : new GlyphCollection (unicode, name); - + + if (!new_glyph_collection) { + gc = (!) current_gc; + } else { + gc = new GlyphCollection (unicode, name); + } + foreach (Tag t in tag) { if (t.get_name () == "selected") { selected_id = parse_selected (t);
--- a/libbirdfont/BirdFontPart.vala +++ b/libbirdfont/BirdFontPart.vala @@ -30,6 +30,11 @@ public BirdFontPart (Font font) { this.font = font; + + font.font_deleted.connect (() => { + this.font = Font.empty; + }); + parts = new Gee.ArrayList<string> (); root_directory = ""; } @@ -154,7 +159,7 @@ bf.write_closing_root_tag (os); os.close (); - foreach (Glyph g in gc.get_version_list ().glyphs) { + foreach (Glyph g in gc.glyphs) { try { write_glyph (bf, gc, g); write_glyph_background_image (bf, gc, g);
diff --git libbirdfont/DropMenu.vala(deleted)
--- a/libbirdfont/DropMenu.vala +++ /dev/null @@ -1,229 +1,1 @@ - /* - Copyright (C) 2012, 2014 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - - namespace BirdFont { - - public enum MenuDirection { - DROP_DOWN, - POP_UP; - } - - public class DropMenu : GLib.Object { - - public delegate void Selected (MenuAction self); - public signal void selected (DropMenu self); - - double x = -1; - double y = -1; - double width = 0; - - double menu_x = -1; - - public bool menu_visible = false; - - Gee.ArrayList <MenuAction> actions = new Gee.ArrayList <MenuAction> (); - - const int item_height = 25; - - MenuDirection direction = MenuDirection.DROP_DOWN; - - public signal void signal_delete_item (int item_index); - - public DropMenu () { - } - - public MenuAction get_action_index (int index) { - if (!(0 <= index < actions.size)) { - warning (@"No action for index $index."); - return new MenuAction ("None"); - } - return actions.get (index); - } - - public void recreate_index () { - int i = -1; - foreach (MenuAction a in actions) { - a.index = i; - i++; - } - } - - public MenuAction get_action_no2 () { - if (actions.size < 2) { - warning ("No such action"); - return new MenuAction ("None"); - } - - return actions.get (1); - } - - public void deselect_all () { - foreach (MenuAction m in actions) { - m.set_selected (false); - } - } - - public void set_direction (MenuDirection d) { - direction = d; - } - - public void close () { - menu_visible = false; - } - - public MenuAction add_item (string label) { - MenuAction m = new MenuAction (label); - add_menu_item (m); - return m; - } - - public void add_menu_item (MenuAction m) { - m.parent = this; - actions.add (m); - } - - public bool is_over_icon (double px, double py) { - if (x == -1 || y == -1) { - return false; - } - - return x - 12 < px <= x && y - 5 < py < y + 12 + 5; - } - - public bool menu_item_action (double px, double py) { - MenuAction? action; - MenuAction a; - MenuAction ma; - int index; - - if (menu_visible) { - action = get_menu_action_at (px, py); - - if (action != null) { - a = (!) action; - - // action for the delete button - if (a.has_delete_button && menu_x + width - 13 < px <= menu_x + width) { - index = 0; - ma = actions.get (0); - while (true) { - if (a == ma) { - actions.remove_at (index); - signal_delete_item (index); - break; - } - - if (ma == actions.get (actions.size - 1)) { - break; - } else { - ma = actions.get (index + 1); - index++; - } - } - return false; - } else { - a.action (a); - selected (this); - menu_visible = false; - } - - return true; - } - } - - return false; - } - - public bool menu_icon_action (double px, double py) { - menu_visible = is_over_icon (px, py); - return menu_visible; - } - - MenuAction? get_menu_action_at (double px, double py) { - double n = 0; - double ix, iy; - - foreach (MenuAction item in actions) { - ix = menu_x - 6; - - if (direction == MenuDirection.DROP_DOWN) { - iy = y + 12 + n * item_height; - } else { - iy = y - 24 - n * item_height; - } - - if (ix <= px <= ix + width && iy <= py <= iy + item_height) { - return item; - } - - n++; - } - - return null; - } - - public void set_position (double px, double py) { - x = px; - y = py; - - foreach (MenuAction item in actions) { - if (item.text.get_sidebearing_extent () + 25 > width) { - width = item.text.get_sidebearing_extent () + 25; - } - } - - if (x - width + 19 < 0) { - menu_x = 30; - } else { - menu_x = x - width; - } - } - - public void draw_menu (Context cr) { - double ix, iy; - int n; - - if (likely (!menu_visible)) { - return; - } - - cr.save (); - Theme.color (cr, "Default Background"); - cr.rectangle (menu_x, y - actions.size * item_height, width, actions.size * item_height); - - cr.fill_preserve (); - cr.stroke (); - cr.restore (); - - cr.save (); - - n = 0; - foreach (MenuAction item in actions) { - item.width = width; - - iy = y - 8 - n * item_height; - ix = menu_x + 2; - - item.draw (ix, iy, cr); - n++; - } - - cr.restore (); - } - } - - }
--- a/libbirdfont/Font.vala +++ b/libbirdfont/Font.vala @@ -103,6 +103,10 @@ public FontSettings settings; public KerningStrings kerning_strings; + + public signal void font_deleted (); + + public static Font empty; public Font () { KerningClasses kerning_classes; @@ -141,6 +145,10 @@ settings = new FontSettings (); kerning_strings = new KerningStrings (); + } + + ~Font () { + font_deleted (); } public static void set_default_license (string license) { @@ -473,7 +481,7 @@ glyph_name.remove (glyph.get_name ()); ligature.remove (glyph.get_current ().get_name ()); - foreach (Glyph g in glyph.get_version_list ().glyphs) { + foreach (Glyph g in glyph.glyphs) { deleted_glyphs.add (g); } } @@ -492,7 +500,6 @@ /** Get glyph collection by name. */ public GlyphCollection? get_glyph_collection_by_name (string? glyph) { - // TODO: load from disk here if needed. GlyphCollection? gc = null; if (glyph != null) {
--- a/libbirdfont/Glyph.vala +++ b/libbirdfont/Glyph.vala @@ -2232,6 +2232,7 @@ } public bool has_cache (string key) { + // FIXME: DELETE print (@"glyph_cache.keys.size $(glyph_cache.keys.size)\n"); return glyph_cache.has_key (key); }
--- a/libbirdfont/GlyphCollection.vala +++ b/libbirdfont/GlyphCollection.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012, 2014 Johan Mattsson + Copyright (C) 2012, 2014 2015 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -18,15 +18,15 @@ namespace BirdFont { public class GlyphCollection : GLib.Object { - public VersionList versions; unichar unicode_character; string name; bool unassigned = false; + public Gee.ArrayList<Glyph> glyphs = new Gee.ArrayList<Glyph> (); + public int selected = 0; public GlyphCollection (unichar unicode_character, string name) { this.unicode_character = unicode_character; this.name = name; - versions = new VersionList (null, this); } public GlyphCollection.with_glyph (unichar unicode_character, string name) { @@ -36,11 +36,32 @@ this.name = name; g = new Glyph (name, unicode_character); - versions = new VersionList (g, this); + glyphs.add (g); } - - ~GlyphCollection () { - versions.glyphs.clear (); + + public void remove (int index) { + return_if_fail (0 <= index < glyphs.size); + + if (selected >= index) { + selected--; + } + + glyphs.remove_at (index); + } + + public void set_selected (Glyph g) { + int i = 0; + + foreach (Glyph gl in glyphs) { + if (gl == g) { + selected = i; + return; + } + i++; + } + + selected = 0; + warning ("Glyph is not a part of the collection."); } public void set_unassigned (bool a) { @@ -52,24 +73,27 @@ } public void add_glyph (Glyph g) { - get_version_list ().add_glyph (g); - } - - public VersionList get_version_list () { - return versions; + glyphs.add (g); } public Glyph get_current () { - return versions.get_current (); + if (0 <= selected < glyphs.size) { + return glyphs.get (selected); + } + + return new Glyph ("", '\0'); } - public void insert_glyph (Glyph g, bool selected) { - versions.add_glyph (g, selected); - assert (versions.glyphs.size > 0); + public void insert_glyph (Glyph g, bool selected_glyph) { + glyphs.add (g); + + if (selected_glyph) { + selected = glyphs.size - 1; + } } public uint length () { - return versions.glyphs.size; + return glyphs.size; } public string get_unicode () { @@ -92,18 +116,17 @@ public void set_name (string n) { name = n; - } - - public int get_selected_id () { - return versions.get_current ().version_id; - } - - public int get_last_id () { - return versions.get_last_id (); } public void set_selected_version (int version_id) { - versions.set_selected_version (version_id); + int i = 0; + foreach (Glyph g in glyphs) { + if (g.version_id == version_id) { + selected = i; + break; + } + i++; + } } /** Create a copy of this list. This method will copy the list data but @@ -113,16 +136,21 @@ public GlyphCollection copy () { GlyphCollection n = new GlyphCollection (unicode_character, name); - foreach (Glyph g in versions.glyphs) { + foreach (Glyph g in glyphs) { n.insert_glyph (g, false); } - n.versions.set_selected_version (versions.current_version_id); + n.selected = selected; n.unassigned = unassigned; return n; + } + + public int get_last_id () { + return_val_if_fail (glyphs.size > 0, 0); + return glyphs.get (glyphs.size - 1).version_id; } } }
--- a/libbirdfont/KerningClasses.vala +++ b/libbirdfont/KerningClasses.vala @@ -41,6 +41,10 @@ public KerningClasses (Font font) { this.font = font; + + font.font_deleted.connect (() => { + this.font = Font.empty; + }); classes_first = new Gee.ArrayList<GlyphRange> (); classes_last = new Gee.ArrayList<GlyphRange> ();
--- a/libbirdfont/Ligatures.vala +++ b/libbirdfont/Ligatures.vala @@ -31,6 +31,10 @@ public Ligatures (Font font) { this.font = font; + + font.font_deleted.connect (() => { + this.font = Font.empty; + }); } public void get_ligatures (LigatureIterator iter) {
--- a/libbirdfont/MenuAction.vala +++ b/libbirdfont/MenuAction.vala @@ -18,8 +18,7 @@ public class MenuAction : GLib.Object { public string label; - public DropMenu.Selected action; - public DropMenu? parent = null; + public signal void action (MenuAction a); public int index = -1; public bool has_delete_button = true; public double width = 100;
--- a/libbirdfont/OverView.vala +++ b/libbirdfont/OverView.vala @@ -85,7 +85,7 @@ IdleSource idle = new IdleSource (); idle.set_callback (() => { - // FIXME: selected_canvas (); + selected_canvas (); return false; }); @@ -262,6 +262,7 @@ update_scrollbar (); update_zoom_bar (); OverViewItem.glyph_scale = 1; + update_item_list (); selected_item = get_selected_item (); GlyphCanvas.redraw (); } @@ -371,7 +372,7 @@ return i - 1; } - void update_item_list (int item_list_length = -1) { + public void update_item_list (int item_list_length = -1) { string character_string; Font f = BirdFont.get_current_font (); GlyphCollection? glyphs = null; @@ -455,8 +456,6 @@ public override void draw (WidgetAllocation allocation, Context cr) { this.allocation = allocation; - - update_item_list (); // clear canvas cr.save (); @@ -1074,7 +1073,7 @@ index++; } - update_item_list (); + // FIXME: update_item_list (); GlyphCanvas.redraw (); }
--- a/libbirdfont/OverViewItem.vala +++ b/libbirdfont/OverViewItem.vala @@ -24,7 +24,7 @@ public double y; public bool selected = false; public CharacterInfo info; - + public static double DEFAULT_WIDTH = 100; public static double DEFAULT_HEIGHT = 130; public static double DEFAULT_MARGIN = 20; @@ -35,6 +35,7 @@ public static double glyph_scale = 1.0; + VersionList version_menu; Text icon; public OverViewItem (GlyphCollection? glyphs, unichar character, double x, double y) { @@ -47,8 +48,23 @@ icon = new Text ("dropdown_menu", 17); icon.load_font ("icons.bf"); icon.use_cache (true); + + if (glyphs != null) { + version_menu = new VersionList ((!) glyphs); + version_menu.add_glyph_item.connect ((glyph) => { + ((!) glyphs).insert_glyph (glyph, true); + }); + version_menu.signal_delete_item.connect ((glyph_index) => { + OverView v = MainWindow.get_overview (); + version_menu = new VersionList ((!) glyphs); + v.update_item_list (); + GlyphCanvas.redraw (); + }); + } else { + version_menu = new VersionList (new GlyphCollection (character, (!) character.to_string ())); + } } - + public string get_name () { StringBuilder s; @@ -81,12 +97,13 @@ if (has_icons () && glyphs != null) { g = (!) glyphs; - a = g.get_version_list ().menu_item_action (px, py); // select one item on the menu + + a = version_menu.menu_item_action (px, py); // select one item on the menu if (a) { return s; } - g.get_version_list ().menu_icon_action (px, py); // click in the open menu + version_menu.menu_icon_action (px, py); // click in the open menu } if (has_icons () && info.is_over_icon (px, py)) { @@ -276,18 +293,12 @@ } public void hide_menu () { - GlyphCollection g; - - if (glyphs != null) { - g = (!) glyphs; - g.get_version_list ().menu_visible = false; - } + version_menu.menu_visible = false; } private void draw_menu (Context cr) { GlyphCollection g; - DropMenu menu; - + if (glyphs == null) { return; } @@ -300,12 +311,10 @@ icon.draw_at_top (cr, x + width - 32, y + height - 19); - g = (!) glyphs; - menu = g.get_version_list (); - menu.draw_menu (cr); - menu.set_position (x + width - 21, y + height - 18); + version_menu.set_position (x + width - 21, y + height - 18); + version_menu.draw_menu (cr); } } }
--- a/libbirdfont/Renderer/FallbackFont.vala +++ b/libbirdfont/Renderer/FallbackFont.vala @@ -121,7 +121,7 @@ FontFace* font; bf_font = new Font (); - + for (int i = fallback_fonts.size - 1; i >= 0; i--) { f = fallback_fonts.get (i); @@ -153,8 +153,7 @@ bf_parser = new BirdFontFile (bf_font); bf_parser.load_data (((!) glyph_data).str); - bf_parser = new BirdFontFile (new Font ()); - + return bf_font; }
--- a/libbirdfont/Renderer/FontCache.vala +++ b/libbirdfont/Renderer/FontCache.vala @@ -47,12 +47,12 @@ Font f; bool ok; + if (file_name == "") { + return fallback; + } + if (fonts.has_key (file_name)) { return fonts.get (file_name); - } - - if (file_name == "") { - return fallback; } f = new Font (); @@ -94,13 +94,10 @@ public CachedFont (Font? font) { this.font = font; cached++; - - warning (@"$cached cached fonts\n"); } ~CachedFont () { cached--; - warning (@"$cached cached fonts\n"); } public Glyph? get_glyph_by_name (string name) {
--- a/libbirdfont/Renderer/Text.vala +++ b/libbirdfont/Renderer/Text.vala @@ -113,7 +113,8 @@ index = 0; while (text.get_next_char (ref index, out c)) { name = (!) c.to_string (); - g = cached_font.get_glyph_by_name (name); + g = cached_font.get_glyph_by_name (name); + gs.glyph.add (g); glyph_names.add (name); } @@ -131,6 +132,7 @@ GlyphRange? gr_left, gr_right; GlyphSequence word; KerningClasses kc; + Font empty = new Font (); glyph = new Glyph.no_lines ("", '\0'); @@ -140,7 +142,7 @@ word = glyph_sequence; wi = 0; - + if (cached_font.font != null) { word_with_ligatures = word.process_ligatures ((!) cached_font.font); } else { @@ -153,9 +155,9 @@ if (cached_font.font != null) { kc = ((!) cached_font.font).get_kerning_classes (); } else { - kc = new KerningClasses (new Font ()); + kc = new KerningClasses (empty); } - + for (int i = 0; i < word_with_ligatures.glyph.size; i++) { g = word_with_ligatures.glyph.get (i); @@ -172,7 +174,7 @@ } // process glyph - if (g == null) { + if (g == null && (0 <= i < glyph_names.size)) { g = cached_font.get_glyph_by_name (glyph_names.get (i)); } @@ -180,6 +182,7 @@ iter (glyph, kern, i + 1 == word_with_ligatures.glyph.size); prev = g; wi++; + } } @@ -351,10 +354,10 @@ if (truncated_width > 0 && end - px > truncated_width) { return; } - + draw_chached (cr, glyph, kerning, last, x, y, cc_y, ratio, cacheid); - + x = end; }); } else {
--- a/libbirdfont/VersionList.vala +++ b/libbirdfont/VersionList.vala @@ -14,45 +14,77 @@ using Cairo; using Math; + + public enum MenuDirection { + DROP_DOWN, + POP_UP; + } namespace BirdFont { - public class VersionList : DropMenu { + public class VersionList : GLib.Object { public int current_version_id = -1; - unowned GlyphCollection glyph_collection; + GlyphCollection glyph_collection; public Gee.ArrayList<Glyph> glyphs; - public VersionList (Glyph? g = null, GlyphCollection glyph_collection) { - base (); - - this.glyph_collection = glyph_collection; - glyphs = new Gee.ArrayList<Glyph> (); - set_direction (MenuDirection.POP_UP); - + static int n_lists = 0; + + public delegate void Selected (MenuAction self); + public signal void selected (VersionList self); + + double x = -1; + double y = -1; + double width = 0; + + double menu_x = -1; + public bool menu_visible = false; + Gee.ArrayList <MenuAction> actions = new Gee.ArrayList <MenuAction> (); + const int item_height = 25; + MenuDirection direction = MenuDirection.DROP_DOWN; + + public signal void signal_delete_item (int item_index); + public signal void add_glyph_item (Glyph item); + + public VersionList (GlyphCollection gc) { MenuAction ma = add_item (t_("New version")); ma.has_delete_button = false; - ma.action = (self) => { - return_if_fail (self.parent != null); + ma.action.connect ((self) => { return_if_fail (glyphs.size > 0); BirdFont.get_current_font ().touch (); add_new_version (); current_version_id = glyphs.get (glyphs.size - 1).version_id; - }; + }); // delete one version signal_delete_item.connect ((index) => { delete_item (index); }); + + this.glyph_collection = gc; + glyphs = new Gee.ArrayList<Glyph> (); + set_direction (MenuDirection.POP_UP); + + glyphs = new Gee.ArrayList<Glyph> (); - if (g != null) { - add_glyph ((!) g); + foreach (Glyph g in gc.glyphs) { + add_glyph (g, false); } - + + set_selected_version (gc.get_current ().version_id); + n_lists++; } + + ~VersionList () { + n_lists--; + if (menu_visible) { + warning ("menu is visible"); + } + } + private void delete_item (int index) { int current_version; Font font = BirdFont.get_current_font (); @@ -72,10 +104,10 @@ return_if_fail (0 <= index < glyphs.size); font.deleted_glyphs.add (glyph_collection.get_current ()); - over_view.store_undo_state (glyph_collection.copy ()); glyphs.remove_at (index); + glyph_collection.remove (index); recreate_index (); @@ -85,7 +117,11 @@ } else if (index < current_version) { return_if_fail (0 <= current_version - 1 < glyphs.size); current_version_id = glyphs.get (current_version - 1).version_id; - } + int i = get_current_version_index (); + set_selected_item (get_action_index (i)); + } + + get_current ().selected_canvas (); } private int get_current_version_index () { @@ -96,7 +132,9 @@ } i++; } - return i; + + warning ("No index for menu item."); + return 0; } public void set_selected_version (int version_id) { @@ -133,6 +171,7 @@ Glyph new_version = g.copy (); new_version.version_id = get_last_id () + 1; add_glyph (new_version); + add_glyph_item (new_version); } public int get_last_id () { @@ -149,19 +188,21 @@ g = glyphs.get (i); current_version_id = g.version_id; - - return_if_fail (ma.parent != null); - - ((!)ma.parent).deselect_all (); + deselect_all (); ma.set_selected (true); reload_all_open_glyphs (); + glyph_collection.set_selected (g); + + /* if (!is_null (BirdFont.current_glyph_collection)) { current_glyph = MainWindow.get_current_glyph (); g.set_allocation (current_glyph.allocation); g.set_default_zoom (); } + */ + } /** Reload a glyph when a new version is selected. Updates the path @@ -224,17 +265,19 @@ ma = add_item (t_("Version") + @" $v"); ma.index = (int) glyphs.size - 1; - ma.action = (self) => { + ma.action.connect ((self) => { Font font = BirdFont.get_current_font (); set_selected_item (self); font.touch (); - }; + }); if (selected) { set_selected_item (ma); } - update_selection (); + if (selected) { + update_selection (); + } } bool has_version (int id) { @@ -247,12 +290,193 @@ } void update_selection () { + int index; + if (has_version (current_version_id)) { - set_selected_item (get_action_index (get_current_version_index () + 1)); // the first item is the "new version" + index = get_current_version_index (); + set_selected_item (get_action_index (index + 1)); // the first item is the "new version" } } + public MenuAction get_action_index (int index) { + if (!(0 <= index < actions.size)) { + warning (@"No action for index $index. (actions.size: $(actions.size))"); + return new MenuAction ("None"); + } + return actions.get (index); + } + + public void recreate_index () { + int i = -1; + foreach (MenuAction a in actions) { + a.index = i; + i++; + } + } + + public MenuAction get_action_no2 () { + if (actions.size < 2) { + warning ("No such action"); + return new MenuAction ("None"); + } + + return actions.get (1); + } + + public void deselect_all () { + foreach (MenuAction m in actions) { + m.set_selected (false); + } + } + + public void set_direction (MenuDirection d) { + direction = d; + } + + public void close () { + menu_visible = false; + } + + public MenuAction add_item (string label) { + MenuAction m = new MenuAction (label); + add_menu_item (m); + return m; + } + + public void add_menu_item (MenuAction m) { + actions.add (m); + } + + public bool is_over_icon (double px, double py) { + if (x == -1 || y == -1) { + return false; + } + + return x - 12 < px <= x && y - 5 < py < y + 12 + 5; + } + + public bool menu_item_action (double px, double py) { + MenuAction? action; + MenuAction a; + MenuAction ma; + int index; + + if (menu_visible) { + action = get_menu_action_at (px, py); + + if (action != null) { + a = (!) action; + + // action for the delete button + if (a.has_delete_button && menu_x + width - 13 < px <= menu_x + width) { + index = 0; + ma = actions.get (0); + while (true) { + if (a == ma) { + actions.remove_at (index); + signal_delete_item (index); + break; + } + + if (ma == actions.get (actions.size - 1)) { + break; + } else { + ma = actions.get (index + 1); + index++; + } + } + return false; + } else { + a.action (a); + selected (this); + menu_visible = false; + } + + return true; + } + } + + return false; + } + + public bool menu_icon_action (double px, double py) { + menu_visible = is_over_icon (px, py); + return menu_visible; + } + + MenuAction? get_menu_action_at (double px, double py) { + double n = 0; + double ix, iy; + + foreach (MenuAction item in actions) { + ix = menu_x - 6; + + if (direction == MenuDirection.DROP_DOWN) { + iy = y + 12 + n * item_height; + } else { + iy = y - 24 - n * item_height; + } + + if (ix <= px <= ix + width && iy <= py <= iy + item_height) { + return item; + } + + n++; + } + + return null; + } + + public void set_position (double px, double py) { + x = px; + y = py; + + foreach (MenuAction item in actions) { + item.text = new Text (item.label); + if (item.text.get_sidebearing_extent () + 25 > width) { + width = item.text.get_sidebearing_extent () + 25; + } + } + + if (x - width + 19 < 0) { + menu_x = 30; + } else { + menu_x = x - width; + } + } + + public void draw_menu (Context cr) { + double ix, iy; + int n; + + if (likely (!menu_visible)) { + return; + } + + cr.save (); + Theme.color (cr, "Default Background"); + cr.rectangle (menu_x, y - actions.size * item_height, width, actions.size * item_height); + + cr.fill_preserve (); + cr.stroke (); + cr.restore (); + + cr.save (); + + n = 0; + foreach (MenuAction item in actions) { + item.width = width; + + iy = y - 8 - n * item_height; + ix = menu_x + 2; + + item.draw (ix, iy, cr); + n++; + } + + cr.restore (); + } } }