Commit f319bf5
Fix memory leak
Changed files (16)
modifiedlibbirdfont/BirdFont.valamodifiedlibbirdfont/BirdFontFile.valamodifiedlibbirdfont/BirdFontPart.valadeletedlibbirdfont/DropMenu.valamodifiedlibbirdfont/Font.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/GlyphCollection.valamodifiedlibbirdfont/KerningClasses.valamodifiedlibbirdfont/Ligatures.valamodifiedlibbirdfont/MenuAction.valamodifiedlibbirdfont/OverView.valamodifiedlibbirdfont/OverViewItem.valamodifiedlibbirdfont/Renderer/FallbackFont.valamodifiedlibbirdfont/Renderer/FontCache.valamodifiedlibbirdfont/Renderer/Text.valamodifiedlibbirdfont/VersionList.vala
Diff
@@ -402,6 +402,7 @@ public class BirdFont { CodePageBits codepage_bits; args = new Argument.command_line (arg);+ Font.empty = new Font (); #if ANDROID BirdFont.logging = true;@@ -146,7 +146,7 @@ class BirdFontFile : GLib.Object { 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 @@ class BirdFontFile : GLib.Object { } 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 @@ class BirdFontFile : GLib.Object { 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);@@ -30,6 +30,11 @@ public class BirdFontPart : GLib.Object{ 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 @@ public class BirdFontPart : GLib.Object{ 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);deleted file mode 100644@@ -1,228 +0,0 @@-/*- 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 ();- }-}--}@@ -104,6 +104,10 @@ public class Font : GLib.Object { public FontSettings settings; public KerningStrings kerning_strings; + public signal void font_deleted ();+ + public static Font empty;+ public Font () { KerningClasses kerning_classes; @@ -143,6 +147,10 @@ public class Font : GLib.Object { kerning_strings = new KerningStrings (); } + ~Font () {+ font_deleted ();+ }+ public static void set_default_license (string license) { default_license = license; }@@ -473,7 +481,7 @@ public class Font : GLib.Object { 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 @@ public class Font : GLib.Object { /** 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) {@@ -2232,6 +2232,7 @@ public class Glyph : FontDisplay { } public bool has_cache (string key) {+ // FIXME: DELETE print (@"glyph_cache.keys.size $(glyph_cache.keys.size)\n"); return glyph_cache.has_key (key); } @@ -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 @@ using Math; 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 @@ public class GlyphCollection : GLib.Object { 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 class GlyphCollection : GLib.Object { } 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 () {@@ -94,16 +118,15 @@ public class GlyphCollection : GLib.Object { 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,15 +136,20 @@ public class GlyphCollection : GLib.Object { 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;+ } } }@@ -42,6 +42,10 @@ public class KerningClasses : GLib.Object { 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> (); classes_kerning = new Gee.ArrayList<Kerning> ();@@ -31,6 +31,10 @@ public class Ligatures : GLib.Object { public Ligatures (Font font) { this.font = font;++ font.font_deleted.connect (() => {+ this.font = Font.empty;+ }); } public void get_ligatures (LigatureIterator iter) {@@ -18,8 +18,7 @@ namespace BirdFont { 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;@@ -85,7 +85,7 @@ public class OverView : FontDisplay { IdleSource idle = new IdleSource (); idle.set_callback (() => { - // FIXME: selected_canvas ();+ selected_canvas (); return false; }); @@ -262,6 +262,7 @@ public class OverView : FontDisplay { update_scrollbar (); update_zoom_bar (); OverViewItem.glyph_scale = 1;+ update_item_list (); selected_item = get_selected_item (); GlyphCanvas.redraw (); }@@ -371,7 +372,7 @@ public class OverView : FontDisplay { 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;@@ -456,8 +457,6 @@ public class OverView : FontDisplay { public override void draw (WidgetAllocation allocation, Context cr) { this.allocation = allocation; - update_item_list ();- // clear canvas cr.save (); Theme.color (cr, "Background 1");@@ -1074,7 +1073,7 @@ public class OverView : FontDisplay { index++; } - update_item_list ();+ // FIXME: update_item_list (); GlyphCanvas.redraw (); } @@ -24,7 +24,7 @@ public class OverViewItem : GLib.Object { 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 class OverViewItem : GLib.Object { 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 @@ public class OverViewItem : GLib.Object { 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 @@ public class OverViewItem : GLib.Object { 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 class OverViewItem : GLib.Object { } 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,10 +311,8 @@ public class OverViewItem : GLib.Object { 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); } } @@ -121,7 +121,7 @@ public class FallbackFont : GLib.Object { 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 @@ public class FallbackFont : GLib.Object { bf_parser = new BirdFontFile (bf_font); bf_parser.load_data (((!) glyph_data).str);- bf_parser = new BirdFontFile (new Font ());- + return bf_font; } @@ -47,13 +47,13 @@ public class FontCache { Font f; bool ok; - if (fonts.has_key (file_name)) {- return fonts.get (file_name);- }- if (file_name == "") { return fallback; }+ + if (fonts.has_key (file_name)) {+ return fonts.get (file_name);+ } f = new Font (); f.set_file (file_name);@@ -94,13 +94,10 @@ public class FontCache { 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) {@@ -113,7 +113,8 @@ public class Text : Widget { 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 @@ public class Text : Widget { GlyphRange? gr_left, gr_right; GlyphSequence word; KerningClasses kc;+ Font empty = new Font (); glyph = new Glyph.no_lines ("", '\0'); @@ -140,7 +142,7 @@ public class Text : Widget { word = glyph_sequence; wi = 0;-+ if (cached_font.font != null) { word_with_ligatures = word.process_ligatures ((!) cached_font.font); } else {@@ -153,9 +155,9 @@ public class Text : Widget { 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 @@ public class Text : Widget { } // 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 @@ public class Text : Widget { iter (glyph, kern, i + 1 == word_with_ligatures.glyph.size); prev = g; wi++;+ } } @@ -351,10 +354,10 @@ public class Text : Widget { if (truncated_width > 0 && end - px > truncated_width) { return; }- + draw_chached (cr, glyph, kerning, last, x, y, cc_y, ratio, cacheid);- + x = end; }); } else {@@ -15,44 +15,76 @@ 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); - if (g != null) {- add_glyph ((!) g);+ glyphs = new Gee.ArrayList<Glyph> ();+ + 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 @@ public class VersionList : DropMenu { 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 @@ public class VersionList : DropMenu { } 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 @@ public class VersionList : DropMenu { } i++; }- return i;+ + warning ("No index for menu item.");+ return 0; } public void set_selected_version (int version_id) {@@ -133,6 +171,7 @@ public class VersionList : DropMenu { 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 @@ public class VersionList : DropMenu { 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 @@ public class VersionList : DropMenu { 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,11 +290,192 @@ public class VersionList : DropMenu { } 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 ();+ } } }