missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / gschem_page_view.c
blob981e89de2a6b283246e10a4f8cfb9f580370413a
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 /*!
21 * \file gschem_page_view.c
23 * \brief A widget for viewing a schematic page
26 #include <config.h>
28 #include <stdio.h>
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
36 #include <math.h>
38 #include "gschem.h"
39 #include <gdk/gdkkeysyms.h>
41 #include "gtk/gtkmarshal.h"
45 #define INVALIDATE_MARGIN 1
49 enum
51 PROP_0,
52 PROP_HADJUSTMENT,
53 PROP_PAGE,
54 PROP_PAGE_GEOMETRY,
55 PROP_VADJUSTMENT
59 typedef void (*NotifyFunction) (void*,void*);
61 static void
62 dispose (GObject *object);
64 static void
65 finalize (GObject *object);
67 static void
68 get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec);
70 static void
71 gschem_page_view_class_init (GschemPageViewClass *klass);
73 static void
74 gschem_page_view_init (GschemPageView *view);
76 static void
77 gschem_page_view_update_hadjustment (GschemPageView *view);
79 static void
80 gschem_page_view_update_vadjustment (GschemPageView *view);
82 static void
83 hadjustment_value_changed (GtkAdjustment *vadjustment, GschemPageView *view);
85 static void
86 remove_page_weak_reference (PAGE *page, gpointer geometry, GschemPageView *view);
88 static void
89 page_deleted (PAGE *page, GschemPageView *view);
91 static void
92 set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec);
94 static void
95 set_scroll_adjustments (GschemPageView *view, GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
97 static void
98 vadjustment_value_changed (GtkAdjustment *vadjustment, GschemPageView *view);
102 static GObjectClass *gschem_page_view_parent_class = NULL;
106 * In later versions of GTK+, the GtkScrolledWindow uses an interface, instead
107 * of signals, to set the scrollbar adjustments. When Gschem uses on of these
108 * more recent version of GTK+, this function will no longer be needed.
110 static void
111 cclosure_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
112 GValue *return_value G_GNUC_UNUSED,
113 guint n_param_values,
114 const GValue *param_values,
115 gpointer invocation_hint G_GNUC_UNUSED,
116 gpointer marshal_data)
118 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
119 gpointer arg_1,
120 gpointer arg_2,
121 gpointer data2);
123 register GMarshalFunc_VOID__OBJECT_OBJECT callback;
124 register GCClosure *cc = (GCClosure*) closure;
125 register gpointer data1, data2;
127 g_return_if_fail (n_param_values == 3);
129 if (G_CCLOSURE_SWAP_DATA (closure)) {
130 data1 = closure->data;
131 data2 = g_value_peek_pointer (param_values + 0);
133 else {
134 data1 = g_value_peek_pointer (param_values + 0);
135 data2 = closure->data;
138 callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
140 callback (data1,
141 g_value_get_object (param_values + 1),
142 g_value_get_object (param_values + 2),
143 data2);
148 /*! \brief Dispose of the object
150 static void
151 dispose (GObject *object)
153 GschemPageView *view;
155 g_return_if_fail (object != NULL);
156 view = GSCHEM_PAGE_VIEW (object);
157 g_return_if_fail (view != NULL);
159 gschem_page_view_set_hadjustment (view, NULL);
160 gschem_page_view_set_vadjustment (view, NULL);
162 g_hash_table_foreach (view->geometry_table, (GHFunc)remove_page_weak_reference, view);
163 g_hash_table_remove_all (view->geometry_table);
165 /* We aren't bothering about invoking
166 * gschem_page_view_set_page (view, NULL)
167 * directly here since the current page itself must use its weak
168 * reference to call cleanup closure (page_deleted) for this
169 * view */
171 /* lastly, chain up to the parent dispose */
173 g_return_if_fail (gschem_page_view_parent_class != NULL);
174 gschem_page_view_parent_class->dispose (object);
179 /*! \brief Event handler for window realized
181 static void
182 event_realize(GtkWidget *widget, gpointer unused)
184 GschemPageView *view = GSCHEM_PAGE_VIEW(widget);
185 GdkWindow *window = gtk_widget_get_window (widget);
187 g_return_if_fail (view != NULL);
188 g_return_if_fail (window != NULL);
190 gtk_widget_get_allocation (widget, &(view->previous_allocation));
195 /*! \brief Event handler for window unrealized
197 static void
198 event_unrealize(GtkWidget *widget, gpointer unused)
200 GschemPageView *view = GSCHEM_PAGE_VIEW(widget);
202 g_return_if_fail (view != NULL);
207 /*! \brief Finalize object
209 static void
210 finalize (GObject *object)
212 GschemPageView *view = GSCHEM_PAGE_VIEW (object);
214 g_return_if_fail (view != NULL);
215 g_return_if_fail (view->geometry_table != NULL);
217 g_hash_table_destroy (view->geometry_table);
219 /* lastly, chain up to the parent finalize */
221 g_return_if_fail (gschem_page_view_parent_class != NULL);
222 gschem_page_view_parent_class->finalize (object);
227 /*! \brief Get a property
229 * \param [in] object
230 * \param [in] param_id
231 * \param [in,out] value
232 * \param [in] pspec
234 static void
235 get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec)
237 GschemPageView *view = GSCHEM_PAGE_VIEW (object);
239 switch (param_id) {
240 case PROP_HADJUSTMENT:
241 g_value_set_object (value, gschem_page_view_get_hadjustment (view));
242 break;
244 case PROP_PAGE:
245 g_value_set_pointer (value, gschem_page_view_get_page (view));
246 break;
248 case PROP_PAGE_GEOMETRY:
249 g_value_set_boxed (value, gschem_page_view_get_page_geometry (view));
250 break;
252 case PROP_VADJUSTMENT:
253 g_value_set_object (value, gschem_page_view_get_vadjustment (view));
254 break;
256 default:
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
263 /*! \brief Initialize GschemPageView class
265 * \param [in] klass The class for the GschemPageView
267 static void
268 gschem_page_view_class_init (GschemPageViewClass *klass)
270 gschem_page_view_parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
272 G_OBJECT_CLASS (klass)->dispose = dispose;
273 G_OBJECT_CLASS (klass)->finalize = finalize;
275 G_OBJECT_CLASS (klass)->get_property = get_property;
276 G_OBJECT_CLASS (klass)->set_property = set_property;
278 g_object_class_install_property (G_OBJECT_CLASS (klass),
279 PROP_HADJUSTMENT,
280 g_param_spec_object ("hadjustment",
281 "Horizontal adjustment",
282 "Horizontal adjustment",
283 GTK_TYPE_ADJUSTMENT,
284 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
286 g_object_class_install_property (G_OBJECT_CLASS (klass),
287 PROP_PAGE,
288 g_param_spec_pointer ("page",
289 "Page",
290 "Page",
291 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 g_object_class_install_property (G_OBJECT_CLASS (klass),
294 PROP_PAGE_GEOMETRY,
295 g_param_spec_boxed ("page-geometry",
296 "Page Geometry",
297 "Page Geometry",
298 GSCHEM_TYPE_PAGE_GEOMETRY,
299 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
301 g_object_class_install_property (G_OBJECT_CLASS (klass),
302 PROP_VADJUSTMENT,
303 g_param_spec_object ("vadjustment",
304 "Vertical adjustment",
305 "Vertical adjustment",
306 GTK_TYPE_ADJUSTMENT,
307 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
309 GTK_WIDGET_CLASS (klass)->set_scroll_adjustments_signal = g_signal_new (
310 "set-scroll-adjustments",
311 G_OBJECT_CLASS_TYPE (klass),
312 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
314 NULL,
315 NULL,
316 cclosure_marshal_VOID__OBJECT_OBJECT,
317 G_TYPE_NONE,
319 GTK_TYPE_ADJUSTMENT,
320 GTK_TYPE_ADJUSTMENT);
322 g_signal_new (
323 "update-grid-info",
324 G_OBJECT_CLASS_TYPE (klass),
325 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
327 NULL,
328 NULL,
329 g_cclosure_marshal_VOID__VOID,
330 G_TYPE_NONE,
336 /*! \brief Get the horizontal adjustment for this view
338 * \param [in] view The view
339 * \return The horizontal adjustment for this view
341 GtkAdjustment*
342 gschem_page_view_get_hadjustment (GschemPageView *view)
344 g_return_val_if_fail (view != NULL, NULL);
346 return view->hadjustment;
351 /*! \brief Get page for this view
353 * \param [in] view The view
354 * \return The page for the view
356 PAGE*
357 gschem_page_view_get_page (GschemPageView *view)
359 g_return_val_if_fail (view != NULL, NULL);
361 return view->page;
366 /*! \brief Get page geometry for this view
368 * \param [in] view The view
369 * \return The page geometry for the view
371 GschemPageGeometry*
372 gschem_page_view_get_page_geometry (GschemPageView *view)
374 PAGE *page = NULL;
375 GschemPageGeometry *geometry = NULL;
376 GdkWindow *window = NULL;
377 int screen_width;
378 int screen_height;
380 g_return_val_if_fail (view != NULL, NULL);
382 page = gschem_page_view_get_page (view);
383 if (page == NULL) {
384 return NULL;
387 geometry = g_hash_table_lookup (view->geometry_table, page);
389 window = gtk_widget_get_window (GTK_WIDGET (view));
390 g_return_val_if_fail (window != NULL, NULL);
391 screen_width = gdk_window_get_width (window);
392 screen_height = gdk_window_get_height (window);
394 if (geometry == NULL) {
395 geometry = gschem_page_geometry_new_with_values (screen_width,
396 screen_height,
397 view->page->toplevel->init_left,
398 view->page->toplevel->init_top,
399 view->page->toplevel->init_right,
400 view->page->toplevel->init_bottom,
401 view->page->toplevel->init_left,
402 view->page->toplevel->init_top,
403 view->page->toplevel->init_right,
404 view->page->toplevel->init_bottom);
406 g_hash_table_insert (view->geometry_table, page, geometry);
408 gschem_page_geometry_zoom_extents (geometry,
409 view->page->toplevel,
410 s_page_objects (page));
412 else {
413 gschem_page_geometry_set_values (geometry,
414 max (fabs ((double)(gschem_page_geometry_get_viewport_right (geometry) - gschem_page_geometry_get_viewport_left (geometry)) / screen_width), (fabs ((double)(gschem_page_geometry_get_viewport_top (geometry) - gschem_page_geometry_get_viewport_bottom (geometry)) / screen_height))),
415 screen_width,
416 screen_height,
417 gschem_page_geometry_get_viewport_left (geometry),
418 gschem_page_geometry_get_viewport_top (geometry),
419 gschem_page_geometry_get_viewport_right (geometry),
420 gschem_page_geometry_get_viewport_bottom (geometry));
423 return geometry;
428 /*! \brief Get/register GschemPageView type.
430 GType
431 gschem_page_view_get_type ()
433 static GType type = 0;
435 if (type == 0) {
436 static const GTypeInfo info = {
437 sizeof(GschemPageViewClass),
438 NULL, /* base_init */
439 NULL, /* base_finalize */
440 (GClassInitFunc) gschem_page_view_class_init,
441 NULL, /* class_finalize */
442 NULL, /* class_data */
443 sizeof(GschemPageView),
444 0, /* n_preallocs */
445 (GInstanceInitFunc) gschem_page_view_init,
448 type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "GschemPageView", &info, 0);
451 return type;
456 /*! \brief Get the vertical adjustment for this view
458 * \param [in] view The view
459 * \return The vertical adjustment for this view
461 GtkAdjustment*
462 gschem_page_view_get_vadjustment (GschemPageView *view)
464 g_return_val_if_fail (view != NULL, NULL);
466 return view->vadjustment;
471 /*! \brief Schedule redraw for the entire window
473 * \param [in,out] view The Gschem page view to redraw
475 void
476 gschem_page_view_invalidate_all (GschemPageView *view)
478 GdkWindow *window;
480 /* this function can be called early during initialization */
481 if (view == NULL) {
482 return;
485 window = gtk_widget_get_window (GTK_WIDGET (view));
487 if (window == NULL) {
488 return;
491 gdk_window_invalidate_rect (window, NULL, FALSE);
496 /*! \brief Schedule redraw of the given object
498 * \param [in,out] view The Gschem page view to redraw
499 * \param [in] object The object to redraw
501 void
502 gschem_page_view_invalidate_object (GschemPageView *view, OBJECT *object)
504 g_return_if_fail (object != NULL);
505 g_return_if_fail (view != NULL);
507 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (view))) {
508 return;
511 PAGE *page = gschem_page_view_get_page (view);
513 if (page != NULL) {
514 gboolean success;
515 int world_bottom;
516 int world_right;
517 int world_left;
518 int world_top;
520 success = world_get_single_object_bounds (page->toplevel,
521 object,
522 &world_left,
523 &world_top,
524 &world_right,
525 &world_bottom);
527 if (success) {
528 gschem_page_view_invalidate_world_rect (view,
529 world_left,
530 world_top,
531 world_right,
532 world_bottom);
539 /*! \brief Schedule redraw of the given rectange
541 * \param [in,out] view The Gschem page view to redraw
542 * \param [in] left
543 * \param [in] top
544 * \param [in] right
545 * \param [in] bottom
547 void
548 gschem_page_view_invalidate_screen_rect (GschemPageView *view, int left, int top, int right, int bottom)
550 int bloat;
551 int cue_half_size;
552 int grip_half_size;
553 GdkRectangle rect;
554 GdkWindow *window;
556 g_return_if_fail (view != NULL);
558 window = gtk_widget_get_window (GTK_WIDGET (view));
560 if (window == NULL) {
561 return;
564 grip_half_size = GRIP_SIZE / 2;
565 cue_half_size = gschem_page_view_SCREENabs (view, CUE_BOX_SIZE);
566 bloat = MAX (grip_half_size, cue_half_size) + INVALIDATE_MARGIN;
568 rect.x = MIN(left, right) - bloat;
569 rect.y = MIN(top, bottom) - bloat;
570 rect.width = 1 + abs( left - right ) + 2 * bloat;
571 rect.height = 1 + abs( top - bottom ) + 2 * bloat;
573 gdk_window_invalidate_rect (window, &rect, FALSE);
578 /*! \brief Schedule redraw of the given rectange
580 * \param [in,out] view The Gschem page view to redraw
581 * \param [in] left
582 * \param [in] top
583 * \param [in] right
584 * \param [in] bottom
586 void
587 gschem_page_view_invalidate_world_rect (GschemPageView *view, int left, int top, int right, int bottom)
589 int screen_bottom;
590 int screen_right;
591 int screen_left;
592 int screen_top;
594 g_return_if_fail (view != NULL);
596 gschem_page_view_WORLDtoSCREEN (view, left, top, &screen_left, &screen_top);
597 gschem_page_view_WORLDtoSCREEN (view, right, bottom, &screen_right, &screen_bottom);
599 gschem_page_view_invalidate_screen_rect (view,
600 screen_left,
601 screen_top,
602 screen_right,
603 screen_bottom);
608 /*! \brief Initialize GschemPageView instance
610 * \param [in,out] view the gschem page view
612 static void
613 gschem_page_view_init (GschemPageView *view)
615 g_return_if_fail (view != NULL);
617 view->hadjustment = NULL;
618 view->vadjustment = NULL;
620 view->geometry_table = g_hash_table_new_full (g_direct_hash,
621 g_direct_equal,
622 NULL,
623 (GDestroyNotify) gschem_page_geometry_free);
625 view->page = NULL;
626 view->configured = FALSE;
628 view->doing_pan = FALSE;
629 view->pan_x = 0;
630 view->pan_y = 0;
631 view->throttle = 0;
633 g_signal_connect (view,
634 "set-scroll-adjustments",
635 G_CALLBACK (set_scroll_adjustments),
636 NULL);
638 g_signal_connect(view,
639 "realize",
640 G_CALLBACK (event_realize),
641 NULL);
643 g_signal_connect(view,
644 "unrealize",
645 G_CALLBACK (event_unrealize),
646 NULL);
651 /*! \brief Create a new instance of the GschemPageView
652 * \par Function Description
653 * This function creates a new instance of the GschemPageView
654 * structure. The resulting view becomes a "viewport" for the
655 * given \a page. If the page is not NULL, a weak reference
656 * callback is added for \a page so that it can do necessary
657 * clean-up for the view when the page is deleted (e.g. due to
658 * using close-page! Scheme function).
660 * \param [in] page The page to refer to.
662 * \return A new instance of the GschemPageView
664 GschemPageView*
665 gschem_page_view_new_with_page (PAGE *page)
667 GschemPageView *view = GSCHEM_PAGE_VIEW (g_object_new (GSCHEM_TYPE_PAGE_VIEW,
668 "page", page,
669 NULL));
670 if (page) {
671 s_page_weak_ref (page, (NotifyFunction) page_deleted, view);
674 return view;
679 /*! \brief Pan the view on the given world coordinate using given zoom factor
681 * \param [in,out] page_view This GschemPageView
682 * \param [in] w_x The world x coordinate of the new center
683 * \param [in] w_y The world y coordinate of the new center
684 * \param [in] relativ_zoom_factor The zoom factor
686 void
687 gschem_page_view_pan_general (GschemPageView *view, int w_x, int w_y, double relativ_zoom_factor)
689 GschemPageGeometry *geometry = NULL;
691 g_return_if_fail (view != NULL);
693 geometry = gschem_page_view_get_page_geometry (view);
694 g_return_if_fail (geometry != NULL);
696 /* make mouse to the new world-center;
697 attention: there are information looses because of type cast in mil_x */
699 gschem_page_geometry_pan_general (geometry, w_x, w_y, relativ_zoom_factor);
701 g_signal_emit_by_name (view, "update-grid-info");
702 gschem_page_view_update_scroll_adjustments (view);
703 gschem_page_view_invalidate_all (view);
707 /*! \brief Center the view on the given world coordinate
709 * \param [in,out] page_view This GschemPageView
710 * \param [in] w_x The world x coordinate of the new center
711 * \param [in] w_y The world y coordinate of the new center
713 void
714 gschem_page_view_pan (GschemPageView *view, int w_x, int w_y)
716 gschem_page_view_pan_general (view, w_x, w_y, 1);
717 /* Trigger a motion event to update the objects being drawn */
718 /* This works e.g. if the view is centered at the mouse pointer position */
719 x_event_faked_motion (view, NULL);
721 gschem_page_view_update_scroll_adjustments (view);
722 gschem_page_view_invalidate_all (view);
727 /*! \brief Pan the view by the given screen coordinate displacement
729 * \param [in,out] view This GschemPageView
730 * \param [in] diff_x The screen x coordinate displacement
731 * \param [in] diff_y The screen y coordinate displacement
733 void
734 gschem_page_view_pan_mouse (GschemPageView *view, int diff_x, int diff_y)
736 GschemPageGeometry *geometry = NULL;
737 double world_cx, world_cy;
738 double page_cx, page_cy;
740 g_return_if_fail (view != NULL);
742 geometry = gschem_page_view_get_page_geometry (view);
743 g_return_if_fail (geometry != NULL);
745 #if DEBUG
746 printf("gschem_page_view_pan_mouse(): diff_x=%d, diff_y=%d\n", diff_x, diff_y);
747 #endif
749 page_cx = (gschem_page_geometry_get_viewport_left (geometry) + gschem_page_geometry_get_viewport_right (geometry)) / 2.0;
750 page_cy = (gschem_page_geometry_get_viewport_top (geometry) + gschem_page_geometry_get_viewport_bottom (geometry)) / 2.0;
752 world_cx = page_cx - gschem_page_view_WORLDabs (view, diff_x);
753 world_cy = page_cy + gschem_page_view_WORLDabs (view, diff_y);
755 #if DEBUG
756 printf(" world_cx=%f, world_cy=%f\n", world_cx, world_cy);
757 #endif
759 gschem_page_view_pan_general (view, world_cx, world_cy, 1);
761 /* Trigger a motion event to update the objects being drawn */
762 /* Don't emit such an event if diffs are zero to avoid recursion */
763 if (diff_x == 0 && diff_y == 0) {
764 x_event_faked_motion (view, NULL);
770 /*! \brief Start mouse panning in the view
771 * \par Function Description
772 * This function saves current coordinates of the mouse pointer
773 * to pan_x and pan_y and toggles the view into pan mode.
775 * \param [in,out] view This GschemPageView
776 * \param [in] x The screen x coordinate
777 * \param [in] y The screen y coordinate
779 void gschem_page_view_pan_start (GschemPageView *view, int x, int y)
781 view->doing_pan = TRUE;
782 view->pan_x = x;
783 view->pan_y = y;
784 view->throttle = 0;
789 /*! \brief Continue mouse panning in the view
790 * \par Function Description
791 * In the view pan mode, this function calculates displacement of
792 * the mouse pointer relative to its previous position and repans
793 * the view taking into account the given mouse pan gain setting.
794 * Then it replaces pan_x and pan_y with the new coordinates.
796 * \param [in,out] view This GschemPageView
797 * \param [in] mousepan_gain Mouse pan gain
798 * \param [in] x The new screen x coordinate
799 * \param [in] y The new screen y coordinate
801 void
802 gschem_page_view_pan_motion (GschemPageView *view, int mousepan_gain, int x, int y)
804 int pdiff_x, pdiff_y;
806 if (view->doing_pan) {
807 pdiff_x = x - view->pan_x;
808 pdiff_y = y - view->pan_y;
810 if (!(view->throttle % 5)) {
811 gschem_page_view_pan_mouse(view,
812 pdiff_x * mousepan_gain,
813 pdiff_y * mousepan_gain);
815 view->pan_x = x;
816 view->pan_y = y;
818 view->throttle++;
822 /*! \brief End mouse panning in the view
823 * \par Function Description
824 * This function resets the view pan mode and invalidates the
825 * view after panning.
827 * \param [in,out] view This GschemPageView
828 * \returns TRUE if panning has been finished, or FALSE if there was no panning
830 gboolean
831 gschem_page_view_pan_end (GschemPageView *view)
833 if (view->doing_pan) {
834 gschem_page_view_invalidate_all (view);
835 view->doing_pan = FALSE;
836 return TRUE;
837 } else {
838 return FALSE;
844 /*! \brief Transform SCREEN coordinates to WORLD coordinates
845 * \par Function Description
846 * This function takes in SCREEN x/y coordinates and
847 * transforms them to WORLD x/y coordinates.
849 * \param [in] view The GschemPageView object.
850 * \param [in] mx The x coordinate in SCREEN units.
851 * \param [in] my The y coordinate in SCREEN units.
852 * \param [out] x The x coordinate in WORLD units.
853 * \param [out] y The y coordinate in WORLD units.
854 * \note Question: why are we returning in x and y
855 * if this is SCREEN to WORLD shouldn't WORLD
856 * coordinates be returned in mx and my?
858 void
859 gschem_page_view_SCREENtoWORLD (GschemPageView *view, int mx, int my, int *x, int *y)
861 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
863 g_return_if_fail (geometry != NULL);
865 *x = gschem_page_geometry_mil_x (geometry, mx);
866 *y = gschem_page_geometry_mil_y (geometry, my);
871 /*! \brief Set the horizontal scroll adjustment for this view
873 * \param [in,out] view The view
874 * \param [in] hadjustment The horizontal scroll adjustment
876 void
877 gschem_page_view_set_hadjustment (GschemPageView *view, GtkAdjustment *hadjustment)
879 g_return_if_fail (view != NULL);
881 if (view->hadjustment != NULL) {
882 g_signal_handlers_disconnect_by_func (G_OBJECT (view->hadjustment),
883 G_CALLBACK (hadjustment_value_changed),
884 view);
886 g_object_unref (view->hadjustment);
889 view->hadjustment = hadjustment;
891 if (view->hadjustment != NULL) {
892 g_object_ref (view->hadjustment);
894 g_signal_connect (G_OBJECT (view->hadjustment),
895 "value-changed",
896 G_CALLBACK (hadjustment_value_changed),
897 view);
900 g_object_notify (G_OBJECT (view), "hadjustment");
905 /*! \brief Set the page for this view
907 * The toplevel property must be set and the page must belong to that
908 * toplevel.
910 * \param [in,out] view The view
911 * \param [in] page The page
913 void
914 gschem_page_view_set_page (GschemPageView *view, PAGE *page)
916 g_return_if_fail (view != NULL);
917 g_return_if_fail (view->geometry_table != NULL);
919 if (page != view->page) {
921 view->page = page;
923 if (page) {
924 s_page_weak_ref (page, (NotifyFunction) page_deleted, view);
926 g_return_if_fail (page->toplevel != NULL);
927 s_page_goto (page->toplevel, page);
928 /* gschem_toplevel_page_changed is called by the notify::page handler */
930 /* redraw the current page and update UI */
931 gschem_page_view_invalidate_all (view);
932 if (gtk_widget_get_window (GTK_WIDGET (view)) != NULL)
933 gschem_page_view_update_scroll_adjustments (view);
935 g_object_notify (G_OBJECT (view), "page");
936 g_object_notify (G_OBJECT (view), "page-geometry");
937 g_signal_emit_by_name (view, "update-grid-info");
939 } else {
940 if (view->hadjustment != NULL) {
941 gtk_adjustment_set_page_size (view->hadjustment,
942 gtk_adjustment_get_upper (view->hadjustment));
944 if (view->vadjustment != NULL) {
945 gtk_adjustment_set_page_size (view->vadjustment,
946 gtk_adjustment_get_upper (view->vadjustment));
953 /*! \brief Set the vertical scroll adjustment for this view
955 * \param [in,out] view The view
956 * \param [in] vadjustment The vertical scroll adjustment
958 void
959 gschem_page_view_set_vadjustment (GschemPageView *view, GtkAdjustment *vadjustment)
961 g_return_if_fail (view != NULL);
963 if (view->vadjustment != NULL) {
964 g_signal_handlers_disconnect_by_func (G_OBJECT (view->vadjustment),
965 G_CALLBACK (vadjustment_value_changed),
966 view);
968 g_object_unref (view->vadjustment);
971 view->vadjustment = vadjustment;
973 if (view->vadjustment != NULL) {
974 g_object_ref (view->vadjustment);
976 g_signal_connect (G_OBJECT (view->vadjustment),
977 "value-changed",
978 G_CALLBACK (vadjustment_value_changed),
979 view);
982 g_object_notify (G_OBJECT (view), "vadjustment");
987 /*! \brief Signal handler for a horizontal scroll adjustment change
989 static void
990 hadjustment_value_changed (GtkAdjustment *hadjustment, GschemPageView *view)
992 g_return_if_fail (hadjustment != NULL);
993 g_return_if_fail (view != NULL);
995 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
997 if (view->hadjustment != NULL && geometry != NULL) {
998 int current_left;
999 int new_left;
1001 g_return_if_fail (view->hadjustment == hadjustment);
1003 current_left = gschem_page_geometry_get_viewport_left (geometry),
1004 new_left = (int) hadjustment->value;
1006 geometry->viewport_left = new_left;
1007 geometry->viewport_right = geometry->viewport_right - (current_left - new_left);
1009 gschem_page_view_invalidate_all (view);
1015 /*! \brief Set a gobject property
1017 static void
1018 set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec)
1020 GschemPageView *view = GSCHEM_PAGE_VIEW (object);
1022 switch (param_id) {
1023 case PROP_HADJUSTMENT:
1024 gschem_page_view_set_hadjustment (view, g_value_get_object (value));
1025 break;
1027 case PROP_PAGE:
1028 gschem_page_view_set_page (view, g_value_get_pointer (value));
1029 break;
1031 case PROP_VADJUSTMENT:
1032 gschem_page_view_set_vadjustment (view, g_value_get_object (value));
1033 break;
1035 default:
1036 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1042 /*! \brief Get absolute SCREEN value
1044 * \par Function Description
1045 * Converts WORLD value \a val to absolute SCREEN value.
1047 * \param [in] view This GschemPageView
1048 * \param [in] val The value to convert
1049 * \return The converted value in SCREEN pixels
1052 gschem_page_view_SCREENabs(GschemPageView *view, int val)
1054 double f0,f1;
1055 double i;
1056 int j;
1057 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1059 g_return_val_if_fail (view != NULL, 0);
1061 if (geometry == NULL) return 0;
1063 f0 = gschem_page_geometry_get_viewport_left (geometry);
1064 f1 = gschem_page_geometry_get_viewport_right (geometry);
1065 i = (double)(geometry->screen_width) * (double)(val) / (f1 - f0);
1067 #ifdef HAS_RINT
1068 j = rint(i);
1069 #else
1070 j = i;
1071 #endif
1073 return(j);
1078 /*! \brief Update the horizontal scroll adjustment
1080 static void
1081 gschem_page_view_update_hadjustment (GschemPageView *view)
1083 g_return_if_fail (view != NULL);
1085 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1087 if (view->hadjustment != NULL && geometry != NULL) {
1089 gtk_adjustment_set_page_increment (view->hadjustment,
1090 abs (geometry->viewport_right - geometry->viewport_left) - 100.0);
1092 gtk_adjustment_set_page_size (view->hadjustment,
1093 abs (geometry->viewport_right - geometry->viewport_left));
1095 gtk_adjustment_set_value (view->hadjustment,
1096 geometry->viewport_left);
1098 #if DEBUG
1099 printf("H %f %f\n", view->hadjustment->lower, view->hadjustment->upper);
1100 printf("Hp %f\n", view->hadjustment->page_size);
1101 #endif
1103 gtk_adjustment_changed(view->hadjustment);
1104 gtk_adjustment_value_changed (view->hadjustment);
1110 /*! \brief Update the scroll adjustments
1112 void
1113 gschem_page_view_update_scroll_adjustments (GschemPageView *view)
1115 g_return_if_fail (view != NULL);
1117 gschem_page_view_update_hadjustment (view);
1118 gschem_page_view_update_vadjustment (view);
1123 /*! \brief Update the vertical scroll adjustment
1125 static void
1126 gschem_page_view_update_vadjustment (GschemPageView *view)
1128 g_return_if_fail (view != NULL);
1130 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1132 if (view->vadjustment != NULL && geometry != NULL) {
1134 gtk_adjustment_set_page_increment(view->vadjustment,
1135 abs (geometry->viewport_bottom - geometry->viewport_top) - 100.0);
1137 gtk_adjustment_set_page_size (view->vadjustment,
1138 abs (geometry->viewport_bottom - geometry->viewport_top));
1140 gtk_adjustment_set_value(view->vadjustment,
1141 geometry->world_bottom - geometry->viewport_bottom);
1143 #if DEBUG
1144 printf("V %f %f\n", view->vadjustment->lower, view->vadjustment->upper);
1145 printf("Vp %f\n", view->vadjustment->page_size);
1146 #endif
1148 gtk_adjustment_changed(view->vadjustment);
1149 gtk_adjustment_value_changed (view->vadjustment);
1154 /*! \brief Get absolute WORLD coordinate.
1155 * \par Function Description
1156 * Get absolute WORLD coordinate.
1158 * \param [in,out] view The view
1159 * \param [in] val The coordinate to convert.
1160 * \return The converted WORLD coordinate.
1163 gschem_page_view_WORLDabs(GschemPageView *page_view, int val)
1165 GtkAllocation allocation;
1166 double fw0,fw1,fw,fval;
1167 double i;
1168 int j;
1170 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (page_view);
1172 gtk_widget_get_allocation (GTK_WIDGET(page_view), &allocation);
1174 fw1 = geometry->viewport_right;
1175 fw0 = geometry->viewport_left;
1176 fw = allocation.width;
1177 fval = val;
1178 i = fval * (fw1 - fw0) / fw;
1180 #ifdef HAS_RINT
1181 j = rint(i);
1182 #else
1183 j = i;
1184 #endif
1186 return(j);
1191 /*! \brief
1194 static void
1195 remove_page_weak_reference (PAGE *page, gpointer geometry, GschemPageView *view)
1197 typedef void (*NotifyFunction) (void*,void*);
1199 g_return_if_fail (page != NULL);
1200 g_return_if_fail (view != NULL);
1202 s_page_weak_unref (page, (NotifyFunction) page_deleted, view);
1207 /*! \brief
1210 static void
1211 page_deleted (PAGE *page, GschemPageView *view)
1213 g_return_if_fail (page != NULL);
1214 g_return_if_fail (view != NULL);
1215 g_return_if_fail (view->geometry_table != NULL);
1217 g_hash_table_remove (view->geometry_table, page);
1218 if (view->page == page)
1219 gschem_page_view_set_page (view, NULL);
1224 /*! \brief Signal handler for setting the scroll adjustments
1226 * Sent from the GtkScrolledWindow to set the adjustments for the
1227 * corresponding scroll bars.
1229 static void
1230 set_scroll_adjustments (GschemPageView *view, GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
1232 gschem_page_view_set_hadjustment (view, hadjustment);
1233 gschem_page_view_set_vadjustment (view, vadjustment);
1238 /*! \brief Signal handler for a vertical scroll adjustment change
1240 static void
1241 vadjustment_value_changed (GtkAdjustment *vadjustment, GschemPageView *view)
1243 g_return_if_fail (vadjustment != NULL);
1244 g_return_if_fail (view != NULL);
1246 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1248 if (view->vadjustment != NULL && geometry != NULL) {
1249 int current_bottom;
1250 int new_bottom;
1252 g_return_if_fail (view->vadjustment == vadjustment);
1254 current_bottom = geometry->viewport_bottom;
1255 new_bottom = geometry->world_bottom - (int) vadjustment->value;
1257 geometry->viewport_bottom = new_bottom;
1258 geometry->viewport_top = geometry->viewport_top - (current_bottom - new_bottom);
1260 gschem_page_view_invalidate_all (view);
1266 /*! \brief Transform WORLD coordinates to SCREEN coordinates
1268 void
1269 gschem_page_view_WORLDtoSCREEN (GschemPageView *view, int x, int y, int *px, int *py)
1271 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1273 *px = *py = 0;
1275 g_return_if_fail (geometry != NULL);
1277 *px = gschem_page_geometry_pix_x (geometry, x);
1278 *py = gschem_page_geometry_pix_y (geometry, y);
1283 /*! \brief Zoom the view to the extents of a set of objects
1285 * By providing a NULL for the objects parameter, this function will zoom to
1286 * the extents of all objects in the drawing.
1288 * \param [in,out] view This GschemPageView
1289 * \param [in] objects The list of objects to compute extents, or NULL
1291 void
1292 gschem_page_view_zoom_extents (GschemPageView *view, const GList *objects)
1294 GschemPageGeometry *geometry = NULL;
1295 PAGE *page = NULL;
1296 const GList *temp = objects;
1298 g_return_if_fail (view != NULL);
1300 page = gschem_page_view_get_page (view);
1301 g_return_if_fail (page != NULL);
1303 geometry = gschem_page_view_get_page_geometry (view);
1304 g_return_if_fail (geometry != NULL);
1306 if (temp == NULL) {
1307 temp = s_page_objects (gschem_page_view_get_page (view));
1310 gschem_page_geometry_zoom_extents (geometry, page->toplevel, temp);
1312 /* Trigger a motion event to update the objects being drawn */
1313 x_event_faked_motion (view, NULL);
1315 g_signal_emit_by_name (view, "update-grid-info");
1316 gschem_page_view_update_scroll_adjustments (view);
1317 gschem_page_view_invalidate_all (view);
1320 /*! \brief utility function to find the first parent that has a ->page
1322 * \param [in] object The object
1323 * \return the parent object that had a non-NULL ->page
1325 OBJECT *gschem_page_get_page_object(OBJECT *object)
1327 OBJECT *page_obj = object;
1328 while((page_obj != NULL) && (page_obj->page == NULL))
1329 page_obj = page_obj->parent;
1330 return page_obj;
1333 /*! \brief Zoom in on a single text object
1335 * \param [in] view This GschemPageView
1336 * \param [in] object The text object
1338 void
1339 gschem_page_view_zoom_text (GschemPageView *view, OBJECT *object, gboolean zoom_hidden)
1341 OBJECT *page_obj;
1342 int success;
1343 int x[2];
1344 int y[2];
1345 int viewport_center_x, viewport_center_y, viewport_width, viewport_height;
1346 double k;
1347 int old_show_hidden;
1349 g_return_if_fail (view != NULL);
1350 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1351 g_return_if_fail (geometry != NULL);
1353 g_return_if_fail (object != NULL);
1355 page_obj = gschem_page_get_page_object(object);
1357 g_return_if_fail (page_obj->page != NULL);
1358 g_return_if_fail (page_obj->page->toplevel != NULL);
1359 g_return_if_fail (object->text != NULL);
1361 if (zoom_hidden) {
1362 old_show_hidden = page_obj->page->toplevel->show_hidden_text;
1363 page_obj->page->toplevel->show_hidden_text = 1;
1366 success = world_get_single_object_bounds (page_obj->page->toplevel,
1367 object,
1368 &x[0],
1369 &y[0],
1370 &x[1],
1371 &y[1]);
1372 if (zoom_hidden)
1373 page_obj->page->toplevel->show_hidden_text = old_show_hidden;
1375 if (success) {
1377 /* Here we are trying to make the text screen height to be about */
1378 /* 50 pixels high, perhaps a future enhancement will be to make */
1379 /* this number configurable */
1380 viewport_center_x = (x[1] + x[0]) / 2;
1381 viewport_center_y = (y[1] + y[0]) / 2;
1382 k = ((y[1] - y[0]) / 50);
1383 viewport_height = geometry->screen_height * k;
1384 viewport_width = geometry->screen_width * k;
1386 gschem_page_geometry_set_values (geometry,
1388 geometry->screen_width,
1389 geometry->screen_height,
1390 viewport_center_x - viewport_width / 2,
1391 viewport_center_y - viewport_height / 2,
1392 viewport_center_x + viewport_width / 2,
1393 viewport_center_y + viewport_height / 2);
1395 gschem_page_view_invalidate_all (view);
1400 /*! \brief Redraw page on the view
1402 * \param [in] view The GschemPageView object which page to redraw
1404 void
1405 gschem_page_view_redraw (GschemPageView *view, GdkEventExpose *event, GschemToplevel *w_current)
1407 GschemPageGeometry *geometry;
1408 PAGE *page;
1410 #if DEBUG
1411 printf("EXPOSE\n");
1412 #endif
1414 g_return_if_fail (view != NULL);
1415 g_return_if_fail (w_current != NULL);
1417 page = gschem_page_view_get_page (view);
1419 if (page != NULL) {
1420 geometry = gschem_page_view_get_page_geometry (view);
1422 g_return_if_fail (view != NULL);
1424 o_redraw_rect (w_current,
1425 gtk_widget_get_window (GTK_WIDGET(view)),
1426 page,
1427 geometry,
1428 &(event->area));