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