1 /*
2 * COPYRIGHT
3 *
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
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 * 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.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
24 *
25 */
26
27 /* This file written by Bill Wilson for the PCB Gtk port */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "gui.h"
34 #include "gtkhid.h"
35 #include "hid/common/hid_resource.h"
36
37 #include <gdk/gdkkeysyms.h>
38
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"
48
49 #ifdef HAVE_LIBDMALLOC
50 #include <dmalloc.h>
51 #endif
52
53 #define TOOLTIP_UPDATE_DELAY 200
54
55 void
ghid_port_ranges_changed(void)56 ghid_port_ranges_changed (void)
57 {
58 GtkAdjustment *h_adj, *v_adj;
59
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);
64
65 ghid_invalidate_all ();
66 }
67
68 /* Do scrollbar scaling based on current port drawing area size and
69 | overall PCB board size.
70 */
71 void
ghid_port_ranges_scale(void)72 ghid_port_ranges_scale (void)
73 {
74 GtkAdjustment *adj;
75 gdouble page_size;
76
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.
80 */
81 gport->view.width = gport->width * gport->view.coord_per_px;
82 gport->view.height = gport->height * gport->view.coord_per_px;
83
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 */
93
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 */
103 }
104
105
106 /* ----------------------------------------------------------------------
107 * handles all events from PCB drawing area
108 */
109
110 void
ghid_get_coords(const char * msg,Coord * x,Coord * y)111 ghid_get_coords (const char *msg, Coord *x, Coord *y)
112 {
113 if (!ghid_port.has_entered && msg)
114 ghid_get_user_xy (msg);
115 if (ghid_port.has_entered)
116 {
117 *x = gport->pcb_x;
118 *y = gport->pcb_y;
119 }
120 }
121
122 gboolean
ghid_note_event_location(GdkEventButton * ev)123 ghid_note_event_location (GdkEventButton * ev)
124 {
125 gint event_x, event_y;
126 gboolean moved;
127
128 if (!ev)
129 {
130 gdk_window_get_pointer (gtk_widget_get_window (ghid_port.drawing_area),
131 &event_x, &event_y, NULL);
132 }
133 else
134 {
135 event_x = ev->x;
136 event_y = ev->y;
137 }
138
139 ghid_event_to_pcb_coords (event_x, event_y, &gport->pcb_x, &gport->pcb_y);
140
141 moved = MoveCrosshairAbsolute (gport->pcb_x, gport->pcb_y);
142 if (moved)
143 {
144 AdjustAttachedObjects ();
145 notify_crosshair_change (true);
146 }
147 ghid_set_cursor_position_labels ();
148 return moved;
149 }
150
151 static gboolean
ghid_idle_cb(gpointer data)152 ghid_idle_cb (gpointer data)
153 {
154 if (Settings.Mode == NO_MODE)
155 SetMode (ARROW_MODE);
156 ghid_mode_cursor (Settings.Mode);
157 if (ghidgui->settings_mode != Settings.Mode)
158 {
159 ghid_mode_buttons_update ();
160 }
161 ghidgui->settings_mode = Settings.Mode;
162
163 ghid_update_toggle_flags ();
164 return FALSE;
165 }
166
167 gboolean
ghid_port_key_release_cb(GtkWidget * drawing_area,GdkEventKey * kev,gpointer data)168 ghid_port_key_release_cb (GtkWidget * drawing_area, GdkEventKey * kev,
169 gpointer data)
170 {
171 gint ksym = kev->keyval;
172
173 if (ghid_is_modifier_key_sym (ksym))
174 ghid_note_event_location (NULL);
175
176 AdjustAttachedObjects ();
177 ghid_invalidate_all ();
178 g_idle_add (ghid_idle_cb, NULL);
179 return FALSE;
180 }
181
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.
185 *
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.
191 */
192
193 gboolean
ghid_port_key_press_cb(GtkWidget * drawing_area,GdkEventKey * kev,gpointer data)194 ghid_port_key_press_cb (GtkWidget * drawing_area,
195 GdkEventKey * kev, gpointer data)
196 {
197 ModifierKeysState mk;
198 gint ksym = kev->keyval;
199 gboolean handled;
200 extern void ghid_hotkey_cb (int);
201 GdkModifierType state;
202
203 if (ghid_is_modifier_key_sym (ksym))
204 ghid_note_event_location (NULL);
205
206 state = (GdkModifierType) (kev->state);
207 mk = ghid_modifier_keys_state (&state);
208
209 handled = TRUE; /* Start off assuming we handle it */
210 switch (ksym)
211 {
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;
221
222 case GDK_Up:
223 ghid_hotkey_cb (GHID_KEY_UP);
224 break;
225
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;
235
236 case GDK_ISO_Left_Tab:
237 case GDK_3270_BackTab:
238 switch (mk)
239 {
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;
258
259 default:
260 handled = FALSE;
261 break;
262 }
263 break;
264
265 case GDK_Tab:
266 switch (mk)
267 {
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;
286
287 default:
288 handled = FALSE;
289 break;
290 }
291 break;
292
293 default:
294 handled = FALSE;
295 }
296
297 return handled;
298 }
299
300 gboolean
ghid_port_button_press_cb(GtkWidget * drawing_area,GdkEventButton * ev,gpointer data)301 ghid_port_button_press_cb (GtkWidget * drawing_area,
302 GdkEventButton * ev, gpointer data)
303 {
304 ModifierKeysState mk;
305 GdkModifierType state;
306
307 /* Reject double and triple click events */
308 if (ev->type != GDK_BUTTON_PRESS) return TRUE;
309
310 ghid_note_event_location (ev);
311 state = (GdkModifierType) (ev->state);
312 mk = ghid_modifier_keys_state (&state);
313
314 do_mouse_action(ev->button, mk);
315
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;
322 }
323
324
325 gboolean
ghid_port_button_release_cb(GtkWidget * drawing_area,GdkEventButton * ev,gpointer data)326 ghid_port_button_release_cb (GtkWidget * drawing_area,
327 GdkEventButton * ev, gpointer data)
328 {
329 ModifierKeysState mk;
330 GdkModifierType state;
331
332 ghid_note_event_location (ev);
333 state = (GdkModifierType) (ev->state);
334 mk = ghid_modifier_keys_state (&state);
335
336 do_mouse_action(ev->button, mk + M_Release);
337
338 AdjustAttachedObjects ();
339 ghid_invalidate_all ();
340
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;
345 }
346
347
348 gboolean
ghid_port_drawing_area_configure_event_cb(GtkWidget * widget,GdkEventConfigure * ev,GHidPort * out)349 ghid_port_drawing_area_configure_event_cb (GtkWidget * widget,
350 GdkEventConfigure * ev,
351 GHidPort * out)
352 {
353 static gboolean first_time_done;
354
355 gport->width = ev->width;
356 gport->height = ev->height;
357
358 if (gport->pixmap)
359 g_object_unref (gport->pixmap);
360
361 gport->pixmap = gdk_pixmap_new (gtk_widget_get_window (widget),
362 gport->width, gport->height, -1);
363 gport->drawable = gport->pixmap;
364
365 if (!first_time_done)
366 {
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);
372
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);
380 }
381 else
382 {
383 ghid_drawing_area_configure_hook (out);
384 }
385
386 ghid_port_ranges_scale ();
387 ghid_invalidate_all ();
388 return 0;
389 }
390
391
392 static char *
describe_location(Coord X,Coord Y)393 describe_location (Coord X, Coord Y)
394 {
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;
402
403 /* check if there are any pins or pads at that position */
404
405 type = SearchObjectByLocation (PIN_TYPE | PAD_TYPE,
406 &ptr1, &ptr2, &ptr3, X, Y, Range);
407 if (type == NO_TYPE)
408 return NULL;
409
410 /* don't mess with silk objects! */
411 if (type & SILK_TYPE &&
412 GetLayerNumber (PCB->Data, (LayerType *) ptr1) >= max_copper_layer)
413 return NULL;
414
415 if (type == PIN_TYPE || type == PAD_TYPE)
416 elename = (char *)UNKNOWN_NAME (NAMEONPCB_NAME ((ElementType *) ptr1),
417 _("--"));
418
419 pinname = ConnectionName (type, ptr1, ptr2);
420
421 if (pinname == NULL)
422 return NULL;
423
424 /* Find netlist entry */
425 MENU_LOOP (&PCB->NetlistLib);
426 {
427 if (!menu->Name)
428 continue;
429
430 ENTRY_LOOP (menu);
431 {
432 if (!entry->ListEntry)
433 continue;
434
435 if (strcmp (entry->ListEntry, pinname) == 0) {
436 netname = g_strdup (menu->Name);
437 /* For some reason, the netname has spaces in front of it, strip them */
438 g_strstrip (netname);
439 break;
440 }
441 }
442 END_LOOP;
443
444 if (netname != NULL)
445 break;
446 }
447 END_LOOP;
448
449 description = g_strdup_printf (_("Element name: %s\n"
450 "Pinname : %s\n"
451 "Netname : %s"),
452 elename,
453 (pinname != NULL) ? pinname : _("--"),
454 (netname != NULL) ? netname : _("--"));
455
456 g_free (netname);
457
458 return description;
459 }
460
461
check_object_tooltips(GHidPort * out)462 static gboolean check_object_tooltips (GHidPort *out)
463 {
464 char *description;
465
466 /* check if there are any pins or pads at that position */
467 description = describe_location (out->crosshair_x, out->crosshair_y);
468
469 if (description != NULL)
470 {
471 gtk_widget_set_tooltip_text (out->drawing_area, description);
472 g_free (description);
473 }
474
475 out->tooltip_update_timeout_id = 0;
476 return FALSE;
477 }
478
479
480 static void
cancel_tooltip_update(GHidPort * out)481 cancel_tooltip_update (GHidPort *out)
482 {
483 if (out->tooltip_update_timeout_id)
484 {
485 g_source_remove (out->tooltip_update_timeout_id);
486 out->tooltip_update_timeout_id = 0;
487 }
488 }
489
490 /* FIXME: If the GHidPort is ever destroyed, we must call
491 * cancel_tooltip_update (), otherwise the timeout might
492 * fire after the data it utilises has been free'd.
493 */
494 static void
queue_tooltip_update(GHidPort * out)495 queue_tooltip_update (GHidPort *out)
496 {
497 /* Zap the old tool-tip text and force it to be removed from the screen */
498 gtk_widget_set_tooltip_text (out->drawing_area, NULL);
499 gtk_widget_trigger_tooltip_query (out->drawing_area);
500
501 cancel_tooltip_update (out);
502
503 out->tooltip_update_timeout_id =
504 g_timeout_add (TOOLTIP_UPDATE_DELAY,
505 (GSourceFunc) check_object_tooltips,
506 out);
507 }
508
509 gint
ghid_port_window_motion_cb(GtkWidget * widget,GdkEventMotion * ev,GHidPort * out)510 ghid_port_window_motion_cb (GtkWidget * widget,
511 GdkEventMotion * ev, GHidPort * out)
512 {
513 gdouble dx, dy;
514 static gint x_prev = -1, y_prev = -1;
515
516 gdk_event_request_motions (ev);
517
518 if (out->panning)
519 {
520 dx = gport->view.coord_per_px * (x_prev - ev->x);
521 dy = gport->view.coord_per_px * (y_prev - ev->y);
522 if (x_prev > 0)
523 ghid_pan_view_rel (dx, dy);
524 x_prev = ev->x;
525 y_prev = ev->y;
526 return FALSE;
527 }
528 x_prev = y_prev = -1;
529 ghid_note_event_location ((GdkEventButton *)ev);
530
531 queue_tooltip_update (out);
532
533 return FALSE;
534 }
535
536 gint
ghid_port_window_enter_cb(GtkWidget * widget,GdkEventCrossing * ev,GHidPort * out)537 ghid_port_window_enter_cb (GtkWidget * widget,
538 GdkEventCrossing * ev, GHidPort * out)
539 {
540 /* printf("enter: mode: %d detail: %d\n", ev->mode, ev->detail); */
541
542 /* See comment in ghid_port_window_leave_cb() */
543
544 if(ev->mode != GDK_CROSSING_NORMAL && ev->detail != GDK_NOTIFY_NONLINEAR)
545 {
546 return FALSE;
547 }
548
549 if (!ghidgui->command_entry_status_line_active)
550 {
551 out->has_entered = TRUE;
552 /* Make sure drawing area has keyboard focus when we are in it.
553 */
554 gtk_widget_grab_focus (out->drawing_area);
555 }
556 ghidgui->in_popup = FALSE;
557
558 /* Following expression is true if a you open a menu from the menu bar,
559 * move the mouse to the viewport and click on it. This closes the menu
560 * and moves the pointer to the viewport without the pointer going over
561 * the edge of the viewport */
562 if(ev->mode == GDK_CROSSING_UNGRAB && ev->detail == GDK_NOTIFY_NONLINEAR)
563 {
564 ghid_screen_update ();
565 }
566 return FALSE;
567 }
568
569 gint
ghid_port_window_leave_cb(GtkWidget * widget,GdkEventCrossing * ev,GHidPort * out)570 ghid_port_window_leave_cb (GtkWidget * widget,
571 GdkEventCrossing * ev, GHidPort * out)
572 {
573 /* printf("leave mode: %d detail: %d\n", ev->mode, ev->detail); */
574
575 /* Window leave events can also be triggered because of focus grabs. Some
576 * X applications occasionally grab the focus and so trigger this function.
577 * At least GNOME's window manager is known to do this on every mouse click.
578 *
579 * See http://bugzilla.gnome.org/show_bug.cgi?id=102209
580 */
581
582 if(ev->mode != GDK_CROSSING_NORMAL)
583 {
584 return FALSE;
585 }
586
587 out->has_entered = FALSE;
588
589 ghid_screen_update ();
590
591 return FALSE;
592 }
593
594
595 /* Mouse scroll wheel events
596 */
597 gint
ghid_port_window_mouse_scroll_cb(GtkWidget * widget,GdkEventScroll * ev,GHidPort * out)598 ghid_port_window_mouse_scroll_cb (GtkWidget * widget,
599 GdkEventScroll * ev, GHidPort * out)
600 {
601 ModifierKeysState mk;
602 GdkModifierType state;
603 int button;
604
605 state = (GdkModifierType) (ev->state);
606 mk = ghid_modifier_keys_state (&state);
607
608 /* X11 gtk hard codes buttons 4, 5, 6, 7 as below in
609 * gtk+/gdk/x11/gdkevents-x11.c:1121, but quartz and windows have
610 * special mouse scroll events, so this may conflict with a mouse
611 * who has buttons 4 - 7 that aren't the scroll wheel?
612 */
613 switch(ev->direction)
614 {
615 case GDK_SCROLL_UP: button = 4; break;
616 case GDK_SCROLL_DOWN: button = 5; break;
617 case GDK_SCROLL_LEFT: button = 6; break;
618 case GDK_SCROLL_RIGHT: button = 7; break;
619 default: button = -1;
620 }
621
622 do_mouse_action(button, mk);
623
624 return TRUE;
625 }
626