Fix lockup when a scroll event is received outside the drawing area
[geda-pcb/pcjc2.git] / src / hid / gtk / gui-misc.c
blob31276921e832ea0873ec37e73d83a9a034c23dbb
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
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.
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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /* This file was originally written by Bill Wilson for the PCB Gtk port */
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include "global.h"
30 #include "crosshair.h"
31 #include "data.h"
32 #include "misc.h"
33 #include "action.h"
34 #include "set.h"
35 #include "pcb-printf.h"
37 #include "gui.h"
38 #include <gdk/gdkkeysyms.h>
40 #ifdef HAVE_LIBDMALLOC
41 #include <dmalloc.h>
42 #endif
44 #define CUSTOM_CURSOR_CLOCKWISE (GDK_LAST_CURSOR + 10)
45 #define CUSTOM_CURSOR_DRAG (GDK_LAST_CURSOR + 11)
46 #define CUSTOM_CURSOR_LOCK (GDK_LAST_CURSOR + 12)
48 #define ICON_X_HOT 8
49 #define ICON_Y_HOT 8
52 GdkPixmap *XC_clock_source, *XC_clock_mask,
53 *XC_hand_source, *XC_hand_mask, *XC_lock_source, *XC_lock_mask;
56 static GdkCursorType oldCursor;
58 void
59 ghid_status_line_set_text (const gchar * text)
61 if (ghidgui->command_entry_status_line_active)
62 return;
64 ghid_label_set_markup (ghidgui->status_line_label, text);
67 void
68 ghid_cursor_position_label_set_text (gchar * text)
70 ghid_label_set_markup (ghidgui->cursor_position_absolute_label, text);
73 void
74 ghid_cursor_position_relative_label_set_text (gchar * text)
76 ghid_label_set_markup (ghidgui->cursor_position_relative_label, text);
79 static GdkCursorType
80 gport_set_cursor (GdkCursorType shape)
82 GdkWindow *window;
83 GdkCursorType old_shape = gport->X_cursor_shape;
84 GdkColor fg = { 0, 65535, 65535, 65535 }; /* white */
85 GdkColor bg = { 0, 0, 0, 0 }; /* black */
87 if (gport->drawing_area == NULL)
88 return GDK_X_CURSOR;
90 window = gtk_widget_get_window (gport->drawing_area);
92 if (gport->X_cursor_shape == shape)
93 return shape;
95 /* check if window exists to prevent from fatal errors */
96 if (window == NULL)
97 return GDK_X_CURSOR;
99 gport->X_cursor_shape = shape;
100 if (shape > GDK_LAST_CURSOR)
102 if (shape == CUSTOM_CURSOR_CLOCKWISE)
103 gport->X_cursor =
104 gdk_cursor_new_from_pixmap (XC_clock_source, XC_clock_mask, &fg,
105 &bg, ICON_X_HOT, ICON_Y_HOT);
106 else if (shape == CUSTOM_CURSOR_DRAG)
107 gport->X_cursor =
108 gdk_cursor_new_from_pixmap (XC_hand_source, XC_hand_mask, &fg,
109 &bg, ICON_X_HOT, ICON_Y_HOT);
110 else if (shape == CUSTOM_CURSOR_LOCK)
111 gport->X_cursor =
112 gdk_cursor_new_from_pixmap (XC_lock_source, XC_lock_mask, &fg,
113 &bg, ICON_X_HOT, ICON_Y_HOT);
115 else
116 gport->X_cursor = gdk_cursor_new (shape);
118 gdk_window_set_cursor (window, gport->X_cursor);
119 gdk_cursor_unref (gport->X_cursor);
121 return old_shape;
124 void
125 ghid_point_cursor (void)
127 oldCursor = gport_set_cursor (GDK_DRAPED_BOX);
130 void
131 ghid_hand_cursor (void)
133 oldCursor = gport_set_cursor (GDK_HAND2);
136 void
137 ghid_watch_cursor (void)
139 GdkCursorType tmp;
141 tmp = gport_set_cursor (GDK_WATCH);
142 if (tmp != GDK_WATCH)
143 oldCursor = tmp;
146 void
147 ghid_mode_cursor (int Mode)
149 switch (Mode)
151 case NO_MODE:
152 gport_set_cursor ((GdkCursorType)CUSTOM_CURSOR_DRAG);
153 break;
155 case VIA_MODE:
156 gport_set_cursor (GDK_ARROW);
157 break;
159 case LINE_MODE:
160 gport_set_cursor (GDK_PENCIL);
161 break;
163 case ARC_MODE:
164 gport_set_cursor (GDK_QUESTION_ARROW);
165 break;
167 case ARROW_MODE:
168 gport_set_cursor (GDK_LEFT_PTR);
169 break;
171 case POLYGON_MODE:
172 case POLYGONHOLE_MODE:
173 gport_set_cursor (GDK_SB_UP_ARROW);
174 break;
176 case PASTEBUFFER_MODE:
177 gport_set_cursor (GDK_HAND1);
178 break;
180 case TEXT_MODE:
181 gport_set_cursor (GDK_XTERM);
182 break;
184 case RECTANGLE_MODE:
185 gport_set_cursor (GDK_UL_ANGLE);
186 break;
188 case THERMAL_MODE:
189 gport_set_cursor (GDK_IRON_CROSS);
190 break;
192 case REMOVE_MODE:
193 gport_set_cursor (GDK_PIRATE);
194 break;
196 case ROTATE_MODE:
197 if (ghid_shift_is_pressed ())
198 gport_set_cursor ((GdkCursorType)CUSTOM_CURSOR_CLOCKWISE);
199 else
200 gport_set_cursor (GDK_EXCHANGE);
201 break;
203 case COPY_MODE:
204 case MOVE_MODE:
205 gport_set_cursor (GDK_CROSSHAIR);
206 break;
208 case INSERTPOINT_MODE:
209 gport_set_cursor (GDK_DOTBOX);
210 break;
212 case LOCK_MODE:
213 gport_set_cursor ((GdkCursorType)CUSTOM_CURSOR_LOCK);
217 void
218 ghid_corner_cursor (void)
220 GdkCursorType shape;
222 if (Crosshair.Y <= Crosshair.AttachedBox.Point1.Y)
223 shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ?
224 GDK_UR_ANGLE : GDK_UL_ANGLE;
225 else
226 shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ?
227 GDK_LR_ANGLE : GDK_LL_ANGLE;
228 if (gport->X_cursor_shape != shape)
229 gport_set_cursor (shape);
232 void
233 ghid_restore_cursor (void)
235 gport_set_cursor (oldCursor);
240 /* =============================================================== */
241 static gboolean got_location;
243 /* If user hits a key instead of the mouse button, we'll abort unless
244 | it's the enter key (which accepts the current crosshair location).
246 static gboolean
247 loop_key_press_cb (GtkWidget * drawing_area, GdkEventKey * kev,
248 GMainLoop ** loop)
250 gint ksym = kev->keyval;
252 if (ghid_is_modifier_key_sym (ksym))
253 return TRUE;
255 switch (ksym)
257 case GDK_Return: /* Accept cursor location */
258 if (g_main_loop_is_running (*loop))
259 g_main_loop_quit (*loop);
260 break;
262 default: /* Abort */
263 got_location = FALSE;
264 if (g_main_loop_is_running (*loop))
265 g_main_loop_quit (*loop);
266 break;
268 return TRUE;
271 /* User hit a mouse button in the Output drawing area, so quit the loop
272 | and the cursor values when the button was pressed will be used.
274 static gboolean
275 loop_button_press_cb (GtkWidget * drawing_area, GdkEventButton * ev,
276 GMainLoop ** loop)
278 if (g_main_loop_is_running (*loop))
279 g_main_loop_quit (*loop);
280 ghid_note_event_location (ev);
281 return TRUE;
284 /* Run a glib GMainLoop which intercepts key and mouse button events from
285 | the top level loop. When a mouse or key is hit in the Output drawing
286 | area, quit the loop so the top level loop can continue and use the
287 | the mouse pointer coordinates at the time of the mouse button event.
289 static gboolean
290 run_get_location_loop (const gchar * message)
292 GMainLoop *loop;
293 gulong button_handler, key_handler;
294 gint oldObjState, oldLineState, oldBoxState;
296 ghid_status_line_set_text (message);
298 oldObjState = Crosshair.AttachedObject.State;
299 oldLineState = Crosshair.AttachedLine.State;
300 oldBoxState = Crosshair.AttachedBox.State;
301 notify_crosshair_change (false);
302 Crosshair.AttachedObject.State = STATE_FIRST;
303 Crosshair.AttachedLine.State = STATE_FIRST;
304 Crosshair.AttachedBox.State = STATE_FIRST;
305 ghid_hand_cursor ();
306 notify_crosshair_change (true);
308 /* Stop the top level GMainLoop from getting user input from keyboard
309 | and mouse so we can install our own handlers here. Also set the
310 | control interface insensitive so all the user can do is hit a key
311 | or mouse button in the Output drawing area.
313 ghid_interface_input_signals_disconnect ();
314 ghid_interface_set_sensitive (FALSE);
316 got_location = TRUE; /* Will be unset by hitting most keys */
317 button_handler =
318 g_signal_connect (G_OBJECT (gport->drawing_area),
319 "button_press_event",
320 G_CALLBACK (loop_button_press_cb), &loop);
321 key_handler =
322 g_signal_connect (G_OBJECT (gport->top_window),
323 "key_press_event",
324 G_CALLBACK (loop_key_press_cb), &loop);
326 loop = g_main_loop_new (NULL, FALSE);
328 GDK_THREADS_LEAVE ();
329 g_main_loop_run (loop);
330 GDK_THREADS_ENTER ();
332 g_main_loop_unref (loop);
334 g_signal_handler_disconnect (gport->drawing_area, button_handler);
335 g_signal_handler_disconnect (gport->top_window, key_handler);
337 ghid_interface_input_signals_connect (); /* return to normal */
338 ghid_interface_set_sensitive (TRUE);
340 notify_crosshair_change (false);
341 Crosshair.AttachedObject.State = oldObjState;
342 Crosshair.AttachedLine.State = oldLineState;
343 Crosshair.AttachedBox.State = oldBoxState;
344 notify_crosshair_change (true);
345 ghid_restore_cursor ();
347 ghid_set_status_line_label ();
349 return got_location;
354 /* ---------------------------------------------------------------------------*/
355 void
356 ghid_get_user_xy (const char *msg)
358 run_get_location_loop (msg);
361 /* XXX The abort dialog isn't implemented yet in the Gtk port
363 void
364 ghid_create_abort_dialog (char *msg)
368 gboolean
369 ghid_check_abort (void)
371 return FALSE; /* Abort isn't implemented, so never abort */
374 void
375 ghid_end_abort (void)
379 void
380 ghid_get_pointer (int *x, int *y)
382 gint xp, yp;
384 gdk_window_get_pointer (gtk_widget_get_window (gport->drawing_area),
385 &xp, &yp, NULL);
386 if (x)
387 *x = xp;
388 if (y)
389 *y = yp;
392 /* ---------------------------------------------------------------------------
393 * output of status line
395 void
396 ghid_set_status_line_label (void)
398 gchar *flag = TEST_FLAG (ALLDIRECTIONFLAG, PCB)
399 ? "all"
400 : (PCB->Clipping == 0
401 ? "45"
402 : (PCB->Clipping == 1
403 ? "45_/"
404 : "45\\_"));
405 gchar *text = pcb_g_strdup_printf (
406 _("%m+<b>view</b>=%s "
407 "<b>grid</b>=%$mS "
408 "%s%s "
409 "<b>line</b>=%mS "
410 "<b>via</b>=%mS (%mS) %s"
411 "<b>clearance</b>=%mS "
412 "<b>text</b>=%i%% "
413 "<b>buffer</b>=#%i"),
414 Settings.grid_unit->allow,
415 Settings.ShowSolderSide ? _("bottom") : _("top"),
416 PCB->Grid,
417 flag, TEST_FLAG (RUBBERBANDFLAG, PCB) ? ",R " : " ",
418 Settings.LineThickness,
419 Settings.ViaThickness,
420 Settings.ViaDrillingHole,
421 ghidgui->compact_horizontal ? "\n" : "",
422 Settings.Keepaway,
423 Settings.TextScale, Settings.BufferNumber + 1);
425 ghid_status_line_set_text (text);
426 g_free (text);
429 /* ---------------------------------------------------------------------------
430 * output of cursor position
432 void
433 ghid_set_cursor_position_labels (void)
435 gchar *text;
437 if (Marked.status)
439 Coord dx = Crosshair.X - Marked.X;
440 Coord dy = Crosshair.Y - Marked.Y;
441 Coord r = Distance (Crosshair.X, Crosshair.Y, Marked.X, Marked.Y);
442 double a = atan2 (dy, dx) * RAD_TO_DEG;
444 text = pcb_g_strdup_printf ("%m+r %-mS; phi %-.1f; %-mS %-mS",
445 Settings.grid_unit->allow,
446 r, a, dx, dy);
447 ghid_cursor_position_relative_label_set_text (text);
448 g_free (text);
450 else
451 ghid_cursor_position_relative_label_set_text ("r __.__; phi __._; __.__ __.__");
454 text = pcb_g_strdup_printf ("%m+%-mS %-mS",
455 Settings.grid_unit->allow,
456 Crosshair.X, Crosshair.Y);
457 ghid_cursor_position_label_set_text (text);
458 g_free (text);