1 /* Lepton EDA Schematic Capture
2  * Copyright (C) 1998-2010 Ales Hvezda
3  * Copyright (C) 1998-2016 gEDA Contributors
4  * Copyright (C) 2017-2021 Lepton EDA Contributors
5  *
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.
10  *
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.
15  *
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
19  */
20 /*!
21  * \file gschem_page_view.c
22  *
23  * \brief A widget for viewing a schematic page
24  */
25 
26 #include <config.h>
27 
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
35 
36 #include <math.h>
37 
38 #include "gschem.h"
39 #include <gdk/gdkkeysyms.h>
40 
41 #ifndef ENABLE_GTK3
42 #include "gtk/gtkmarshal.h"
43 #endif
44 
45 #define INVALIDATE_MARGIN 1
46 
47 
48 
49 enum
50 {
51   PROP_0,
52   PROP_HADJUSTMENT,
53   PROP_PAGE,
54   PROP_PAGE_GEOMETRY,
55   PROP_VADJUSTMENT,
56   PROP_SHOW_HIDDEN_TEXT,
57 #ifdef ENABLE_GTK3
58   PROP_HSCROLL_POLICY,
59   PROP_VSCROLL_POLICY
60 #endif
61 };
62 
63 
64 typedef void (*NotifyFunction) (void*,void*);
65 
66 static void
67 dispose (GObject *object);
68 
69 static void
70 finalize (GObject *object);
71 
72 static void
73 get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec);
74 
75 static void
76 gschem_page_view_class_init (GschemPageViewClass *klass);
77 
78 static void
79 gschem_page_view_init (GschemPageView *view);
80 
81 static void
82 gschem_page_view_update_hadjustment (GschemPageView *view);
83 
84 static void
85 gschem_page_view_update_vadjustment (GschemPageView *view);
86 
87 static void
88 gschem_page_view_update_scroll_adjustments (GschemPageView *view);
89 
90 static void
91 hadjustment_value_changed (GtkAdjustment *vadjustment, GschemPageView *view);
92 
93 static void
94 set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec);
95 
96 #ifndef ENABLE_GTK3
97 static void
98 set_scroll_adjustments (GschemPageView *view, GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
99 #endif
100 
101 static void
102 vadjustment_value_changed (GtkAdjustment *vadjustment, GschemPageView *view);
103 
104 static void geometry_cache_create (GschemPageView *view);
105 
106 static
107 GschemPageGeometry *geometry_cache_lookup (const GschemPageView *view,
108                                            const LeptonPage *page);
109 static void
110 geometry_cache_insert (GschemPageView *view,
111                        LeptonPage *page,
112                        GschemPageGeometry *geometry);
113 
114 static void geometry_cache_dispose (GschemPageView *view);
115 
116 static void geometry_cache_finalize (GschemPageView *view);
117 
118 static GObjectClass *gschem_page_view_parent_class = NULL;
119 
120 
121 #ifndef ENABLE_GTK3
122 /*
123  *  In later versions of GTK+, the GtkScrolledWindow uses an interface, instead
124  *  of signals, to set the scrollbar adjustments. When Gschem uses on of these
125  *  more recent version of GTK+, this function will no longer be needed.
126  */
127 static void
cclosure_marshal_VOID__OBJECT_OBJECT(GClosure * closure,GValue * return_value G_GNUC_UNUSED,guint n_param_values,const GValue * param_values,gpointer invocation_hint G_GNUC_UNUSED,gpointer marshal_data)128 cclosure_marshal_VOID__OBJECT_OBJECT (GClosure     *closure,
129                                       GValue       *return_value G_GNUC_UNUSED,
130                                       guint         n_param_values,
131                                       const GValue *param_values,
132                                       gpointer      invocation_hint G_GNUC_UNUSED,
133                                       gpointer      marshal_data)
134 {
135   typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer     data1,
136                                                     gpointer     arg_1,
137                                                     gpointer     arg_2,
138                                                     gpointer     data2);
139 
140   register GMarshalFunc_VOID__OBJECT_OBJECT callback;
141   register GCClosure *cc = (GCClosure*) closure;
142   register gpointer data1, data2;
143 
144   g_return_if_fail (n_param_values == 3);
145 
146   if (G_CCLOSURE_SWAP_DATA (closure)) {
147     data1 = closure->data;
148     data2 = g_value_peek_pointer (param_values + 0);
149   }
150   else {
151     data1 = g_value_peek_pointer (param_values + 0);
152     data2 = closure->data;
153   }
154 
155   callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
156 
157   callback (data1,
158             g_value_get_object (param_values + 1),
159             g_value_get_object (param_values + 2),
160             data2);
161 }
162 #endif
163 
164 
165 /*! \brief Dispose of the object
166  */
167 static void
dispose(GObject * object)168 dispose (GObject *object)
169 {
170   GschemPageView *view;
171 
172   g_return_if_fail (object != NULL);
173   view = GSCHEM_PAGE_VIEW (object);
174   g_return_if_fail (view != NULL);
175 
176   if (view->_page) {
177     lepton_page_remove_weak_ptr (view->_page, &view->_page);
178     view->_page = NULL;
179   }
180 
181   gschem_page_view_set_hadjustment (view, NULL);
182   gschem_page_view_set_vadjustment (view, NULL);
183 
184   geometry_cache_dispose (view);
185 
186   /* lastly, chain up to the parent dispose */
187 
188   g_return_if_fail (gschem_page_view_parent_class != NULL);
189   gschem_page_view_parent_class->dispose (object);
190 }
191 
192 
193 
194 /*! \brief Event handler for window realized
195  */
196 static void
event_realize(GtkWidget * widget,gpointer unused)197 event_realize(GtkWidget *widget, gpointer unused)
198 {
199   GschemPageView *view = GSCHEM_PAGE_VIEW(widget);
200   GdkWindow *window = gtk_widget_get_window (widget);
201 
202   g_return_if_fail (view != NULL);
203   g_return_if_fail (window != NULL);
204 
205   gtk_widget_get_allocation (widget, &(view->previous_allocation));
206 }
207 
208 
209 
210 /*! \brief Event handler for window unrealized
211  */
212 static void
event_unrealize(GtkWidget * widget,gpointer unused)213 event_unrealize(GtkWidget *widget, gpointer unused)
214 {
215   GschemPageView *view = GSCHEM_PAGE_VIEW(widget);
216 
217   g_return_if_fail (view != NULL);
218 }
219 
220 
221 /*! \brief Event handler for window unrealized
222  */
223 static void
event_toggle_hidden_text(GtkWidget * widget,gpointer unused)224 event_toggle_hidden_text (GtkWidget *widget, gpointer unused)
225 {
226   GschemPageView *view = GSCHEM_PAGE_VIEW (widget);
227 
228   g_return_if_fail (view != NULL);
229 
230   view->show_hidden_text = !view->show_hidden_text;
231 }
232 
233 
234 /*! \brief Finalize object
235  */
236 static void
finalize(GObject * object)237 finalize (GObject *object)
238 {
239   GschemPageView *view = GSCHEM_PAGE_VIEW (object);
240 
241   g_return_if_fail (view != NULL);
242 
243   geometry_cache_finalize (view);
244 
245   /* lastly, chain up to the parent finalize */
246 
247   g_return_if_fail (gschem_page_view_parent_class != NULL);
248   gschem_page_view_parent_class->finalize (object);
249 }
250 
251 
252 
253 /*! \brief Get a property
254  *
255  *  \param [in]     object
256  *  \param [in]     param_id
257  *  \param [in,out] value
258  *  \param [in]     pspec
259  */
260 static void
get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)261 get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec)
262 {
263   GschemPageView *view = GSCHEM_PAGE_VIEW (object);
264 
265   switch (param_id) {
266     case PROP_HADJUSTMENT:
267       g_value_set_object (value, gschem_page_view_get_hadjustment (view));
268       break;
269 
270     case PROP_PAGE:
271       g_value_set_pointer (value, gschem_page_view_get_page (view));
272       break;
273 
274     case PROP_PAGE_GEOMETRY:
275       g_value_set_boxed (value, gschem_page_view_get_page_geometry (view));
276       break;
277 
278     case PROP_VADJUSTMENT:
279       g_value_set_object (value, gschem_page_view_get_vadjustment (view));
280       break;
281 
282     case PROP_SHOW_HIDDEN_TEXT:
283       g_value_set_boolean (value, gschem_page_view_get_show_hidden_text (view));
284       break;
285 
286 #ifdef ENABLE_GTK3
287     case PROP_HSCROLL_POLICY:
288       g_value_set_enum (value, (gint) gschem_page_view_get_hscroll_policy (view));
289       break;
290 
291     case PROP_VSCROLL_POLICY:
292       g_value_set_enum (value, (gint) gschem_page_view_get_vscroll_policy (view));
293       break;
294 #endif
295 
296     default:
297       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
298   }
299 }
300 
301 
302 
303 /*! \brief Initialize GschemPageView class
304  *
305  *  \param [in] klass The class for the GschemPageView
306  */
307 static void
gschem_page_view_class_init(GschemPageViewClass * klass)308 gschem_page_view_class_init (GschemPageViewClass *klass)
309 {
310   gschem_page_view_parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
311 
312   G_OBJECT_CLASS (klass)->dispose  = dispose;
313   G_OBJECT_CLASS (klass)->finalize = finalize;
314 
315   G_OBJECT_CLASS (klass)->get_property = get_property;
316   G_OBJECT_CLASS (klass)->set_property = set_property;
317 
318   g_object_class_install_property (G_OBJECT_CLASS (klass),
319                                    PROP_HADJUSTMENT,
320                                    g_param_spec_object ("hadjustment",
321                                                         "Horizontal adjustment",
322                                                         "Horizontal adjustment",
323                                                         GTK_TYPE_ADJUSTMENT,
324                                                         (GParamFlags) (G_PARAM_READWRITE
325                                                                        | G_PARAM_CONSTRUCT)));
326 
327   g_object_class_install_property (G_OBJECT_CLASS (klass),
328                                    PROP_PAGE,
329                                    g_param_spec_pointer ("page",
330                                                          "Page",
331                                                          "Page",
332                                                          (GParamFlags) (G_PARAM_READWRITE
333                                                                         | G_PARAM_STATIC_STRINGS)));
334 
335   g_object_class_install_property (G_OBJECT_CLASS (klass),
336                                    PROP_PAGE_GEOMETRY,
337                                    g_param_spec_boxed ("page-geometry",
338                                                        "Page Geometry",
339                                                        "Page Geometry",
340                                                        GSCHEM_TYPE_PAGE_GEOMETRY,
341                                                        (GParamFlags) (G_PARAM_READABLE
342                                                                       | G_PARAM_STATIC_STRINGS)));
343 
344   g_object_class_install_property (G_OBJECT_CLASS (klass),
345                                    PROP_VADJUSTMENT,
346                                    g_param_spec_object ("vadjustment",
347                                                         "Vertical adjustment",
348                                                         "Vertical adjustment",
349                                                         GTK_TYPE_ADJUSTMENT,
350                                                         (GParamFlags) (G_PARAM_READWRITE
351                                                                        | G_PARAM_CONSTRUCT)));
352 
353   g_object_class_install_property (G_OBJECT_CLASS (klass),
354                                    PROP_SHOW_HIDDEN_TEXT,
355                                    g_param_spec_boolean ("show-hidden-text",
356                                                         "Show hidden text",
357                                                         "Show hidden text in page-view",
358                                                         FALSE,
359                                                         (GParamFlags) (G_PARAM_READWRITE |
360                                                                        G_PARAM_CONSTRUCT)));
361 
362 #ifdef ENABLE_GTK3
363   g_object_class_install_property (G_OBJECT_CLASS (klass),
364                                    PROP_HSCROLL_POLICY,
365                                    g_param_spec_enum ("hscroll-policy",
366                                                       "hscroll-policy",
367                                                       "hscroll-policy",
368                                                       GTK_TYPE_SCROLLABLE_POLICY,
369                                                       GTK_SCROLL_MINIMUM,
370                                                       (GParamFlags) (G_PARAM_READWRITE |
371                                                                      G_PARAM_CONSTRUCT)));
372 
373   g_object_class_install_property (G_OBJECT_CLASS (klass),
374                                    PROP_VSCROLL_POLICY,
375                                    g_param_spec_enum ("vscroll-policy",
376                                                       "vscroll-policy",
377                                                       "vscroll-policy",
378                                                       GTK_TYPE_SCROLLABLE_POLICY,
379                                                       GTK_SCROLL_MINIMUM,
380                                                       (GParamFlags) (G_PARAM_READWRITE |
381                                                                      G_PARAM_CONSTRUCT)));
382 #else
383   GTK_WIDGET_CLASS (klass)->set_scroll_adjustments_signal = g_signal_new (
384     "set-scroll-adjustments",
385     G_OBJECT_CLASS_TYPE (klass),
386     (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
387     0,
388     NULL,
389     NULL,
390     cclosure_marshal_VOID__OBJECT_OBJECT,
391     G_TYPE_NONE,
392     2,
393     GTK_TYPE_ADJUSTMENT,
394     GTK_TYPE_ADJUSTMENT);
395 #endif
396 
397   g_signal_new (
398     "update-grid-info",
399     G_OBJECT_CLASS_TYPE (klass),
400     (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
401     0,
402     NULL,
403     NULL,
404     g_cclosure_marshal_VOID__VOID,
405     G_TYPE_NONE,
406     0);
407 
408   g_signal_new ("toggle-hidden-text",
409                 G_OBJECT_CLASS_TYPE (klass),
410                 (GSignalFlags) (G_SIGNAL_RUN_LAST), /*signal_flags */
411                 0, /*class_offset */
412                 NULL, /* accumulator */
413                 NULL, /* accu_data */
414                 NULL,
415                 G_TYPE_NONE,
416                 0 /* n_params */
417                 );
418 }
419 
420 
421 
422 /*! \brief Get the horizontal adjustment for this view
423  *
424  *  \param [in] view The view
425  *  \return The horizontal adjustment for this view
426  */
427 GtkAdjustment*
gschem_page_view_get_hadjustment(GschemPageView * view)428 gschem_page_view_get_hadjustment (GschemPageView *view)
429 {
430   g_return_val_if_fail (view != NULL, NULL);
431 
432   return view->hadjustment;
433 }
434 
435 
436 #ifdef ENABLE_GTK3
437 /*! \brief Get the horizontal scrolling policy for this view
438  *
439  *  \param [in] view The view
440  *  \return The horizontal scrolling policy for this view
441  */
442 GtkScrollablePolicy
gschem_page_view_get_hscroll_policy(GschemPageView * view)443 gschem_page_view_get_hscroll_policy (GschemPageView *view)
444 {
445   g_return_val_if_fail (view != NULL, GTK_SCROLL_MINIMUM);
446 
447   return view->hscroll_policy;
448 }
449 
450 
451 /*! \brief Get the vertical scrolling policy for this view
452  *
453  *  \param [in] view The view
454  *  \return The vertical scrolling policy for this view
455  */
456 GtkScrollablePolicy
gschem_page_view_get_vscroll_policy(GschemPageView * view)457 gschem_page_view_get_vscroll_policy (GschemPageView *view)
458 {
459   g_return_val_if_fail (view != NULL, GTK_SCROLL_MINIMUM);
460 
461   return view->vscroll_policy;
462 }
463 #endif
464 
465 
466 /*! \brief Get page for this view
467  *
468  *  \param [in] view The view
469  *  \return The page for the view
470  */
471 LeptonPage*
gschem_page_view_get_page(GschemPageView * view)472 gschem_page_view_get_page (GschemPageView *view)
473 {
474   g_return_val_if_fail (view != NULL, NULL);
475 
476   return view->_page;
477 }
478 
479 
480 
481 /*! \brief Get page geometry for this view
482  *
483  *  \param [in] view The view
484  *  \return The page geometry for the view
485  */
486 GschemPageGeometry*
gschem_page_view_get_page_geometry(GschemPageView * view)487 gschem_page_view_get_page_geometry (GschemPageView *view)
488 {
489   LeptonPage *page = NULL;
490   GschemPageGeometry *geometry = NULL;
491   int screen_width;
492   int screen_height;
493 
494   g_return_val_if_fail (view != NULL, NULL);
495 
496   page = gschem_page_view_get_page (view);
497   if (page == NULL) {
498     return NULL;
499   }
500 
501   GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (view));
502   /* If there's no window yet, defer geometry calculation until
503    * later. */
504 #ifdef ENABLE_GTK3
505   if (!GDK_IS_WINDOW (window))
506     return NULL;
507 #else
508   if (!GDK_IS_DRAWABLE (window))
509     return NULL;
510 #endif
511 
512   geometry = geometry_cache_lookup (view, page);
513 
514   screen_width  = gdk_window_get_width  (window);
515   screen_height = gdk_window_get_height (window);
516 
517   if (geometry == NULL) {
518     geometry = gschem_page_geometry_new_with_values (screen_width,
519                                                      screen_height,
520                                                      WORLD_DEFAULT_LEFT,
521                                                      WORLD_DEFAULT_TOP,
522                                                      WORLD_DEFAULT_RIGHT,
523                                                      WORLD_DEFAULT_BOTTOM,
524                                                      WORLD_DEFAULT_LEFT,
525                                                      WORLD_DEFAULT_TOP,
526                                                      WORLD_DEFAULT_RIGHT,
527                                                      WORLD_DEFAULT_BOTTOM);
528 
529     geometry_cache_insert (view, page, geometry);
530 
531     gschem_page_geometry_zoom_extents (geometry,
532                                        lepton_page_objects (page),
533                                        view->show_hidden_text);
534   }
535   else {
536 
537     int right = gschem_page_geometry_get_viewport_right (geometry);
538     int left = gschem_page_geometry_get_viewport_left (geometry);
539     double val1 = fabs ((double) (right - left) / screen_width);
540 
541     int top = gschem_page_geometry_get_viewport_top (geometry);
542     int bottom = gschem_page_geometry_get_viewport_bottom (geometry);
543     double val2 = fabs ((double) (top - bottom) / screen_height);
544 
545     double scale = MAX (val1, val2);
546 
547     gschem_page_geometry_set_values (geometry,
548                                      scale,
549                                      screen_width,
550                                      screen_height,
551                                      gschem_page_geometry_get_viewport_left (geometry),
552                                      gschem_page_geometry_get_viewport_top (geometry),
553                                      gschem_page_geometry_get_viewport_right (geometry),
554                                      gschem_page_geometry_get_viewport_bottom (geometry));
555   }
556 
557   return geometry;
558 }
559 
560 
561 gboolean
gschem_page_view_get_show_hidden_text(GschemPageView * view)562 gschem_page_view_get_show_hidden_text (GschemPageView *view)
563 {
564   g_return_val_if_fail (view != NULL, FALSE);
565 
566   return view->show_hidden_text;
567 }
568 
569 
570 /*! \brief Get/register GschemPageView type.
571  */
572 GType
gschem_page_view_get_type()573 gschem_page_view_get_type ()
574 {
575   static GType type = 0;
576 
577   if (type == 0) {
578     static const GTypeInfo info = {
579       sizeof(GschemPageViewClass),
580       NULL,                                                    /* base_init */
581       NULL,                                                    /* base_finalize */
582       (GClassInitFunc) gschem_page_view_class_init,
583       NULL,                                                    /* class_finalize */
584       NULL,                                                    /* class_data */
585       sizeof(GschemPageView),
586       0,                                                       /* n_preallocs */
587       (GInstanceInitFunc) gschem_page_view_init,
588     };
589 
590     type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
591                                    "GschemPageView",
592                                    &info,
593                                    (GTypeFlags) 0);
594 
595 #ifdef ENABLE_GTK3
596     static const GInterfaceInfo scrollable_info =
597       {
598        (GInterfaceInitFunc) NULL,
599        (GInterfaceFinalizeFunc) NULL,
600        (gpointer) NULL
601       };
602 
603     g_type_add_interface_static (type,
604                                  GTK_TYPE_SCROLLABLE,
605                                  &scrollable_info);
606 #endif
607   }
608 
609   return type;
610 }
611 
612 
613 
614 /*! \brief Get the vertical adjustment for this view
615  *
616  *  \param [in] view The view
617  *  \return The vertical adjustment for this view
618  */
619 GtkAdjustment*
gschem_page_view_get_vadjustment(GschemPageView * view)620 gschem_page_view_get_vadjustment (GschemPageView *view)
621 {
622   g_return_val_if_fail (view != NULL, NULL);
623 
624   return view->vadjustment;
625 }
626 
627 
628 
629 /*! \brief Schedule redraw for the entire window
630  *
631  *  \param [in,out] view The Gschem page view to redraw
632  */
633 void
gschem_page_view_invalidate_all(GschemPageView * view)634 gschem_page_view_invalidate_all (GschemPageView *view)
635 {
636   GdkWindow *window;
637 
638   /* this function can be called early during initialization */
639   if (view == NULL) {
640     return;
641   }
642 
643   window = gtk_widget_get_window (GTK_WIDGET (view));
644 
645   if (window == NULL) {
646     return;
647   }
648 
649   gdk_window_invalidate_rect (window, NULL, FALSE);
650 }
651 
652 
653 /*! \brief Schedule redraw of the given rectange
654  *
655  *  \param [in,out] view   The Gschem page view to redraw
656  *  \param [in]     left
657  *  \param [in]     top
658  *  \param [in]     right
659  *  \param [in]     bottom
660  */
661 void
gschem_page_view_invalidate_screen_rect(GschemPageView * view,int left,int top,int right,int bottom)662 gschem_page_view_invalidate_screen_rect (GschemPageView *view, int left, int top, int right, int bottom)
663 {
664   int bloat;
665   int cue_half_size;
666   int grip_half_size;
667   GdkRectangle rect;
668   GdkWindow *window;
669 
670   g_return_if_fail (view != NULL);
671 
672   window = gtk_widget_get_window (GTK_WIDGET (view));
673 
674   if (window == NULL) {
675     return;
676   }
677 
678   grip_half_size = GRIP_SIZE / 2;
679   cue_half_size = gschem_page_view_SCREENabs (view, CUE_BOX_SIZE);
680   bloat = MAX (grip_half_size, cue_half_size) + INVALIDATE_MARGIN;
681 
682   rect.x = MIN(left, right) - bloat;
683   rect.y = MIN(top, bottom) - bloat;
684   rect.width = 1 + abs( left - right ) + 2 * bloat;
685   rect.height = 1 + abs( top - bottom ) + 2 * bloat;
686 
687   gdk_window_invalidate_rect (window, &rect, FALSE);
688 }
689 
690 
691 
692 /*! \brief Schedule redraw of the given rectange
693  *
694  *  \param [in,out] view   The Gschem page view to redraw
695  *  \param [in]     left
696  *  \param [in]     top
697  *  \param [in]     right
698  *  \param [in]     bottom
699  */
700 void
gschem_page_view_invalidate_world_rect(GschemPageView * view,int left,int top,int right,int bottom)701 gschem_page_view_invalidate_world_rect (GschemPageView *view, int left, int top, int right, int bottom)
702 {
703   int screen_bottom = 0;
704   int screen_right = 0;
705   int screen_left = 0;
706   int screen_top = 0;
707 
708   g_return_if_fail (view != NULL);
709 
710   gschem_page_view_WORLDtoSCREEN (view, left, top, &screen_left, &screen_top);
711   gschem_page_view_WORLDtoSCREEN (view, right, bottom, &screen_right, &screen_bottom);
712 
713   gschem_page_view_invalidate_screen_rect (view,
714                                            screen_left,
715                                            screen_top,
716                                            screen_right,
717                                            screen_bottom);
718 }
719 
720 
721 
722 /*! \brief Initialize GschemPageView instance
723  *
724  *  \param [in,out] view the gschem page view
725  */
726 static void
gschem_page_view_init(GschemPageView * view)727 gschem_page_view_init (GschemPageView *view)
728 {
729   g_return_if_fail (view != NULL);
730 
731   view->hadjustment = NULL;
732   view->vadjustment = NULL;
733 
734 #ifdef ENABLE_GTK3
735   view->hscroll_policy = GTK_SCROLL_MINIMUM;
736   view->vscroll_policy = GTK_SCROLL_MINIMUM;
737 #endif
738 
739   geometry_cache_create (view);
740 
741   view->_page = NULL;
742   view->configured = FALSE;
743 
744   view->doing_pan = FALSE;
745   view->pan_x = 0;
746   view->pan_y = 0;
747   view->throttle = 0;
748 
749 #ifndef ENABLE_GTK3
750   g_signal_connect (view,
751                     "set-scroll-adjustments",
752                     G_CALLBACK (set_scroll_adjustments),
753                     NULL);
754 
755 #endif
756   g_signal_connect(view,
757                    "realize",
758                    G_CALLBACK (event_realize),
759                    NULL);
760 
761   g_signal_connect(view,
762                    "unrealize",
763                    G_CALLBACK (event_unrealize),
764                    NULL);
765 
766   g_signal_connect (view,
767                    "toggle-hidden-text",
768                    G_CALLBACK (event_toggle_hidden_text),
769                    NULL);
770 }
771 
772 
773 
774 /*! \brief Create a new instance of the GschemPageView
775  *  \par Function Description
776  *  This function creates a new instance of the GschemPageView
777  *  structure. The resulting view becomes a "viewport" for the
778  *  given \a page. If the page is not NULL, a weak reference
779  *  callback is added for \a page so that it can do necessary
780  *  clean-up for the view when the page is deleted (e.g. due to
781  *  using close-page! Scheme function).
782  *
783  *  \param [in] page The page to refer to.
784  *
785  *  \return A new instance of the GschemPageView
786  */
787 GschemPageView*
gschem_page_view_new_with_page(LeptonPage * page)788 gschem_page_view_new_with_page (LeptonPage *page)
789 {
790   GschemPageView *view = GSCHEM_PAGE_VIEW (g_object_new (GSCHEM_TYPE_PAGE_VIEW,
791                                                          "page", page,
792                                                          NULL));
793   return view;
794 }
795 
796 
797 
798 /*! \brief Pan the view on the given world coordinate using given zoom factor
799  *
800  *  \param [in,out] page_view This GschemPageView
801  *  \param [in]     w_x       The world x coordinate of the new center
802  *  \param [in]     w_y       The world y coordinate of the new center
803  *  \param [in]     relativ_zoom_factor  The zoom factor
804  */
805 void
gschem_page_view_pan_general(GschemPageView * view,int w_x,int w_y,double relativ_zoom_factor)806 gschem_page_view_pan_general (GschemPageView *view, int w_x, int w_y, double relativ_zoom_factor)
807 {
808   GschemPageGeometry *geometry = NULL;
809 
810   g_return_if_fail (view != NULL);
811 
812   geometry = gschem_page_view_get_page_geometry (view);
813   g_return_if_fail (geometry != NULL);
814 
815   /* make mouse to the new world-center;
816      attention: there are information looses because of type cast in mil_x */
817 
818   gschem_page_geometry_pan_general (geometry, w_x, w_y, relativ_zoom_factor);
819 
820   g_signal_emit_by_name (view, "update-grid-info");
821   gschem_page_view_update_scroll_adjustments (view);
822   gschem_page_view_invalidate_all (view);
823 }
824 
825 
826 /*! \brief Center the view on the given world coordinate
827  *
828  *  \param [in,out] page_view This GschemPageView
829  *  \param [in]     w_x       The world x coordinate of the new center
830  *  \param [in]     w_y       The world y coordinate of the new center
831  */
832 void
gschem_page_view_pan(GschemPageView * view,int w_x,int w_y)833 gschem_page_view_pan (GschemPageView *view, int w_x, int w_y)
834 {
835   gschem_page_view_pan_general (view, w_x, w_y, 1);
836   /* Trigger a motion event to update the objects being drawn */
837   /* This works e.g. if the view is centered at the mouse pointer position */
838   x_event_faked_motion (view, NULL);
839 
840   gschem_page_view_update_scroll_adjustments (view);
841   gschem_page_view_invalidate_all (view);
842 }
843 
844 
845 
846 /*! \brief Pan the view by the given screen coordinate displacement
847  *
848  *  \param [in,out] view      This GschemPageView
849  *  \param [in]     diff_x    The screen x coordinate displacement
850  *  \param [in]     diff_y    The screen y coordinate displacement
851  */
852 void
gschem_page_view_pan_mouse(GschemPageView * view,int diff_x,int diff_y)853 gschem_page_view_pan_mouse (GschemPageView *view, int diff_x, int diff_y)
854 {
855   GschemPageGeometry *geometry = NULL;
856   double world_cx, world_cy;
857   double page_cx, page_cy;
858 
859   g_return_if_fail (view != NULL);
860 
861   geometry = gschem_page_view_get_page_geometry (view);
862   g_return_if_fail (geometry != NULL);
863 
864 #if DEBUG
865   printf("gschem_page_view_pan_mouse(): diff_x=%1$d, diff_y=%2$d\n", diff_x, diff_y);
866 #endif
867 
868   page_cx = (gschem_page_geometry_get_viewport_left (geometry) + gschem_page_geometry_get_viewport_right (geometry)) / 2.0;
869   page_cy = (gschem_page_geometry_get_viewport_top (geometry) + gschem_page_geometry_get_viewport_bottom (geometry)) / 2.0;
870 
871   world_cx = page_cx - gschem_page_view_WORLDabs (view, diff_x);
872   world_cy = page_cy + gschem_page_view_WORLDabs (view, diff_y);
873 
874 #if DEBUG
875   printf("  world_cx=%1$f, world_cy=%2$f\n", world_cx, world_cy);
876 #endif
877 
878   gschem_page_view_pan_general (view, world_cx, world_cy, 1);
879 
880   /* Trigger a motion event to update the objects being drawn */
881   /* Don't emit such an event if diffs are zero to avoid recursion */
882   if (diff_x == 0 && diff_y == 0) {
883     x_event_faked_motion (view, NULL);
884   }
885 }
886 
887 
888 
889 /*! \brief Start mouse panning in the view
890  *  \par Function Description
891  *  This function saves current coordinates of the mouse pointer
892  *  to pan_x and pan_y  and toggles the view into pan mode.
893  *
894  *  \param [in,out] view  This GschemPageView
895  *  \param [in]     x     The screen x coordinate
896  *  \param [in]     y     The screen y coordinate
897  */
gschem_page_view_pan_start(GschemPageView * view,int x,int y)898 void gschem_page_view_pan_start (GschemPageView *view, int x, int y)
899 {
900   view->doing_pan = TRUE;
901   view->pan_x = x;
902   view->pan_y = y;
903   view->throttle = 0;
904 }
905 
906 
907 
908 /*! \brief Continue mouse panning in the view
909  *  \par Function Description
910  *  In the view pan mode, this function calculates displacement of
911  *  the mouse pointer relative to its previous position and repans
912  *  the view taking into account the given mouse pan gain setting.
913  *  Then it replaces pan_x and pan_y with the new coordinates.
914  *
915  *  \param [in,out] view            This GschemPageView
916  *  \param [in]     mousepan_gain   Mouse pan gain
917  *  \param [in]     x               The new screen x coordinate
918  *  \param [in]     y               The new screen y coordinate
919  */
920 void
gschem_page_view_pan_motion(GschemPageView * view,int mousepan_gain,int x,int y)921 gschem_page_view_pan_motion (GschemPageView *view, int mousepan_gain, int x, int y)
922 {
923   int pdiff_x, pdiff_y;
924 
925   if (view->doing_pan) {
926     pdiff_x = x - view->pan_x;
927     pdiff_y = y - view->pan_y;
928 
929     if (!(view->throttle % 5)) {
930       gschem_page_view_pan_mouse(view,
931                                  pdiff_x * mousepan_gain,
932                                  pdiff_y * mousepan_gain);
933 
934       view->pan_x = x;
935       view->pan_y = y;
936     }
937     view->throttle++;
938   }
939 }
940 
941 /*! \brief End mouse panning in the view
942  *  \par Function Description
943  *  This function resets the view pan mode and invalidates the
944  *  view after panning.
945  *
946  *  \param [in,out] view      This GschemPageView
947  *  \returns TRUE if panning has been finished, or FALSE if there was no panning
948  */
949 gboolean
gschem_page_view_pan_end(GschemPageView * view)950 gschem_page_view_pan_end (GschemPageView *view)
951 {
952   if (view->doing_pan) {
953     gschem_page_view_invalidate_all (view);
954     view->doing_pan = FALSE;
955     return TRUE;
956   } else {
957     return FALSE;
958   }
959 }
960 
961 
962 
963 /*! \brief Transform SCREEN coordinates to WORLD coordinates
964  *  \par Function Description
965  *  This function takes in SCREEN x/y coordinates and
966  *  transforms them to WORLD x/y coordinates.
967  *
968  *  \param [in]  view       The GschemPageView object.
969  *  \param [in]  mx         The x coordinate in SCREEN units.
970  *  \param [in]  my         The y coordinate in SCREEN units.
971  *  \param [out] x          The x coordinate in WORLD units.
972  *  \param [out] y          The y coordinate in WORLD units.
973  *  \note Question: why are we returning in x and y
974  *                  if this is SCREEN to WORLD shouldn't WORLD
975  *                  coordinates be returned in mx and my?
976  */
977 void
gschem_page_view_SCREENtoWORLD(GschemPageView * view,int mx,int my,int * x,int * y)978 gschem_page_view_SCREENtoWORLD (GschemPageView *view, int mx, int my, int *x, int *y)
979 {
980   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
981 
982   g_return_if_fail (geometry != NULL);
983 
984   *x = gschem_page_geometry_mil_x (geometry, mx);
985   *y = gschem_page_geometry_mil_y (geometry, my);
986 }
987 
988 
989 
990 /*! \brief Set the horizontal scroll adjustment for this view
991  *
992  *  \param [in,out] view The view
993  *  \param [in]     hadjustment The horizontal scroll adjustment
994  */
995 void
gschem_page_view_set_hadjustment(GschemPageView * view,GtkAdjustment * hadjustment)996 gschem_page_view_set_hadjustment (GschemPageView *view, GtkAdjustment *hadjustment)
997 {
998   g_return_if_fail (view != NULL);
999 
1000   if (view->hadjustment != NULL) {
1001     g_signal_handlers_disconnect_by_func (G_OBJECT (view->hadjustment),
1002                                           (gpointer) hadjustment_value_changed,
1003                                           view);
1004 
1005     g_object_unref (view->hadjustment);
1006   }
1007 
1008   view->hadjustment = hadjustment;
1009 
1010   if (view->hadjustment != NULL) {
1011     g_object_ref (view->hadjustment);
1012 
1013     g_signal_connect (G_OBJECT (view->hadjustment),
1014                       "value-changed",
1015                       G_CALLBACK (hadjustment_value_changed),
1016                       view);
1017   }
1018 
1019   g_object_notify (G_OBJECT (view), "hadjustment");
1020 }
1021 
1022 
1023 
1024 /*! \brief Set the page for this view
1025  *
1026  *  \note
1027  *  Be careful when calling this function when tabbed GUI
1028  *  is enabled (see x_tabs.c) to not disrupt 1:1 relationship
1029  *  between page and page view objects which that code tries to maintain.
1030  *  Most likely you want to
1031  *  call x_window_set_current_page() or x_window_open_page() instead.
1032  *
1033  *  The toplevel property must be set and the page must belong to that
1034  *  toplevel.
1035  *
1036  *  \param [in,out] view The view
1037  *  \param [in]     page The page
1038  */
1039 void
gschem_page_view_set_page(GschemPageView * view,LeptonPage * page)1040 gschem_page_view_set_page (GschemPageView *view,
1041                            LeptonPage *page)
1042 {
1043   g_return_if_fail (view != NULL);
1044 
1045   if (page != view->_page) {
1046 
1047     if (view->_page) {
1048       lepton_page_remove_weak_ptr (view->_page, &view->_page);
1049       view->_page = NULL;
1050     }
1051 
1052     if (page) {
1053       view->_page = page;
1054       lepton_page_add_weak_ptr (view->_page, &view->_page);
1055 
1056       g_return_if_fail (page->toplevel != NULL);
1057       lepton_toplevel_goto_page (page->toplevel, page);
1058 
1059       /* redraw the current page and update UI */
1060       gschem_page_view_invalidate_all (view);
1061       gschem_page_view_update_scroll_adjustments (view);
1062 
1063       g_object_notify (G_OBJECT (view), "page");
1064       g_object_notify (G_OBJECT (view), "page-geometry");
1065       g_signal_emit_by_name (view, "update-grid-info");
1066 
1067     } else {
1068       if (view->hadjustment != NULL) {
1069         gtk_adjustment_set_page_size (view->hadjustment,
1070                                       gtk_adjustment_get_upper (view->hadjustment));
1071       }
1072       if (view->vadjustment != NULL) {
1073         gtk_adjustment_set_page_size (view->vadjustment,
1074                                       gtk_adjustment_get_upper (view->vadjustment));
1075       }
1076     }
1077   }
1078 }
1079 
1080 
1081 /*! \brief Set the vertical scroll adjustment for this view
1082  *
1083  *  \param [in,out] view The view
1084  *  \param [in]     vadjustment The vertical scroll adjustment
1085  */
1086 void
gschem_page_view_set_vadjustment(GschemPageView * view,GtkAdjustment * vadjustment)1087 gschem_page_view_set_vadjustment (GschemPageView *view, GtkAdjustment *vadjustment)
1088 {
1089   g_return_if_fail (view != NULL);
1090 
1091   if (view->vadjustment != NULL) {
1092     g_signal_handlers_disconnect_by_func (G_OBJECT (view->vadjustment),
1093                                           (gpointer) vadjustment_value_changed,
1094                                           view);
1095 
1096     g_object_unref (view->vadjustment);
1097   }
1098 
1099   view->vadjustment = vadjustment;
1100 
1101   if (view->vadjustment != NULL) {
1102     g_object_ref (view->vadjustment);
1103 
1104     g_signal_connect (G_OBJECT (view->vadjustment),
1105                       "value-changed",
1106                       G_CALLBACK (vadjustment_value_changed),
1107                       view);
1108   }
1109 
1110   g_object_notify (G_OBJECT (view), "vadjustment");
1111 }
1112 
1113 
1114 #ifdef ENABLE_GTK3
1115 /*! \brief Set the horizontal scrolling policy for this view
1116  *
1117  *  \param [in] view The view
1118  */
1119 void
gschem_page_view_set_hscroll_policy(GschemPageView * view,GtkScrollablePolicy policy)1120 gschem_page_view_set_hscroll_policy (GschemPageView *view, GtkScrollablePolicy policy)
1121 {
1122   g_return_if_fail (view != NULL);
1123 
1124   view->hscroll_policy = policy;
1125 }
1126 
1127 
1128 /*! \brief Get the vertical scrolling policy for this view
1129  *
1130  *  \param [in] view The view
1131  */
1132 void
gschem_page_view_set_vscroll_policy(GschemPageView * view,GtkScrollablePolicy policy)1133 gschem_page_view_set_vscroll_policy (GschemPageView *view, GtkScrollablePolicy policy)
1134 {
1135   g_return_if_fail (view != NULL);
1136 
1137   view->vscroll_policy = policy;
1138 }
1139 #endif
1140 
1141 
1142 void
gschem_page_view_set_show_hidden_text(GschemPageView * view,gboolean show_hidden_text)1143 gschem_page_view_set_show_hidden_text (GschemPageView *view,
1144                                        gboolean show_hidden_text)
1145 {
1146   g_return_if_fail (view != NULL);
1147   view->show_hidden_text = show_hidden_text;
1148 }
1149 
1150 
1151 /*! \brief Signal handler for a horizontal scroll adjustment change
1152  */
1153 static void
hadjustment_value_changed(GtkAdjustment * hadjustment,GschemPageView * view)1154 hadjustment_value_changed (GtkAdjustment *hadjustment, GschemPageView *view)
1155 {
1156   g_return_if_fail (hadjustment != NULL);
1157   g_return_if_fail (view != NULL);
1158 
1159   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1160 
1161   if (view->hadjustment != NULL && geometry != NULL) {
1162     int current_left;
1163     int new_left;
1164 
1165     g_return_if_fail (view->hadjustment == hadjustment);
1166 
1167     current_left = gschem_page_geometry_get_viewport_left (geometry);
1168     new_left = (int) gtk_adjustment_get_value (hadjustment);
1169 
1170     geometry->viewport_left = new_left;
1171     geometry->viewport_right = geometry->viewport_right - (current_left - new_left);
1172 
1173     gschem_page_view_invalidate_all (view);
1174   }
1175 }
1176 
1177 
1178 
1179 /*! \brief Set a gobject property
1180  */
1181 static void
set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)1182 set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec)
1183 {
1184   GschemPageView *view = GSCHEM_PAGE_VIEW (object);
1185 
1186   switch (param_id) {
1187     case PROP_HADJUSTMENT:
1188       gschem_page_view_set_hadjustment (view,
1189                                         GTK_ADJUSTMENT (g_value_get_object (value)));
1190       break;
1191 
1192     case PROP_PAGE:
1193       gschem_page_view_set_page (view, (LeptonPage*) g_value_get_pointer (value));
1194       break;
1195 
1196     case PROP_VADJUSTMENT:
1197       gschem_page_view_set_vadjustment (view,
1198                                         GTK_ADJUSTMENT (g_value_get_object (value)));
1199       break;
1200 
1201     case PROP_SHOW_HIDDEN_TEXT:
1202       gschem_page_view_set_show_hidden_text (view,
1203                                              g_value_get_boolean (value));
1204       break;
1205 
1206 #ifdef ENABLE_GTK3
1207     case PROP_HSCROLL_POLICY:
1208       gschem_page_view_set_hscroll_policy (view, (GtkScrollablePolicy) g_value_get_enum (value));
1209       break;
1210 
1211     case PROP_VSCROLL_POLICY:
1212       gschem_page_view_set_vscroll_policy (view, (GtkScrollablePolicy) g_value_get_enum (value));
1213       break;
1214 #endif
1215 
1216     default:
1217       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1218   }
1219 }
1220 
1221 
1222 
1223 /*! \brief Get absolute SCREEN value
1224  *
1225  *  \par Function Description
1226  *  Converts WORLD value \a val to absolute SCREEN value.
1227  *
1228  *  \param [in]     view       This GschemPageView
1229  *  \param [in]     val        The value to convert
1230  *  \return The converted value in SCREEN pixels
1231  */
1232 int
gschem_page_view_SCREENabs(GschemPageView * view,int val)1233 gschem_page_view_SCREENabs(GschemPageView *view, int val)
1234 {
1235   double f0,f1;
1236   double i;
1237   int j;
1238   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1239 
1240   g_return_val_if_fail (view != NULL, 0);
1241 
1242   if (geometry == NULL) return 0;
1243 
1244   f0 = gschem_page_geometry_get_viewport_left  (geometry);
1245   f1 = gschem_page_geometry_get_viewport_right (geometry);
1246   i = (double)(geometry->screen_width) * (double)(val) / (f1 - f0);
1247 
1248 #ifdef HAS_RINT
1249   j = rint(i);
1250 #else
1251   j = i;
1252 #endif
1253 
1254   return(j);
1255 }
1256 
1257 
1258 
1259 /*! \brief Update the horizontal scroll adjustment
1260  */
1261 static void
gschem_page_view_update_hadjustment(GschemPageView * view)1262 gschem_page_view_update_hadjustment (GschemPageView *view)
1263 {
1264   g_return_if_fail (view != NULL);
1265 
1266   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1267 
1268   if (view->hadjustment != NULL && geometry != NULL) {
1269 
1270     gtk_adjustment_set_page_increment (view->hadjustment,
1271                                        abs (geometry->viewport_right - geometry->viewport_left) - 100.0);
1272 
1273     gtk_adjustment_set_page_size (view->hadjustment,
1274                                   abs (geometry->viewport_right - geometry->viewport_left));
1275 
1276     gtk_adjustment_set_value (view->hadjustment,
1277                                geometry->viewport_left);
1278 
1279 #if DEBUG
1280 #ifdef ENABLE_GTK3
1281     printf ("Horizontal: "
1282             "lower=%f "
1283             "upper=%f "
1284             "page_size=%f "
1285             "page_increment=%f "
1286             "step_increment=%f "
1287             "value=%f\n",
1288             gtk_adjustment_get_lower (view->hadjustment),
1289             gtk_adjustment_get_upper (view->hadjustment),
1290             gtk_adjustment_get_page_size (view->hadjustment),
1291             gtk_adjustment_get_page_increment (view->hadjustment),
1292             gtk_adjustment_get_step_increment (view->hadjustment),
1293             gtk_adjustment_get_value (view->hadjustment));
1294 #else /* GTK2 */
1295     printf("H %1$f %2$f\n", view->hadjustment->lower, view->hadjustment->upper);
1296     printf("Hp %1$f\n", view->hadjustment->page_size);
1297 #endif
1298 #endif
1299 
1300 #ifndef ENABLE_GTK3
1301     gtk_adjustment_changed(view->hadjustment);
1302     gtk_adjustment_value_changed (view->hadjustment);
1303 #endif
1304   }
1305 }
1306 
1307 
1308 
1309 /*! \brief Update the scroll adjustments
1310  */
1311 static void
gschem_page_view_update_scroll_adjustments(GschemPageView * view)1312 gschem_page_view_update_scroll_adjustments (GschemPageView *view)
1313 {
1314   g_return_if_fail (view != NULL);
1315 
1316   gschem_page_view_update_hadjustment (view);
1317   gschem_page_view_update_vadjustment (view);
1318 }
1319 
1320 
1321 
1322 /*! \brief Update the vertical scroll adjustment
1323  */
1324 static void
gschem_page_view_update_vadjustment(GschemPageView * view)1325 gschem_page_view_update_vadjustment (GschemPageView *view)
1326 {
1327   g_return_if_fail (view != NULL);
1328 
1329   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1330 
1331   if (view->vadjustment != NULL && geometry != NULL) {
1332 
1333     gtk_adjustment_set_page_increment(view->vadjustment,
1334                                       abs (geometry->viewport_bottom - geometry->viewport_top) - 100.0);
1335 
1336     gtk_adjustment_set_page_size (view->vadjustment,
1337                                   abs (geometry->viewport_bottom - geometry->viewport_top));
1338 
1339     gtk_adjustment_set_value(view->vadjustment,
1340                              geometry->world_bottom - geometry->viewport_bottom);
1341 
1342 #if DEBUG
1343 #ifdef ENABLE_GTK3
1344     printf ("Vertical: "
1345             "lower=%f "
1346             "upper=%f "
1347             "page_size=%f "
1348             "page_increment=%f "
1349             "step_increment=%f "
1350             "value=%f\n",
1351             gtk_adjustment_get_lower (view->vadjustment),
1352             gtk_adjustment_get_upper (view->vadjustment),
1353             gtk_adjustment_get_page_size (view->vadjustment),
1354             gtk_adjustment_get_page_increment (view->vadjustment),
1355             gtk_adjustment_get_step_increment (view->vadjustment),
1356             gtk_adjustment_get_value (view->vadjustment));
1357 #else /* GTK2 */
1358     printf("V %1$f %2$f\n", view->vadjustment->lower, view->vadjustment->upper);
1359     printf("Vp %1$f\n", view->vadjustment->page_size);
1360 #endif
1361 #endif
1362 
1363 #ifndef ENABLE_GTK3
1364     gtk_adjustment_changed(view->vadjustment);
1365     gtk_adjustment_value_changed (view->vadjustment);
1366 #endif
1367   }
1368 }
1369 
1370 
1371 /*! \brief Get absolute WORLD coordinate.
1372  *  \par Function Description
1373  *  Get absolute WORLD coordinate.
1374  *
1375  *  \param [in,out] view The view
1376  *  \param [in]     val        The coordinate to convert.
1377  *  \return The converted WORLD coordinate.
1378  */
1379 int
gschem_page_view_WORLDabs(GschemPageView * page_view,int val)1380 gschem_page_view_WORLDabs(GschemPageView *page_view, int val)
1381 {
1382   GtkAllocation allocation;
1383   double fw0,fw1,fw,fval;
1384   double i;
1385   int j;
1386 
1387   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (page_view);
1388 
1389   gtk_widget_get_allocation (GTK_WIDGET(page_view), &allocation);
1390 
1391   fw1 = geometry->viewport_right;
1392   fw0 = geometry->viewport_left;
1393   fw  = allocation.width;
1394   fval = val;
1395   i = fval * (fw1 - fw0) / fw;
1396 
1397 #ifdef HAS_RINT
1398   j = rint(i);
1399 #else
1400   j = i;
1401 #endif
1402 
1403   return(j);
1404 }
1405 
1406 
1407 
1408 #ifndef ENABLE_GTK3
1409 /*! \brief Signal handler for setting the scroll adjustments
1410  *
1411  *  Sent from the GtkScrolledWindow to set the adjustments for the
1412  *  corresponding scroll bars.
1413  */
1414 static void
set_scroll_adjustments(GschemPageView * view,GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)1415 set_scroll_adjustments (GschemPageView *view, GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
1416 {
1417   gschem_page_view_set_hadjustment (view, hadjustment);
1418   gschem_page_view_set_vadjustment (view, vadjustment);
1419 }
1420 #endif
1421 
1422 
1423 
1424 /*! \brief Signal handler for a vertical scroll adjustment change
1425  */
1426 static void
vadjustment_value_changed(GtkAdjustment * vadjustment,GschemPageView * view)1427 vadjustment_value_changed (GtkAdjustment *vadjustment, GschemPageView *view)
1428 {
1429   g_return_if_fail (vadjustment != NULL);
1430   g_return_if_fail (view != NULL);
1431 
1432   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1433 
1434   if (view->vadjustment != NULL && geometry != NULL) {
1435     int current_bottom;
1436     int new_bottom;
1437 
1438     g_return_if_fail (view->vadjustment == vadjustment);
1439 
1440     current_bottom = geometry->viewport_bottom;
1441     new_bottom = geometry->world_bottom - (int) gtk_adjustment_get_value (vadjustment);
1442 
1443     geometry->viewport_bottom = new_bottom;
1444     geometry->viewport_top = geometry->viewport_top - (current_bottom - new_bottom);
1445 
1446     gschem_page_view_invalidate_all (view);
1447   }
1448 }
1449 
1450 
1451 
1452 /*! \brief Transform WORLD coordinates to SCREEN coordinates
1453  */
1454 void
gschem_page_view_WORLDtoSCREEN(GschemPageView * view,int x,int y,int * px,int * py)1455 gschem_page_view_WORLDtoSCREEN (GschemPageView *view, int x, int y, int *px, int *py)
1456 {
1457   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1458 
1459   g_return_if_fail (geometry != NULL);
1460 
1461   *px = gschem_page_geometry_pix_x (geometry, x);
1462   *py = gschem_page_geometry_pix_y (geometry, y);
1463 }
1464 
1465 
1466 
1467 /*! \brief Zoom the view to the extents of a set of objects
1468  *
1469  *  By providing a NULL for the objects parameter, this function will zoom to
1470  *  the extents of all objects in the drawing.
1471  *
1472  *  \param [in,out] view    This GschemPageView
1473  *  \param [in]     objects The list of objects to compute extents, or NULL
1474  */
1475 void
gschem_page_view_zoom_extents(GschemPageView * view,const GList * objects)1476 gschem_page_view_zoom_extents (GschemPageView *view, const GList *objects)
1477 {
1478   GschemPageGeometry *geometry = NULL;
1479   LeptonPage *page = NULL;
1480   const GList *temp = objects;
1481 
1482   g_return_if_fail (view != NULL);
1483 
1484   page = gschem_page_view_get_page (view);
1485   g_return_if_fail (page != NULL);
1486 
1487   geometry = gschem_page_view_get_page_geometry (view);
1488   g_return_if_fail (geometry != NULL);
1489 
1490   if (temp == NULL) {
1491     temp = lepton_page_objects (gschem_page_view_get_page (view));
1492   }
1493 
1494   gschem_page_geometry_zoom_extents (geometry, temp, view->show_hidden_text);
1495 
1496   /* Trigger a motion event to update the objects being drawn */
1497   x_event_faked_motion (view, NULL);
1498 
1499   g_signal_emit_by_name (view, "update-grid-info");
1500   gschem_page_view_update_scroll_adjustments (view);
1501   gschem_page_view_invalidate_all (view);
1502 }
1503 
1504 /*! \brief Zoom in on a single object
1505  *
1506  *  \param [in] view      This GschemPageView
1507  *  \param [in] object    The object
1508  */
1509 void
gschem_page_view_zoom_object(GschemPageView * view,LeptonObject * object)1510 gschem_page_view_zoom_object (GschemPageView *view, LeptonObject *object)
1511 {
1512   int success;
1513   int x[2];
1514   int y[2];
1515   int viewport_center_x, viewport_center_y, viewport_width, viewport_height;
1516   double scale;
1517 
1518   g_return_if_fail (view != NULL);
1519   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
1520   g_return_if_fail (geometry != NULL);
1521 
1522   g_return_if_fail (object != NULL);
1523   g_return_if_fail (object->page != NULL);
1524   g_return_if_fail (object->page->toplevel != NULL);
1525 
1526   success = lepton_object_calculate_visible_bounds (object,
1527                                                     view->show_hidden_text,
1528                                                     &x[0],
1529                                                     &y[0],
1530                                                     &x[1],
1531                                                     &y[1]);
1532 
1533   if (success) {
1534 
1535     /* Here we are trying to make the text screen height to be about */
1536     /* 50 pixels high, perhaps a future enhancement will be to make */
1537     /* this number configurable */
1538     viewport_center_x = (x[1] + x[0]) / 2;
1539     viewport_center_y = (y[1] + y[0]) / 2;
1540 
1541     /* .5 is scale to show really small objects like zero-sized pins:
1542     */
1543     scale = (y[1] - y[0]) / 50;
1544 
1545     if (scale == 0)
1546       scale = (x[1] - x[0]) / 50;
1547 
1548     if (scale == 0)
1549       scale = .5;
1550 
1551     viewport_height = geometry->screen_height * scale;
1552     viewport_width  = geometry->screen_width  * scale;
1553 
1554     gschem_page_geometry_set_values (geometry,
1555                                      scale,
1556                                      geometry->screen_width,
1557                                      geometry->screen_height,
1558                                      viewport_center_x - viewport_width / 2,
1559                                      viewport_center_y - viewport_height / 2,
1560                                      viewport_center_x + viewport_width / 2,
1561                                      viewport_center_y + viewport_height / 2);
1562 
1563     gschem_page_view_invalidate_all (view);
1564   }
1565 }
1566 
1567 
1568 /*! \brief Redraw page on the view
1569  *
1570  *  \param [in] view      The GschemPageView object which page to redraw
1571  */
1572 void
gschem_page_view_redraw(GschemPageView * view,cairo_t * cr,GschemToplevel * w_current)1573 gschem_page_view_redraw (GschemPageView *view,
1574 #ifdef ENABLE_GTK3
1575                          cairo_t *cr,
1576 #else
1577                          GdkEventExpose *event,
1578 #endif
1579                          GschemToplevel *w_current)
1580 {
1581   GschemPageGeometry *geometry;
1582   LeptonPage *page;
1583 
1584 #if DEBUG
1585   printf("EXPOSE\n");
1586 #endif
1587 
1588   g_return_if_fail (view != NULL);
1589   g_return_if_fail (w_current != NULL);
1590 
1591   page = gschem_page_view_get_page (view);
1592 
1593   if (page != NULL) {
1594     geometry = gschem_page_view_get_page_geometry (view);
1595 
1596     g_return_if_fail (view != NULL);
1597 
1598 #ifdef ENABLE_GTK3
1599     o_redraw_rect (w_current,
1600                    GTK_WIDGET(view),
1601                    page,
1602                    geometry,
1603                    cr);
1604 #else
1605     o_redraw_rect (w_current,
1606                    gtk_widget_get_window (GTK_WIDGET(view)),
1607                    page,
1608                    geometry,
1609                    &(event->area));
1610 #endif
1611   }
1612 }
1613 
1614 static void
geometry_cache_page_weak_ref_notify(gpointer target,gpointer user_data)1615 geometry_cache_page_weak_ref_notify (gpointer target,
1616                                      gpointer user_data)
1617 {
1618   g_return_if_fail (target);
1619   g_return_if_fail (user_data);
1620   GschemPageView *view = GSCHEM_PAGE_VIEW (user_data);
1621   if (!view->_geometry_cache)
1622     return;
1623   g_hash_table_remove (view->_geometry_cache, target);
1624 }
1625 
1626 static void
geometry_cache_create(GschemPageView * view)1627 geometry_cache_create (GschemPageView *view)
1628 {
1629   g_return_if_fail (view && !view->_geometry_cache);
1630 
1631   view->_geometry_cache =
1632     g_hash_table_new_full (NULL, /* hash_func */
1633                            NULL, /* equal_func */
1634                            NULL, /* key_destroy_func */
1635                            (GDestroyNotify) gschem_page_geometry_free);
1636 }
1637 
1638 static GschemPageGeometry *
geometry_cache_lookup(const GschemPageView * view,const LeptonPage * page)1639 geometry_cache_lookup (const GschemPageView *view,
1640                        const LeptonPage *page)
1641 {
1642   g_return_val_if_fail (view && view->_geometry_cache, NULL);
1643   g_return_val_if_fail (page, NULL);
1644 
1645   return (GschemPageGeometry*) g_hash_table_lookup (view->_geometry_cache, page);
1646 }
1647 
1648 static void
geometry_cache_insert(GschemPageView * view,LeptonPage * page,GschemPageGeometry * geometry)1649 geometry_cache_insert (GschemPageView *view,
1650                        LeptonPage *page,
1651                        GschemPageGeometry *geometry)
1652 {
1653   g_return_if_fail (view && view->_geometry_cache);
1654   g_return_if_fail (page);
1655   g_return_if_fail (geometry);
1656   g_return_if_fail (!g_hash_table_contains (view->_geometry_cache, page));
1657 
1658   lepton_page_weak_ref (page, geometry_cache_page_weak_ref_notify, view);
1659   g_hash_table_insert (view->_geometry_cache, page, geometry);
1660 }
1661 
1662 static gboolean
geometry_cache_dispose_func(gpointer key,gpointer value,gpointer user_data)1663 geometry_cache_dispose_func (gpointer key,
1664                              gpointer value,
1665                              gpointer user_data)
1666 {
1667   lepton_page_weak_unref ((LeptonPage*) key,
1668                           geometry_cache_page_weak_ref_notify,
1669                           user_data);
1670   gschem_page_geometry_free ((GschemPageGeometry*) value);
1671   return TRUE;
1672 }
1673 
1674 static void
geometry_cache_dispose(GschemPageView * view)1675 geometry_cache_dispose (GschemPageView *view)
1676 {
1677   g_return_if_fail (view && view->_geometry_cache);
1678   g_hash_table_foreach_steal (view->_geometry_cache,
1679                               geometry_cache_dispose_func,
1680                               view);
1681 }
1682 
1683 static void
geometry_cache_finalize(GschemPageView * view)1684 geometry_cache_finalize (GschemPageView *view)
1685 {
1686   g_return_if_fail (view);
1687   if (!view->_geometry_cache)
1688     return;
1689 
1690   geometry_cache_dispose (view);
1691   g_hash_table_destroy (view->_geometry_cache);
1692   view->_geometry_cache = NULL;
1693 }
1694