pcb 4.1.1
An interactive printed circuit board layout editor.
|
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 }