Introduce POLYGONHOLE_MODE for creating holes in polygons
[geda-pcb/gde.git] / src / hid / gtk / gui-output-events.c
blob7230aa3ef6fa793b64bc90f533aa516d36a33603
1 /* $Id$ */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
29 /* This file written by Bill Wilson for the PCB Gtk port */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include "gui.h"
36 #include "gtkhid.h"
37 #include "hid/common/hid_resource.h"
39 #include <gdk/gdkkeysyms.h>
41 #include "action.h"
42 #include "crosshair.h"
43 #include "draw.h"
44 #include "error.h"
45 #include "misc.h"
46 #include "set.h"
47 #include "find.h"
48 #include "search.h"
49 #include "rats.h"
51 #ifdef HAVE_LIBDMALLOC
52 #include <dmalloc.h>
53 #endif
55 #define TOOLTIP_UPDATE_DELAY 200
57 RCSID ("$Id$");
59 static gint x_pan_speed, y_pan_speed;
61 /* Set to true if cursor is currently in viewport. This is a hack to prevent
62 * Crosshair stack corruption due to unmatching window enter / leave events */
63 gboolean cursor_in_viewport = false;
65 void
66 ghid_port_ranges_changed (void)
68 GtkAdjustment *h_adj, *v_adj;
70 if (!ghidgui->combine_adjustments)
71 HideCrosshair (FALSE);
72 if (ghidgui->combine_adjustments)
74 ghidgui->combine_adjustments = FALSE;
75 return;
78 ghidgui->need_restore_crosshair = TRUE;
80 h_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
81 v_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
82 gport->view_x0 = h_adj->value;
83 gport->view_y0 = v_adj->value;
85 ghid_invalidate_all ();
88 gboolean
89 ghid_port_ranges_pan (gdouble x, gdouble y, gboolean relative)
91 GtkAdjustment *h_adj, *v_adj;
92 gdouble x0, y0, x1, y1;
94 h_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
95 v_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
96 x0 = h_adj->value;
97 y0 = v_adj->value;
99 if (relative)
101 x1 = x0 + x;
102 y1 = y0 + y;
104 else
106 x1 = x;
107 y1 = y;
110 if (x1 < h_adj->lower)
111 x1 = h_adj->lower;
112 if (x1 > h_adj->upper - h_adj->page_size)
113 x1 = h_adj->upper - h_adj->page_size;
115 if (y1 < v_adj->lower)
116 y1 = v_adj->lower;
117 if (y1 > v_adj->upper - v_adj->page_size)
118 y1 = v_adj->upper - v_adj->page_size;
120 if (x0 != x1 && y0 != y1)
121 ghidgui->combine_adjustments = TRUE;
122 if (x0 != x1)
123 gtk_range_set_value (GTK_RANGE (ghidgui->h_range), x1);
124 if (y0 != y1)
125 gtk_range_set_value (GTK_RANGE (ghidgui->v_range), y1);
127 ghid_note_event_location (NULL);
128 return ((x0 != x1) || (y0 != y1));
131 /* Do scrollbar scaling based on current port drawing area size and
132 | overall PCB board size.
134 void
135 ghid_port_ranges_scale (gboolean emit_changed)
137 GtkAdjustment *adj;
139 /* Update the scrollbars with PCB units. So Scale the current
140 | drawing area size in pixels to PCB units and that will be
141 | the page size for the Gtk adjustment.
143 gport->view_width = gport->width * gport->zoom;
144 gport->view_height = gport->height * gport->zoom;
146 if (gport->view_width >= PCB->MaxWidth)
147 gport->view_width = PCB->MaxWidth;
148 if (gport->view_height >= PCB->MaxHeight)
149 gport->view_height = PCB->MaxHeight;
151 adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
152 adj->page_size = gport->view_width;
153 adj->page_increment = adj->page_size/10.0;
154 adj->step_increment = adj->page_size/100.0;
155 adj->upper = PCB->MaxWidth;
156 if (emit_changed)
157 gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
159 adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
160 adj->page_size = gport->view_height;
161 adj->page_increment = adj->page_size/10.0;
162 adj->step_increment = adj->page_size/100.0;
163 adj->upper = PCB->MaxHeight;
164 if (emit_changed)
165 gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
168 void
169 ghid_port_ranges_zoom (gdouble zoom)
171 gdouble xtmp, ytmp;
172 gint x0, y0;
174 /* figure out zoom values in that would just make the width fit and
175 * that would just make the height fit
177 xtmp = (gdouble) PCB->MaxWidth / gport->width;
178 ytmp = (gdouble) PCB->MaxHeight / gport->height;
180 /* if we've tried to zoom further out than what would make the
181 * entire board fit or we passed 0, then pick a zoom that just makes
182 * the board fit.
184 if ((zoom > xtmp && zoom > ytmp) || zoom == 0.0)
185 zoom = (xtmp > ytmp) ? xtmp : ytmp;
187 xtmp = (gport->view_x - gport->view_x0) / (gdouble) gport->view_width;
188 ytmp = (gport->view_y - gport->view_y0) / (gdouble) gport->view_height;
190 gport->zoom = zoom;
191 pixel_slop = zoom;
192 ghid_port_ranges_scale(FALSE);
194 x0 = gport->view_x - xtmp * gport->view_width;
195 if (x0 < 0)
196 x0 = 0;
197 gport->view_x0 = x0;
199 y0 = gport->view_y - ytmp * gport->view_height;
200 if (y0 < 0)
201 y0 = 0;
202 gport->view_y0 = y0;
204 ghidgui->adjustment_changed_holdoff = TRUE;
205 gtk_range_set_value (GTK_RANGE (ghidgui->h_range), gport->view_x0);
206 gtk_range_set_value (GTK_RANGE (ghidgui->v_range), gport->view_y0);
207 ghidgui->adjustment_changed_holdoff = FALSE;
209 ghid_port_ranges_changed();
213 /* ----------------------------------------------------------------------
214 * handles all events from PCB drawing area
217 static gint event_x, event_y;
219 void
220 ghid_get_coords (const char *msg, int *x, int *y)
222 if (!ghid_port.has_entered && msg)
223 ghid_get_user_xy (msg);
224 if (ghid_port.has_entered)
226 *x = SIDE_X (gport->view_x);
227 *y = SIDE_Y (gport->view_y);
231 gboolean
232 ghid_note_event_location (GdkEventButton * ev)
234 gint x, y;
235 gboolean moved;
237 if (!ev)
239 gdk_window_get_pointer (ghid_port.drawing_area->window, &x, &y, NULL);
240 event_x = x;
241 event_y = y;
243 else
245 event_x = ev->x;
246 event_y = ev->y;
248 gport->view_x = event_x * gport->zoom + gport->view_x0;
249 gport->view_y = event_y * gport->zoom + gport->view_y0;
251 moved = MoveCrosshairAbsolute (SIDE_X (gport->view_x),
252 SIDE_Y (gport->view_y));
253 if (moved)
255 AdjustAttachedObjects ();
256 RestoreCrosshair (false);
258 ghid_set_cursor_position_labels ();
259 return moved;
262 static gboolean
263 have_crosshair_attachments (void)
265 gboolean result = FALSE;
267 switch (Settings.Mode)
269 case COPY_MODE:
270 case MOVE_MODE:
271 case INSERTPOINT_MODE:
272 if (Crosshair.AttachedObject.Type != NO_TYPE)
273 result = TRUE;
274 break;
275 case PASTEBUFFER_MODE:
276 case VIA_MODE:
277 result = TRUE;
278 break;
279 case POLYGON_MODE:
280 case POLYGONHOLE_MODE:
281 if (Crosshair.AttachedLine.State != STATE_FIRST)
282 result = TRUE;
283 break;
284 case ARC_MODE:
285 if (Crosshair.AttachedBox.State != STATE_FIRST)
286 result = TRUE;
287 break;
288 case LINE_MODE:
289 if (Crosshair.AttachedLine.State != STATE_FIRST)
290 result = TRUE;
291 break;
292 default:
293 if (Crosshair.AttachedBox.State == STATE_SECOND
294 || Crosshair.AttachedBox.State == STATE_THIRD)
295 result = TRUE;
296 break;
298 return result;
302 #define VCW 16
303 #define VCD 8
305 static void
306 draw_right_cross (GdkGC *xor_gc, gint x, gint y)
308 gdk_draw_line (gport->drawing_area->window, xor_gc,
309 x, 0, x, gport->height);
310 gdk_draw_line (gport->drawing_area->window, xor_gc,
311 0, y, gport->width, y);
314 static void
315 draw_slanted_cross (GdkGC *xor_gc, gint x, gint y)
317 gint x0, y0, x1, y1;
319 x0 = x + (gport->height - y);
320 x0 = MAX(0, MIN (x0, gport->width));
321 x1 = x - y;
322 x1 = MAX(0, MIN (x1, gport->width));
323 y0 = y + (gport->width - x);
324 y0 = MAX(0, MIN (y0, gport->height));
325 y1 = y - x;
326 y1 = MAX(0, MIN (y1, gport->height));
327 gdk_draw_line (gport->drawing_area->window, xor_gc,
328 x0, y0, x1, y1);
329 x0 = x - (gport->height - y);
330 x0 = MAX(0, MIN (x0, gport->width));
331 x1 = x + y;
332 x1 = MAX(0, MIN (x1, gport->width));
333 y0 = y + x;
334 y0 = MAX(0, MIN (y0, gport->height));
335 y1 = y - (gport->width - x);
336 y1 = MAX(0, MIN (y1, gport->height));
337 gdk_draw_line (gport->drawing_area->window, xor_gc,
338 x0, y0, x1, y1);
341 static void
342 draw_dozen_cross (GdkGC *xor_gc, gint x, gint y)
344 gint x0, y0, x1, y1;
345 gdouble tan60 = sqrt (3);
347 x0 = x + (gport->height - y) / tan60;
348 x0 = MAX(0, MIN (x0, gport->width));
349 x1 = x - y / tan60;
350 x1 = MAX(0, MIN (x1, gport->width));
351 y0 = y + (gport->width - x) * tan60;
352 y0 = MAX(0, MIN (y0, gport->height));
353 y1 = y - x * tan60;
354 y1 = MAX(0, MIN (y1, gport->height));
355 gdk_draw_line (gport->drawing_area->window, xor_gc,
356 x0, y0, x1, y1);
358 x0 = x + (gport->height - y) * tan60;
359 x0 = MAX(0, MIN (x0, gport->width));
360 x1 = x - y * tan60;
361 x1 = MAX(0, MIN (x1, gport->width));
362 y0 = y + (gport->width - x) / tan60;
363 y0 = MAX(0, MIN (y0, gport->height));
364 y1 = y - x / tan60;
365 y1 = MAX(0, MIN (y1, gport->height));
366 gdk_draw_line (gport->drawing_area->window, xor_gc,
367 x0, y0, x1, y1);
369 x0 = x - (gport->height - y) / tan60;
370 x0 = MAX(0, MIN (x0, gport->width));
371 x1 = x + y / tan60;
372 x1 = MAX(0, MIN (x1, gport->width));
373 y0 = y + x * tan60;
374 y0 = MAX(0, MIN (y0, gport->height));
375 y1 = y - (gport->width - x) * tan60;
376 y1 = MAX(0, MIN (y1, gport->height));
377 gdk_draw_line (gport->drawing_area->window, xor_gc,
378 x0, y0, x1, y1);
380 x0 = x - (gport->height - y) * tan60;
381 x0 = MAX(0, MIN (x0, gport->width));
382 x1 = x + y * tan60;
383 x1 = MAX(0, MIN (x1, gport->width));
384 y0 = y + x / tan60;
385 y0 = MAX(0, MIN (y0, gport->height));
386 y1 = y - (gport->width - x) / tan60;
387 y1 = MAX(0, MIN (y1, gport->height));
388 gdk_draw_line (gport->drawing_area->window, xor_gc,
389 x0, y0, x1, y1);
392 static void
393 draw_crosshair (GdkGC *xor_gc, gint x, gint y)
395 static enum crosshair_shape prev = Basic_Crosshair_Shape;
397 draw_right_cross (xor_gc, x, y);
398 if (prev == Union_Jack_Crosshair_Shape)
399 draw_slanted_cross (xor_gc, x, y);
400 if (prev == Dozen_Crosshair_Shape)
401 draw_dozen_cross (xor_gc, x, y);
402 prev = Crosshair.shape;
405 void
406 ghid_show_crosshair (gboolean show)
408 gint x, y;
409 static gint x_prev = -1, y_prev = -1;
410 static GdkGC *xor_gc;
411 static GdkColor cross_color;
413 if (gport->x_crosshair < 0 || ghidgui->creating || !gport->has_entered)
414 return;
416 if (!xor_gc)
418 xor_gc = gdk_gc_new (ghid_port.drawing_area->window);
419 gdk_gc_copy (xor_gc, ghid_port.drawing_area->style->white_gc);
420 gdk_gc_set_function (xor_gc, GDK_XOR);
421 /* FIXME: when CrossColor changed from config */
422 ghid_map_color_string (Settings.CrossColor, &cross_color);
424 x = DRAW_X (gport->x_crosshair);
425 y = DRAW_Y (gport->y_crosshair);
427 gdk_gc_set_foreground (xor_gc, &cross_color);
429 if (x_prev >= 0)
431 draw_crosshair (xor_gc, x_prev, y_prev);
432 if (ghidgui->auto_pan_on && have_crosshair_attachments ())
434 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
435 0, y_prev - VCD, VCD, VCW);
436 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
437 gport->width - VCD, y_prev - VCD, VCD, VCW);
438 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
439 x_prev - VCD, 0, VCW, VCD);
440 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
441 x_prev - VCD, gport->height - VCD, VCW, VCD);
445 if (x >= 0 && show)
447 draw_crosshair (xor_gc, x, y);
448 if (ghidgui->auto_pan_on && have_crosshair_attachments ())
450 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
451 0, y - VCD, VCD, VCW);
452 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
453 gport->width - VCD, y - VCD, VCD, VCW);
454 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
455 x - VCD, 0, VCW, VCD);
456 gdk_draw_rectangle (gport->drawing_area->window, xor_gc, TRUE,
457 x - VCD, gport->height - VCD, VCW, VCD);
459 x_prev = x;
460 y_prev = y;
462 else
463 x_prev = y_prev = -1;
466 static gboolean
467 ghid_idle_cb (gpointer data)
469 if (Settings.Mode == NO_MODE)
470 SetMode (ARROW_MODE);
471 ghid_mode_cursor (Settings.Mode);
472 if (ghidgui->settings_mode != Settings.Mode)
474 ghid_mode_buttons_update ();
476 ghidgui->settings_mode = Settings.Mode;
478 ghid_update_toggle_flags ();
479 return FALSE;
482 gboolean
483 ghid_port_key_release_cb (GtkWidget * drawing_area, GdkEventKey * kev,
484 GtkUIManager * ui)
486 gint ksym = kev->keyval;
488 if (ghid_is_modifier_key_sym (ksym))
489 ghid_note_event_location (NULL);
491 HideCrosshair (TRUE);
492 AdjustAttachedObjects ();
493 ghid_invalidate_all ();
494 RestoreCrosshair (TRUE);
495 ghid_screen_update ();
496 g_idle_add (ghid_idle_cb, NULL);
497 return FALSE;
500 /* Handle user keys in the output drawing area.
501 * Note that the default is for all hotkeys to be handled by the
502 * menu accelerators.
504 * Key presses not handled by the menus will show up here. This means
505 * the key press was either not defined in the menu resource file or
506 * that the key press is special in that gtk doesn't allow the normal
507 * menu code to ever see it. We capture those here (like Tab and the
508 * arrow keys) and feed it back to the normal menu callback.
511 gboolean
512 ghid_port_key_press_cb (GtkWidget * drawing_area,
513 GdkEventKey * kev, GtkUIManager * ui)
515 ModifierKeysState mk;
516 gint ksym = kev->keyval;
517 gboolean handled;
518 extern void ghid_hotkey_cb (int);
519 GdkModifierType state;
521 if (ghid_is_modifier_key_sym (ksym))
522 ghid_note_event_location (NULL);
524 state = (GdkModifierType) (kev->state);
525 mk = ghid_modifier_keys_state (&state);
527 ghid_show_crosshair (FALSE);
529 handled = TRUE; /* Start off assuming we handle it */
530 switch (ksym)
532 case GDK_Alt_L:
533 case GDK_Alt_R:
534 case GDK_Control_L:
535 case GDK_Control_R:
536 case GDK_Shift_L:
537 case GDK_Shift_R:
538 case GDK_Shift_Lock:
539 break;
541 case GDK_Up:
542 ghid_hotkey_cb (GHID_KEY_UP);
543 break;
545 case GDK_Down:
546 ghid_hotkey_cb (GHID_KEY_DOWN);
547 break;
548 case GDK_Left:
549 ghid_hotkey_cb (GHID_KEY_LEFT);
550 break;
551 case GDK_Right:
552 ghid_hotkey_cb (GHID_KEY_RIGHT);
553 break;
555 case GDK_ISO_Left_Tab:
556 case GDK_3270_BackTab:
557 switch (mk)
559 case NONE_PRESSED:
560 ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
561 break;
562 case CONTROL_PRESSED:
563 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
564 break;
565 case MOD1_PRESSED:
566 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
567 break;
568 case SHIFT_PRESSED:
569 ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
570 break;
571 case SHIFT_CONTROL_PRESSED:
572 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
573 break;
574 case SHIFT_MOD1_PRESSED:
575 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
576 break;
578 default:
579 handled = FALSE;
580 break;
582 break;
584 case GDK_Tab:
585 switch (mk)
587 case NONE_PRESSED:
588 ghid_hotkey_cb (GHID_KEY_TAB);
589 break;
590 case CONTROL_PRESSED:
591 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_TAB);
592 break;
593 case MOD1_PRESSED:
594 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_TAB);
595 break;
596 case SHIFT_PRESSED:
597 ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
598 break;
599 case SHIFT_CONTROL_PRESSED:
600 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
601 break;
602 case SHIFT_MOD1_PRESSED:
603 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
604 break;
606 default:
607 handled = FALSE;
608 break;
610 break;
612 default:
613 handled = FALSE;
616 return handled;
619 gboolean
620 ghid_port_button_press_cb (GtkWidget * drawing_area,
621 GdkEventButton * ev, GtkUIManager * ui)
623 ModifierKeysState mk;
624 gboolean drag;
625 GdkModifierType state;
627 /* Reject double and triple click events */
628 if (ev->type != GDK_BUTTON_PRESS) return TRUE;
630 ghid_note_event_location (ev);
631 state = (GdkModifierType) (ev->state);
632 mk = ghid_modifier_keys_state (&state);
633 ghid_show_crosshair (FALSE);
634 HideCrosshair (TRUE);
635 drag = have_crosshair_attachments ();
637 do_mouse_action(ev->button, mk);
639 ghid_invalidate_all ();
640 RestoreCrosshair (TRUE);
641 ghid_set_status_line_label ();
642 ghid_show_crosshair (TRUE);
643 if (!gport->panning)
644 g_idle_add (ghid_idle_cb, NULL);
645 return TRUE;
649 gboolean
650 ghid_port_button_release_cb (GtkWidget * drawing_area,
651 GdkEventButton * ev, GtkUIManager * ui)
653 ModifierKeysState mk;
654 gboolean drag;
655 GdkModifierType state;
657 ghid_note_event_location (ev);
658 state = (GdkModifierType) (ev->state);
659 mk = ghid_modifier_keys_state (&state);
661 drag = have_crosshair_attachments ();
662 if (drag)
663 HideCrosshair (TRUE);
665 do_mouse_action(ev->button, mk + M_Release);
667 if (drag)
669 AdjustAttachedObjects ();
670 ghid_invalidate_all ();
671 RestoreCrosshair (TRUE);
672 ghid_screen_update ();
674 ghid_set_status_line_label ();
675 g_idle_add (ghid_idle_cb, NULL);
676 return TRUE;
680 gboolean
681 ghid_port_drawing_area_configure_event_cb (GtkWidget * widget,
682 GdkEventConfigure * ev,
683 GHidPort * out)
685 static gboolean first_time_done;
687 HideCrosshair (TRUE);
688 gport->width = ev->width;
689 gport->height = ev->height;
691 if (gport->pixmap)
692 gdk_pixmap_unref (gport->pixmap);
694 gport->pixmap = gdk_pixmap_new (widget->window,
695 gport->width, gport->height, -1);
696 gport->drawable = gport->pixmap;
698 if (!first_time_done)
700 gport->colormap = gtk_widget_get_colormap (gport->top_window);
701 gport->bg_gc = gdk_gc_new (gport->drawable);
702 if (gdk_color_parse (Settings.BackgroundColor, &gport->bg_color))
703 gdk_color_alloc (gport->colormap, &gport->bg_color);
704 else
705 gdk_color_white (gport->colormap, &gport->bg_color);
706 gdk_gc_set_foreground (gport->bg_gc, &gport->bg_color);
708 gport->offlimits_gc = gdk_gc_new (gport->drawable);
709 if (gdk_color_parse (Settings.OffLimitColor, &gport->offlimits_color))
710 gdk_color_alloc (gport->colormap, &gport->offlimits_color);
711 else
712 gdk_color_white (gport->colormap, &gport->offlimits_color);
713 gdk_gc_set_foreground (gport->offlimits_gc, &gport->offlimits_color);
714 first_time_done = TRUE;
715 PCBChanged (0, NULL, 0, 0);
717 if (gport->mask)
719 gdk_pixmap_unref (gport->mask);
720 gport->mask = gdk_pixmap_new (0, gport->width, gport->height, 1);
722 ghid_port_ranges_scale (FALSE);
723 ghid_invalidate_all ();
724 RestoreCrosshair (TRUE);
725 return 0;
729 void
730 ghid_screen_update (void)
733 ghid_show_crosshair (FALSE);
734 gdk_draw_drawable (gport->drawing_area->window, gport->bg_gc, gport->pixmap,
735 0, 0, 0, 0, gport->width, gport->height);
736 ghid_show_crosshair (TRUE);
739 gboolean
740 ghid_port_drawing_area_expose_event_cb (GtkWidget * widget,
741 GdkEventExpose * ev, GHidPort * port)
743 ghid_show_crosshair (FALSE);
744 gdk_draw_drawable (widget->window, port->bg_gc, port->pixmap,
745 ev->area.x, ev->area.y, ev->area.x, ev->area.y,
746 ev->area.width, ev->area.height);
747 ghid_show_crosshair (TRUE);
748 return FALSE;
751 #if GTK_CHECK_VERSION(2,12,0)
752 # define ENABLE_TOOLTIPS 1
753 #else
754 # define ENABLE_TOOLTIPS 0
755 #endif
757 #if ENABLE_TOOLTIPS
758 static char *
759 describe_location (LocationType X, LocationType Y)
761 void *ptr1, *ptr2, *ptr3;
762 int type;
763 int Range = 0;
764 char *elename = "";
765 char *pinname;
766 char *netname = NULL;
767 char *description;
769 /* check if there are any pins or pads at that position */
771 type = SearchObjectByLocation (PIN_TYPE | PAD_TYPE,
772 &ptr1, &ptr2, &ptr3, X, Y, Range);
773 if (type == NO_TYPE)
774 return NULL;
776 /* don't mess with silk objects! */
777 if (type & SILK_TYPE &&
778 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1) >= max_layer)
779 return NULL;
781 if (type == PIN_TYPE || type == PAD_TYPE)
782 elename = UNKNOWN (NAMEONPCB_NAME ((ElementTypePtr) ptr1));
784 pinname = ConnectionName (type, ptr1, ptr2);
786 if (pinname == NULL)
787 return NULL;
789 /* Find netlist entry */
790 MENU_LOOP (&PCB->NetlistLib);
792 if (!menu->Name)
793 continue;
795 ENTRY_LOOP (menu);
797 if (!entry->ListEntry)
798 continue;
800 if (strcmp (entry->ListEntry, pinname) == 0) {
801 netname = g_strdup (menu->Name);
802 /* For some reason, the netname has spaces in front of it, strip them */
803 g_strstrip (netname);
804 break;
807 END_LOOP;
809 if (netname != NULL)
810 break;
812 END_LOOP;
814 description = g_strdup_printf ("Element name: %s\n"
815 "Pinname : %s\n"
816 "Netname : %s",
817 elename,
818 (pinname != NULL) ? pinname : "--",
819 (netname != NULL) ? netname : "--");
821 g_free (netname);
823 return description;
827 static gboolean check_object_tooltips (GHidPort *out)
829 char *description;
831 /* check if there are any pins or pads at that position */
832 description = describe_location (out->x_crosshair, out->y_crosshair);
834 if (description == NULL)
835 return FALSE;
837 gtk_widget_set_tooltip_text (out->drawing_area, description);
838 g_free (description);
840 return FALSE;
843 static int tooltip_update_timeout_id = 0;
845 static void
846 cancel_tooltip_update ()
848 if (tooltip_update_timeout_id)
849 g_source_remove (tooltip_update_timeout_id);
850 tooltip_update_timeout_id = 0;
853 /* FIXME: If the GHidPort is ever destroyed, we must call
854 * cancel_tooltip_update (), otherwise the timeout might
855 * fire after the data it utilises has been free'd.
857 static void
858 queue_tooltip_update (GHidPort *out)
860 /* Zap the old tool-tip text and force it to be removed from the screen */
861 gtk_widget_set_tooltip_text (out->drawing_area, NULL);
862 gtk_widget_trigger_tooltip_query (out->drawing_area);
864 cancel_tooltip_update ();
866 tooltip_update_timeout_id =
867 g_timeout_add (TOOLTIP_UPDATE_DELAY,
868 (GSourceFunc) check_object_tooltips,
869 out);
871 #endif
873 gint
874 ghid_port_window_motion_cb (GtkWidget * widget,
875 GdkEventButton * ev, GHidPort * out)
877 gdouble dx, dy;
878 static gint x_prev = -1, y_prev = -1;
879 gboolean moved;
881 if (out->panning)
883 if (gtk_events_pending ())
884 return FALSE;
885 dx = gport->zoom * (x_prev - ev->x);
886 dy = gport->zoom * (y_prev - ev->y);
887 if (x_prev > 0)
888 ghid_port_ranges_pan (dx, dy, TRUE);
889 x_prev = ev->x;
890 y_prev = ev->y;
891 return FALSE;
893 x_prev = y_prev = -1;
894 moved = ghid_note_event_location (ev);
896 #if ENABLE_TOOLTIPS
897 queue_tooltip_update (out);
898 #endif
900 ghid_show_crosshair (TRUE);
901 if (moved && have_crosshair_attachments ())
902 ghid_draw_area_update (gport, NULL);
903 return FALSE;
906 gint
907 ghid_port_window_enter_cb (GtkWidget * widget,
908 GdkEventCrossing * ev, GHidPort * out)
910 /* printf("enter: mode: %d detail: %d\n", ev->mode, ev->detail); */
912 /* See comment in ghid_port_window_leave_cb() */
914 if(ev->mode != GDK_CROSSING_NORMAL && ev->detail != GDK_NOTIFY_NONLINEAR)
916 return FALSE;
919 if (!ghidgui->command_entry_status_line_active)
921 out->has_entered = TRUE;
922 /* Make sure drawing area has keyboard focus when we are in it.
924 gtk_widget_grab_focus (out->drawing_area);
926 ghidgui->in_popup = FALSE;
928 /* Following expression is true if a you open a menu from the menu bar,
929 * move the mouse to the viewport and click on it. This closes the menu
930 * and moves the pointer to the viewport without the pointer going over
931 * the edge of the viewport */
932 if(ev->mode == GDK_CROSSING_UNGRAB && ev->detail == GDK_NOTIFY_NONLINEAR)
934 ghid_screen_update ();
937 if(! cursor_in_viewport)
939 RestoreCrosshair (TRUE);
940 cursor_in_viewport = TRUE;
943 return FALSE;
946 static gboolean
947 ghid_pan_idle_cb (gpointer data)
949 gdouble dx = 0, dy = 0;
951 if (gport->has_entered)
952 return FALSE;
953 dy = gport->zoom * y_pan_speed;
954 dx = gport->zoom * x_pan_speed;
955 return (ghid_port_ranges_pan (dx, dy, TRUE));
958 gint
959 ghid_port_window_leave_cb (GtkWidget * widget,
960 GdkEventCrossing * ev, GHidPort * out)
962 gint x0, y0, x, y, dx, dy, w, h;
964 /* printf("leave mode: %d detail: %d\n", ev->mode, ev->detail); */
966 /* Window leave events can also be triggered because of focus grabs. Some
967 * X applications occasionally grab the focus and so trigger this function.
968 * At least GNOME's window manager is known to do this on every mouse click.
970 * See http://bugzilla.gnome.org/show_bug.cgi?id=102209
973 if(ev->mode != GDK_CROSSING_NORMAL)
975 return FALSE;
978 if(out->has_entered && !ghidgui->in_popup)
980 /* if actively drawing, start scrolling */
982 if (have_crosshair_attachments () && ghidgui->auto_pan_on)
984 /* GdkEvent coords are set to 0,0 at leave events, so must figure
985 | out edge the cursor left.
987 w = ghid_port.width * gport->zoom;
988 h = ghid_port.height * gport->zoom;
990 x0 = VIEW_X (0);
991 y0 = VIEW_Y (0);
992 ghid_get_coords (NULL, &x, &y);
993 x -= x0;
994 y -= y0;
996 if (ghid_flip_x )
997 x = -x;
998 if (ghid_flip_y )
999 y = -y;
1001 dx = w - x;
1002 dy = h - y;
1004 x_pan_speed = y_pan_speed = 2 * ghidgui->auto_pan_speed;
1006 if (x < dx)
1008 x_pan_speed = -x_pan_speed;
1009 dx = x;
1011 if (y < dy)
1013 y_pan_speed = -y_pan_speed;
1014 dy = y;
1016 if (dx < dy)
1018 if (dy < h / 3)
1019 y_pan_speed = y_pan_speed - (3 * dy * y_pan_speed) / h;
1020 else
1021 y_pan_speed = 0;
1023 else
1025 if (dx < w / 3)
1026 x_pan_speed = x_pan_speed - (3 * dx * x_pan_speed) / w;
1027 else
1028 x_pan_speed = 0;
1030 g_idle_add (ghid_pan_idle_cb, NULL);
1034 if(cursor_in_viewport)
1036 HideCrosshair (TRUE);
1037 cursor_in_viewport = FALSE;
1040 ghid_show_crosshair (FALSE);
1041 out->has_entered = FALSE;
1043 ghid_screen_update ();
1045 return FALSE;
1049 /* Mouse scroll wheel events
1051 gint
1052 ghid_port_window_mouse_scroll_cb (GtkWidget * widget,
1053 GdkEventScroll * ev, GHidPort * out)
1055 ModifierKeysState mk;
1056 GdkModifierType state;
1057 int button;
1059 state = (GdkModifierType) (ev->state);
1060 mk = ghid_modifier_keys_state (&state);
1062 /* X11 gtk hard codes buttons 4, 5, 6, 7 as below in
1063 * gtk+/gdk/x11/gdkevents-x11.c:1121, but quartz and windows have
1064 * special mouse scroll events, so this may conflict with a mouse
1065 * who has buttons 4 - 7 that aren't the scroll wheel?
1067 switch(ev->direction)
1069 case GDK_SCROLL_UP: button = 4; break;
1070 case GDK_SCROLL_DOWN: button = 5; break;
1071 case GDK_SCROLL_LEFT: button = 6; break;
1072 case GDK_SCROLL_RIGHT: button = 7; break;
1073 default: button = -1;
1076 do_mouse_action(button, mk);
1078 return TRUE;