Clone in a terminal:
ClipTool.vala
/*
	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.
*/

namespace BirdFont {

public class ClipTool : Tool {

	public static void copy_text (TextArea t) {
		MainWindow.native_window.set_clipboard_text (t.get_selected_text ());		
	}

	public static void paste_text (TextArea t) {
		if (t.carret_is_visible) {
			t.insert_text (MainWindow.native_window.get_clipboard_text ());
		}
	}

	public static void copy () {
		FontDisplay fd = MainWindow.get_current_display ();
		string svg_data;
		string bf_data;
		string data;
		
		if (fd is GlyphTab) {
			svg_data = ExportTool.export_selected_paths_to_svg ();
			
			bf_data = export_selected_paths_to_birdfont_clipboard ();
			data = svg_data + bf_data;
			MainWindow.native_window.set_clipboard (data);
			MainWindow.native_window.set_inkscape_clipboard (data);
		} else if (fd is Overview) {
			copy_overview_glyphs ();
		} else if (fd is DescriptionDisplay) {
			DescriptionDisplay description_tab = (DescriptionDisplay) fd;
			string t = description_tab.get_copy_selection ();
			if (t != "") {
				MainWindow.native_window.set_clipboard_text (t);
			}
		} else if (fd is ExportSettings) {
			ExportSettings export_tab = (ExportSettings) fd;
			string t = export_tab.get_copy_selection ();
			if (t != "") {
				MainWindow.native_window.set_clipboard_text (t);
			}
		}
	}

	/** Copy entire glyph. */
	public static void copy_glyph (Glyph glyph) {
		string svg_data;
		string bf_data;
		string data;
			
		svg_data = ExportTool.export_to_inkscape_clipboard (glyph, false);
		bf_data = export_paths_to_birdfont_clipboard (false, false);
		
		data = svg_data + bf_data;
		MainWindow.native_window.set_clipboard (data);
		MainWindow.native_window.set_inkscape_clipboard (data);	
	}

	public static void copy_overview_glyphs () {
		string svg_data = "";
		string bf_data = "";
		string data;
		Overview o = MainWindow.get_overview ();
		
		if (o.selected_items.size > 0) {
			svg_data = ExportTool.export_to_inkscape_clipboard (
				o.selected_items.get (0).get_current (), false);

			bf_data = export_paths_to_birdfont_clipboard (true, false);
			
			data = svg_data + bf_data;
			MainWindow.native_window.set_clipboard (data);
			MainWindow.native_window.set_inkscape_clipboard (data);	
		}
	}
	
	public static void paste_in_place () {
		paste_paths (true);
	}
	
	/** Paste at cursor. */
	public static void paste () {
		FontDisplay fd = MainWindow.get_current_display ();
		Glyph g = MainWindow.get_current_glyph ();
		double x, y, w, h;
		double dx, dy;
		
		if (fd is GlyphTab) {
			paste_paths (false);
			
			g.selection_boundaries (out x, out y, out w, out h);
			
			dx = g.motion_x - x - w / 2.0;
			dy = g.motion_y - y + h / 2.0;
			
			foreach (Path path in g.active_paths) {
				path.move (dx, dy);
			}
		} else if (fd is SpacingTab) {
			paste_letters_to_spacing_tab ();
		} else if (fd is KerningDisplay) {
			paste_letters_to_kerning_tab ();
		} else if (fd is Overview) {
			paste_to_overview ();
		} else if (fd is DescriptionDisplay) {
			DescriptionDisplay description_tab = (DescriptionDisplay) fd;
			string clipboard_data = MainWindow.native_window.get_clipboard_data ();
			
			if (!clipboard_data.has_prefix ("<?xml")) {
				description_tab.paste (clipboard_data);
				GlyphCanvas.redraw ();
			}
		} else if (fd is ExportSettings) {
			ExportSettings export_tab = (ExportSettings) fd;
			string clipboard_data = MainWindow.native_window.get_clipboard_data ();
			
			if (!clipboard_data.has_prefix ("<?xml")) {
				export_tab.paste (clipboard_data);
				GlyphCanvas.redraw ();
			}
		}
	}
	
	static void paste_paths (bool paste_guide_lines) {
		bool is_bf_clipboard;
		FontDisplay fd;
		string clipboard_data = MainWindow.native_window.get_clipboard_data ();
		
		fd = MainWindow.get_current_display ();
		
		// Determine if clipboard contains data in birdfont format.
		is_bf_clipboard = clipboard_data.index_of ("BirdFontClipboard") > -1; 

		if (fd is GlyphTab) {
			paste_to_glyph (is_bf_clipboard, paste_guide_lines);
		}
		
		BirdFont.get_current_font ().touch ();
	}
	
	static void paste_to_overview () {
		string data = MainWindow.native_window.get_clipboard_data ();
		import_birdfont_clipboard (data, true, true);
		GlyphCanvas.redraw ();
	}
	
	static void paste_to_glyph (bool bf_clipboard_data, bool paste_guide_lines) {
		FontDisplay fd = MainWindow.get_current_display ();
		Glyph? destination = null;
		string data;
		return_if_fail (fd is GlyphTab);
		
		GlyphTab glyph_tab = (GlyphTab) fd; 
		destination = glyph_tab.glyphs.get_current ();
		((!)destination).store_undo_state ();
		((!)destination).clear_active_paths ();
		
		data = MainWindow.native_window.get_clipboard_data ();

		if (bf_clipboard_data) {
			import_birdfont_clipboard (data, paste_guide_lines, false);
		} else if (data != "") {
			SvgParser.import_svg_data (data, SvgFormat.INKSCAPE);
		}
		
		((!)destination).update_view ();	
	}

	static string export_selected_paths_to_birdfont_clipboard () {
		return export_paths_to_birdfont_clipboard (false, true);
	}
	
	static string export_paths_to_birdfont_clipboard (bool overview, bool selected = false) {
		StringBuilder s = new StringBuilder ();
		Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> ();
		Path new_path;
		Glyph glyph;
		GlyphCollection glyph_collection;
		Overview o;
		
		if (overview) {
			o = MainWindow.get_overview ();
			unichar last_assigned = '\0';
			foreach (GlyphCollection gc in o.selected_items) {
				s.append ("\n");
				s.append ("<!-- BirdFontClipboard\n");

				if (!gc.is_unassigned ()) {
					last_assigned = gc.get_unicode_character ();
					
					s.append ("BF glyph: ");
					s.append (@"$(Font.to_hex (gc.get_unicode_character ()))");
					s.append ("\n");
				} else {
					last_assigned++;
					
					s.append ("BF glyph: ");
					s.append (@"$(Font.to_hex (last_assigned))");
					s.append ("\n");
				}

				s.append ("BF name: ");
				s.append (@"$(gc.get_name ())");
				s.append ("\n");				

				s.append ("BF unassigned: ");
				s.append (@"$(gc.is_unassigned ())");
				s.append ("\n");
							
				s.append ("BF left: ");
				s.append (@"$(gc.get_current ().left_limit)");
				s.append ("\n");
				
				s.append ("BF right: ");
				s.append (@"$(gc.get_current ().right_limit)");
				s.append ("\n");

				foreach (Path path in gc.get_current ().get_visible_paths ()) {
					s.append ("BF path: ");
					s.append (BirdFontFile.get_point_data (path));
					s.append ("\n");

					s.append ("BF stroke: ");
					s.append (@"$(path.stroke)");
					s.append ("\n");
					
					if (path.line_cap == LineCap.ROUND) {
						s.append ("BF cap: round\n");
					} else if (path.line_cap == LineCap.SQUARE) {
						s.append ("BF cap: square\n");
					}
				}
				
				s.append ("BF end -->");
			}
		} else {
			glyph = MainWindow.get_current_glyph ();
			glyph_collection = MainWindow.get_current_glyph_collection ();
			
			s.append ("\n");
			s.append ("<!-- BirdFontClipboard\n");

			s.append ("BF glyph: ");
			s.append (@"$(Font.to_hex (glyph.unichar_code))");
			s.append ("\n");

			s.append ("BF name: ");
			s.append (@"$(glyph_collection.get_name ())");
			s.append ("\n");
			
			s.append ("BF unassigned: ");
			s.append (@"$(glyph_collection.is_unassigned ())");
			s.append ("\n");
						
			s.append ("BF left: ");
			s.append (@"$(glyph.left_limit)");
			s.append ("\n");
			
			s.append ("BF right: ");
			s.append (@"$(glyph.right_limit)");
			s.append ("\n");
									
			if (!selected) {
				foreach (Path path in glyph.get_visible_paths ()) {
					s.append ("BF path: ");
					s.append (BirdFontFile.get_point_data (path));
					s.append ("\n");
					
					s.append ("BF stroke: ");
					s.append (@"$(path.stroke)");
					s.append ("\n");
					
					if (path.line_cap == LineCap.ROUND) {
						s.append ("BF cap: round\n");
					} else if (path.line_cap == LineCap.SQUARE) {
						s.append ("BF cap: square\n");
					}
				}
			} else if (glyph.get_visible_paths ().size > 0) {
				foreach (Path path in glyph.active_paths) {
					s.append ("BF path: ");
					s.append (BirdFontFile.get_point_data (path));
					s.append ("\n");
					
					s.append ("BF stroke: ");
					s.append (@"$(path.stroke)");
					s.append ("\n");
					
					if (path.line_cap == LineCap.ROUND) {
						s.append ("BF cap: round\n");
					} else if (path.line_cap == LineCap.SQUARE) {
						s.append ("BF cap: square\n");
					}
				}
			} else {
				new_path = new Path ();
				foreach (Path path in glyph.get_visible_paths ()) {
					if (path.points.size > 0
						&& path.points.get (0).is_selected ()
						&& path.points.get (path.points.size - 1).is_selected ()) {
						
						foreach (EditPoint ep in path.points) {
							if (!ep.is_selected ()) {
								path.set_new_start (ep);
								break;
							}	
						}
					}
					
					foreach (EditPoint ep in path.points) {
						if (!ep.is_selected ()) {
							if (path.points.size > 0) {
								paths.add (new_path);
								new_path = new Path ();
							}
						} else {
							new_path.add_point (ep);
						}
					}

					if (all_points_selected (path)) {
						new_path.close ();
					}
				}
				
				paths.add (new_path);
				
				foreach (Path path in paths) {
					if (path.points.size > 0) {
						s.append ("BF path: ");
						s.append (BirdFontFile.get_point_data (path));
						s.append ("\n");
						
						s.append ("BF stroke: ");
						s.append (@"$(path.stroke)");
						s.append ("\n");
						
						if (path.line_cap == LineCap.ROUND) {
							s.append ("BF cap: round\n");
						} else if (path.line_cap == LineCap.SQUARE) {
							s.append ("BF cap: square\n");
						}
					}
				}
			}
			
			s.append ("BF end -->");
		}
		
		return s.str;
	}
	
	static bool all_points_selected (Path p) {
		foreach (EditPoint ep in p.points) {
			if (!ep.is_selected ()) {
				return false;
			}
		}
		return true;
	}
	
	static void import_birdfont_clipboard (string data, bool paste_guide_lines, bool overview) {
		Gee.ArrayList<GlyphCollection> glyphs = new Gee.ArrayList<GlyphCollection> ();
		Glyph glyph = new Glyph ("null", '\0');
		string[] items = data.split ("\nBF ");
		unichar c;
		Glyph destination;
		GlyphCollection glyph_collection = new GlyphCollection ('\0', "");
		Overview o;
		Path path = new Path ();
		
		foreach (string p in items) {
			if (p.has_prefix ("glyph:")) {
				p = p.replace ("glyph: ", "");
				p = p.replace ("\n", "");
				c = Font.to_unichar (p);
				glyph_collection = new GlyphCollection (c, (!) c.to_string ());
				glyphs.add (glyph_collection);
				
				glyph = new Glyph ((!) c.to_string (), c);
				GlyphMaster master = new GlyphMaster ();
				master.add_glyph (glyph);
				glyph_collection.add_master (master);
			}

			if (p.has_prefix ("unassigned:")) {
				p = p.replace ("unassigned: ", "");
				p = p.replace ("\n", "");
				glyph_collection.set_unassigned (bool.parse (p));
			}

			if (p.has_prefix ("name:")) {
				p = p.replace ("name: ", "");
				p = p.replace ("\n", "");
				glyph_collection.set_name (p);
				glyph.name = p;
			}
									
			if (p.has_prefix ("path:")) {
				p = p.replace ("path: ", "");
				p = p.replace ("\n", "");
				path = import_birdfont_path (glyph, p);
			}
			
			if (p.has_prefix ("left:")) {
				glyph.left_limit = double.parse (p.replace ("left: ", ""));
				glyph.remove_lines ();
				glyph.add_help_lines ();			
			}
			
			if (p.has_prefix ("right:")) {
				glyph.right_limit = double.parse (p.replace ("right: ", ""));
				glyph.remove_lines ();
				glyph.add_help_lines ();	
			}
			
			if (p.has_prefix ("stroke:")) {
				path.stroke = double.parse (p.replace ("stroke: ", ""));
			}

			if (p.has_prefix ("cap:")) {
				string cap = p.replace ("cap: ", "");
				
				if (cap == "round") {
					path.line_cap = LineCap.ROUND;
				} else if (cap == "square") {
					path.line_cap = LineCap.SQUARE;
				}
			}
		}
		
		if (!overview) {
			return_if_fail (glyphs.size > 0);
			destination = MainWindow.get_current_glyph ();
			glyph = glyphs.get (0).get_current ();
			
			foreach (Path p in glyph.get_visible_paths ()) {
				PenTool.clear_directions ();
				destination.add_path (p);
				destination.add_active_path (null, p);
			}
			
			if (paste_guide_lines) {
				destination.left_limit = glyph.left_limit;
				destination.right_limit = glyph.right_limit;
				destination.add_help_lines ();
				destination.update_other_spacing_classes ();
			}
		} else {
			o = MainWindow.get_overview ();
			o.copied_glyphs.clear ();
			foreach (GlyphCollection gc in glyphs) {
				o.copied_glyphs.add (gc);
			}
			o.paste ();
		}
	}
	
	static Path import_birdfont_path (Glyph glyph, string data) {
		Path path = new Path ();
		
		BirdFontFile.parse_path_data (data, path);

		if (path.points.size > 0) {
			PenTool.clear_directions ();
			glyph.add_path (path);
			glyph.active_paths.add (path);
			path.update_region_boundaries ();
		}
		
		PenTool.remove_all_selected_points ();
		
		foreach (Path p in glyph.active_paths) {
			if (p.is_open ()) {
				foreach (EditPoint e in p.points) {
					e.set_selected (true);
				}
			}
		}
		
		PenTool.update_selection ();
		return path;
	}
	
	static void paste_letters_to_kerning_tab () {
		string clipboard_data = MainWindow.native_window.get_clipboard_data ();
		KerningDisplay kerning_display = MainWindow.get_kerning_display ();
		
		if (!clipboard_data.has_prefix ("<?xml")) {
			kerning_display.add_text (clipboard_data);
		}
	}
	
	static void paste_letters_to_spacing_tab () {
		string clipboard_data = MainWindow.native_window.get_clipboard_data ();
		SpacingTab spacing_tab = MainWindow.get_spacing_tab ();
		
		if (!clipboard_data.has_prefix ("<?xml")) {
			spacing_tab.add_text (clipboard_data);
		}
	}
}

}

Contact

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