pcb 4.1.1
An interactive printed circuit board layout editor.

gui-output-events.c

Go to the documentation of this file.
00001 /*
00002  *                            COPYRIGHT
00003  *
00004  *  PCB, interactive printed circuit board design
00005  *  Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License along
00018  *  with this program; if not, write to the Free Software Foundation, Inc.,
00019  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  *  Contact addresses for paper mail and Email:
00022  *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
00023  *  Thomas.Nau@rz.uni-ulm.de
00024  *
00025  */
00026 
00027 /* This file written by Bill Wilson for the PCB Gtk port */
00028 
00029 #ifdef HAVE_CONFIG_H
00030 #include "config.h"
00031 #endif
00032 
00033 #include "gui.h"
00034 #include "gtkhid.h"
00035 #include "hid/common/hid_resource.h"
00036 
00037 #include <gdk/gdkkeysyms.h>
00038 
00039 #include "action.h"
00040 #include "crosshair.h"
00041 #include "draw.h"
00042 #include "error.h"
00043 #include "misc.h"
00044 #include "set.h"
00045 #include "find.h"
00046 #include "search.h"
00047 #include "rats.h"
00048 
00049 #ifdef HAVE_LIBDMALLOC
00050 #include <dmalloc.h>
00051 #endif
00052 
00053 #define TOOLTIP_UPDATE_DELAY 200
00054 
00055 void
00056 ghid_port_ranges_changed (void)
00057 {
00058   GtkAdjustment *h_adj, *v_adj;
00059 
00060   h_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
00061   v_adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
00062   gport->view.x0 = gtk_adjustment_get_value (h_adj);
00063   gport->view.y0 = gtk_adjustment_get_value (v_adj);
00064 
00065   ghid_invalidate_all ();
00066 }
00067 
00068 /* Do scrollbar scaling based on current port drawing area size and
00069    |  overall PCB board size.
00070  */
00071 void
00072 ghid_port_ranges_scale (void)
00073 {
00074   GtkAdjustment *adj;
00075   gdouble page_size;
00076 
00077   /* Update the scrollbars with PCB units.  So Scale the current
00078      |  drawing area size in pixels to PCB units and that will be
00079      |  the page size for the Gtk adjustment.
00080    */
00081   gport->view.width = gport->width * gport->view.coord_per_px;
00082   gport->view.height = gport->height * gport->view.coord_per_px;
00083 
00084   adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->h_range));
00085   page_size = MIN (gport->view.width, PCB->MaxWidth);
00086   gtk_adjustment_configure (adj,
00087                             gtk_adjustment_get_value (adj), /* value          */
00088                             -gport->view.width,             /* lower          */
00089                              PCB->MaxWidth + page_size,     /* upper          */
00090                              page_size / 100.0,             /* step_increment */
00091                              page_size / 10.0,              /* page_increment */
00092                              page_size);                    /* page_size      */
00093 
00094   adj = gtk_range_get_adjustment (GTK_RANGE (ghidgui->v_range));
00095   page_size = MIN (gport->view.height, PCB->MaxHeight);
00096   gtk_adjustment_configure (adj,
00097                             gtk_adjustment_get_value (adj), /* value          */
00098                             -gport->view.height,            /* lower          */
00099                             PCB->MaxHeight + page_size,     /* upper          */
00100                             page_size / 100.0,              /* step_increment */
00101                             page_size / 10.0,               /* page_increment */
00102                             page_size);                     /* page_size      */
00103 }
00104 
00105 
00106 /* ---------------------------------------------------------------------- 
00107  * handles all events from PCB drawing area
00108  */
00109 
00110 void
00111 ghid_get_coords (const char *msg, Coord *x, Coord *y)
00112 {
00113   if (!ghid_port.has_entered && msg)
00114     ghid_get_user_xy (msg);
00115   if (ghid_port.has_entered)
00116     {
00117       *x = gport->pcb_x;
00118       *y = gport->pcb_y;
00119     }
00120 }
00121 
00122 gboolean
00123 ghid_note_event_location (GdkEventButton * ev)
00124 {
00125   gint event_x, event_y;
00126   gboolean moved;
00127 
00128   if (!ev)
00129     {
00130       gdk_window_get_pointer (gtk_widget_get_window (ghid_port.drawing_area),
00131                               &event_x, &event_y, NULL);
00132     }
00133   else
00134     {
00135       event_x = ev->x;
00136       event_y = ev->y;
00137     }
00138 
00139   ghid_event_to_pcb_coords (event_x, event_y, &gport->pcb_x, &gport->pcb_y);
00140 
00141   moved = MoveCrosshairAbsolute (gport->pcb_x, gport->pcb_y);
00142   if (moved)
00143     {
00144       AdjustAttachedObjects ();
00145       notify_crosshair_change (true);
00146     }
00147   ghid_set_cursor_position_labels ();
00148   return moved;
00149 }
00150 
00151 static gboolean
00152 ghid_idle_cb (gpointer data)
00153 {
00154   if (Settings.Mode == NO_MODE)
00155     SetMode (ARROW_MODE);
00156   ghid_mode_cursor (Settings.Mode);
00157   if (ghidgui->settings_mode != Settings.Mode)
00158     {
00159       ghid_mode_buttons_update ();
00160     }
00161   ghidgui->settings_mode = Settings.Mode;
00162 
00163   ghid_update_toggle_flags ();
00164   return FALSE;
00165 }
00166 
00167 gboolean
00168 ghid_port_key_release_cb (GtkWidget * drawing_area, GdkEventKey * kev,
00169                           gpointer data)
00170 {
00171   gint ksym = kev->keyval;
00172 
00173   if (ghid_is_modifier_key_sym (ksym))
00174     ghid_note_event_location (NULL);
00175 
00176   AdjustAttachedObjects ();
00177   ghid_invalidate_all ();
00178   g_idle_add (ghid_idle_cb, NULL);
00179   return FALSE;
00180 }
00181 
00182 /* Handle user keys in the output drawing area.
00183  * Note that the default is for all hotkeys to be handled by the
00184  * menu accelerators.
00185  *
00186  * Key presses not handled by the menus will show up here.  This means
00187  * the key press was either not defined in the menu resource file or
00188  * that the key press is special in that gtk doesn't allow the normal
00189  * menu code to ever see it.  We capture those here (like Tab and the
00190  * arrow keys) and feed it back to the normal menu callback.
00191  */
00192 
00193 gboolean
00194 ghid_port_key_press_cb (GtkWidget * drawing_area,
00195                         GdkEventKey * kev, gpointer data)
00196 {
00197   ModifierKeysState mk;
00198   gint  ksym = kev->keyval;
00199   gboolean handled;
00200   extern  void ghid_hotkey_cb (int);
00201   GdkModifierType state;
00202 
00203   if (ghid_is_modifier_key_sym (ksym))
00204     ghid_note_event_location (NULL);
00205 
00206   state = (GdkModifierType) (kev->state);
00207   mk = ghid_modifier_keys_state (&state);
00208 
00209   handled = TRUE;               /* Start off assuming we handle it */
00210   switch (ksym)
00211     {
00212     case GDK_Alt_L:
00213     case GDK_Alt_R:
00214     case GDK_Control_L:
00215     case GDK_Control_R:
00216     case GDK_Shift_L:
00217     case GDK_Shift_R:
00218     case GDK_Shift_Lock:
00219     case GDK_ISO_Level3_Shift:
00220       break;
00221 
00222     case GDK_Up:
00223       ghid_hotkey_cb (GHID_KEY_UP);
00224       break;
00225       
00226     case GDK_Down:
00227       ghid_hotkey_cb (GHID_KEY_DOWN);
00228       break;
00229     case GDK_Left:
00230       ghid_hotkey_cb (GHID_KEY_LEFT);
00231       break;
00232     case GDK_Right:
00233       ghid_hotkey_cb (GHID_KEY_RIGHT);
00234       break;
00235 
00236     case GDK_ISO_Left_Tab: 
00237     case GDK_3270_BackTab: 
00238       switch (mk) 
00239         {
00240         case NONE_PRESSED:
00241           ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
00242           break;
00243         case CONTROL_PRESSED:
00244           ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
00245           break;
00246         case MOD1_PRESSED:
00247           ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
00248           break;
00249         case SHIFT_PRESSED:
00250           ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
00251           break;
00252         case SHIFT_CONTROL_PRESSED:
00253           ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
00254           break;
00255         case SHIFT_MOD1_PRESSED:
00256           ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
00257           break;
00258           
00259         default:
00260           handled = FALSE;
00261           break;
00262         }
00263       break;
00264 
00265     case GDK_Tab: 
00266       switch (mk) 
00267         {
00268         case NONE_PRESSED:
00269           ghid_hotkey_cb (GHID_KEY_TAB);
00270           break;
00271         case CONTROL_PRESSED:
00272           ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_TAB);
00273           break;
00274         case MOD1_PRESSED:
00275           ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_TAB);
00276           break;
00277         case SHIFT_PRESSED:
00278           ghid_hotkey_cb (GHID_KEY_SHIFT | GHID_KEY_TAB);
00279           break;
00280         case SHIFT_CONTROL_PRESSED:
00281           ghid_hotkey_cb (GHID_KEY_CONTROL | GHID_KEY_SHIFT | GHID_KEY_TAB);
00282           break;
00283         case SHIFT_MOD1_PRESSED:
00284           ghid_hotkey_cb (GHID_KEY_ALT | GHID_KEY_SHIFT | GHID_KEY_TAB);
00285           break;
00286           
00287         default:
00288           handled = FALSE;
00289           break;
00290         }
00291       break;
00292 
00293     default:
00294       handled = FALSE;
00295     }
00296 
00297   return handled;
00298 }
00299 
00300 gboolean
00301 ghid_port_button_press_cb (GtkWidget * drawing_area,
00302                            GdkEventButton * ev, gpointer data)
00303 {
00304   ModifierKeysState mk;
00305   GdkModifierType state;
00306 
00307   /* Reject double and triple click events */
00308   if (ev->type != GDK_BUTTON_PRESS) return TRUE;
00309 
00310   ghid_note_event_location (ev);
00311   state = (GdkModifierType) (ev->state);
00312   mk = ghid_modifier_keys_state (&state);
00313 
00314   do_mouse_action(ev->button, mk);
00315 
00316   ghid_invalidate_all ();
00317   ghid_window_set_name_label (PCB->Name);
00318   ghid_set_status_line_label ();
00319   if (!gport->panning)
00320     g_idle_add (ghid_idle_cb, NULL);
00321   return TRUE;
00322 }
00323 
00324 
00325 gboolean
00326 ghid_port_button_release_cb (GtkWidget * drawing_area,
00327                              GdkEventButton * ev, gpointer data)
00328 {
00329   ModifierKeysState mk;
00330   GdkModifierType state;
00331 
00332   ghid_note_event_location (ev);
00333   state = (GdkModifierType) (ev->state);
00334   mk = ghid_modifier_keys_state (&state);
00335 
00336   do_mouse_action(ev->button, mk + M_Release);
00337 
00338   AdjustAttachedObjects ();
00339   ghid_invalidate_all ();
00340 
00341   ghid_window_set_name_label (PCB->Name);
00342   ghid_set_status_line_label ();
00343   g_idle_add (ghid_idle_cb, NULL);
00344   return TRUE;
00345 }
00346 
00347 
00348 gboolean
00349 ghid_port_drawing_area_configure_event_cb (GtkWidget * widget,
00350                                            GdkEventConfigure * ev,
00351                                            GHidPort * out)
00352 {
00353   static gboolean first_time_done;
00354 
00355   gport->width = ev->width;
00356   gport->height = ev->height;
00357 
00358   if (gport->pixmap)
00359     g_object_unref (gport->pixmap);
00360 
00361   gport->pixmap = gdk_pixmap_new (gtk_widget_get_window (widget),
00362                                   gport->width, gport->height, -1);
00363   gport->drawable = gport->pixmap;
00364 
00365   if (!first_time_done)
00366     {
00367       gport->colormap = gtk_widget_get_colormap (gport->top_window);
00368       if (gdk_color_parse (Settings.BackgroundColor, &gport->bg_color))
00369         gdk_color_alloc (gport->colormap, &gport->bg_color);
00370       else
00371         gdk_color_white (gport->colormap, &gport->bg_color);
00372 
00373       if (gdk_color_parse (Settings.OffLimitColor, &gport->offlimits_color))
00374         gdk_color_alloc (gport->colormap, &gport->offlimits_color);
00375       else
00376         gdk_color_white (gport->colormap, &gport->offlimits_color);
00377       first_time_done = TRUE;
00378       ghid_drawing_area_configure_hook (out);
00379       PCBChanged (0, NULL, 0, 0);
00380     }
00381   else
00382     {
00383       ghid_drawing_area_configure_hook (out);
00384     }
00385 
00386   ghid_port_ranges_scale ();
00387   ghid_invalidate_all ();
00388   return 0;
00389 }
00390 
00391 
00392 static char *
00393 describe_location (Coord X, Coord Y)
00394 {
00395   void *ptr1, *ptr2, *ptr3;
00396   int type;
00397   int Range = 0;
00398   char *elename = "";
00399   char *pinname;
00400   char *netname = NULL;
00401   char *description;
00402 
00403   /* check if there are any pins or pads at that position */
00404 
00405   type = SearchObjectByLocation (PIN_TYPE | PAD_TYPE,
00406                                  &ptr1, &ptr2, &ptr3, X, Y, Range);
00407   if (type == NO_TYPE)
00408     return NULL;
00409 
00410   /* don't mess with silk objects! */
00411   if (type & SILK_TYPE &&
00412       GetLayerNumber (PCB->Data, (LayerType *) ptr1) >= max_copper_layer)
00413     return NULL;
00414 
00415   if (type == PIN_TYPE || type == PAD_TYPE)
00416     elename = (char *)UNKNOWN_NAME (NAMEONPCB_NAME ((ElementType *) ptr1),
00417         _("--"));
00418 
00419   pinname = ConnectionName (type, ptr1, ptr2);
00420 
00421   if (pinname == NULL)
00422     return NULL;
00423 
00424   /* Find netlist entry */
00425   MENU_LOOP (&PCB->NetlistLib);
00426   {
00427     if (!menu->Name)
00428     continue;
00429 
00430     ENTRY_LOOP (menu);
00431     {
00432       if (!entry->ListEntry)
00433         continue;
00434 
00435       if (strcmp (entry->ListEntry, pinname) == 0) {
00436         netname = g_strdup (menu->Name);
00437         /* For some reason, the netname has spaces in front of it, strip them */
00438         g_strstrip (netname);
00439         break;
00440       }
00441     }
00442     END_LOOP;
00443 
00444     if (netname != NULL)
00445       break;
00446   }
00447   END_LOOP;
00448 
00449   description = g_strdup_printf (_("Element name: %s\n"
00450                                  "Pinname : %s\n"
00451                                  "Netname : %s"),
00452                                  elename,
00453                                  (pinname != NULL) ? pinname : _("--"),
00454                                  (netname != NULL) ? netname : _("--"));
00455 
00456   g_free (netname);
00457 
00458   return description;
00459 }
00460 
00461 
00462 static gboolean check_object_tooltips (GHidPort *out)
00463 {
00464   char *description;
00465 
00466   /* check if there are any pins or pads at that position */
00467   description = describe_location (out->crosshair_x, out->crosshair_y);
00468 
00469   if (description != NULL)
00470     {
00471       gtk_widget_set_tooltip_text (out->drawing_area, description);
00472       g_free (description);
00473     }
00474 
00475   out->tooltip_update_timeout_id = 0;
00476   return FALSE;
00477 }
00478 
00479 
00480 static void
00481 cancel_tooltip_update (GHidPort *out)
00482 {
00483   if (out->tooltip_update_timeout_id)
00484     {
00485       g_source_remove (out->tooltip_update_timeout_id);
00486       out->tooltip_update_timeout_id = 0;
00487     }
00488 }
00489 
00490 /* FIXME: If the GHidPort is ever destroyed, we must call
00491  * cancel_tooltip_update (), otherwise the timeout might
00492  * fire after the data it utilises has been free'd.
00493  */
00494 static void
00495 queue_tooltip_update (GHidPort *out)
00496 {
00497   /* Zap the old tool-tip text and force it to be removed from the screen */
00498   gtk_widget_set_tooltip_text (out->drawing_area, NULL);
00499   gtk_widget_trigger_tooltip_query (out->drawing_area);
00500 
00501   cancel_tooltip_update (out);
00502 
00503   out->tooltip_update_timeout_id =
00504       g_timeout_add (TOOLTIP_UPDATE_DELAY,
00505                      (GSourceFunc) check_object_tooltips,
00506                      out);
00507 }
00508 
00509 gint
00510 ghid_port_window_motion_cb (GtkWidget * widget,
00511                             GdkEventMotion * ev, GHidPort * out)
00512 {
00513   gdouble dx, dy;
00514   static gint x_prev = -1, y_prev = -1;
00515 
00516   gdk_event_request_motions (ev);
00517 
00518   if (out->panning)
00519     {
00520       dx = gport->view.coord_per_px * (x_prev - ev->x);
00521       dy = gport->view.coord_per_px * (y_prev - ev->y);
00522       if (x_prev > 0)
00523         ghid_pan_view_rel (dx, dy);
00524       x_prev = ev->x;
00525       y_prev = ev->y;
00526       return FALSE;
00527     }
00528   x_prev = y_prev = -1;
00529   ghid_note_event_location ((GdkEventButton *)ev);
00530 
00531   queue_tooltip_update (out);
00532 
00533   return FALSE;
00534 }
00535 
00536 gint
00537 ghid_port_window_enter_cb (GtkWidget * widget,
00538                            GdkEventCrossing * ev, GHidPort * out)
00539 {
00540   /* printf("enter: mode: %d detail: %d\n", ev->mode, ev->detail); */
00541 
00542   /* See comment in ghid_port_window_leave_cb() */
00543 
00544   if(ev->mode != GDK_CROSSING_NORMAL && ev->detail != GDK_NOTIFY_NONLINEAR) 
00545     {
00546       return FALSE;
00547     }
00548 
00549   if (!ghidgui->command_entry_status_line_active)
00550     {
00551       out->has_entered = TRUE;
00552       /* Make sure drawing area has keyboard focus when we are in it.
00553        */
00554       gtk_widget_grab_focus (out->drawing_area);
00555     }
00556   ghidgui->in_popup = FALSE;
00557 
00558   /* Following expression is true if a you open a menu from the menu bar, 
00559    * move the mouse to the viewport and click on it. This closes the menu 
00560    * and moves the pointer to the viewport without the pointer going over 
00561    * the edge of the viewport */
00562   if(ev->mode == GDK_CROSSING_UNGRAB && ev->detail == GDK_NOTIFY_NONLINEAR)
00563     {
00564       ghid_screen_update ();
00565     }
00566   return FALSE;
00567 }
00568 
00569 gint
00570 ghid_port_window_leave_cb (GtkWidget * widget, 
00571                            GdkEventCrossing * ev, GHidPort * out)
00572 {
00573   /* printf("leave mode: %d detail: %d\n", ev->mode, ev->detail); */
00574 
00575   /* Window leave events can also be triggered because of focus grabs. Some
00576    * X applications occasionally grab the focus and so trigger this function.
00577    * At least GNOME's window manager is known to do this on every mouse click.
00578    *
00579    * See http://bugzilla.gnome.org/show_bug.cgi?id=102209 
00580    */
00581 
00582   if(ev->mode != GDK_CROSSING_NORMAL)
00583     {
00584       return FALSE;
00585     }
00586 
00587   out->has_entered = FALSE;
00588 
00589   ghid_screen_update ();
00590 
00591   return FALSE;
00592 }
00593 
00594 
00595   /* Mouse scroll wheel events
00596    */
00597 gint
00598 ghid_port_window_mouse_scroll_cb (GtkWidget * widget,
00599                                   GdkEventScroll * ev, GHidPort * out)
00600 {
00601   ModifierKeysState mk;
00602   GdkModifierType state;
00603   int button;
00604 
00605   state = (GdkModifierType) (ev->state);
00606   mk = ghid_modifier_keys_state (&state);
00607 
00608   /* X11 gtk hard codes buttons 4, 5, 6, 7 as below in
00609    * gtk+/gdk/x11/gdkevents-x11.c:1121, but quartz and windows have
00610    * special mouse scroll events, so this may conflict with a mouse
00611    * who has buttons 4 - 7 that aren't the scroll wheel?
00612    */
00613   switch(ev->direction)
00614     {
00615     case GDK_SCROLL_UP: button = 4; break;
00616     case GDK_SCROLL_DOWN: button = 5; break;
00617     case GDK_SCROLL_LEFT: button = 6; break;
00618     case GDK_SCROLL_RIGHT: button = 7; break;
00619     default: button = -1;
00620     }
00621 
00622   do_mouse_action(button, mk);
00623 
00624   return TRUE;
00625 }