pcb 4.1.1
An interactive printed circuit board layout editor.

gui-misc.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 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  */
00022 
00023 /* This file was originally written by Bill Wilson for the PCB Gtk port */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif
00028 
00029 #include "global.h"
00030 #include "crosshair.h"
00031 #include "data.h"
00032 #include "misc.h"
00033 #include "action.h"
00034 #include "set.h"
00035 #include "pcb-printf.h"
00036 
00037 #include "gui.h"
00038 #include <gdk/gdkkeysyms.h>
00039 
00040 #ifdef HAVE_LIBDMALLOC
00041 #include <dmalloc.h>
00042 #endif
00043 
00044 #define CUSTOM_CURSOR_CLOCKWISE         (GDK_LAST_CURSOR + 10)
00045 #define CUSTOM_CURSOR_DRAG                      (GDK_LAST_CURSOR + 11)
00046 #define CUSTOM_CURSOR_LOCK                      (GDK_LAST_CURSOR + 12)
00047 
00048 #define ICON_X_HOT 8
00049 #define ICON_Y_HOT 8
00050 
00051 
00052 GdkPixmap *XC_clock_source, *XC_clock_mask,
00053   *XC_hand_source, *XC_hand_mask, *XC_lock_source, *XC_lock_mask;
00054 
00055 
00056 static GdkCursorType oldCursor;
00057 
00058 void
00059 ghid_status_line_set_text (const gchar * text)
00060 {
00061   if (ghidgui->command_entry_status_line_active)
00062     return;
00063 
00064   ghid_label_set_markup (ghidgui->status_line_label, text);
00065 }
00066 
00067 void
00068 ghid_cursor_position_label_set_text (gchar * text)
00069 {
00070   ghid_label_set_markup (ghidgui->cursor_position_absolute_label, text);  
00071 }
00072 
00073 void
00074 ghid_cursor_position_relative_label_set_text (gchar * text)
00075 {
00076   ghid_label_set_markup (ghidgui->cursor_position_relative_label, text);
00077 }
00078 
00079 static GdkCursorType
00080 gport_set_cursor (GdkCursorType shape)
00081 {
00082   GdkWindow *window;
00083   GdkCursorType old_shape = gport->X_cursor_shape;
00084   GdkColor fg = { 0, 65535, 65535, 65535 };     /* white */
00085   GdkColor bg = { 0, 0, 0, 0 }; /* black */
00086 
00087   if (gport->drawing_area == NULL)
00088     return GDK_X_CURSOR;
00089 
00090   window = gtk_widget_get_window (gport->drawing_area);
00091 
00092   if (gport->X_cursor_shape == shape)
00093     return shape;
00094 
00095   /* check if window exists to prevent from fatal errors */
00096   if (window == NULL)
00097     return GDK_X_CURSOR;
00098 
00099   gport->X_cursor_shape = shape;
00100   if (shape > GDK_LAST_CURSOR)
00101     {
00102       if (shape == CUSTOM_CURSOR_CLOCKWISE)
00103         gport->X_cursor =
00104           gdk_cursor_new_from_pixmap (XC_clock_source, XC_clock_mask, &fg,
00105                                       &bg, ICON_X_HOT, ICON_Y_HOT);
00106       else if (shape == CUSTOM_CURSOR_DRAG)
00107         gport->X_cursor =
00108           gdk_cursor_new_from_pixmap (XC_hand_source, XC_hand_mask, &fg,
00109                                       &bg, ICON_X_HOT, ICON_Y_HOT);
00110       else if (shape == CUSTOM_CURSOR_LOCK)
00111         gport->X_cursor =
00112           gdk_cursor_new_from_pixmap (XC_lock_source, XC_lock_mask, &fg,
00113                                       &bg, ICON_X_HOT, ICON_Y_HOT);
00114     }
00115   else
00116     gport->X_cursor = gdk_cursor_new (shape);
00117 
00118   gdk_window_set_cursor (window, gport->X_cursor);
00119   gdk_cursor_unref (gport->X_cursor);
00120 
00121   return old_shape;
00122 }
00123 
00124 void
00125 ghid_point_cursor (void)
00126 {
00127   oldCursor = gport_set_cursor (GDK_DRAPED_BOX);
00128 }
00129 
00130 void
00131 ghid_hand_cursor (void)
00132 {
00133   oldCursor = gport_set_cursor (GDK_HAND2);
00134 }
00135 
00136 void
00137 ghid_watch_cursor (void)
00138 {
00139   GdkCursorType tmp;
00140 
00141   tmp = gport_set_cursor (GDK_WATCH);
00142   if (tmp != GDK_WATCH)
00143     oldCursor = tmp;
00144 }
00145 
00146 void
00147 ghid_mode_cursor (int Mode)
00148 {
00149   switch (Mode)
00150     {
00151     case NO_MODE:
00152       gport_set_cursor ((GdkCursorType)CUSTOM_CURSOR_DRAG);
00153       break;
00154 
00155     case VIA_MODE:
00156       gport_set_cursor (GDK_ARROW);
00157       break;
00158 
00159     case LINE_MODE:
00160       gport_set_cursor (GDK_PENCIL);
00161       break;
00162 
00163     case ARC_MODE:
00164       gport_set_cursor (GDK_QUESTION_ARROW);
00165       break;
00166 
00167     case ARROW_MODE:
00168       gport_set_cursor (GDK_LEFT_PTR);
00169       break;
00170 
00171     case POLYGON_MODE:
00172     case POLYGONHOLE_MODE:
00173       gport_set_cursor (GDK_SB_UP_ARROW);
00174       break;
00175 
00176     case PASTEBUFFER_MODE:
00177       gport_set_cursor (GDK_HAND1);
00178       break;
00179 
00180     case TEXT_MODE:
00181       gport_set_cursor (GDK_XTERM);
00182       break;
00183 
00184     case RECTANGLE_MODE:
00185       gport_set_cursor (GDK_UL_ANGLE);
00186       break;
00187 
00188     case THERMAL_MODE:
00189       gport_set_cursor (GDK_IRON_CROSS);
00190       break;
00191 
00192     case REMOVE_MODE:
00193       gport_set_cursor (GDK_PIRATE);
00194       break;
00195 
00196     case ROTATE_MODE:
00197       if (ghid_shift_is_pressed ())
00198         gport_set_cursor ((GdkCursorType)CUSTOM_CURSOR_CLOCKWISE);
00199       else
00200         gport_set_cursor (GDK_EXCHANGE);
00201       break;
00202 
00203     case COPY_MODE:
00204     case MOVE_MODE:
00205       gport_set_cursor (GDK_CROSSHAIR);
00206       break;
00207 
00208     case INSERTPOINT_MODE:
00209       gport_set_cursor (GDK_DOTBOX);
00210       break;
00211 
00212     case LOCK_MODE:
00213       gport_set_cursor ((GdkCursorType)CUSTOM_CURSOR_LOCK);
00214     }
00215 }
00216 
00217 void
00218 ghid_corner_cursor (void)
00219 {
00220   GdkCursorType shape;
00221 
00222   if (Crosshair.Y <= Crosshair.AttachedBox.Point1.Y)
00223     shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ?
00224       GDK_UR_ANGLE : GDK_UL_ANGLE;
00225   else
00226     shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ?
00227       GDK_LR_ANGLE : GDK_LL_ANGLE;
00228   if (gport->X_cursor_shape != shape)
00229     gport_set_cursor (shape);
00230 }
00231 
00232 void
00233 ghid_restore_cursor (void)
00234 {
00235   gport_set_cursor (oldCursor);
00236 }
00237 
00238 
00239 
00240   /* =============================================================== */
00241 static gboolean got_location;
00242 
00243   /* If user hits a key instead of the mouse button, we'll abort unless
00244      |  it's the enter key (which accepts the current crosshair location).
00245    */
00246 static gboolean
00247 loop_key_press_cb (GtkWidget * drawing_area, GdkEventKey * kev,
00248                    GMainLoop ** loop)
00249 {
00250   gint ksym = kev->keyval;
00251 
00252   if (ghid_is_modifier_key_sym (ksym))
00253     return TRUE;
00254 
00255   switch (ksym)
00256     {
00257     case GDK_Return:            /* Accept cursor location */
00258       if (g_main_loop_is_running (*loop))
00259         g_main_loop_quit (*loop);
00260       break;
00261 
00262     default:                    /* Abort */
00263       got_location = FALSE;
00264       if (g_main_loop_is_running (*loop))
00265         g_main_loop_quit (*loop);
00266       break;
00267     }
00268   return TRUE;
00269 }
00270 
00271   /* User hit a mouse button in the Output drawing area, so quit the loop
00272      |  and the cursor values when the button was pressed will be used.
00273    */
00274 static gboolean
00275 loop_button_press_cb (GtkWidget * drawing_area, GdkEventButton * ev,
00276                       GMainLoop ** loop)
00277 {
00278   if (g_main_loop_is_running (*loop))
00279     g_main_loop_quit (*loop);
00280   ghid_note_event_location (ev);
00281   return TRUE;
00282 }
00283 
00284   /* Run a glib GMainLoop which intercepts key and mouse button events from
00285      |  the top level loop.  When a mouse or key is hit in the Output drawing
00286      |  area, quit the loop so the top level loop can continue and use the
00287      |  the mouse pointer coordinates at the time of the mouse button event.
00288    */
00289 static gboolean
00290 run_get_location_loop (const gchar * message)
00291 {
00292   GMainLoop *loop;
00293   gulong button_handler, key_handler;
00294   gint oldObjState, oldLineState, oldBoxState;
00295 
00296   /* Make the text cue bold so it hides less and looks like command prompt  */
00297   GString *bold_message = g_string_new (message);
00298   g_string_prepend (bold_message, "<b>");
00299   g_string_append (bold_message, "</b>");
00300 
00301   ghid_status_line_set_text (bold_message->str);
00302 
00303   g_string_free (bold_message, TRUE);
00304 
00305   oldObjState = Crosshair.AttachedObject.State;
00306   oldLineState = Crosshair.AttachedLine.State;
00307   oldBoxState = Crosshair.AttachedBox.State;
00308   notify_crosshair_change (false);
00309   Crosshair.AttachedObject.State = STATE_FIRST;
00310   Crosshair.AttachedLine.State = STATE_FIRST;
00311   Crosshair.AttachedBox.State = STATE_FIRST;
00312   ghid_hand_cursor ();
00313   notify_crosshair_change (true);
00314 
00315   /* Stop the top level GMainLoop from getting user input from keyboard
00316      |  and mouse so we can install our own handlers here.  Also set the
00317      |  control interface insensitive so all the user can do is hit a key
00318      |  or mouse button in the Output drawing area.
00319    */
00320   ghid_interface_input_signals_disconnect ();
00321   ghid_interface_set_sensitive (FALSE);
00322 
00323   got_location = TRUE;          /* Will be unset by hitting most keys */
00324   button_handler =
00325     g_signal_connect (G_OBJECT (gport->drawing_area),
00326                       "button_press_event",
00327                       G_CALLBACK (loop_button_press_cb), &loop);
00328   key_handler =
00329     g_signal_connect (G_OBJECT (gport->top_window),
00330                       "key_press_event",
00331                       G_CALLBACK (loop_key_press_cb), &loop);
00332 
00333   loop = g_main_loop_new (NULL, FALSE);
00334 
00335   GDK_THREADS_LEAVE ();
00336   g_main_loop_run (loop);
00337   GDK_THREADS_ENTER ();
00338 
00339   g_main_loop_unref (loop);
00340 
00341   g_signal_handler_disconnect (gport->drawing_area, button_handler);
00342   g_signal_handler_disconnect (gport->top_window, key_handler);
00343 
00344   ghid_interface_input_signals_connect ();      /* return to normal */
00345   ghid_interface_set_sensitive (TRUE);
00346 
00347   notify_crosshair_change (false);
00348   Crosshair.AttachedObject.State = oldObjState;
00349   Crosshair.AttachedLine.State = oldLineState;
00350   Crosshair.AttachedBox.State = oldBoxState;
00351   notify_crosshair_change (true);
00352   ghid_restore_cursor ();
00353 
00354   ghid_set_status_line_label ();
00355 
00356   return got_location;
00357 }
00358 
00359 
00360 
00361 /* ---------------------------------------------------------------------------*/
00362 void
00363 ghid_get_user_xy (const char *msg)
00364 {
00365   run_get_location_loop (msg);
00366 }
00367 
00368   /* XXX The abort dialog isn't implemented yet in the Gtk port
00369    */
00370 void
00371 ghid_create_abort_dialog (char *msg)
00372 {
00373 }
00374 
00375 gboolean
00376 ghid_check_abort (void)
00377 {
00378   return FALSE;                 /* Abort isn't implemented, so never abort */
00379 }
00380 
00381 void
00382 ghid_end_abort (void)
00383 {
00384 }
00385 
00386 void
00387 ghid_get_pointer (int *x, int *y)
00388 {
00389   gint xp, yp;
00390 
00391   gdk_window_get_pointer (gtk_widget_get_window (gport->drawing_area),
00392                           &xp, &yp, NULL);
00393   if (x)
00394     *x = xp;
00395   if (y)
00396     *y = yp;
00397 }
00398 
00399 /* ---------------------------------------------------------------------------
00400  * output of status line
00401  */
00402 void
00403 ghid_set_status_line_label (void)
00404 {
00405   gchar *flag = TEST_FLAG (ALLDIRECTIONFLAG, PCB)
00406                 ? "all"
00407                 : (PCB->Clipping == 0
00408                     ? "45"
00409                     : (PCB->Clipping == 1
00410                       ? "45_/"
00411                       : "45\\_"));
00412   gchar *text = pcb_g_strdup_printf (
00413         _("%m+<b>view</b>=%s  "
00414           "<b>grid</b>=%$mS  "
00415           "%s%s  "
00416           "<b>line</b>=%mS  "
00417           "<b>via</b>=%mS (%mS)  %s"
00418           "<b>clearance</b>=%mS  "
00419           "<b>text</b>=%i%%  "
00420           "<b>buffer</b>=#%i"),
00421       Settings.grid_unit->allow,
00422       Settings.ShowBottomSide ? C_("status", "bottom") : C_("status", "top"),
00423       PCB->Grid,
00424       flag, TEST_FLAG (RUBBERBANDFLAG, PCB) ? ",R  " : "  ",
00425       Settings.LineThickness,
00426       Settings.ViaThickness,
00427       Settings.ViaDrillingHole,
00428       ghidgui->compact_horizontal ? "\n" : "",
00429       Settings.Keepaway,
00430       Settings.TextScale, Settings.BufferNumber + 1);
00431 
00432   ghid_status_line_set_text (text);
00433   g_free (text);
00434 }
00435 
00436 /* ---------------------------------------------------------------------------
00437  * output of cursor position
00438  */
00439 void
00440 ghid_set_cursor_position_labels (void)
00441 {
00442   gchar *text;
00443 
00444   if (Marked.status)
00445     {
00446       Coord dx = Crosshair.X - Marked.X;
00447       Coord dy = Crosshair.Y - Marked.Y;
00448       Coord r  = Distance (Crosshair.X, Crosshair.Y, Marked.X, Marked.Y);
00449       double a = atan2 (dy, dx) * RAD_TO_DEG;
00450 
00451       text = pcb_g_strdup_printf (_("%m+r %-mS;\nphi %-.1f;\n%-mS %-mS"),
00452                                   Settings.grid_unit->allow,
00453                                   r, a, dx, dy);
00454       ghid_cursor_position_relative_label_set_text (text);
00455       g_free (text);
00456     }
00457   else
00458     ghid_cursor_position_relative_label_set_text (_("r __.__;\nphi __._;\n__.__ __.__"));
00459 
00460 
00461   text = pcb_g_strdup_printf (_("%m+%-mS %-mS"),
00462                               Settings.grid_unit->allow,
00463                               Crosshair.X, Crosshair.Y);
00464   ghid_cursor_position_label_set_text (text);
00465   g_free (text);
00466 }