Introduce POLYGONHOLE_MODE for creating holes in polygons
[geda-pcb/gde.git] / src / hid / gtk / gui-misc.c
bloba607a697aa00780b8c7b969a1e4d1b212fab2af0
1 /* $Id$ */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 Thomas Nau
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 /* This file was originally written by Bill Wilson for the PCB Gtk port */
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
31 #include "global.h"
32 #include "crosshair.h"
33 #include "data.h"
34 #include "misc.h"
35 #include "action.h"
36 #include "set.h"
38 #include "gui.h"
39 #include <gdk/gdkkeysyms.h>
41 #ifdef HAVE_LIBDMALLOC
42 #include <dmalloc.h>
43 #endif
45 RCSID ("$Id$");
47 #define DEFAULT_CURSORSHAPE GDK_CROSSHAIR
49 #define CUSTOM_CURSOR_CLOCKWISE (GDK_LAST_CURSOR + 10)
50 #define CUSTOM_CURSOR_DRAG (GDK_LAST_CURSOR + 11)
51 #define CUSTOM_CURSOR_LOCK (GDK_LAST_CURSOR + 12)
53 #define ICON_X_HOT 8
54 #define ICON_Y_HOT 8
57 GdkPixmap *XC_clock_source, *XC_clock_mask,
58 *XC_hand_source, *XC_hand_mask, *XC_lock_source, *XC_lock_mask;
61 static GdkCursorType oldCursor;
63 void
64 ghid_status_line_set_text (const gchar * text)
66 if (ghidgui->command_entry_status_line_active)
67 return;
69 ghid_label_set_markup (ghidgui->status_line_label, text);
72 void
73 ghid_cursor_position_label_set_text (gchar * text)
75 ghid_label_set_markup (ghidgui->cursor_position_absolute_label, text);
78 void
79 ghid_cursor_position_relative_label_set_text (gchar * text)
81 ghid_label_set_markup (ghidgui->cursor_position_relative_label, text);
84 void
85 ghid_size_increment_get_value (const gchar * saction, gchar ** value,
86 gchar ** units)
88 gdouble increment;
89 gchar *fmt;
90 static gchar s_buf[64];
92 increment = Settings.grid_units_mm
93 ? Settings.size_increment_mm : Settings.size_increment_mil;
94 fmt = (*saction == '+') ? "+%f" : "-%f";
95 snprintf (s_buf, sizeof (s_buf), fmt, increment);
96 *value = s_buf;
97 *units = Settings.grid_units_mm ? "mm" : "mil";
100 void
101 ghid_line_increment_get_value (const gchar * saction, gchar ** value,
102 gchar ** units)
104 gdouble increment;
105 gchar *fmt;
106 static gchar s_buf[64];
108 increment = Settings.grid_units_mm
109 ? Settings.line_increment_mm : Settings.line_increment_mil;
110 fmt = (*saction == '+') ? "+%f" : "-%f";
111 snprintf (s_buf, sizeof (s_buf), fmt, increment);
112 *value = s_buf;
113 *units = Settings.grid_units_mm ? "mm" : "mil";
116 void
117 ghid_clear_increment_get_value (const gchar * saction, gchar ** value,
118 gchar ** units)
120 gdouble increment;
121 gchar *fmt;
122 static gchar s_buf[64];
124 increment = Settings.grid_units_mm
125 ? Settings.clear_increment_mm : Settings.clear_increment_mil;
126 fmt = (*saction == '+') ? "+%f" : "-%f";
127 snprintf (s_buf, sizeof (s_buf), fmt, increment);
128 *value = s_buf;
129 *units = Settings.grid_units_mm ? "mm" : "mil";
133 static GdkCursorType
134 gport_set_cursor (GdkCursorType shape)
136 GdkCursorType old_shape = gport->X_cursor_shape;
137 GdkColor fg = { 0, 65535, 65535, 65535 }; /* white */
138 GdkColor bg = { 0, 0, 0, 0 }; /* black */
140 if (!gport->drawing_area || !gport->drawing_area->window)
141 return 0;
142 if (gport->X_cursor_shape == shape)
143 return shape;
145 /* check if window exists to prevent from fatal errors */
146 if (gport->drawing_area->window)
148 gport->X_cursor_shape = shape;
149 if (shape > GDK_LAST_CURSOR)
151 if (shape == CUSTOM_CURSOR_CLOCKWISE)
152 gport->X_cursor =
153 gdk_cursor_new_from_pixmap (XC_clock_source, XC_clock_mask, &fg,
154 &bg, ICON_X_HOT, ICON_Y_HOT);
155 else if (shape == CUSTOM_CURSOR_DRAG)
156 gport->X_cursor =
157 gdk_cursor_new_from_pixmap (XC_hand_source, XC_hand_mask, &fg,
158 &bg, ICON_X_HOT, ICON_Y_HOT);
159 else if (shape == CUSTOM_CURSOR_LOCK)
160 gport->X_cursor =
161 gdk_cursor_new_from_pixmap (XC_lock_source, XC_lock_mask, &fg,
162 &bg, ICON_X_HOT, ICON_Y_HOT);
164 else
165 gport->X_cursor = gdk_cursor_new (shape);
167 gdk_window_set_cursor (gport->drawing_area->window, gport->X_cursor);
168 gdk_cursor_unref (gport->X_cursor);
170 return (old_shape);
172 return (DEFAULT_CURSORSHAPE);
175 void
176 ghid_point_cursor (void)
178 oldCursor = gport_set_cursor (GDK_DRAPED_BOX);
181 void
182 ghid_hand_cursor (void)
184 oldCursor = gport_set_cursor (GDK_HAND2);
187 void
188 ghid_watch_cursor (void)
190 GdkCursorType tmp;
192 tmp = gport_set_cursor (GDK_WATCH);
193 if (tmp != GDK_WATCH)
194 oldCursor = tmp;
197 void
198 ghid_mode_cursor (int Mode)
200 switch (Mode)
202 case NO_MODE:
203 gport_set_cursor (CUSTOM_CURSOR_DRAG);
204 break;
206 case VIA_MODE:
207 gport_set_cursor (GDK_ARROW);
208 break;
210 case LINE_MODE:
211 gport_set_cursor (GDK_PENCIL);
212 break;
214 case ARC_MODE:
215 gport_set_cursor (GDK_QUESTION_ARROW);
216 break;
218 case ARROW_MODE:
219 gport_set_cursor (GDK_LEFT_PTR);
220 break;
222 case POLYGON_MODE:
223 case POLYGONHOLE_MODE:
224 gport_set_cursor (GDK_SB_UP_ARROW);
225 break;
227 case PASTEBUFFER_MODE:
228 gport_set_cursor (GDK_HAND1);
229 break;
231 case TEXT_MODE:
232 gport_set_cursor (GDK_XTERM);
233 break;
235 case RECTANGLE_MODE:
236 gport_set_cursor (GDK_UL_ANGLE);
237 break;
239 case THERMAL_MODE:
240 gport_set_cursor (GDK_IRON_CROSS);
241 break;
243 case REMOVE_MODE:
244 gport_set_cursor (GDK_PIRATE);
245 break;
247 case ROTATE_MODE:
248 if (ghid_shift_is_pressed ())
249 gport_set_cursor (CUSTOM_CURSOR_CLOCKWISE);
250 else
251 gport_set_cursor (GDK_EXCHANGE);
252 break;
254 case COPY_MODE:
255 case MOVE_MODE:
256 gport_set_cursor (GDK_CROSSHAIR);
257 break;
259 case INSERTPOINT_MODE:
260 gport_set_cursor (GDK_DOTBOX);
261 break;
263 case LOCK_MODE:
264 gport_set_cursor (CUSTOM_CURSOR_LOCK);
268 void
269 ghid_corner_cursor (void)
271 GdkCursorType shape;
273 if (Crosshair.Y <= Crosshair.AttachedBox.Point1.Y)
274 shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ?
275 GDK_UR_ANGLE : GDK_UL_ANGLE;
276 else
277 shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ?
278 GDK_LR_ANGLE : GDK_LL_ANGLE;
279 if (gport->X_cursor_shape != shape)
280 gport_set_cursor (shape);
283 void
284 ghid_restore_cursor (void)
286 gport_set_cursor (oldCursor);
291 /* =============================================================== */
292 static gboolean got_location;
294 /* If user hits a key instead of the mouse button, we'll abort unless
295 | it's one of the cursor keys. Move the layout if a cursor key.
297 static gboolean
298 loop_key_press_cb (GtkWidget * drawing_area, GdkEventKey * kev,
299 GMainLoop ** loop)
301 ModifierKeysState mk;
302 GdkModifierType state;
303 gint ksym = kev->keyval;
305 if (ghid_is_modifier_key_sym (ksym))
306 return TRUE;
307 state = (GdkModifierType) (kev->state);
308 mk = ghid_modifier_keys_state (&state);
310 /* Duplicate the cursor key actions in gui-output-events.c
312 switch (ksym)
314 case GDK_Up:
315 if (mk == CONTROL_PRESSED)
317 hid_actionl ("Display", "Scroll", "8", NULL);
318 hid_actionl ("Display", "Scroll", "0", NULL);
320 else if (mk == SHIFT_PRESSED)
321 hid_actionl ("MovePointer", "0", "-10", NULL);
322 else if (mk == NONE_PRESSED)
323 hid_actionl ("MovePointer", "0", "-1", NULL);
324 break;
326 case GDK_Down:
327 if (mk == CONTROL_PRESSED)
329 hid_actionl ("Display", "Scroll", "2", NULL);
330 hid_actionl ("Display", "Scroll", "0", NULL);
332 else if (mk == SHIFT_PRESSED)
333 hid_actionl ("MovePointer", "0", "10", NULL);
334 else if (mk == NONE_PRESSED)
335 hid_actionl ("MovePointer", "0", "1", NULL);
336 break;
338 case GDK_Left:
339 if (mk == CONTROL_PRESSED)
341 hid_actionl ("Display", "Scroll", "4", NULL);
342 hid_actionl ("Display", "Scroll", "0", NULL);
344 else if (mk == SHIFT_PRESSED)
345 hid_actionl ("MovePointer", "-10", "0", NULL);
346 else if (mk == NONE_PRESSED)
347 hid_actionl ("MovePointer", "-1", "0", NULL);
348 break;
350 case GDK_Right:
351 if (mk == CONTROL_PRESSED)
353 hid_actionl ("Display", "Scroll", "6", NULL);
354 hid_actionl ("Display", "Scroll", "0", NULL);
356 else if (mk == SHIFT_PRESSED)
357 hid_actionl ("MovePointer", "10", "0", NULL);
358 else if (mk == NONE_PRESSED)
359 hid_actionl ("MovePointer", "1", "0", NULL);
360 break;
362 case GDK_Return: /* Accept cursor location */
363 if (g_main_loop_is_running (*loop))
364 g_main_loop_quit (*loop);
365 break;
367 default: /* Abort */
368 got_location = FALSE;
369 if (g_main_loop_is_running (*loop))
370 g_main_loop_quit (*loop);
371 break;
373 return TRUE;
376 /* User hit a mouse button in the Output drawing area, so quit the loop
377 | and the cursor values when the button was pressed will be used.
379 static gboolean
380 loop_button_press_cb (GtkWidget * drawing_area, GdkEventButton * ev,
381 GMainLoop ** loop)
383 if (g_main_loop_is_running (*loop))
384 g_main_loop_quit (*loop);
385 ghid_note_event_location (ev);
386 return TRUE;
389 /* Run a glib GMainLoop which intercepts key and mouse button events from
390 | the top level loop. When a mouse or key is hit in the Output drawing
391 | area, quit the loop so the top level loop can continue and use the
392 | the mouse pointer coordinates at the time of the mouse button event.
394 static gboolean
395 run_get_location_loop (const gchar * message)
397 GMainLoop *loop;
398 gulong button_handler, key_handler;
399 gint oldObjState, oldLineState, oldBoxState;
401 ghid_status_line_set_text (message);
403 oldObjState = Crosshair.AttachedObject.State;
404 oldLineState = Crosshair.AttachedLine.State;
405 oldBoxState = Crosshair.AttachedBox.State;
406 HideCrosshair (true);
407 Crosshair.AttachedObject.State = STATE_FIRST;
408 Crosshair.AttachedLine.State = STATE_FIRST;
409 Crosshair.AttachedBox.State = STATE_FIRST;
410 ghid_hand_cursor ();
411 RestoreCrosshair (true);
413 /* Stop the top level GMainLoop from getting user input from keyboard
414 | and mouse so we can install our own handlers here. Also set the
415 | control interface insensitive so all the user can do is hit a key
416 | or mouse button in the Output drawing area.
418 ghid_interface_input_signals_disconnect ();
419 ghid_interface_set_sensitive (FALSE);
421 got_location = TRUE; /* Will be unset by hitting most keys */
422 button_handler =
423 g_signal_connect (G_OBJECT (gport->drawing_area),
424 "button_press_event",
425 G_CALLBACK (loop_button_press_cb), &loop);
426 key_handler =
427 g_signal_connect (G_OBJECT (gport->top_window),
428 "key_press_event",
429 G_CALLBACK (loop_key_press_cb), &loop);
431 loop = g_main_loop_new (NULL, FALSE);
432 g_main_loop_run (loop);
434 g_main_loop_unref (loop);
436 g_signal_handler_disconnect (gport->drawing_area, button_handler);
437 g_signal_handler_disconnect (gport->top_window, key_handler);
439 ghid_interface_input_signals_connect (); /* return to normal */
440 ghid_interface_set_sensitive (TRUE);
442 HideCrosshair (true);
443 Crosshair.AttachedObject.State = oldObjState;
444 Crosshair.AttachedLine.State = oldLineState;
445 Crosshair.AttachedBox.State = oldBoxState;
446 RestoreCrosshair (true);
447 ghid_restore_cursor ();
449 ghid_set_status_line_label ();
451 return got_location;
456 /* ---------------------------------------------------------------------------*/
457 void
458 ghid_get_user_xy (const char *msg)
460 run_get_location_loop (msg);
463 /* XXX The abort dialog isn't implemented yet in the Gtk port
465 void
466 ghid_create_abort_dialog (char *msg)
470 gboolean
471 ghid_check_abort (void)
473 return FALSE; /* Abort isn't implemented, so never abort */
476 void
477 ghid_end_abort (void)
481 void
482 ghid_get_pointer (int *x, int *y)
484 gint xp, yp;
486 gdk_window_get_pointer (gport->drawing_area->window, &xp, &yp, NULL);
487 if (x)
488 *x = xp;
489 if (y)
490 *y = yp;
493 /* ---------------------------------------------------------------------------
494 * output of status line
496 void
497 ghid_set_status_line_label (void)
499 gchar text[512];
501 if (!Settings.grid_units_mm)
502 snprintf (text, sizeof (text),
503 _("<b>%c view</b>=%s "
504 "<b>grid</b>=%.1f:%i "
505 "%s%s "
506 "<b>line</b>=%.1f "
507 "<b>via</b>=%.1f(%.1f) %s"
508 "<b>clearance</b>=%.1f "
509 "<b>text</b>=%i%% "
510 "<b>buffer</b>=#%i"),
511 PCB->Changed ? '*' : ' ',
512 Settings.ShowSolderSide ? _("solder") : _("component"),
513 PCB->Grid / 100.0,
514 (int) Settings.GridFactor,
515 TEST_FLAG (ALLDIRECTIONFLAG, PCB) ? "all" :
516 (PCB->Clipping == 0 ? "45" :
517 (PCB->Clipping == 1 ? "45_/" : "45\\_")),
518 TEST_FLAG (RUBBERBANDFLAG, PCB) ? ",R " : " ",
519 Settings.LineThickness / 100.0,
520 Settings.ViaThickness / 100.0,
521 Settings.ViaDrillingHole / 100.0,
522 ghidgui->compact_horizontal ? "\n" : "",
523 Settings.Keepaway / 100.0,
524 Settings.TextScale, Settings.BufferNumber + 1);
525 else
526 snprintf (text, sizeof (text),
527 _("<b>%c view</b>=%s "
528 "<b>grid</b>=%5.3f:%i "
529 "%s%s "
530 "<b>line</b>=%5.3f "
531 "<b>via</b>=%5.3f(%5.3f) %s"
532 "<b>clearance</b>=%5.3f "
533 "<b>text</b>=%i%% "
534 "<b>buffer</b>=#%i"),
535 PCB->Changed ? '*' : ' ',
536 Settings.ShowSolderSide ? _("solder") : _("component"),
537 PCB->Grid * COOR_TO_MM, (int) Settings.GridFactor,
538 TEST_FLAG (ALLDIRECTIONFLAG, PCB) ? "all" :
539 (PCB->Clipping == 0 ? "45" :
540 (PCB->Clipping == 1 ? "45_/" : "45\\_")),
541 TEST_FLAG (RUBBERBANDFLAG, PCB) ? ",R " : " ",
542 Settings.LineThickness * COOR_TO_MM,
543 Settings.ViaThickness * COOR_TO_MM,
544 Settings.ViaDrillingHole * COOR_TO_MM,
545 ghidgui->compact_horizontal ? "\n" : "",
546 Settings.Keepaway * COOR_TO_MM,
547 Settings.TextScale, Settings.BufferNumber + 1);
549 ghid_status_line_set_text (text);
552 /* ---------------------------------------------------------------------------
553 * output of cursor position
555 void
556 ghid_set_cursor_position_labels (void)
558 gchar text[128];
560 if (Marked.status)
561 {double scale, dx, dy, r, a;
562 scale = Settings.grid_units_mm ? COOR_TO_MM: 1. / 100;
563 dx = (Crosshair.X - Marked.X) * scale;
564 dy = (Marked.Y - Crosshair.Y) * scale;
565 r = sqrt( dx * dx + dy * dy);
566 a = atan2(dy, dx) * 180 / M_PI;
567 if (Settings.grid_units_mm)
568 snprintf (text, sizeof (text), "r %-.4f; phi %-.1f; %-.4f %-.4f",
569 r, a, dx, dy);
571 else
572 snprintf (text, sizeof (text), "r %-.2f; phi %-.1f; %-.2f %-.2f",
573 r, a, dx, dy);
574 ghid_cursor_position_relative_label_set_text (text);
576 else
577 ghid_cursor_position_relative_label_set_text ("r __.__; phi __._; __.__ __.__");
579 if (Settings.grid_units_mm)
580 snprintf (text, sizeof (text), "%-.4f %-.4f",
581 COOR_TO_MM * Crosshair.X, COOR_TO_MM * Crosshair.Y);
582 else
583 snprintf (text, sizeof (text), "%-.2f %-.2f",
584 Crosshair.X / 100., Crosshair.Y / 100.);
586 ghid_cursor_position_label_set_text (text);