The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

Line.vala in /libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
Circle boundaries heads/master
1 /* 2 Copyright (C) 2012 2014 2015 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Cairo; 16 17 namespace BirdFont { 18 19 /** Guide */ 20 public class Line : GLib.Object { 21 public static const bool VERTICAL = true; 22 public static const bool HORIZONTAL = false; 23 24 public bool dashed { get; set; } 25 26 public string label; 27 public string translated_label; 28 bool vertical; 29 string metrics; 30 31 public double pos; 32 33 bool active = false; 34 bool move = false; 35 36 public signal void queue_draw_area (int x, int y, int w, int h); 37 public signal void position_updated (double pos); 38 39 double r; 40 double g; 41 double b; 42 double a; 43 44 bool visible = true; 45 bool moveable = true; 46 47 public bool rsb = false; 48 public bool lsb = false; 49 50 public Line (string label = "No label", 51 string translated_label = "No label", 52 double position = 10, 53 bool vertical = false) { 54 55 this.label = label; 56 this.translated_label = translated_label; 57 this.vertical = vertical; 58 this.pos = position; 59 60 dashed = false; 61 metrics = ""; 62 63 set_color_theme ("Guide 1"); 64 } 65 66 public Line copy () { 67 Line l = new Line (label, translated_label, pos, vertical); 68 69 l.r = r; 70 l.g = g; 71 l.b = b; 72 l.a = a; 73 74 l.visible = visible; 75 l.dashed = dashed; 76 77 return l; 78 } 79 80 public void set_metrics (double m) { 81 string t = @"$m"; 82 string s = ""; 83 84 int i; 85 unichar c; 86 87 i = 0; 88 while (t.get_next_char (ref i, out c)) { 89 s = s + (!) c.to_string (); 90 91 if (i >= 5) { 92 break; 93 } 94 } 95 96 metrics = s; 97 } 98 99 public void set_visible (bool v) { 100 visible = v; 101 } 102 103 public bool is_visible () { 104 return visible; 105 } 106 107 public void set_moveable (bool m) { 108 moveable = m; 109 } 110 111 public void set_color_theme (string color) { 112 Color c = Theme.get_color (color); 113 114 r = c.r; 115 g = c.g; 116 b = c.b; 117 a = c.a; 118 } 119 120 public void set_color (double r, double g, double b, double a) { 121 this.r = r; 122 this.g = g; 123 this.b = b; 124 this.a = a; 125 } 126 127 public bool is_moving () { 128 return move; 129 } 130 131 public bool set_move (bool moving) { 132 bool r = move; 133 move = moving; 134 return (r == moving); 135 } 136 137 public bool button_press (uint button) { 138 Glyph g; 139 TextListener listener; 140 string position; 141 bool text_input = false; 142 143 if (get_active ()) { 144 if (button == 3 || KeyBindings.has_shift ()) { 145 move = false; 146 text_input = true; 147 148 g = MainWindow.get_current_glyph (); 149 if (lsb) { 150 position = @"$(g.get_left_side_bearing ())"; 151 } else if (rsb) { 152 position = @"$(g.get_right_side_bearing ())"; 153 } else { 154 position = @"$pos"; 155 } 156 157 listener = new TextListener (t_("Position"), position, t_("Move")); 158 159 listener.signal_text_input.connect ((text) => { 160 string submitted_value; 161 double parsed_value; 162 Glyph glyph; 163 double x1, y1, x2, y2; 164 165 glyph = MainWindow.get_current_glyph (); 166 submitted_value = text.replace (",", "."); 167 parsed_value = double.parse (submitted_value); 168 169 if (lsb) { 170 if (glyph.boundaries (out x1, out y1, out x2, out y2)) { 171 parsed_value = x1 - parsed_value; 172 } else { 173 parsed_value = glyph.right_limit - parsed_value; 174 } 175 } else if (rsb) { 176 if (glyph.boundaries (out x1, out y1, out x2, out y2)) { 177 parsed_value += x2; 178 } else { 179 parsed_value = glyph.left_limit - parsed_value; 180 } 181 } 182 183 pos = parsed_value; 184 position_updated (parsed_value); 185 GlyphCanvas.redraw (); 186 }); 187 188 listener.signal_submit.connect (() => { 189 TabContent.hide_text_input (); 190 }); 191 192 TabContent.show_text_input (listener); 193 } else { 194 move = true; 195 } 196 197 g = MainWindow.get_current_glyph (); 198 g.store_undo_state (); 199 } else { 200 move = false; 201 active = false; 202 } 203 204 return move || text_input; 205 } 206 207 void redraw_line () { 208 GlyphCanvas.redraw (); 209 } 210 211 public bool event_move_to (int x, int y, WidgetAllocation allocation) { 212 double p, c; 213 bool a = false; 214 Glyph g = MainWindow.get_current_glyph (); 215 double ivz = 1/g.view_zoom; 216 double margin = 10; 217 double none = 0; 218 219 if (!moveable) { 220 return false; 221 } 222 223 if (is_vertical ()) { // over line handle (y) 224 if (y > g.allocation.height - 10 * MainWindow.units 225 || y < 10 * MainWindow.units) { 226 227 p = pos; 228 c = Glyph.path_coordinate_x (x); 229 a = (p - margin * ivz <= c <= p + margin * ivz); 230 } 231 232 if (a != get_active ()) { 233 redraw_line (); 234 } 235 236 set_active (a); 237 238 } else { // over line handle (x) 239 if (x > g.allocation.width - 10 * MainWindow.units 240 || x < 10 * MainWindow.units) { 241 242 p = pos; 243 c = Glyph.path_coordinate_y (y); 244 a = (p - margin * ivz <= c <= p + margin * ivz); 245 } 246 247 if (a != get_active ()) { 248 redraw_line (); 249 } 250 251 set_active (a); 252 } 253 254 // move the line 255 if (move) { 256 double np = pos; 257 redraw_line (); // clear old position 258 259 if (is_vertical ()) { 260 pos = Glyph.path_coordinate_x (x); 261 262 if (GridTool.is_visible ()) { 263 GridTool.tie_coordinate (ref pos, ref none); 264 } 265 redraw_line (); // draw at new position 266 } else if (!GridTool.lock_grid) { 267 pos = Glyph.path_coordinate_y (y); 268 269 if (GridTool.is_visible ()) { 270 GridTool.tie_coordinate (ref none, ref pos); 271 } 272 redraw_line (); 273 } 274 275 if (Math.fabs (np - pos) > 10) { 276 queue_draw_area (0, 0, g.allocation.width, g.allocation.height); 277 } 278 279 position_updated (pos); // signal update 280 281 BirdFont.get_current_font ().touch (); 282 } 283 284 if (GridTool.is_visible ()) { 285 GridTool.update_lines (); 286 } 287 288 return move; 289 } 290 291 public bool get_active () { 292 return active; 293 } 294 295 public void set_active (bool active) { 296 Glyph g; 297 298 if (active) { 299 g = MainWindow.get_current_glyph (); 300 301 if (lsb) { 302 set_metrics (g.get_left_side_bearing ()); 303 } else if (rsb) { 304 set_metrics (g.get_right_side_bearing ()); 305 } 306 } 307 308 this.active = active; 309 } 310 311 public string get_label () { 312 return label; 313 } 314 315 public bool is_vertical () { 316 return vertical; 317 } 318 319 public int get_position_pixel () { 320 if (is_vertical ()) { 321 return Glyph.reverse_path_coordinate_x (pos); 322 } 323 324 return Glyph.reverse_path_coordinate_y (pos) ; 325 } 326 327 public double get_pos () { 328 return pos; 329 } 330 331 public void draw (Context cr, WidgetAllocation allocation) { 332 Glyph g = MainWindow.get_current_glyph (); 333 double p, h, w; 334 double size = (active) ? 8 : 5; 335 Text glyph_metrics; 336 Text line_label; 337 338 if (!visible) { 339 return; 340 } 341 342 cr.save (); 343 cr.set_line_width (1); 344 345 if (dashed) { 346 cr.set_dash ({20, 20}, 0); 347 } 348 349 if (active) { 350 Theme.color (cr, "Highlighted Guide"); 351 } else { 352 cr.set_source_rgba (r, this.g, b, a); 353 } 354 355 // Line 356 if (is_vertical ()) { 357 p = Glyph.reverse_path_coordinate_x (pos); 358 h = g.allocation.height; 359 360 cr.move_to (p, 0); 361 cr.line_to (p, h); 362 cr.stroke (); 363 364 cr.scale (1, 1); 365 366 if (moveable) { 367 cr.new_path (); 368 cr.move_to (p - size, h); 369 cr.line_to (p, h - size); 370 cr.line_to (p + size, h); 371 cr.close_path(); 372 cr.fill (); 373 374 cr.new_path (); 375 cr.move_to (p - size, 0); 376 cr.line_to (p, size); 377 cr.line_to (p + size, 0); 378 cr.close_path(); 379 cr.fill (); 380 381 if (get_active ()) { 382 glyph_metrics = new Text (metrics, 17); 383 Theme.text_color (glyph_metrics, "Highlighted Guide"); 384 glyph_metrics.widget_x = p + 10; 385 glyph_metrics.widget_y = h - 25; 386 glyph_metrics.draw (cr); 387 } 388 } 389 390 } else { 391 p = Glyph.reverse_path_coordinate_y (pos); 392 w = g.allocation.width; 393 394 cr.move_to (0, p); 395 cr.line_to (w, p); 396 cr.stroke (); 397 398 if (moveable) { 399 cr.new_path (); 400 cr.move_to (w, p - size); 401 cr.line_to (w - size, p); 402 cr.line_to (w, p + size); 403 cr.close_path(); 404 cr.fill (); 405 406 cr.new_path (); 407 cr.move_to (0, p - size); 408 cr.line_to (0 + size, p); 409 cr.line_to (0, p + size); 410 cr.close_path(); 411 cr.fill (); 412 } 413 } 414 415 // Label 416 if (get_active ()) { 417 line_label = new Text (translated_label, 19 * MainWindow.units); 418 419 if (is_vertical ()) { 420 line_label.widget_x = p + 8 * MainWindow.units; 421 line_label.widget_y = allocation.height - 55 * MainWindow.units; 422 } else { 423 line_label.widget_x = g.allocation.width 424 - 10 * MainWindow.units 425 - line_label.get_extent (); 426 427 line_label.widget_y = p + 10 * MainWindow.units; 428 } 429 430 if (active) { 431 Theme.text_color (line_label, "Highlighted Guide"); 432 } else { 433 line_label.set_source_rgba (r, this.g, b, a); 434 } 435 436 line_label.draw (cr); 437 } 438 439 cr.restore (); 440 } 441 } 442 443 } 444