Root/
Source at commit 49a1bbdd0525c12d09a365cc36998d52c0ee46ba created 13 years 2 months ago. By werner, Added a new pad type: trace pads, for antennas and other trace-like elements. | |
---|---|
1 | /* |
2 | * gui_inst.c - GUI, instance functions |
3 | * |
4 | * Written 2009-2011 by Werner Almesberger |
5 | * Copyright 2009-2011 by Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <math.h> |
16 | #include <gtk/gtk.h> |
17 | |
18 | #include "util.h" |
19 | #include "coord.h" |
20 | #include "inst.h" |
21 | #include "gui.h" |
22 | #include "gui_util.h" |
23 | #include "gui_style.h" |
24 | #include "gui_status.h" |
25 | #include "gui_inst.h" |
26 | |
27 | |
28 | /* ----- coordinate translation -------------------------------------------- */ |
29 | |
30 | |
31 | struct coord translate(struct coord pos) |
32 | { |
33 | pos.x -= draw_ctx.center.x; |
34 | pos.y -= draw_ctx.center.y; |
35 | pos.x /= draw_ctx.scale; |
36 | pos.y /= draw_ctx.scale; |
37 | pos.y = -pos.y; |
38 | pos.x += draw_ctx.widget->allocation.width/2; |
39 | pos.y += draw_ctx.widget->allocation.height/2; |
40 | return pos; |
41 | } |
42 | |
43 | |
44 | struct coord canvas_to_coord(int x, int y) |
45 | { |
46 | struct coord pos; |
47 | |
48 | x -= draw_ctx.widget->allocation.width/2; |
49 | y -= draw_ctx.widget->allocation.height/2; |
50 | y = -y; |
51 | pos.x = x*draw_ctx.scale+draw_ctx.center.x; |
52 | pos.y = y*draw_ctx.scale+draw_ctx.center.y; |
53 | return pos; |
54 | } |
55 | |
56 | |
57 | /* ----- drawing primitives ------------------------------------------------ */ |
58 | |
59 | |
60 | static void draw_eye(GdkGC *gc, struct coord center, int r1, int r2) |
61 | { |
62 | draw_circle(DA, gc, TRUE, center.x, center.y, r1); |
63 | draw_circle(DA, gc, FALSE, center.x, center.y, r2); |
64 | } |
65 | |
66 | |
67 | #define MAX_POINTS 10 |
68 | |
69 | |
70 | static void draw_poly(GdkGC *gc, int fill, |
71 | const struct coord *points, int n_points) |
72 | { |
73 | GdkPoint gp[MAX_POINTS]; |
74 | int i; |
75 | |
76 | if (n_points > MAX_POINTS) |
77 | abort(); |
78 | for (i = 0; i != n_points; i++) { |
79 | gp[i].x = points[i].x; |
80 | gp[i].y = points[i].y; |
81 | } |
82 | if (fill) |
83 | gdk_draw_polygon(DA, gc, fill, gp, n_points); |
84 | else { |
85 | gdk_draw_line(DA, gc, gp[0].x, gp[0].y, gp[1].x, gp[1].y); |
86 | gdk_draw_line(DA, gc, gp[1].x, gp[1].y, gp[2].x, gp[2].y); |
87 | } |
88 | } |
89 | |
90 | |
91 | static void draw_arrow(GdkGC *gc, int fill, |
92 | struct coord from, struct coord to, int len, double angle) |
93 | { |
94 | struct coord p[3]; |
95 | struct coord side; |
96 | |
97 | if (from.x == to.x && from.y == to.y) { |
98 | side.x = 0; |
99 | side.y = -len; |
100 | } else { |
101 | side = normalize(sub_vec(to, from), len); |
102 | } |
103 | p[0] = add_vec(to, rotate(side, 180-angle)); |
104 | p[1] = to; |
105 | p[2] = add_vec(to, rotate(side, 180+angle)); |
106 | draw_poly(gc, fill, p, 3); |
107 | } |
108 | |
109 | |
110 | static enum mode get_mode(const struct inst *self) |
111 | { |
112 | if (selected_inst == self) |
113 | return mode_selected; |
114 | return self->active || bright(self) ? mode_active : mode_inactive; |
115 | } |
116 | |
117 | |
118 | /* ----- vec --------------------------------------------------------------- */ |
119 | |
120 | |
121 | unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale) |
122 | { |
123 | unit_type d; |
124 | |
125 | d = dist_point(pos, self->u.vec.end)/scale; |
126 | return d > VEC_EYE_R ? -1 : d; |
127 | } |
128 | |
129 | |
130 | /* |
131 | * The circles at a vector's tip enjoy the highest selection priority. However, |
132 | * users will probably also expected a click on a nicely exposed stem to work. |
133 | * So we give it second look after having exhausted all other options. |
134 | */ |
135 | |
136 | unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos, |
137 | unit_type scale) |
138 | { |
139 | unit_type d; |
140 | |
141 | d = dist_line(pos, self->base, self->u.vec.end)/scale; |
142 | return d > SELECT_R ? -1 : d; |
143 | } |
144 | |
145 | |
146 | void gui_highlight_vec(struct inst *self) |
147 | { |
148 | struct coord center = translate(self->u.vec.end); |
149 | |
150 | draw_circle(DA, gc_highlight, FALSE, center.x, center.y, VEC_EYE_R); |
151 | } |
152 | |
153 | |
154 | void gui_draw_vec(struct inst *self) |
155 | { |
156 | struct coord from = translate(self->base); |
157 | struct coord to = translate(self->u.vec.end); |
158 | GdkGC *gc; |
159 | |
160 | gc = gc_vec[get_mode(self)]; |
161 | draw_arrow(gc, TRUE, from, to, VEC_ARROW_LEN, VEC_ARROW_ANGLE); |
162 | gdk_draw_line(DA, gc, from.x, from.y, to.x, to.y); |
163 | draw_circle(DA, gc, FALSE, to.x, to.y, VEC_EYE_R); |
164 | } |
165 | |
166 | |
167 | /* ----- line -------------------------------------------------------------- */ |
168 | |
169 | |
170 | unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale) |
171 | { |
172 | unit_type r, d; |
173 | |
174 | r = self->u.rect.width/scale/2; |
175 | if (r < SELECT_R) |
176 | r = SELECT_R; |
177 | d = dist_line(pos, self->base, self->u.rect.end)/scale; |
178 | return d > r ? -1 : d; |
179 | } |
180 | |
181 | |
182 | void gui_draw_line(struct inst *self) |
183 | { |
184 | struct coord min = translate(self->base); |
185 | struct coord max = translate(self->u.rect.end); |
186 | GdkGC *gc; |
187 | |
188 | gc = gc_obj[get_mode(self)]; |
189 | set_width(gc, self->u.rect.width/draw_ctx.scale); |
190 | gdk_draw_line(DA, gc, min.x, min.y, max.x, max.y); |
191 | } |
192 | |
193 | |
194 | /* ----- rect -------------------------------------------------------------- */ |
195 | |
196 | |
197 | unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale) |
198 | { |
199 | unit_type r, d; |
200 | |
201 | r = self->u.rect.width/scale/2; |
202 | if (r < SELECT_R) |
203 | r = SELECT_R; |
204 | d = dist_rect(pos, self->base, self->u.rect.end)/scale; |
205 | return d > r ? -1 : d; |
206 | } |
207 | |
208 | |
209 | void gui_draw_rect(struct inst *self) |
210 | { |
211 | struct coord min = translate(self->base); |
212 | struct coord max = translate(self->u.rect.end); |
213 | GdkGC *gc; |
214 | |
215 | sort_coord(&min, &max); |
216 | gc = gc_obj[get_mode(self)]; |
217 | set_width(gc, self->u.rect.width/draw_ctx.scale); |
218 | gdk_draw_rectangle(DA, gc, FALSE, |
219 | min.x, min.y, max.x-min.x, max.y-min.y); |
220 | } |
221 | |
222 | |
223 | /* ----- pad --------------------------------------------------------------- */ |
224 | |
225 | |
226 | unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale) |
227 | { |
228 | unit_type d; |
229 | |
230 | if (inside_rect(pos, self->base, self->u.pad.other)) |
231 | return SELECT_R; |
232 | d = dist_rect(pos, self->base, self->u.pad.other)/scale; |
233 | return d > SELECT_R ? -1 : d; |
234 | } |
235 | |
236 | |
237 | static void pad_text_in_rect(struct inst *self, |
238 | struct coord min, struct coord max) |
239 | { |
240 | GdkGC *gc; |
241 | struct coord c; |
242 | unit_type h, w; |
243 | int rot; |
244 | |
245 | w = max.x-min.x; |
246 | h = max.y-min.y; |
247 | rot = w/1.1 < h; |
248 | gc = gc_ptext[get_mode(self)]; |
249 | c = add_vec(min, max); |
250 | h = max.y-min.y; |
251 | w = max.x-min.x; |
252 | render_text(DA, gc, c.x/2, c.y/2, rot ? 0 : 90, |
253 | self->u.pad.name, PAD_FONT, 0.5, 0.5, |
254 | w-2*PAD_BORDER, h-2*PAD_BORDER); |
255 | } |
256 | |
257 | |
258 | static void maximize_box(struct coord *min_box, struct coord *max_box, |
259 | unit_type x_min, unit_type y_min, unit_type x_max, unit_type y_max) |
260 | { |
261 | unit_type d_box, d_new, d; |
262 | |
263 | d_box = max_box->x-min_box->x; |
264 | d = max_box->y-min_box->y; |
265 | if (d < d_box) |
266 | d_box = d; |
267 | |
268 | d_new = x_max-x_min; |
269 | d = y_max-y_min; |
270 | if (d < d_new) |
271 | d_new = d; |
272 | |
273 | if (d_new < d_box) |
274 | return; |
275 | |
276 | min_box->x = x_min; |
277 | min_box->y = y_min; |
278 | max_box->x = x_max; |
279 | max_box->y = y_max; |
280 | } |
281 | |
282 | |
283 | static void gui_draw_pad_text(struct inst *self) |
284 | { |
285 | struct coord pad_min = translate(self->base); |
286 | struct coord pad_max = translate(self->u.pad.other); |
287 | struct coord hole_min, hole_max; |
288 | struct coord box_min, box_max; |
289 | |
290 | sort_coord(&pad_min, &pad_max); |
291 | if (!self->u.pad.hole) { |
292 | pad_text_in_rect(self, pad_min, pad_max); |
293 | return; |
294 | } |
295 | |
296 | hole_min = translate(self->u.pad.hole->base); |
297 | hole_max = translate(self->u.pad.hole->u.hole.other); |
298 | sort_coord(&hole_min, &hole_max); |
299 | |
300 | box_min.x = pad_min.x; /* top */ |
301 | box_min.y = pad_min.y; |
302 | box_max.x = pad_max.x; |
303 | box_max.y = hole_min.y; |
304 | |
305 | maximize_box(&box_min, &box_max, |
306 | pad_min.x, hole_max.y, pad_max.x, pad_max.y); /* bottom */ |
307 | maximize_box(&box_min, &box_max, |
308 | pad_min.x, pad_min.y, hole_min.x, pad_max.y); /* left */ |
309 | maximize_box(&box_min, &box_max, |
310 | hole_max.x, pad_min.y, pad_max.x, pad_max.y); /* right */ |
311 | |
312 | pad_text_in_rect(self, box_min, box_max); |
313 | } |
314 | |
315 | |
316 | static GdkGC *pad_gc(const struct inst *inst, int *fill) |
317 | { |
318 | *fill = TRUE; |
319 | switch (layers_to_pad_type(inst->u.pad.layers)) { |
320 | case pt_bare: |
321 | return gc_pad_bare[get_mode(inst)]; |
322 | case pt_trace: |
323 | return gc_pad_trace[get_mode(inst)]; |
324 | case pt_mask: |
325 | *fill = FALSE; |
326 | return gc_pad_mask[get_mode(inst)]; |
327 | default: |
328 | return gc_pad[get_mode(inst)]; |
329 | } |
330 | } |
331 | |
332 | |
333 | void gui_draw_pad(struct inst *self) |
334 | { |
335 | struct coord min = translate(self->base); |
336 | struct coord max = translate(self->u.pad.other); |
337 | GdkGC *gc; |
338 | int fill; |
339 | |
340 | gc = pad_gc(self, &fill); |
341 | sort_coord(&min, &max); |
342 | gdk_draw_rectangle(DA, gc, fill, |
343 | min.x, min.y, max.x-min.x, max.y-min.y); |
344 | |
345 | gui_draw_pad_text(self); |
346 | } |
347 | |
348 | |
349 | static void draw_rounded_rect(GdkGC *gc, struct coord a, struct coord b, |
350 | int fill) |
351 | { |
352 | struct coord min = translate(a); |
353 | struct coord max = translate(b); |
354 | unit_type h, w, r; |
355 | |
356 | sort_coord(&min, &max); |
357 | h = max.y-min.y; |
358 | w = max.x-min.x; |
359 | if (h > w) { |
360 | r = w/2; |
361 | draw_arc(DA, gc, fill, min.x+r, max.y-r, r, 180, 0); |
362 | if (fill) |
363 | gdk_draw_rectangle(DA, gc, fill, |
364 | min.x, min.y+r, w, h-2*r); |
365 | else { |
366 | gdk_draw_line(DA, gc, min.x, min.y+r, min.x, max.y-r); |
367 | gdk_draw_line(DA, gc, max.x, min.y+r, max.x, max.y-r); |
368 | } |
369 | draw_arc(DA, gc, fill, min.x+r, min.y+r, r, 0, 180); |
370 | } else { |
371 | r = h/2; |
372 | draw_arc(DA, gc, fill, min.x+r, min.y+r, r, 90, 270); |
373 | if (fill) |
374 | gdk_draw_rectangle(DA, gc, fill, |
375 | min.x+r, min.y, w-2*r, h); |
376 | else { |
377 | gdk_draw_line(DA, gc, min.x+r, min.y, max.x-r, min.y); |
378 | gdk_draw_line(DA, gc, min.x+r, max.y, max.x-r, max.y); |
379 | } |
380 | draw_arc(DA, gc, fill, max.x-r, min.y+r, r, 270, 90); |
381 | } |
382 | } |
383 | |
384 | |
385 | void gui_draw_rpad(struct inst *self) |
386 | { |
387 | GdkGC *gc; |
388 | int fill; |
389 | |
390 | gc = pad_gc(self, &fill); |
391 | draw_rounded_rect(gc, self->base, self->u.pad.other, fill); |
392 | gui_draw_pad_text(self); |
393 | } |
394 | |
395 | |
396 | /* ----- hole -------------------------------------------------------------- */ |
397 | |
398 | |
399 | unit_type gui_dist_hole(struct inst *self, struct coord pos, unit_type scale) |
400 | { |
401 | unit_type d; |
402 | |
403 | /* @@@ not quite right ... */ |
404 | if (inside_rect(pos, self->base, self->u.hole.other)) |
405 | return SELECT_R; |
406 | d = dist_rect(pos, self->base, self->u.hole.other)/scale; |
407 | return d > SELECT_R ? -1 : d; |
408 | } |
409 | |
410 | |
411 | void gui_draw_hole(struct inst *self) |
412 | { |
413 | draw_rounded_rect(gc_hole[get_mode(self)], |
414 | self->base, self->u.hole.other, 1); |
415 | draw_rounded_rect(gc_rim[get_mode(self)], |
416 | self->base, self->u.hole.other, 0); |
417 | } |
418 | |
419 | |
420 | /* ----- arc --------------------------------------------------------------- */ |
421 | |
422 | |
423 | unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale) |
424 | { |
425 | struct coord c = self->base; |
426 | struct coord p; |
427 | unit_type r, d_min, d; |
428 | double angle, a1, a2; |
429 | |
430 | r = self->u.arc.width/scale/2; |
431 | if (r < SELECT_R) |
432 | r = SELECT_R; |
433 | |
434 | /* check endpoints */ |
435 | |
436 | p = rotate_r(c, self->u.arc.r, self->u.arc.a1); |
437 | d_min = hypot(pos.x-p.x, pos.y-p.y); |
438 | |
439 | p = rotate_r(c, self->u.arc.r, self->u.arc.a2); |
440 | d = hypot(pos.x-p.x, pos.y-p.y); |
441 | if (d < d_min) |
442 | d_min = d; |
443 | |
444 | if (d_min/scale <= r) |
445 | return d; |
446 | |
447 | /* distance from the circle containing the arc */ |
448 | |
449 | d = dist_circle(pos, c, self->u.arc.r)/scale; |
450 | if (d > r) |
451 | return -1; |
452 | if (self->u.arc.a1 == self->u.arc.a2) |
453 | return d; |
454 | |
455 | /* see if we're close to the part that's actually drawn */ |
456 | |
457 | angle = theta(c, pos); |
458 | a1 = self->u.arc.a1; |
459 | a2 = self->u.arc.a2; |
460 | if (angle < 0) |
461 | angle += 360; |
462 | if (a2 < a1) |
463 | a2 += 360; |
464 | if (angle < a1) |
465 | angle += 360; |
466 | return angle >= a1 && angle <= a2 ? d : -1; |
467 | } |
468 | |
469 | |
470 | void gui_draw_arc(struct inst *self) |
471 | { |
472 | struct coord center = translate(self->base); |
473 | GdkGC *gc; |
474 | |
475 | gc = gc_obj[get_mode(self)]; |
476 | set_width(gc, self->u.arc.width/draw_ctx.scale); |
477 | draw_arc(DA, gc, FALSE, center.x, center.y, |
478 | self->u.arc.r/draw_ctx.scale, self->u.arc.a1, self->u.arc.a2); |
479 | } |
480 | |
481 | |
482 | /* ----- meas -------------------------------------------------------------- */ |
483 | |
484 | |
485 | static struct coord offset_vec(struct coord a, struct coord b, |
486 | const struct inst *self) |
487 | { |
488 | struct coord res; |
489 | double f; |
490 | |
491 | res.x = a.y-b.y; |
492 | res.y = b.x-a.x; |
493 | if (res.x == 0 && res.y == 0) |
494 | return res; |
495 | f = self->u.meas.offset/hypot(res.x, res.y); |
496 | res.x *= f; |
497 | res.y *= f; |
498 | return res; |
499 | } |
500 | |
501 | |
502 | void project_meas(const struct inst *inst, struct coord *a1, struct coord *b1) |
503 | { |
504 | const struct meas *meas = &inst->obj->u.meas; |
505 | struct coord off; |
506 | |
507 | *a1 = inst->base; |
508 | *b1 = inst->u.meas.end; |
509 | switch (meas->type) { |
510 | case mt_xy_next: |
511 | case mt_xy_max: |
512 | break; |
513 | case mt_x_next: |
514 | case mt_x_max: |
515 | b1->y = a1->y; |
516 | break; |
517 | case mt_y_next: |
518 | case mt_y_max: |
519 | b1->x = a1->x; |
520 | break; |
521 | default: |
522 | abort(); |
523 | } |
524 | off = offset_vec(*a1, *b1, inst); |
525 | *a1 = add_vec(*a1, off); |
526 | *b1 = add_vec(*b1, off); |
527 | } |
528 | |
529 | |
530 | unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale) |
531 | { |
532 | struct coord a1, b1; |
533 | unit_type d; |
534 | |
535 | project_meas(self, &a1, &b1); |
536 | d = dist_line(pos, a1, b1)/scale; |
537 | return d > SELECT_R ? -1 : d; |
538 | } |
539 | |
540 | |
541 | char *format_len(const char *label, unit_type len, enum curr_unit unit) |
542 | { |
543 | const char *u = ""; |
544 | double n; |
545 | int mm; |
546 | |
547 | switch (unit) { |
548 | case curr_unit_mm: |
549 | n = units_to_mm(len); |
550 | mm = 1; |
551 | break; |
552 | case curr_unit_mil: |
553 | n = units_to_mil(len); |
554 | mm = 0; |
555 | break; |
556 | case curr_unit_auto: |
557 | n = units_to_best(len, &mm); |
558 | u = mm ? "mm" : "mil"; |
559 | break; |
560 | default: |
561 | abort(); |
562 | } |
563 | return stralloc_printf(mm ? |
564 | "%s" MM_FORMAT_SHORT "%s" : |
565 | "%s" MIL_FORMAT_SHORT "%s", |
566 | label, n, u); |
567 | } |
568 | |
569 | |
570 | void gui_draw_meas(struct inst *self) |
571 | { |
572 | const struct meas *meas = &self->obj->u.meas; |
573 | struct coord a0, b0, a1, b1, c, d; |
574 | GdkGC *gc; |
575 | double len; |
576 | char *s; |
577 | |
578 | a0 = translate(self->base); |
579 | b0 = translate(self->u.meas.end); |
580 | project_meas(self, &a1, &b1); |
581 | |
582 | len = dist_point(a1, b1); |
583 | a1 = translate(a1); |
584 | b1 = translate(b1); |
585 | gc = gc_meas[get_mode(self)]; |
586 | gdk_draw_line(DA, gc, a0.x, a0.y, a1.x, a1.y); |
587 | gdk_draw_line(DA, gc, b0.x, b0.y, b1.x, b1.y); |
588 | gdk_draw_line(DA, gc, a1.x, a1.y, b1.x, b1.y); |
589 | draw_arrow(gc, FALSE, a1, b1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE); |
590 | draw_arrow(gc, FALSE, b1, a1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE); |
591 | |
592 | c = add_vec(a1, b1); |
593 | d = sub_vec(b1, a1); |
594 | s = format_len(meas->label ? meas->label : "", len, curr_unit); |
595 | render_text(DA, gc, c.x/2, c.y/2, -atan2(d.y, d.x)/M_PI*180, s, |
596 | MEAS_FONT, 0.5, -MEAS_BASELINE_OFFSET, |
597 | dist_point(a1, b1)-1.5*MEAS_ARROW_LEN, 0); |
598 | free(s); |
599 | } |
600 | |
601 | |
602 | /* ----- frame ------------------------------------------------------------- */ |
603 | |
604 | |
605 | unit_type gui_dist_frame_eye(struct inst *self, struct coord pos, |
606 | unit_type scale) |
607 | { |
608 | unit_type d; |
609 | |
610 | d = dist_point(pos, self->base)/scale; |
611 | return d > FRAME_EYE_R2 ? -1 : d; |
612 | } |
613 | |
614 | |
615 | static unit_type dist_from_corner_line(struct inst *self, struct coord pos, |
616 | struct coord vec, unit_type scale) |
617 | { |
618 | struct coord ref; |
619 | |
620 | ref.x = self->bbox.min.x; |
621 | ref.y = self->bbox.max.y; |
622 | return dist_line(pos, ref, add_vec(ref, vec))/scale; |
623 | } |
624 | |
625 | |
626 | unit_type gui_dist_frame(struct inst *self, struct coord pos, unit_type scale) |
627 | { |
628 | unit_type d_min, d; |
629 | struct coord vec; |
630 | |
631 | d_min = dist_point(pos, self->base)/scale; |
632 | |
633 | vec.x = FRAME_SHORT_X*scale; |
634 | vec.y = 0; |
635 | d = dist_from_corner_line(self, pos, vec, scale); |
636 | if (d < d_min) |
637 | d_min = d; |
638 | |
639 | vec.x = 0; |
640 | vec.y = FRAME_SHORT_Y*scale; |
641 | d = dist_from_corner_line(self, pos, vec, scale); |
642 | if (d < d_min) |
643 | d_min = d; |
644 | |
645 | return d_min > SELECT_R ? -1 : d_min; |
646 | } |
647 | |
648 | |
649 | void gui_draw_frame(struct inst *self) |
650 | { |
651 | struct coord center = translate(self->base); |
652 | struct coord corner = { self->bbox.min.x, self->bbox.max.y }; |
653 | GdkGC *gc; |
654 | |
655 | gc = self->u.frame.active ? gc_active_frame : gc_frame[get_mode(self)]; |
656 | draw_eye(gc, center, FRAME_EYE_R1, FRAME_EYE_R2); |
657 | if (self->u.frame.ref == frames) |
658 | return; |
659 | corner = translate(corner); |
660 | corner.x -= FRAME_CLEARANCE; |
661 | corner.y -= FRAME_CLEARANCE; |
662 | gdk_draw_line(DA, gc, corner.x, corner.y, |
663 | corner.x+FRAME_SHORT_X, corner.y); |
664 | gdk_draw_line(DA, gc, corner.x, corner.y, |
665 | corner.x, corner.y+FRAME_SHORT_Y); |
666 | render_text(DA, gc, corner.x, corner.y, 0, self->u.frame.ref->name, |
667 | FRAME_FONT, 0, -FRAME_BASELINE_OFFSET, 0, 0); |
668 | } |
669 |
Branches:
master