Clone in a terminal:
Commit 674f9ea
Johan Mattsson - 2013-07-16T12:07:24+00:00
674f9ea2bbbc8d43e258ad098b07fbf601b48833johan.mattsson.m@gmail.com

new overview

Changed files (7)

Diff

diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex 610d5b6..f468d30 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -643,12 +643,29 @@ class BirdFontFile { 	 	private void parse_font_boundries (Xml.Node* node) { 		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {-			if (iter->name == "top_limit") font.top_limit = parse_double_from_node (iter);-			if (iter->name == "top_position") font.top_position = parse_double_from_node (iter);-			if (iter->name == "x-height") font.xheight_position = parse_double_from_node (iter);-			if (iter->name == "base_line") font.base_line = parse_double_from_node (iter);-			if (iter->name == "bottom_position") font.bottom_position = parse_double_from_node (iter);-			if (iter->name == "bottom_limit") font.bottom_limit = parse_double_from_node (iter);+			if (iter->name == "top_limit" && "" == iter->children->content) {+				font.top_limit = parse_double_from_node (iter);+			}+			+			if (iter->name == "top_position" && "" == iter->children->content) {+				font.top_position = parse_double_from_node (iter);+			}+			+			if (iter->name == "x-height" && "" == iter->children->content) {+				font.xheight_position = parse_double_from_node (iter);+			}+			+			if (iter->name == "base_line" && "" == iter->children->content) {+				font.base_line = parse_double_from_node (iter);+			}+			+			if (iter->name == "bottom_position" && "" == iter->children->content) {+				font.bottom_position = parse_double_from_node (iter);+			}+			+			if (iter->name == "bottom_limit" && "" == iter->children->content) {+				font.bottom_limit = parse_double_from_node (iter);+			} 		}			 	} 	@@ -662,7 +679,7 @@ class BirdFontFile { 			else warning (@"Failed to parse double for \"$(iter->content)\"\n"); 		} 		-		return (r) ? d : 0;+		return (r) ? d : 0.0; 	}  	/** Parse the new glyph format */diff --git a/libbirdfont/DropMenu.vala b/libbirdfont/DropMenu.valaindex ce20638..1a6d2e1 100644--- a/libbirdfont/DropMenu.vala+++ b/libbirdfont/DropMenu.vala@@ -188,7 +188,7 @@ public class DropMenu : GLib.Object { 		if (direction == MenuDirection.DROP_DOWN) { 			cr.rectangle (x - 100 + 18, y + 18, 88, actions.length () * item_height - 12); 		} else {-			cr.rectangle (x - 100 + 18, y + 6 - actions.length () * item_height, 88, actions.length () * item_height - 12);+			cr.rectangle (x - 100 + 19, y + 6 - actions.length () * item_height, 88, actions.length () * item_height - 12); 		} 		 		cr.fill_preserve ();@@ -201,7 +201,7 @@ public class DropMenu : GLib.Object { 			cr.set_source_rgba (122/255.0, 150/255.0, 169/255.0, 1); 			cr.set_line_width (0);		 			-			cr.rectangle (x, y - 7, 12, 7);+			cr.rectangle (x, y - 7, 13, 7);  			cr.fill_preserve (); 			cr.stroke ();@@ -218,7 +218,7 @@ public class DropMenu : GLib.Object { 			else  				iy = y - 8 - n * item_height; 			-			ix = x - 100 + 20;+			ix = x - 100 + 21; 			 			item.draw (ix, iy, cr); 			n++;diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex 38f140c..653614a 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -82,12 +82,12 @@ public class Font : GLib.Object { 	public Font () { 		// positions in pixels at first zoom level 		// default x-height should be 60 in 1:1-		top_limit = -66 ;-		top_position = -54;-		xheight_position = -38;-		base_line = 18;-		bottom_position = 38;-		bottom_limit = 45;+		top_limit = -84 ;+		top_position = -72;+		xheight_position = -56;+		base_line = 0;+		bottom_position = 20;+		bottom_limit = 27; 	}  	public void touch () {diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 4946e53..91e76b0 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -534,11 +534,18 @@ public class Glyph : FontDisplay { 	} 			 	private void help_line_event (double x, double y) {-		double mx = x;-		double my = y;+		bool m = false;+		+		foreach (Line line in vertical_help_lines) {+			if (!m && line.event_move_to (x, y, allocation)) {+				m = true;+			}+		} -		foreach (Line line in get_all_help_lines ()) {-			line.event_move_to (mx, my, x, y, allocation);+		foreach (Line line in horizontal_help_lines) {+			if (!m && line.event_move_to (x, y, allocation)) {+				m = true;+			} 		} 	} @@ -572,8 +579,8 @@ public class Glyph : FontDisplay { 			return; 		} 		-		help_line_event (x, y);	-+		help_line_event (x, y);+		 		t.move_action (t, (int) x, (int) y);  		if (BirdFont.show_coordinates) {@@ -601,7 +608,9 @@ public class Glyph : FontDisplay { 	}  	private unowned List<Line> get_all_help_lines () {-		while (all_lines.length () > 0) all_lines.delete_link (all_lines.first ());+		while (all_lines.length () > 0) {+			all_lines.delete_link (all_lines.first ());+		} 		 		all_lines.concat (vertical_help_lines.copy ()); 		all_lines.concat (horizontal_help_lines.copy ());@@ -624,15 +633,26 @@ public class Glyph : FontDisplay { 	} 	 	public override void button_press (uint button, double ex, double ey) {				+		bool moving_lines = false; 		pointer_begin_x = ex; 		pointer_begin_y = ey; 		-		foreach (Line line in get_all_help_lines ()) {-			if (line.button_press ()) {-				return;+		foreach (Line line in horizontal_help_lines) {+			if (!moving_lines && line.is_visible () && line.button_press ()) {+				moving_lines = true;+			}+		}++		foreach (Line line in vertical_help_lines) {+			if (!moving_lines && line.is_visible () && line.button_press ()) {+				moving_lines = true; 			} 		} 				+		if (moving_lines) {+			return;+		}+			 		if (KeyBindings.has_ctrl ()) { 			move_view = true; 			move_offset_x = view_offset_x;diff --git a/libbirdfont/Line.vala b/libbirdfont/Line.valaindex d3f7a27..f9658c0 100644--- a/libbirdfont/Line.vala+++ b/libbirdfont/Line.vala@@ -69,7 +69,10 @@ public class Line : GLib.Object { 	public void set_visible (bool v) { 		visible = v; 	}-	++	public bool is_visible () {+		return visible;+	}  	public void set_moveable (bool m) { 		moveable = m;@@ -99,11 +102,12 @@ public class Line : GLib.Object { 			 			g = MainWindow.get_current_glyph (); 			g.store_undo_state ();-			-			return true;+		} else {+			move = false;+			active = false; 		} 		-		return false;+		return move; 	}  	void redraw_line () {@@ -121,60 +125,28 @@ public class Line : GLib.Object { 	 	public void move_line_to (double x, double y, Allocation allocation) { 		set_move (true);-		event_move_to (x, y, x, y, allocation);+		event_move_to (x, y, allocation); 	} 	-	public void event_move_to (double x, double y, double ax, double ay, Allocation allocation) {+	public bool event_move_to (double x, double y, Allocation allocation) { 		double p, c; 		bool a = false; 		Glyph g = MainWindow.get_current_glyph (); 		double ivz = 1/g.view_zoom; 		double margin = 10;--		if (!moveable) return;--		if (move) {-			double np = pos;+		double none = 0;+		double tpy; 		-			redraw_line (); // clear old position-			-			if (is_vertical ()) {-				pos = Glyph.path_coordinate_x (x);--				if (GridTool.is_visible ()) {-					GridTool.tie_coordinate (ref pos, ref ay);-				}-				-				redraw_line (); // draw at new position-			} else {-				// FIXME: flip back the y axis for lines-				if (GridTool.is_visible ()) {-					double tpy = Glyph.path_coordinate_y (y);-					GridTool.tie_coordinate (ref ax, ref tpy);-					pos = -tpy;-				} else {-					pos = -Glyph.path_coordinate_y (y);-				}-				redraw_line ();-			}--			if (Math.fabs (np - pos) > 10) {-				queue_draw_area (0, 0, g.allocation.width, g.allocation.height);-			}--			position_updated (pos); // signal update--			BirdFont.get_current_font ().touch ();-			-			swap_lines ();+		if (!moveable) {+			return false; 		} -		if (is_vertical ()) { // over line handle y-			if (ay > g.allocation.height - 10) {+		if (is_vertical ()) { // over line handle (y)+			if (y > g.allocation.height - 10) { 				 				p = get_coordinate (); -				c = ax * ivz + g.view_offset_x;+				c = x * ivz + g.view_offset_x; 				a = (p - margin * ivz <= c <= p + margin * ivz); 			} 					@@ -184,10 +156,10 @@ public class Line : GLib.Object {  			set_active (a); 			-		} else { // over line handle x-			if (ax > g.allocation.width - 10) {+		} else { // over line handle (x+			if (x > g.allocation.width - 10) { 				p = get_coordinate ();-				c = ay * ivz + g.view_offset_y;+				c = y * ivz + g.view_offset_y; 				a = (p - margin * ivz <= c <= p + margin * ivz); 			} 			@@ -197,52 +169,49 @@ public class Line : GLib.Object { 			 			set_active (a); 		}-	}-	-	private void swap_lines () {-		double ivz = 1 / MainWindow.get_current_glyph ().view_zoom;-		-		if (!moveable) return;-		-		// switch to correct line if this line passes the next one in the sorted list-		if (list_item != null) {-			unowned List<Line> ll = (!) list_item;++		// move the line+		if (move) {+			double np = pos;+			redraw_line (); // clear old position 			-			if (ll != ll.first ()) {-				if (ll.prev.data.pos > pos) {-					ll.prev.data.set_move (true);-					ll.prev.data.set_visible (true);-					set_move (false);-					pos = ll.prev.data.pos;-					ll.prev.data.pos += 1 * ivz;-					-					ll.data.position_updated (ll.data.pos);-					ll.prev.data.position_updated (ll.prev.data.pos);+			if (is_vertical ()) {+				pos = Glyph.path_coordinate_x (x);++				if (GridTool.is_visible ()) {+					GridTool.tie_coordinate (ref pos, ref none); 				}+				+				redraw_line (); // draw at new position+			} else {+				// FIXME: flip the y axis for lines+				if (GridTool.is_visible ()) {+					tpy = Glyph.path_coordinate_y (y);+					GridTool.tie_coordinate (ref none, ref tpy);+					pos = -tpy;+				} else {+					pos = -Glyph.path_coordinate_y (y);+				}+				redraw_line (); 			}-			-			if (ll != ll.last ()) {-				if (ll.next.data.pos < pos) {-					ll.next.data.set_move (true);-					ll.next.data.set_visible (true);-					pos = ll.next.data.pos;-					ll.next.data.pos -= 1 * ivz;-					set_move (false); -					ll.data.position_updated (ll.data.pos);-					ll.prev.data.position_updated (ll.prev.data.pos);-				}+			if (Math.fabs (np - pos) > 10) {+				queue_draw_area (0, 0, g.allocation.width, g.allocation.height); 			}-			++			position_updated (pos); // signal update++			BirdFont.get_current_font ().touch (); 		}-		++		return move; 	} 	 	public bool get_active () { 		return active; 	} 	-	protected void set_active (bool active) {+	public void set_active (bool active) { 		this.active = active; 	} 	diff --git a/libbirdfont/OverView.vala b/libbirdfont/OverView.valaindex 64581ab..0cdbb3d 100644--- a/libbirdfont/OverView.vala+++ b/libbirdfont/OverView.vala@@ -18,35 +18,25 @@ namespace BirdFont {  /** A display with all glyphs present in this font. */ public class OverView : FontDisplay {-	Allocation allocation;+	public Allocation allocation; 	+	int selected = 0;+	int first_visible = 0; 	int rows = 0; 	int items_per_row = 0;-	uint32 first_character = 0;-	int64 first_visible = 0;-	uint32 selected = 0; 	 	double view_offset_y = 0; 	double view_offset_x = 0; 	-	int nail_height = 0;-	int nail_width = 0;--	double nail_zoom_height = 0;-	double nail_zoom_width = 0;+	public signal void open_new_glyph_signal (unichar c);+	public signal void open_glyph_signal (GlyphCollection c); 	-	public signal void open_glyph_signal (string c);- 	GlyphRange glyph_range;--	List<GlyphCollection> visible_characters = new List<GlyphCollection> ();--	List<unichar> zoom_list = new List<unichar> ();-	int zoom_list_index = 0;+	+	List<OverViewItem> visible_items = new List<OverViewItem> ();  	List<GlyphCollection> deleted_glyphs = new List<GlyphCollection> ();-	List<CharacterInfo> unicode_database_info = new List<CharacterInfo> ();-	+		 	bool all_available = true; 	 	/** Show unicode database info. */@@ -62,42 +52,63 @@ public class OverView : FontDisplay { 		 		reset_zoom (); -		this.open_glyph_signal.connect ((t, s) => {+		this.open_glyph_signal.connect ((glyph_collection) => { 			TabBar tabs = MainWindow.get_tab_bar ();-			Toolbox tools = MainWindow.get_toolbox ();-			bool selected = tabs.select_char (s);-			unichar new_char = s.get_char (0);-			Font f = BirdFont.get_current_font ();-							+			string n = glyph_collection.get_current ().name;+			bool selected = tabs.select_char (n);+			GlyphCanvas canvas;+			Glyph g = glyph_collection.get_current (); +			 			if (!selected) {-				GlyphCollection? fg;-				Glyph g;+				canvas = MainWindow.get_glyph_canvas ();+				tabs.add_tab (g);+				canvas.set_current_glyph (g);+				set_initial_zoom ();+				g.close_path ();+			}+		});++		this.open_new_glyph_signal.connect ((character) => {+			StringBuilder name = new StringBuilder ();+			TabBar tabs = MainWindow.get_tab_bar ();+			Font font = BirdFont.get_current_font ();+			bool selected;+			GlyphCollection? fg;+			Glyph glyph;+			GlyphCanvas canvas; 				+			name.append_unichar (character);+			selected = tabs.select_char (name.str);+					+			if (!selected) { 				if (all_available) {-					fg = f.get_glyph_collection_by_name (s);+					fg = font.get_glyph_collection_by_name (name.str); 				} else {-					fg = f.get_glyph_collection (s);+					fg = font.get_glyph_collection (name.str); 				} 				-				g = (fg == null) ? new Glyph (s, new_char) : ((!) fg).get_current ();-				ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool");+				glyph = (fg == null) ? new Glyph (name.str, character) : ((!) fg).get_current ();+				font.add_glyph (glyph);+				tabs.add_tab (glyph); 				-				stdout.printf (@"Open '%s' %u (%s)\n", s, new_char, Font.to_hex (new_char));+				canvas = MainWindow.get_glyph_canvas ();+				canvas.set_current_glyph (glyph); 				-				z.store_current_view ();-				f.add_glyph (g);-				tabs.add_tab (g);-				-				MainWindow.get_glyph_canvas ().set_current_glyph (g);-				-				g.close_path ();-				g.default_zoom ();-				z.store_current_view ();+				glyph.close_path ();+				set_initial_zoom (); 			} 		}); 		 		update_scrollbar (); 	}+	+	private void set_initial_zoom () {+		Toolbox tools = MainWindow.get_toolbox ();+		ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool");+		z.store_current_view ();+		MainWindow.get_current_glyph ().default_zoom ();+		z.store_current_view ();+	}  	public double get_height () { 		double l;@@ -114,7 +125,7 @@ public class OverView : FontDisplay { 			l = glyph_range.length (); 		} 				-		return 2.0 * nail_height * (l / rows);+		return 2.0 * OverViewItem.height * (l / rows); 	}  	public bool selected_char_is_visible () {@@ -127,13 +138,11 @@ public class OverView : FontDisplay {  	public override void scroll_wheel_up (double x, double y) { 		key_up ();-		close_menus (); 		redraw_area (0, 0, allocation.width, allocation.height); 	} 	 	public override void scroll_wheel_down (double x, double y) { 		key_down ();-		close_menus (); 		redraw_area (0, 0, allocation.width, allocation.height); 	} 	@@ -143,83 +152,39 @@ public class OverView : FontDisplay { 	} 	 	public override void zoom_min () {-		int minw = 26 * 2;-		int minh = 46 * 2;-		-		if (zoom_value_in_range (minw, minh)) {-			nail_width = minw;-			nail_height = minh;-				-			nail_zoom_height = minw; -			nail_zoom_width = minh;		+		OverViewItem.width = OverViewItem.DEFAULT_WIDTH * 0.5;+		OverViewItem.height = OverViewItem.DEFAULT_HEIGHT * 0.5;+		OverViewItem.margin = OverViewItem.DEFAULT_MARGIN * 0.5; 		-			adjust_scroll ();-			redraw_area (0, 0, allocation.width, allocation.height);-		} else {-			warn_if_reached ();-		}	+		redraw_area (0, 0, allocation.width, allocation.height); 	} 	 	public override void reset_zoom () {-		nail_height = 210;-		nail_width = 150;-		-		nail_zoom_height = nail_height; -		nail_zoom_width = nail_width;+		OverViewItem.width = OverViewItem.DEFAULT_WIDTH;+		OverViewItem.height = OverViewItem.DEFAULT_HEIGHT;+		OverViewItem.margin = OverViewItem.DEFAULT_MARGIN; 		-		adjust_scroll (); 		redraw_area (0, 0, allocation.width, allocation.height); 	}  	public override void zoom_max () {-		if (zoom_value_in_range (allocation.width, allocation.height)) {-			nail_width = allocation.width;-			nail_height = allocation.height;-				-			nail_zoom_height = allocation.height; -			nail_zoom_width = allocation.width;		-		-			adjust_scroll ();-			redraw_area (0, 0, allocation.width, allocation.height);-		} else {-			warn_if_reached ();-		}+		OverViewItem.width = allocation.width;+		OverViewItem.height = allocation.height;+		redraw_area (0, 0, allocation.width, allocation.height); 	} 	 	public override void zoom_in () {-		double w = (1.1 * nail_width);-		double h = (1.1 * nail_height);-		int nw = (int) w;-		int nh = (int) h;-		-		if (zoom_value_in_range (nw, nh)) {-			nail_width = nw;-			nail_height = nh;-			-			nail_zoom_height = h; -			nail_zoom_width = w;-			-			adjust_scroll ();-			redraw_area (0, 0, allocation.width, allocation.height);-		}+		OverViewItem.width *= 1.1;+		OverViewItem.height *= 1.1;+		OverViewItem.margin *= 1.1;		+		redraw_area (0, 0, allocation.width, allocation.height); 	} 	 	public override void zoom_out () {-		double w = (nail_width / 1.1);-		double h = (nail_height / 1.1);-		int nw = (int) w;-		int nh = (int) h;-		-		if (zoom_value_in_range (nw, nh)) {-			nail_width = nw;-			nail_height = nh;--			nail_zoom_height = h; -			nail_zoom_width = w;-			-			adjust_scroll ();		-			redraw_area (0, 0, allocation.width, allocation.height);-		}+		OverViewItem.width *= 0.9;+		OverViewItem.height *= 0.9;+		OverViewItem.margin *= 0.9;	+		redraw_area (0, 0, allocation.width, allocation.height); 	} 	 	private bool zoom_value_in_range (int w, int h) {@@ -227,33 +192,12 @@ public class OverView : FontDisplay { 	}  	public override void store_current_view () {-		if (zoom_list_index + 1 < zoom_list.length ()) {-			unowned List<unichar> n = zoom_list.nth (zoom_list_index);-			while (n != zoom_list.last ()) zoom_list.delete_link (zoom_list.last ());-		}-		-		zoom_list.append (selected);-		zoom_list_index = (int) zoom_list.length () - 1;-		-		if (zoom_list.length () > 50) zoom_list.delete_link (zoom_list.first ()); 	} 	-	public override void restore_last_view () -		requires (zoom_list.length () > 0)-	{		-		if (zoom_list_index - 1 < 0 || zoom_list.length () == 0)-			return;-		-		zoom_list_index--;+	public override void restore_last_view () { 	} -	public override void next_view () -		requires (zoom_list.length () > 0)-	{-		if (zoom_list_index + 1 >= zoom_list.length ())-			return;-		-		zoom_list_index++;+	public override void next_view () { 	}  	public override string get_name () {@@ -262,330 +206,85 @@ public class OverView : FontDisplay { 	 	public void display_all_available_glyphs () { 		all_available = true;-		-		first_character = 0;+ 		first_visible = 0; 		selected = 0; 	} 	-	private unowned List<GlyphCollection> get_visible_glyphs () {-		return visible_characters;-	}-	-	public void draw_caption (int row, double width, Context cr, double y, uint64 index_begin) {+	void update_item_list () { 		string character_string;-		double left_margin, x, caption_y; // for glyph caption-		uint64 index = index_begin;-		int i = row * items_per_row; 		Font f = BirdFont.get_current_font ();+		GlyphCollection? glyphs = null;+		uint32 index;+		OverViewItem item;+		double x, y;+		unichar character;+		Glyph glyph; 		-		cr.save ();-		cr.set_line_width (1);+		items_per_row = (int) Math.floor (allocation.width / OverViewItem.full_width ());+		rows = (int) Math.floor (allocation.height /  OverViewItem.full_height ()); 		-		x = 0;+		while (visible_items.length () > 0) {+			visible_items.remove_link (visible_items.first ());+		} 		-		cr.set_font_size (14);--		left_margin = 0;-			-		for (int j = 0; j < items_per_row; j++) {-			+		visible_items = new List<OverViewItem> ();+		+		index = (uint32) first_visible;+		x = OverViewItem.margin;+		y = OverViewItem.margin;+		for (int i = 0; i < items_per_row * rows; i++) { 			if (all_available) { 				if (! (0 <= index < f.length ())) { 					break; 				} 				-				character_string = ((!) f.get_glyph_indice ((uint32) index)).get_name ();+				glyphs = f.get_glyph_collection_indice ((uint32) index);+				return_if_fail (glyphs != null);+				+				glyph = ((!) glyphs).get_current ();+				character_string = glyph.name;+				character = glyph.unichar_code; 			} else { 				if (!(0 <= index < glyph_range.get_length ())) { 					break; 				} 				 				character_string = glyph_range.get_char ((uint32) index);+				glyphs = f.get_glyph_collection_by_name (character_string);+				character = character_string.get_char (0); 			} 			-			cr.save ();-			draw_thumbnail (cr, character_string, x, y);-			cr.restore ();--			cr.save ();-						-			if (index == selected) {-				cr.set_source_rgba (142/255.0, 158/255.0, 190/255.0, 1);-			} else {-				cr.set_source_rgba (164/255.0, 185/255.0, 215/255.0, 1);-			}-			-			cr.set_line_join (LineJoin.ROUND);-			cr.set_line_width(12);-			-			cr.rectangle (x, y + nail_height - 20, nail_width - 20, 10);			-			cr.stroke ();-			cr.restore ();-			-			draw_menu (cr, character_string, x, y);+			item = new OverViewItem (glyphs, character, x, y);+			x += OverViewItem.full_width (); 			-			draw_character_info_icon (cr, character_string, x, y);-			-			caption_y = y + 10 + nail_height - 20;-			-			cr.move_to (x, caption_y);+			if (x + OverViewItem.full_width () >= allocation.width) {+				x = OverViewItem.margin;+				y += OverViewItem.full_height ();+			} 			-			cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);-			cr.show_text (character_string);+			item.selected = (i == selected); 			-			x += nail_width;+			visible_items.append (item); 			index++;-			i++; 		}-		-		cr.stroke(); 	} 	-	private void draw_character_info_icon (Context cr, string character_string, double x, double y) {-		double px = x + nail_width - 40;-		double py = y + nail_height - 21;-		CharacterInfo info;-		unichar c = character_string.get_char ();-		-		if (character_string.char_count () > 1) {-			warning ("Ligature - not a character");-			c = '\0';-		}-		-		info = new CharacterInfo (c);-		unicode_database_info.append (info);-		info.set_position (px, py);-		info.draw_icon (cr);-	}-	-	private void draw_menu (Context cr, string name, double x, double y) {-		Font font = BirdFont.get_current_font ();-		GlyphCollection? gl = font.get_glyph_collection (name);-		GlyphCollection g;-		DropMenu menu;-		-		if (gl == null) {-			return;-		}-		-		g = (!) gl;-		-		menu = g.get_version_list ();-		menu.set_position (x + nail_width - 55, y + nail_height - 21);-		menu.draw_icon (cr);-		menu.draw_menu (cr);-		-		visible_characters.append (g);-	}-		-	private bool draw_thumbnail (Context cr, string caption, double x, double y) {-		Glyph? gl;-		Glyph g;-		Font f = BirdFont.get_current_font ();-		-		double gx, gy;-		double x1, x2, y1, y2;-		double scale = nail_zoom_width / 150.0;-		double w, h;--		if (all_available) {-			gl = f.get_glyph_by_name (caption);-		} else {-			gl = f.get_glyph (caption);-		}--		w = nail_width  - 19;-		h = nail_height - 22;-		-		scale = nail_zoom_height / (210.0 - 15.0);-		-		if (gl == null) {-			return false;-		}-		-		g = (!) gl;-		g.boundries (out x1, out y1, out x2, out y2);--		gx = 0;-		gy = h / scale - 40;-		-		Surface s = new Surface.similar (cr.get_target (), Content.COLOR_ALPHA, (int) w, (int) h);-		Context c = new Context (s);-		-		c.scale (scale, scale);				-		Svg.draw_svg_path (c, g.get_svg_data (), gx, gy);-		-		cr.save ();-		cr.set_source_surface (s, x - 6, y);-		cr.paint ();-		cr.restore ();--		return true;-	}--	// 150 * 190 px 	public override void draw (Allocation allocation, Context cr) {-		double width;-		double y;-		uint64 t;-		int i;-		int n_items;-		Font font = BirdFont.get_current_font ();-		-		while (unicode_database_info.length () > 0) {-			unicode_database_info.remove_link (unicode_database_info.first ());-		}-		-		while (visible_characters.length () > 9) {-			visible_characters.remove_link (visible_characters.first ());-		}- 		this.allocation = allocation; 		-		cr.save ();-		cr.set_source_rgba (1, 1, 1, 1);-		cr.rectangle (0, 0, allocation.width, allocation.height + nail_height);-		cr.fill ();-		cr.restore ();--		if (all_available && font.length () == 0) {-			draw_empty_canvas (allocation, cr);-		}+		update_item_list (); 		-		n_items = allocation.width / nail_width;-		rows = allocation.height / nail_height;--		if (items_per_row != n_items) { 	-			if (items_per_row == 0) {-				items_per_row = n_items;-				first_visible = first_character;-			-				key_down ();-				key_up ();-			}	else {-				items_per_row = n_items;-				first_visible = first_character;-			}-		}--		width = (nail_width - 5) * items_per_row;-		view_offset_x = (allocation.width - width) / 2.0;-		-		cr.translate (view_offset_x, view_offset_y);-	-		y = 0;-		t = first_visible;-		i = 0;-		for (int j = 0; j < rows + 1; j++) {-			draw_caption (i, width, cr, y, t);--			t += items_per_row;-			y += nail_height;-			i++;+		// FIXME: cr.translate (view_offset_x, view_offset_y);+		foreach (OverViewItem i in visible_items) {+			i.draw (cr); 		} 		-		cr.restore (); // restore translation-		-		if (character_info != null) {-			draw_character_info (allocation, cr, (!) character_info);+		if (visible_items.length () == 0) {+			draw_empty_canvas (allocation, cr); 		} 	}--	/** Display one entry from the Unicode Character Database. */-	void draw_character_info (Allocation allocation, Context cr, CharacterInfo info) {-		double x, y, w, h;-		int i;-		string unicode_value, unicode_description;-		string[] column;-		string entry;-		int len = 0;-		int length = 0;-		bool see_also = false; 		-		entry = info.get_entry ();-		-		foreach (string line in entry.split ("\n")) {-			len = line.char_count ();-			if (len > length) {-				length = len;-			}-		}-		-		x = allocation.width * 0.1 - view_offset_x;-		y = allocation.height * 0.1 - view_offset_y;-		w = allocation.width * 0.9 - x - view_offset_x; -		h = allocation.height * 0.9 - y - view_offset_y;-		-		if (w < 8 * length) {-			w = 8 * length;-			if (w > allocation.width - view_offset_x) {-				x = -view_offset_x;-			} else {-				x = (allocation.width - view_offset_x- w) / 2.0;-			}-		}-		-		// background	-		cr.save ();-		cr.set_source_rgba (1, 1, 1, 0.8);-		cr.rectangle (x, y, w, h);-		cr.fill ();-		cr.restore ();--		cr.save ();-		cr.set_source_rgba (0, 0, 0, 0.8);-		cr.set_line_width (2);-		cr.rectangle (x, y, w, h);-		cr.stroke ();-		cr.restore ();--		// database entry-		i = 0;-		foreach (string line in entry.split ("\n")) {-			if (i == 0) {-				column = line.split ("\t");-				return_if_fail (column.length == 2);-				unicode_value = "U+" + column[0];-				unicode_description = column[1];--				draw_line (unicode_description, cr, x, y, i);-				i++;--				draw_line (unicode_value, cr, x, y, i);-				i++;			-			} else {-				-				if (line.has_prefix ("\t*")) {-					draw_line (line.replace ("\t*", "•"), cr, x, y, i);-					i++;					-				} else if (line.has_prefix ("\tx (")) {-					if (!see_also) {-						i++;-						draw_line (_("See also:"), cr, x, y, i);-						i++;-						see_also = true;-					}-					-					draw_line (line.replace ("\tx (", "•").replace (")", ""), cr, x, y, i);-					i++;-				} else {--					i++;-				}-			}-		}-	}-	-	void draw_line (string line, Context cr, double x, double y, int row) {-		cr.save ();-		cr.set_font_size (12);-		cr.set_source_rgba (0, 0, 0, 1);-		cr.move_to (x + 10, y + 28 + row * 18 * 1.2);-		cr.show_text (line);-		cr.restore ();		-	}-	 	void draw_empty_canvas (Allocation allocation, Context cr) { 		cr.save (); 		cr.set_source_rgba (156/255.0, 156/255.0, 156/255.0, 1);@@ -594,17 +293,14 @@ public class OverView : FontDisplay { 		cr.show_text (_("No glyphs in this view.")); 		cr.restore (); 	}--	private void close_menus () {-	} 	 	public void scroll_rows (int row_adjustment) { 		for (int i = 0; i < row_adjustment; i++) {-			scroll (-nail_height);+			scroll (-OverViewItem.height); 		} 		 		for (int i = 0; i > row_adjustment; i--) {-			scroll (nail_height);+			scroll (OverViewItem.height); 		} 	} 	@@ -652,14 +348,11 @@ public class OverView : FontDisplay { 		} 		 		default_position ();--		first_visible = r;-		selected = (uint32) r; 		-		adjust_scroll ();+		first_visible = (int) r; 	} 	-	public override void scroll_to (double percent) {+	public override void scroll_to (double percent) requires (items_per_row > 0) { 		int64 r; 		double nrows; 		Font f;@@ -676,8 +369,7 @@ public class OverView : FontDisplay { 		redraw_area (0, 0, allocation.width, allocation.height); 	} 		-	private void scroll (int64 pixel_adjustment) {-+	private void scroll (double pixel_adjustment) { 		if (first_visible < 0 && pixel_adjustment < 0) { 			scroll_top (); 			return;@@ -686,165 +378,95 @@ public class OverView : FontDisplay { 		view_offset_y += pixel_adjustment; 		 		if (view_offset_y >= 0) {-			while (view_offset_y > nail_height) {			-				view_offset_y -= nail_height;+			while (view_offset_y > OverViewItem.height) {			+				view_offset_y -= OverViewItem.height; 				first_visible -= items_per_row; 			}  			first_visible -= items_per_row;-			view_offset_y -= nail_height;-		} else if (view_offset_y < -nail_height) {+			view_offset_y -= OverViewItem.height;+		} else if (view_offset_y < -OverViewItem.height) { 			view_offset_y = 0; 			first_visible += items_per_row; 		}-		 	} 	 	public void scroll_top () {-		first_visible = first_character;-		view_offset_y = 0;-	}-	-	private int selected_offset_y (unichar item) {-		int64 di;-		int64 a, b;-		-		if (unlikely (items_per_row == 0)) {-			return 0;-		}-		-		a = (int64) item;-		b = (int64) first_visible;-		di = (a - b);-		-		return (int) (view_offset_y + ((di / items_per_row) * nail_height));-	}--	/** Make selected item fully visible if it partly off screen. */-	private void adjust_scroll ()  {-		int off =  selected_offset_y (selected);--		if (off < 0)  {-			close_menus ();-			scroll (-1 * off);-		} else if (off > allocation.height - nail_height) {-			scroll ((allocation.height - nail_height) - off);-		}+		selected = 0;+		first_visible = 0; 	}  	public void key_down () {-		double l;-		Font f;-				-		if (all_available) {-			f = BirdFont.get_current_font ();-			l = f.length ();-		} else {-			l = glyph_range.length ();-		}-		-		update_scrollbar ();+		Font f = BirdFont.get_current_font ();+		int64 len = (all_available) ? f.length () : glyph_range.length (); 		-		if (l == 0) return;-	-		if (at_bottom ()) {-			int len = (int) l;-			int s = (int) selected;-			-			if (len - items_per_row >= s) {-				selected += items_per_row; -			} else if (s < len) {-				selected++;-			}-			-			if (selected >= len) {-				selected = len - 1;-			}-			-			adjust_scroll ();-			+		selected += items_per_row;++		if (at_bottom () && selected + items_per_row > len) {  			return; 		} 		-		selected += items_per_row;-		-		if (selected > rows * items_per_row + first_visible) {-			scroll (-nail_height);+		if (selected >= items_per_row * rows) {+			first_visible += items_per_row;+			selected -= items_per_row; 		} 		-		adjust_scroll ();+		if (first_visible + selected > len) {+			selected = (int) (len - first_visible - 1);+			+			if (selected < items_per_row * (rows - 1)) {+				first_visible -= items_per_row;+				selected += items_per_row;+			}+		} 	} -	public void key_up () {-		update_scrollbar ();+	public void key_right () {+		Font f = BirdFont.get_current_font ();+		int64 len = (all_available) ? f.length () : glyph_range.length (); -		if (selected < first_character) {		-			warn_if_reached ();-		}-		-		if (selected < first_character + items_per_row) {-			scroll_top ();+		if (at_bottom () && selected + items_per_row > len) {  			return; 		} 		-		if (selected - items_per_row >= first_character) {-			selected -= items_per_row;-			if (selected < first_visible) {-				scroll (-nail_height);-			}-		} -		-		if (first_visible < first_character) {-			selected = first_character;-		}+		selected += 1; 		-		adjust_scroll ();+		if (selected >= items_per_row * rows) {+			first_visible += items_per_row;+			selected -= items_per_row;+			selected -= 1;+		}		 -		if (first_visible < first_character) {-			scroll_top ();+		if (first_visible + selected > glyph_range.length ()) {+			first_visible -= items_per_row;+			selected = (int) (len - first_visible - 1); 		}-	}	-	-	public void open_current_glyph () {-		key_press (Key.ENTER); 	}--	public void select_next_glyph () {-		key_press (Key.RIGHT);-	}--	public void key_right () {		-		uint len;-		Font f;+	+	public void key_up () {+		selected -= items_per_row; 		-		if (all_available) {-			f = BirdFont.get_current_font ();-			len = f.length ();-		} else {-			len = glyph_range.length ();+		if (selected < 0) {+			first_visible -= items_per_row;+			selected += items_per_row;			 		} 		-		if (len == 0) return;-		-		selected++;-		-		if (selected >= len) {-			selected = len - 1;-		}--		if (selected_offset_y (selected) > allocation.height - nail_height) {-			selected -= items_per_row;-			key_down ();-		}+		if (first_visible < 0) {+			scroll_top ();+		}		 	} 	 	public void key_left () {-		if (selected > first_character) {-			selected--;					-			if (selected_offset_y (selected) < 0) {-				selected -= items_per_row;-				key_up ();-			}+		selected -= 1;++		if (selected < 0) {+			first_visible -= items_per_row;+			selected += items_per_row;+			selected += 1;			+		}++		if (first_visible < 0) {+			scroll_top (); 		} 	} 	@@ -864,7 +486,6 @@ public class OverView : FontDisplay { 	 	public override void key_press (uint keyval) { 		redraw_area (0, 0, allocation.width, allocation.height);-		close_menus ();  		if (KeyBindings.modifier == CTRL) { 			return;@@ -872,7 +493,7 @@ public class OverView : FontDisplay {  		switch (keyval) { 			case Key.ENTER:-				open_glyph_signal (get_selected_char ());+				open_current_glyph (); 				return; 			 			case Key.UP:@@ -892,13 +513,13 @@ public class OverView : FontDisplay { 				return; 				 			case Key.PG_UP:-				for (int i = 0; i++ < rows; i++) {+				for (int i = 0; i < rows; i++) { 					key_up (); 				} 				return; 				 			case Key.PG_DOWN:-				for (int i = 0; i++ < rows; i++) {+				for (int i = 0; i < rows; i++) { 					key_down (); 				} 				return;@@ -954,123 +575,37 @@ public class OverView : FontDisplay { 		DefaultCharacterSet.use_full_unicode_range (full); 		 		set_glyph_range (full);-		selected = c;+		// DELETE+		//selected = c; 		set_glyph_range (gr); 	} 	 	public override void double_click (uint button, double ex, double ey) {-		selection_click (button, ex, ey);-		open_glyph_signal (get_selected_char ());-	}+		foreach (OverViewItem i in visible_items) {+			i.double_click (button, ex, ey);+		} 	-	private void selection_click (uint button, double ex, double ey) {-		double x = view_offset_x;-		double y = view_offset_y;-		double px, py;-		-		// click in margin-		int n_items = (allocation.width - 40 / nail_width);-		double m = (allocation.width - 40 - (n_items * nail_width)) / 2.0;-		-		bool menu_action = false;-		-		Font f;-		uint len;- 		redraw_area (0, 0, allocation.width, allocation.height);+	} -		if (character_info != null) {-			character_info = null;-			return;-		}--		if (all_available) {-			f = BirdFont.get_current_font ();-			len = f.length ();-		} else {-			len = glyph_range.length ();-		}-		-		px = ex - view_offset_x;-		py = ey - view_offset_y;-		foreach (GlyphCollection g in get_visible_glyphs ()) {-			bool a;-			-			a = g.get_version_list ().menu_item_action (px, py); // select one item on the menu-			-			if (a) {-				return;-			}-						-			a = g.get_version_list ().menu_icon_action (px, py); // open menu-			-			if (a) {-				menu_action = true;-			}-		}--		character_info = null;-		foreach (CharacterInfo info in unicode_database_info) {-			if (info.is_over_icon (px, py)) {-				character_info = info;+	public override void button_press (uint button, double x, double y) {+		// DELETE selection_click (button, x, y);+		int index = 0;+		foreach (OverViewItem i in visible_items) {+			if (i.click (button, x, y)) {+				selected = index; 			}-		}-		-		if (ex < m) {-			ex = m + 1;-		}-		-		if (ex > allocation.width - m) {-			ex = allocation.width - m - 1;-		}-		-		if (ey < 5) {-			ey = 5;-		}-		-		if (ey > allocation.height - 5) {-			ey = allocation.height - 5;-		} 			-		// empty set-		if (len == 0) {-			return;-		}--		// scroll to the selected glyph-		selected = (uint32) first_visible;--		while (y < ey < allocation.height) {-			selected += items_per_row;-			y += nail_height;-		}-		selected -= items_per_row;--		while (x < ex < allocation.width) {-			selected++;-			x += nail_width;-		}-		-		selected--;-	-		if (at_bottom () && selected >= len) {-			selected = len - 1;+			index++; 		}-		-		adjust_scroll ();-		-		adjust_scroll ();-		adjust_scroll ();-	} 	-	public override void button_press (uint button, double x, double y) {-		selection_click (button, x, y);+		redraw_area (0, 0, allocation.width, allocation.height); 	}  	/** Returns true if overview shows the last character. */ 	private bool at_bottom () { 		Font f;-		double t = rows * items_per_row + items_per_row + first_visible;+		double t = rows * items_per_row + first_visible; 		 		if (all_available) { 			f = BirdFont.get_current_font ();@@ -1082,10 +617,7 @@ public class OverView : FontDisplay {  	public void set_glyph_range (GlyphRange range) { 		GlyphRange? current = glyph_range;-		string c = "";-		bool done = false;-		uint32 i;-		uint32 len = 0;+		string c; 		 		if (current != null) { 			c = glyph_range.get_char (selected);@@ -1094,73 +626,23 @@ public class OverView : FontDisplay { 		all_available = false; 		 		glyph_range = range;--		// Todo: optimized search when full range is selected.-		-		// scroll to the selected character-		if (c == "") {-			c = glyph_range.get_char (0);-		}--		if (items_per_row == 0) {-			return;-		}-		 		scroll_top (); -		// Skip scroll if this is a unassigned character-		if (c.char_count () > 1) {-			return;-		}--		while (true) {-			for (i = (uint32) first_visible; i < first_visible + items_per_row * (rows + 1); i++) {-				if (range.get_char (i) == c) {-					selected = i;-					done = true;-					break;-				}-			}-			-			if (done) break;-			if (at_bottom ()) break;--			scroll_rows (rows);-		}--		if (unlikely (!done)) {-			Toolbox.select_tool_by_name ("custom_character_set");-			warning (@"No glyph \"$c\"\n");-			return;-		}-		-		adjust_scroll ();-		-		if (at_bottom ()) {-			while (c != glyph_range.get_char (selected) && glyph_range.get_length () >= items_per_row) {-				key_right ();	-				if (selected >= glyph_range.get_length () - items_per_row) {-					break;-				}-			}+		// TODO: scroll down to c -			len = glyph_range.get_length ();-			for (i = selected; i < len; i++) {-				if (c == glyph_range.get_char (i)) {-					break;-				}-			}--			if (i == len) {-				selected = first_character;-				scroll_top ();-			}-		}--		update_scrollbar (); 		redraw_area (0, 0, allocation.width, allocation.height); 	} +	public void select_next_glyph () {+		key_right ();+	}+	+	public void open_current_glyph () +	requires (0 <= selected < visible_items.length ())  {+		OverViewItem o = visible_items.nth (selected).data;+		o.edit_glyph ();+	}+ 	public void update_scrollbar () { 		Font f; 		double nrows = 0;diff --git a/libbirdfont/OverViewItem.vala b/libbirdfont/OverViewItem.valanew file mode 100644index 0000000..249a3d6--- /dev/null+++ b/libbirdfont/OverViewItem.vala@@ -0,0 +1,282 @@+/*+    Copyright (C) 2012 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 class OverViewItem : GLib.Object {+	public unichar character = 'A'; // FIXME: +	public GlyphCollection? glyphs;+	public double x;+	public double y;+	public bool selected = false;+	public CharacterInfo info;+	public bool display_info = false;+	+	public static double DEFAULT_WIDTH = 100;+	public static double DEFAULT_HEIGHT = 130;+	public static double DEFAULT_MARGIN = 20;+	+	public static double width = 100;+	public static double height = 130;+	public static double margin = 20;+	+	public OverViewItem (GlyphCollection? glyphs, unichar character, double x, double y) {	+		this.x = x;+		this.y = y;+		this.character = character;+		this.glyphs = glyphs;+		this.info = new CharacterInfo (character);+	}+	+	public static double full_width () {+		return width + margin;+	}++	public static double full_height () {+		return height + margin;+	}+	+	public bool click (uint button, double px, double py) {+		bool a;+		GlyphCollection g;+		selected = (x <= px <= x + width) && (y <= py <= y + height);+		+		if (glyphs != null) {+			g = (!) glyphs;+			a = g.get_version_list ().menu_item_action (px, py); // select one item on the menu+			if (a) {+				return selected;+			}+			+			g.get_version_list ().menu_icon_action (px, py); // click in the open menu+		}+		+		return selected;+	}++	public void double_click (uint button, double px, double py) {+		selected = (x <= px <= x + width) && (y <= py <= y + height);+		+		if (selected) {+			edit_glyph ();+		}+		+		display_info = info.is_over_icon (px, py);+	}+	+	public void edit_glyph () {+		OverView overview = MainWindow.get_overview ();+		+		if (glyphs == null) {+			overview.open_new_glyph_signal (character);+		} else {+			overview.open_glyph_signal ((!) glyphs);+		}		+	}+	+	public void draw (Context cr) {+		cr.save ();+		cr.set_source_rgba (1, 1, 1, 1);+		cr.rectangle (x, y, width, height);+		cr.fill ();+		cr.restore ();+		+		cr.save ();+		cr.set_source_rgba (0, 0, 0, 1);+		cr.rectangle (x, y, width, height);+		cr.set_line_width (0.5);+		cr.stroke ();+		cr.restore ();+				+		draw_thumbnail (cr, glyphs, x, y + height); 	+		draw_caption (cr);+		+		if (display_info) {+			draw_character_info (cr);+		}		+	}++	private bool draw_thumbnail (Context cr, GlyphCollection? gl, double x, double y) {+		Glyph g;+		double gx, gy;+		double x1, x2, y1, y2;+		double scale = width / DEFAULT_WIDTH;+		double w, h;++		w = width;+		h = height;+		+		if (gl == null) {+			return false;+		}+		+		g = ((!) gl).get_current ();+		g.boundries (out x1, out y1, out x2, out y2);++		gx = (width - (x2 - x1)) / 2*scale;+		+		if (gx < 0) {+			gx = 0;+		}+		+		gy = (h / scale) - 25 / scale;+		+		Surface s = new Surface.similar (cr.get_target (), Content.COLOR_ALPHA, (int) w, (int) h - 20);+		Context c = new Context (s);+		+		c.scale (scale, scale);				+		Svg.draw_svg_path (c, g.get_svg_data (), gx, gy);+		+		cr.save ();+		cr.set_source_surface (s, x, y - h);+		cr.paint ();+		cr.restore ();++		return true;+	}++	public void draw_caption (Context cr) {+		cr.save ();+		+		if (selected) {+			cr.set_source_rgba (50/255.0, 50/255.0, 50/255.0, 1);+		} else {+			cr.set_source_rgba (100/255.0, 100/255.0, 100/255.0, 1);			+		}+		+		cr.rectangle (x + 1, y + height - 20, width - 2, 20 - 1);+		cr.fill ();+		draw_menu (cr);+		draw_character_info_icon (cr);+		cr.restore ();+	}++	private void draw_character_info_icon (Context cr) {+		double px = x + width - 17;+		double py = y + height - 16;+		info.set_position (px, py);+		info.draw_icon (cr);+	}+	+	private void draw_menu (Context cr) {+		GlyphCollection g;+		DropMenu menu;+		+		if (glyphs == null) {+			return;+		}+		+		g = (!) glyphs;+		+		menu = g.get_version_list ();+		menu.set_position (x + width - 32, y + height - 16);+		menu.draw_icon (cr);+		menu.draw_menu (cr);+	}++	/** Display one entry from the Unicode Character Database. */+	void draw_character_info (Context cr) {+		double x, y, w, h;+		int i;+		string unicode_value, unicode_description;+		string[] column;+		string entry;+		int len = 0;+		int length = 0;+		bool see_also = false;+		Allocation allocation = MainWindow.get_overview ().allocation;+		+		entry = info.get_entry ();+		+		foreach (string line in entry.split ("\n")) {+			len = line.char_count ();+			if (len > length) {+				length = len;+			}+		}+		+		x = allocation.width * 0.1;+		y = allocation.height * 0.1;+		w = allocation.width * 0.9 - x; +		h = allocation.height * 0.9 - y;+		+		if (w < 8 * length) {+			w = 8 * length;+			x = (allocation.width - w) / 2.0;+		}+		+		// background	+		cr.save ();+		cr.set_source_rgba (1, 1, 1, 0.8);+		cr.rectangle (x, y, w, h);+		cr.fill ();+		cr.restore ();++		cr.save ();+		cr.set_source_rgba (0, 0, 0, 0.8);+		cr.set_line_width (2);+		cr.rectangle (x, y, w, h);+		cr.stroke ();+		cr.restore ();++		// database entry+		i = 0;+		foreach (string line in entry.split ("\n")) {+			if (i == 0) {+				column = line.split ("\t");+				return_if_fail (column.length == 2);+				unicode_value = "U+" + column[0];+				unicode_description = column[1];++				draw_info_line (unicode_description, cr, x, y, i);+				i++;++				draw_info_line (unicode_value, cr, x, y, i);+				i++;			+			} else {+				+				if (line.has_prefix ("\t*")) {+					draw_info_line (line.replace ("\t*", "•"), cr, x, y, i);+					i++;					+				} else if (line.has_prefix ("\tx (")) {+					if (!see_also) {+						i++;+						draw_info_line (_("See also:"), cr, x, y, i);+						i++;+						see_also = true;+					}+					+					draw_info_line (line.replace ("\tx (", "•").replace (")", ""), cr, x, y, i);+					i++;+				} else {++					i++;+				}+			}+		}+	}++	void draw_info_line (string line, Context cr, double x, double y, int row) {+		cr.save ();+		cr.set_font_size (12);+		cr.set_source_rgba (0, 0, 0, 1);+		cr.move_to (x + 10, y + 28 + row * 18 * 1.2);+		cr.show_text (line);+		cr.restore ();		+	}+}++}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112