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