Regenerate tests after "Make xy file output IPC 7531 compliant.".
[geda-pcb/kupson.git] / src / hid / gtk / gui-output-events.c
blob2ca39de97e282941c8799dcea4a470c4971efdcf
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
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.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
27 /* This file written by Bill Wilson for the PCB Gtk port */
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
33 #include "gui.h"
34 #include "gtkhid.h"
35 #include "hid/common/hid_resource.h"
37 #include <gdk/gdkkeysyms.h>
39 #include "action.h"
40 #include "crosshair.h"
41 #include "draw.h"
42 #include "error.h"
43 #include "misc.h"
44 #include "set.h"
45 #include "find.h"
46 #include "search.h"
47 #include "rats.h"
49 #ifdef HAVE_LIBDMALLOC
50 #include <dmalloc.h>
51 #endif
53 #define TOOLTIP_UPDATE_DELAY 200
55 void
56 ghid_port_ranges_changed (void)
58 GtkAdjustment *h_adj, *v_adj;
60 h_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
61 v_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
62 gport->view.x0 = gtk_adjustment_get_value (h_adj);
63 gport->view.y0 = gtk_adjustment_get_value (v_adj);
65 ghid_invalidate_all ();
68 /* Do scrollbar scaling based on current port drawing area size and
69 | overall PCB board size.
71 void
72 ghid_port_ranges_scale (void)
74 GtkAdjustment *adj;
75 gdouble page_size;
77 /* Update the scrollbars with PCB units. So Scale the current
78 | drawing area size in pixels to PCB units and that will be
79 | the page size for the Gtk adjustment.
81 gport->view.width = gport->width * gport->view.coord_per_px;
82 gport->view.height = gport->height * gport->view.coord_per_px;
84 adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
85 page_size = MIN (gport->view.width, PCB->MaxWidth);
86 gtk_adjustment_configure (adj,
87 gtk_adjustment_get_value (adj), /* value */
88 -gport->view.width, /* lower */
89 PCB->MaxWidth + page_size, /* upper */
90 page_size / 100.0, /* step_increment */
91 page_size / 10.0, /* page_increment */
92 page_size); /* page_size */
94 adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
95 page_size = MIN (gport->view.height, PCB->MaxHeight);
96 gtk_adjustment_configure (adj,
97 gtk_adjustment_get_value (adj), /* value */
98 -gport->view.height, /* lower */
99 PCB->MaxHeight + page_size, /* upper */
100 page_size / 100.0, /* step_increment */
101 page_size / 10.0, /* page_increment */
102 page_size); /* page_size */
106 /* ----------------------------------------------------------------------
107 * handles all events from PCB drawing area
110 void
111 ghid_get_coords (const char *msg, Coord *x, Coord *y)
113 if (!ghid_port.has_entered && msg)
114 ghid_get_user_xy (msg);
115 if (ghid_port.has_entered)
117 *x = gport->pcb_x;
118 *y = gport->pcb_y;
122 gboolean
123 ghid_note_event_location (GdkEventButton * ev)
125 gint event_x, event_y;
126 gboolean moved;
128 if (!ev)
130 gdk_window_get_pointer (gtk_widget_get_window (ghid_port.drawing_area),
131 &event_x, &event_y, NULL);
133 else
135 event_x = ev->x;
136 event_y = ev->y;
139 ghid_event_to_pcb_coords (event_x, event_y, &gport->pcb_x, &gport->pcb_y);
141 moved = MoveCrosshairAbsolute (gport->pcb_x, gport->pcb_y);
142 if (moved)
144 AdjustAttachedObjects ();
145 notify_crosshair_change (true);
147 ghid_set_cursor_position_labels ();
148 return moved;
151 static gboolean
152 ghid_idle_cb (gpointer data)
154 if (Settings.Mode == NO_MODE)
155 SetMode (ARROW_MODE);
156 ghid_mode_cursor (Settings.Mode);
157 if (ghidgui->settings_mode != Settings.Mode)
159 ghid_mode_buttons_update ();
161 ghidgui->settings_mode = Settings.Mode;
163 ghid_update_toggle_flags ();
164 return FALSE;
167 gboolean
168 ghid_port_key_release_cb (GtkWidget * drawing_area, GdkEventKey * kev,
169 gpointer data)
171 gint ksym = kev->keyval;
173 if (ghid_is_modifier_key_sym (ksym))
174 ghid_note_event_location (NULL);
176 AdjustAttachedObjects ();
177 ghid_invalidate_all ();
178 g_idle_add (ghid_idle_cb, NULL);
179 return FALSE;
182 /* Handle user keys in the output drawing area.
183 * Note that the default is for all hotkeys to be handled by the
184 * menu accelerators.
186 * Key presses not handled by the menus will show up here. This means
187 * the key press was either not defined in the menu resource file or
188 * that the key press is special in that gtk doesn't allow the normal
189 * menu code to ever see it. We capture those here (like Tab and the
190 * arrow keys) and feed it back to the normal menu callback.
193 gboolean
194 ghid_port_key_press_cb (GtkWidget * drawing_area,
195 GdkEventKey * kev, gpointer data)
197 ModifierKeysState mk;
198 gint ksym = kev->keyval;
199 gboolean handled;
200 extern void ghid_hotkey_cb (int);
201 GdkModifierType state;
203 if (ghid_is_modifier_key_sym (ksym))
204 ghid_note_event_location (NULL);
206 state = (GdkModifierType) (kev->state);
207 mk = ghid_modifier_keys_state (&state);
209 handled = TRUE; /* Start off assuming we handle it */
210 switch (ksym)
212 case GDK_Alt_L:
213 case GDK_Alt_R:
214 case GDK_Control_L:
215 case GDK_Control_R:
216 case GDK_Shift_L:
217 case GDK_Shift_R:
218 case GDK_Shift_Lock:
219 case GDK_ISO_Level3_Shift:
220 break;
222 case GDK_Up:
223 ghid_hotkey_cb (GHID_KEY_UP);
224 break;
226 case GDK_Down:
227 ghid_hotkey_cb (GHID_KEY_DOWN);
228 break;
229 case GDK_Left:
230 ghid_hotkey_cb (GHID_KEY_LEFT);
231 break;
232 case GDK_Right:
233 ghid_hotkey_cb (GHID_KEY_RIGHT);
234 break;
236 case GDK_ISO_Left_Tab:
237 case GDK_3270_BackTab:
238 switch (mk)
240 case NONE_PRESSED:
241 ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
242 break;
243 case CONTROL_PRESSED:
244 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
245 break;
246 case MOD1_PRESSED:
247 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
248 break;
249 case SHIFT_PRESSED:
250 ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
251 break;
252 case SHIFT_CONTROL_PRESSED:
253 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
254 break;
255 case SHIFT_MOD1_PRESSED:
256 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
257 break;
259 default:
260 handled = FALSE;
261 break;
263 break;
265 case GDK_Tab:
266 switch (mk)
268 case NONE_PRESSED:
269 ghid_hotkey_cb (GHID_KEY_TAB);
270 break;
271 case CONTROL_PRESSED:
272 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_TAB);
273 break;
274 case MOD1_PRESSED:
275 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_TAB);
276 break;
277 case SHIFT_PRESSED:
278 ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
279 break;
280 case SHIFT_CONTROL_PRESSED:
281 ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
282 break;
283 case SHIFT_MOD1_PRESSED:
284 ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
285 break;
287 default:
288 handled = FALSE;
289 break;
291 break;
293 default:
294 handled = FALSE;
297 return handled;
300 gboolean
301 ghid_port_button_press_cb (GtkWidget * drawing_area,
302 GdkEventButton * ev, gpointer data)
304 ModifierKeysState mk;
305 GdkModifierType state;
307 /* Reject double and triple click events */
308 if (ev->type != GDK_BUTTON_PRESS) return TRUE;
310 ghid_note_event_location (ev);
311 state = (GdkModifierType) (ev->state);
312 mk = ghid_modifier_keys_state (&state);
314 do_mouse_action(ev->button, mk);
316 ghid_invalidate_all ();
317 ghid_window_set_name_label (PCB->Name);
318 ghid_set_status_line_label ();
319 if (!gport->panning)
320 g_idle_add (ghid_idle_cb, NULL);
321 return TRUE;
325 gboolean
326 ghid_port_button_release_cb (GtkWidget * drawing_area,
327 GdkEventButton * ev, gpointer data)
329 ModifierKeysState mk;
330 GdkModifierType state;
332 ghid_note_event_location (ev);
333 state = (GdkModifierType) (ev->state);
334 mk = ghid_modifier_keys_state (&state);
336 do_mouse_action(ev->button, mk + M_Release);
338 AdjustAttachedObjects ();
339 ghid_invalidate_all ();
341 ghid_window_set_name_label (PCB->Name);
342 ghid_set_status_line_label ();
343 g_idle_add (ghid_idle_cb, NULL);
344 return TRUE;
348 gboolean
349 ghid_port_drawing_area_configure_event_cb (GtkWidget * widget,
350 GdkEventConfigure * ev,
351 GHidPort * out)
353 static gboolean first_time_done;
355 gport->width = ev->width;
356 gport->height = ev->height;
358 if (gport->pixmap)
359 gdk_pixmap_unref (gport->pixmap);
361 gport->pixmap = gdk_pixmap_new (gtk_widget_get_window (widget),
362 gport->width, gport->height, -1);
363 gport->drawable = gport->pixmap;
365 if (!first_time_done)
367 gport->colormap = gtk_widget_get_colormap (gport->top_window);
368 if (gdk_color_parse (Settings.BackgroundColor, &gport->bg_color))
369 gdk_color_alloc (gport->colormap, &gport->bg_color);
370 else
371 gdk_color_white (gport->colormap, &gport->bg_color);
373 if (gdk_color_parse (Settings.OffLimitColor, &gport->offlimits_color))
374 gdk_color_alloc (gport->colormap, &gport->offlimits_color);
375 else
376 gdk_color_white (gport->colormap, &gport->offlimits_color);
377 first_time_done = TRUE;
378 ghid_drawing_area_configure_hook (out);
379 PCBChanged (0, NULL, 0, 0);
381 else
383 ghid_drawing_area_configure_hook (out);
386 ghid_port_ranges_scale ();
387 ghid_invalidate_all ();
388 return 0;
392 static char *
393 describe_location (Coord X, Coord Y)
395 void *ptr1, *ptr2, *ptr3;
396 int type;
397 int Range = 0;
398 char *elename = "";
399 char *pinname;
400 char *netname = NULL;
401 char *description;
403 /* check if there are any pins or pads at that position */
405 type = SearchObjectByLocation (PIN_TYPE | PAD_TYPE,
406 &ptr1, &ptr2, &ptr3, X, Y, Range);
407 if (type == NO_TYPE)
408 return NULL;
410 /* don't mess with silk objects! */
411 if (type & SILK_TYPE &&
412 GetLayerNumber (PCB->Data, (LayerType *) ptr1) >= max_copper_layer)
413 return NULL;
415 if (type == PIN_TYPE || type == PAD_TYPE)
416 elename = (char *)UNKNOWN (NAMEONPCB_NAME ((ElementType *) ptr1));
418 pinname = ConnectionName (type, ptr1, ptr2);
420 if (pinname == NULL)
421 return NULL;
423 /* Find netlist entry */
424 MENU_LOOP (&PCB->NetlistLib);
426 if (!menu->Name)
427 continue;
429 ENTRY_LOOP (menu);
431 if (!entry->ListEntry)
432 continue;
434 if (strcmp (entry->ListEntry, pinname) == 0) {
435 netname = g_strdup (menu->Name);
436 /* For some reason, the netname has spaces in front of it, strip them */
437 g_strstrip (netname);
438 break;
441 END_LOOP;
443 if (netname != NULL)
444 break;
446 END_LOOP;
448 description = g_strdup_printf ("Element name: %s\n"
449 "Pinname : %s\n"
450 "Netname : %s",
451 elename,
452 (pinname != NULL) ? pinname : "--",
453 (netname != NULL) ? netname : "--");
455 g_free (netname);
457 return description;
461 static gboolean check_object_tooltips (GHidPort *out)
463 char *description;
465 /* check if there are any pins or pads at that position */
466 description = describe_location (out->crosshair_x, out->crosshair_y);
468 if (description == NULL)
469 return FALSE;
471 gtk_widget_set_tooltip_text (out->drawing_area, description);
472 g_free (description);
474 return FALSE;
477 static int tooltip_update_timeout_id = 0;
479 static void
480 cancel_tooltip_update ()
482 if (tooltip_update_timeout_id)
483 g_source_remove (tooltip_update_timeout_id);
484 tooltip_update_timeout_id = 0;
487 /* FIXME: If the GHidPort is ever destroyed, we must call
488 * cancel_tooltip_update (), otherwise the timeout might
489 * fire after the data it utilises has been free'd.
491 static void
492 queue_tooltip_update (GHidPort *out)
494 /* Zap the old tool-tip text and force it to be removed from the screen */
495 gtk_widget_set_tooltip_text (out->drawing_area, NULL);
496 gtk_widget_trigger_tooltip_query (out->drawing_area);
498 cancel_tooltip_update ();
500 tooltip_update_timeout_id =
501 g_timeout_add (TOOLTIP_UPDATE_DELAY,
502 (GSourceFunc) check_object_tooltips,
503 out);
506 gint
507 ghid_port_window_motion_cb (GtkWidget * widget,
508 GdkEventMotion * ev, GHidPort * out)
510 gdouble dx, dy;
511 static gint x_prev = -1, y_prev = -1;
513 gdk_event_request_motions (ev);
515 if (out->panning)
517 dx = gport->view.coord_per_px * (x_prev - ev->x);
518 dy = gport->view.coord_per_px * (y_prev - ev->y);
519 if (x_prev > 0)
520 ghid_pan_view_rel (dx, dy);
521 x_prev = ev->x;
522 y_prev = ev->y;
523 return FALSE;
525 x_prev = y_prev = -1;
526 ghid_note_event_location ((GdkEventButton *)ev);
528 queue_tooltip_update (out);
530 return FALSE;
533 gint
534 ghid_port_window_enter_cb (GtkWidget * widget,
535 GdkEventCrossing * ev, GHidPort * out)
537 /* printf("enter: mode: %d detail: %d\n", ev->mode, ev->detail); */
539 /* See comment in ghid_port_window_leave_cb() */
541 if(ev->mode != GDK_CROSSING_NORMAL && ev->detail != GDK_NOTIFY_NONLINEAR)
543 return FALSE;
546 if (!ghidgui->command_entry_status_line_active)
548 out->has_entered = TRUE;
549 /* Make sure drawing area has keyboard focus when we are in it.
551 gtk_widget_grab_focus (out->drawing_area);
553 ghidgui->in_popup = FALSE;
555 /* Following expression is true if a you open a menu from the menu bar,
556 * move the mouse to the viewport and click on it. This closes the menu
557 * and moves the pointer to the viewport without the pointer going over
558 * the edge of the viewport */
559 if(ev->mode == GDK_CROSSING_UNGRAB && ev->detail == GDK_NOTIFY_NONLINEAR)
561 ghid_screen_update ();
563 return FALSE;
566 gint
567 ghid_port_window_leave_cb (GtkWidget * widget,
568 GdkEventCrossing * ev, GHidPort * out)
570 /* printf("leave mode: %d detail: %d\n", ev->mode, ev->detail); */
572 /* Window leave events can also be triggered because of focus grabs. Some
573 * X applications occasionally grab the focus and so trigger this function.
574 * At least GNOME's window manager is known to do this on every mouse click.
576 * See http://bugzilla.gnome.org/show_bug.cgi?id=102209
579 if(ev->mode != GDK_CROSSING_NORMAL)
581 return FALSE;
584 out->has_entered = FALSE;
586 ghid_screen_update ();
588 return FALSE;
592 /* Mouse scroll wheel events
594 gint
595 ghid_port_window_mouse_scroll_cb (GtkWidget * widget,
596 GdkEventScroll * ev, GHidPort * out)
598 ModifierKeysState mk;
599 GdkModifierType state;
600 int button;
602 state = (GdkModifierType) (ev->state);
603 mk = ghid_modifier_keys_state (&state);
605 /* X11 gtk hard codes buttons 4, 5, 6, 7 as below in
606 * gtk+/gdk/x11/gdkevents-x11.c:1121, but quartz and windows have
607 * special mouse scroll events, so this may conflict with a mouse
608 * who has buttons 4 - 7 that aren't the scroll wheel?
610 switch(ev->direction)
612 case GDK_SCROLL_UP: button = 4; break;
613 case GDK_SCROLL_DOWN: button = 5; break;
614 case GDK_SCROLL_LEFT: button = 6; break;
615 case GDK_SCROLL_RIGHT: button = 7; break;
616 default: button = -1;
619 do_mouse_action(button, mk);
621 return TRUE;