1 /*
2 * gog-view.c :
3 *
4 * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
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 St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24 #include <goffice/goffice-debug.h>
25
26 #include <gsf/gsf-impl-utils.h>
27 #include <glib/gi18n-lib.h>
28
29 /**
30 * GogViewClass:
31 * @base: base class.
32 * @state_init: state intialization.
33 * @padding_request: padding request.
34 * @size_request: size request.
35 * @size_allocate: size allocate.
36 * @render: render to cairo.
37 * @build_toolkit: builds the associated toolkit.
38 * @get_tip_at_point: gets tip at pointer position.
39 * @natural_size: gets natural size.
40 *
41 * the GogViewClass::clip firled should be set to %TRUE to clip drawings to
42 * the vew allocation.
43 **/
44
45 /**
46 * GogViewAllocation:
47 * @w: width.
48 * @h: height.
49 * @x: horizontal position.
50 * @y: vertical position.
51 **/
52
53 /**
54 * GogViewPadding:
55 * @wr: right padding.
56 * @hb: bottom padding.
57 * @wl: left pdding.
58 * @ht: top padding.
59 **/
60
61 /**
62 * GogViewRequisition:
63 * @w: width.
64 * @h: height.
65 **/
66
67 static GogViewAllocation *
gog_view_allocation_copy(GogViewAllocation * alloc)68 gog_view_allocation_copy (GogViewAllocation *alloc)
69 {
70 GogViewAllocation *res = g_new (GogViewAllocation, 1);
71 memcpy (res, alloc, sizeof (GogViewAllocation));
72 return res;
73 }
74
75 GType
gog_view_allocation_get_type(void)76 gog_view_allocation_get_type (void)
77 {
78 static GType type = 0;
79
80 if (type == 0)
81 type = g_boxed_type_register_static
82 ("GogViewAllocation",
83 (GBoxedCopyFunc) gog_view_allocation_copy,
84 (GBoxedFreeFunc) g_free);
85
86 return type;
87 }
88
89 /*****************************************************************************/
90
91 /**
92 * GogTool:
93 * @name: tool name.
94 * @cursor_type: pointer cursor type for the tool.
95 * @point: points an object.
96 * @render: displays the tool.
97 * @init: initalizes an action.
98 * @move: callback for pointer move.
99 * @double_click: callback on double click.
100 * @destroy: destroys the action.
101 **/
102
103 #ifdef GOFFICE_WITH_GTK
104 static gboolean
gog_tool_select_object_point(GogView * view,double x,double y,GogObject ** gobj)105 gog_tool_select_object_point (GogView *view, double x, double y, GogObject **gobj)
106 {
107 return (x >= view->allocation.x &&
108 x <= (view->allocation.x + view->allocation.w) &&
109 y >= view->allocation.y &&
110 y <= (view->allocation.y + view->allocation.h));
111 }
112
113 static void
gog_tool_select_object_render(GogView * view)114 gog_tool_select_object_render (GogView *view)
115 {
116 GogViewAllocation rect = view->allocation;
117 GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (view->model));
118 double line_width = gog_renderer_line_size (view->renderer, style->line.width);
119
120 rect.x -= line_width / 2.0;
121 rect.y -= line_width / 2.0;
122 rect.w += line_width;
123 rect.h += line_width;
124
125 gog_renderer_draw_selection_rectangle (view->renderer, &rect);
126 }
127
128 static GogTool gog_tool_select_object = {
129 N_("Select object"),
130 GDK_LEFT_PTR,
131 gog_tool_select_object_point,
132 gog_tool_select_object_render,
133 NULL /* init */,
134 NULL /* move */,
135 NULL /* double_click */,
136 NULL /* destroy */
137 };
138
139 typedef struct {
140 GogViewAllocation parent_allocation;
141 GogViewAllocation start_position;
142 } MoveObjectData;
143
144 static gboolean
gog_tool_move_object_point(GogView * view,double x,double y,GogObject ** gobj)145 gog_tool_move_object_point (GogView *view, double x, double y, GogObject **gobj)
146 {
147 if (view->model->role == NULL)
148 return FALSE;
149
150 if ((view->model->role->allowable_positions &
151 (GOG_POSITION_MANUAL)) == 0)
152 return FALSE;
153
154 return (x >= view->allocation.x &&
155 x <= (view->allocation.x + view->allocation.w) &&
156 y >= view->allocation.y &&
157 y <= (view->allocation.y + view->allocation.h));
158 }
159
160 static void
gog_tool_move_object_init(GogToolAction * action)161 gog_tool_move_object_init (GogToolAction *action)
162 {
163 MoveObjectData *data = g_new0 (MoveObjectData, 1);
164
165 action->data = data;
166 data->parent_allocation = action->view->parent->allocation;
167 gog_object_get_manual_position (action->view->model, &data->start_position);
168
169 if ((gog_object_get_position_flags (action->view->model, GOG_POSITION_MANUAL) &
170 GOG_POSITION_MANUAL) == 0) {
171 data->start_position.x = action->view->allocation.x / data->parent_allocation.w;
172 data->start_position.y = action->view->allocation.y / data->parent_allocation.h;
173 data->start_position.w = action->view->allocation.w / data->parent_allocation.w;
174 data->start_position.h = action->view->allocation.h / data->parent_allocation.h;
175 }
176 }
177
178 static void
gog_tool_move_object_move(GogToolAction * action,double x,double y)179 gog_tool_move_object_move (GogToolAction *action, double x, double y)
180 {
181 GogViewAllocation position;
182 MoveObjectData *data = action->data;
183
184 position.x = data->start_position.x + (x - action->start_x) / data->parent_allocation.w;
185 position.y = data->start_position.y + (y - action->start_y) / data->parent_allocation.h;
186 position.w = data->start_position.w;
187 position.h = data->start_position.h;
188 gog_object_set_manual_position (action->view->model, &position);
189 gog_object_set_position_flags (action->view->model, GOG_POSITION_MANUAL, GOG_POSITION_MANUAL);
190 }
191
192 static void
gog_tool_move_object_double_click(GogToolAction * action)193 gog_tool_move_object_double_click (GogToolAction *action)
194 {
195 gog_object_set_position_flags (action->view->model, 0, GOG_POSITION_MANUAL);
196 }
197
198
199 static GogTool gog_tool_move_object = {
200 N_("Move"),
201 GDK_FLEUR,
202 gog_tool_move_object_point,
203 NULL /* render */,
204 gog_tool_move_object_init,
205 gog_tool_move_object_move,
206 gog_tool_move_object_double_click,
207 NULL /* destroy */
208 };
209
210 static gboolean
gog_tool_resize_object_point(GogView * view,double x,double y,GogObject ** gobj)211 gog_tool_resize_object_point (GogView *view, double x, double y, GogObject **gobj)
212 {
213 if (GOG_MANUAL_SIZE_AUTO == gog_object_get_manual_size_mode (view->model))
214 return FALSE;
215
216 return gog_renderer_in_grip (x, y,
217 view->allocation.x + view->allocation.w,
218 view->allocation.y + view->allocation.h);
219 }
220
221 static void
gog_tool_resize_object_render(GogView * view)222 gog_tool_resize_object_render (GogView *view)
223 {
224 if (GOG_MANUAL_SIZE_AUTO == gog_object_get_manual_size_mode (view->model))
225 return;
226
227 gog_renderer_draw_grip (view->renderer,
228 view->allocation.x + view->allocation.w,
229 view->allocation.y + view->allocation.h);
230 }
231
232 static void
gog_tool_resize_object_move(GogToolAction * action,double x,double y)233 gog_tool_resize_object_move (GogToolAction *action, double x, double y)
234 {
235 GogViewAllocation position;
236 MoveObjectData *data = action->data;
237
238 position.x = data->start_position.x;
239 position.y = data->start_position.y;
240 position.w = data->start_position.w + (x - action->start_x) / data->parent_allocation.w;
241 position.h = data->start_position.h + (y - action->start_y) / data->parent_allocation.h;
242 gog_object_set_manual_position (action->view->model, &position);
243 gog_object_set_position_flags (action->view->model, GOG_POSITION_MANUAL, GOG_POSITION_MANUAL);
244 }
245
246 static GogTool gog_tool_resize_object = {
247 N_("Resize object"),
248 GDK_BOTTOM_RIGHT_CORNER,
249 gog_tool_resize_object_point,
250 gog_tool_resize_object_render,
251 gog_tool_move_object_init,
252 gog_tool_resize_object_move,
253 NULL /* double-click */,
254 NULL /* destroy */
255 };
256 #endif
257
258 /*****************************************************************************/
259
260 /**
261 * GogToolAction:
262 * @start_x: initial pointer horizontal position.
263 * @start_y: initial pointer vertical position.
264 * @view: #GogView
265 * @tool: #GogTool
266 * @data: user data.
267 **/
268
269 static GogToolAction *
gog_tool_action_ref(GogToolAction * action)270 gog_tool_action_ref (GogToolAction *action)
271 {
272 action->ref_count++;
273 return action;
274 }
275
276 GType
gog_tool_action_get_type(void)277 gog_tool_action_get_type (void)
278 {
279 static GType t = 0;
280
281 if (t == 0) {
282 t = g_boxed_type_register_static ("GogToolAction",
283 (GBoxedCopyFunc)gog_tool_action_ref,
284 (GBoxedFreeFunc)gog_tool_action_free);
285 }
286 return t;
287 }
288
289 GogToolAction *
gog_tool_action_new(GogView * view,GogTool * tool,double x,double y)290 gog_tool_action_new (GogView *view, GogTool *tool, double x, double y)
291 {
292 GogToolAction *action;
293
294 g_return_val_if_fail (GOG_IS_VIEW (view), NULL);
295 g_return_val_if_fail (tool != NULL, NULL);
296
297 action = g_new0 (GogToolAction, 1);
298
299 g_object_ref (view);
300 action->tool = tool;
301 action->view = view;
302 action->data = NULL;
303 action->start_x = x;
304 action->start_y = y;
305 action->ref_count = 1;
306
307 if (tool->init != NULL)
308 (tool->init) (action);
309
310 return action;
311 }
312
313 void
gog_tool_action_move(GogToolAction * action,double x,double y)314 gog_tool_action_move (GogToolAction *action, double x, double y)
315 {
316 g_return_if_fail (action != NULL);
317
318 if (action->tool->move != NULL)
319 (action->tool->move) (action, x, y);
320 }
321
322 void
gog_tool_action_double_click(GogToolAction * action)323 gog_tool_action_double_click (GogToolAction *action)
324 {
325 g_return_if_fail (action != NULL);
326
327 if (action->tool->double_click != NULL)
328 (action->tool->double_click) (action);
329 }
330
331 void
gog_tool_action_free(GogToolAction * action)332 gog_tool_action_free (GogToolAction *action)
333 {
334 g_return_if_fail (action != NULL);
335
336 if (action->ref_count-- > 1)
337 return;
338
339 if (action->tool->destroy != NULL)
340 (action->tool->destroy) (action);
341
342 g_object_unref (action->view);
343 g_free (action->data);
344 g_free (action);
345 }
346
347 /****/
348
349 /* this should be per model */
350 #define PAD_HACK 4 /* pts */
351
352 enum {
353 GOG_VIEW_PROP_0,
354 GOG_VIEW_PROP_PARENT,
355 GOG_VIEW_PROP_MODEL
356 };
357
358 static GObjectClass *parent_klass;
359
360 static void
cb_child_added(GogObject * parent,GogObject * child,GogView * view)361 cb_child_added (GogObject *parent, GogObject *child,
362 GogView *view)
363 {
364 g_return_if_fail (view->model == parent);
365
366 gog_object_new_view (child, view);
367 gog_view_queue_resize (view);
368 }
369
370 static void
cb_remove_child(GogObject * parent,GogObject * child,GogView * view)371 cb_remove_child (GogObject *parent, GogObject *child,
372 GogView *view)
373 {
374 GSList *ptr = view->children;
375 GogObjectClass const *klass;
376 GogView *tmp;
377
378 g_return_if_fail (view->model == parent);
379
380 gog_view_queue_resize (view);
381 for (; ptr != NULL ; ptr = ptr->next) {
382 tmp = GOG_VIEW (ptr->data);
383
384 g_return_if_fail (tmp != NULL);
385
386 if (tmp->model == child) {
387 g_object_unref (tmp);
388 return;
389 }
390 }
391
392 /* The object may not create a view */
393 klass = GOG_OBJECT_GET_CLASS (child);
394 if (klass->view_type != 0)
395 g_warning ("%s (%p) saw %s(%p) being removed from %s(%p) for which I didn't have a child",
396 G_OBJECT_TYPE_NAME (view), view,
397 G_OBJECT_TYPE_NAME (child), child,
398 G_OBJECT_TYPE_NAME (parent), parent);
399 }
400
401 static void
cb_model_changed(GogObject * model,gboolean resized,GogView * view)402 cb_model_changed (GogObject *model, gboolean resized, GogView *view)
403 {
404 gog_debug (0, g_warning ("model %s(%p) for view %s(%p) changed %d",
405 G_OBJECT_TYPE_NAME (model), model,
406 G_OBJECT_TYPE_NAME (view), view, resized););
407 if (resized)
408 gog_view_queue_resize (view);
409 else
410 gog_view_queue_redraw (view);
411 }
412
413 /* make the list of view children match the models order */
414 static void
cb_model_reordered(GogView * view)415 cb_model_reordered (GogView *view)
416 {
417 GSList *tmp, *new_order = NULL;
418 GSList *ptr = view->model->children;
419
420 for (; ptr != NULL ; ptr = ptr->next) {
421 tmp = view->children;
422 /* not all the views may be created yet check for NULL */
423 while (tmp != NULL && GOG_VIEW (tmp->data)->model != ptr->data)
424 tmp = tmp->next;
425 if (tmp != NULL)
426 new_order = g_slist_prepend (new_order, tmp->data);
427 }
428 g_slist_free (view->children);
429 view->children = g_slist_reverse (new_order);
430 }
431
432 static void
gog_view_set_property(GObject * gobject,guint param_id,GValue const * value,GParamSpec * pspec)433 gog_view_set_property (GObject *gobject, guint param_id,
434 GValue const *value, GParamSpec *pspec)
435 {
436 GogView *view = GOG_VIEW (gobject);
437 gboolean init_state = (view->renderer == NULL || view->model == NULL);
438
439 switch (param_id) {
440 case GOG_VIEW_PROP_PARENT:
441 g_return_if_fail (view->parent == NULL);
442
443 view->parent = GOG_VIEW (g_value_get_object (value));
444 if (view->parent != NULL) {
445 view->renderer = view->parent->renderer;
446 view->parent->children = g_slist_prepend (view->parent->children, view);
447 cb_model_reordered (view->parent);
448 }
449 break;
450
451 case GOG_VIEW_PROP_MODEL:
452 g_return_if_fail (view->model == NULL);
453
454 view->model = GOG_OBJECT (g_value_get_object (value));
455 break;
456
457 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
458 return; /* NOTE : RETURN */
459 }
460
461 /* renderer set via parent or manually */
462 if (init_state && view->renderer != NULL && view->model != NULL) {
463 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
464 GSList *ptr = view->model->children;
465
466 for ( ;ptr != NULL ; ptr = ptr->next)
467 gog_object_new_view (ptr->data, view);
468
469 g_signal_connect_object (G_OBJECT (view->model),
470 "child_added",
471 G_CALLBACK (cb_child_added), view, 0);
472 g_signal_connect_object (G_OBJECT (view->model),
473 "child_removed",
474 G_CALLBACK (cb_remove_child), view, 0);
475 g_signal_connect_object (G_OBJECT (view->model),
476 "changed",
477 G_CALLBACK (cb_model_changed), view, 0);
478 g_signal_connect_object (G_OBJECT (view->model),
479 "children-reordered",
480 G_CALLBACK (cb_model_reordered), view, G_CONNECT_SWAPPED);
481
482 if (klass->state_init != NULL)
483 (klass->state_init) (view);
484 }
485 }
486
487 static void
gog_view_finalize(GObject * obj)488 gog_view_finalize (GObject *obj)
489 {
490 GogView *tmp, *view = GOG_VIEW (obj);
491 GSList *ptr;
492
493 if (view->parent != NULL)
494 view->parent->children = g_slist_remove (view->parent->children, view);
495
496 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
497 tmp = GOG_VIEW (ptr->data);
498 /* not really necessary, but helpful during initial deployment
499 * when not everything has a view yet */
500 if (tmp != NULL) {
501 tmp->parent = NULL; /* short circuit */
502 g_object_unref (tmp);
503 }
504 }
505 g_slist_free (view->children);
506 view->children = NULL;
507
508 g_slist_free (view->toolkit);
509 view->toolkit = NULL;
510
511 (*parent_klass->finalize) (obj);
512 }
513
514 static void
gog_view_padding_request_real(GogView * view,GogViewAllocation const * bbox,GogViewPadding * padding)515 gog_view_padding_request_real (GogView *view, GogViewAllocation const *bbox, GogViewPadding *padding)
516 {
517 GSList *ptr;
518 GogView *child;
519 GogViewPadding child_padding;
520
521 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
522 child = ptr->data;
523 if (GOG_POSITION_IS_PADDING (child->model->position)) {
524 gog_view_padding_request (child, bbox, &child_padding);
525 padding->wr = MAX (padding->wr, child_padding.wr);
526 padding->wl = MAX (padding->wl, child_padding.wl);
527 padding->hb = MAX (padding->hb, child_padding.hb);
528 padding->ht = MAX (padding->ht, child_padding.ht);
529 }
530 }
531 }
532
533 static void
gog_view_size_request_real(GogView * view,GogViewRequisition const * available,GogViewRequisition * req)534 gog_view_size_request_real (GogView *view,
535 GogViewRequisition const *available,
536 GogViewRequisition *req)
537 {
538 req->w = req->h = 1.;
539 }
540
541 static void
gog_view_size_allocate_real(GogView * view,GogViewAllocation const * allocation)542 gog_view_size_allocate_real (GogView *view, GogViewAllocation const *allocation)
543 {
544 GSList *ptr;
545 GogView *child;
546 GogObjectPosition pos;
547 GogViewRequisition req, available;
548 GogViewAllocation tmp, res = *allocation, align;
549 double const pad_h = gog_renderer_pt2r_y (view->renderer, PAD_HACK);
550 double const pad_w = gog_renderer_pt2r_x (view->renderer, PAD_HACK);
551
552 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
553 child = ptr->data;
554
555 pos = child->model->position;
556 if (pos & GOG_POSITION_MANUAL) {
557 available.w = res.w;
558 available.h = res.h;
559
560 gog_view_size_request (child, &available, &req);
561 tmp = gog_object_get_manual_allocation (gog_view_get_model (child),
562 allocation, &req);
563 gog_view_size_allocate (child, &tmp);
564 } else if (pos & GOG_POSITION_COMPASS) {
565 gboolean vertical = TRUE;
566
567 /* Dead simple */
568 available.w = res.w;
569 available.h = res.h;
570 gog_view_size_request (child, &available, &req);
571 if (req.h > res.h)
572 req.h = res.h;
573 if (req.w > res.w)
574 req.w = res.w;
575 tmp = res;
576
577 if (pos & GOG_POSITION_N) {
578 if (req.h > 0) {
579 res.y += req.h + pad_h;
580 res.h -= req.h + pad_h;
581 } else
582 req.h = 0;
583 tmp.h = req.h;
584 vertical = FALSE;
585 } else if (pos & GOG_POSITION_S) {
586 if (req.h > 0) {
587 res.h -= req.h + pad_h;
588 tmp.y = res.y + res.h + pad_h;
589 } else
590 req.h = 0;
591 tmp.h = req.h;
592 vertical = FALSE;
593 }/* else
594 tmp.h = req.h;*/
595
596 if (pos & GOG_POSITION_E) {
597 if (req.w > 0) {
598 res.w -= req.w + pad_w;
599 tmp.x = res.x + res.w + pad_w;
600 } else
601 req.w = 0;
602 tmp.w = req.w;
603 /* For NE & NW only alignment fill makes sense */
604 if (pos & (GOG_POSITION_N|GOG_POSITION_S))
605 pos = GOG_POSITION_ALIGN_FILL;
606 } else if (pos & GOG_POSITION_W) {
607 if (req.w > 0) {
608 res.x += req.w + pad_w;
609 res.w -= req.w + pad_w;
610 } else
611 req.w = 0;
612 tmp.w = req.w;
613 /* For NE & NW only alignment fill makes sense */
614 if (pos & (GOG_POSITION_N|GOG_POSITION_S))
615 pos = GOG_POSITION_ALIGN_FILL;
616 }/* else
617 tmp.w = req.w;*/
618
619 /* the following line adjust the manual sizes if needed */
620 align = gog_object_get_manual_allocation (gog_view_get_model (child),
621 allocation, &req);
622 req.h = align.h;
623 req.w = align.w;
624 pos &= GOG_POSITION_ALIGNMENT;
625 if (GOG_POSITION_ALIGN_FILL != pos) {
626 if (vertical) {
627 if (GOG_POSITION_ALIGN_END == pos) {
628 if (tmp.h >= req.h)
629 tmp.y += tmp.h - req.h;
630 } else if (GOG_POSITION_ALIGN_CENTER == pos) {
631 if (tmp.h >= req.h)
632 tmp.y += (tmp.h - req.h) / 2.;
633 }
634 tmp.h = req.h;
635 } else {
636 if (GOG_POSITION_ALIGN_END == pos) {
637 if (tmp.w >= req.w)
638 tmp.x += tmp.w - req.w;
639 } else if (GOG_POSITION_ALIGN_CENTER == pos) {
640 if (tmp.w >= req.w)
641 tmp.x += (tmp.w - req.w) / 2.;
642 }
643 tmp.w = req.w;
644 }
645 }
646
647 gog_view_size_allocate (child, &tmp);
648 } else if (!(GOG_POSITION_IS_SPECIAL (pos)) &&
649 !(GOG_POSITION_IS_PADDING (pos)))
650 g_warning ("[GogView::size_allocate_real] unexpected position %x for child %p of %p",
651 pos, child, view);
652 }
653
654 view->residual = res;
655 }
656
657 /* A simple default implementation */
658 static void
gog_view_render_real(GogView * view,GogViewAllocation const * bbox)659 gog_view_render_real (GogView *view, GogViewAllocation const *bbox)
660 {
661 GSList *ptr;
662 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next)
663 gog_view_render (ptr->data, bbox);
664 }
665
666 static void
gog_view_build_toolkit(GogView * view)667 gog_view_build_toolkit (GogView *view)
668 {
669 #ifdef GOFFICE_WITH_GTK
670 view->toolkit = g_slist_prepend (view->toolkit, &gog_tool_select_object);
671 view->toolkit = g_slist_prepend (view->toolkit, &gog_tool_move_object);
672 view->toolkit = g_slist_prepend (view->toolkit, &gog_tool_resize_object);
673 #endif
674 }
675
676 static void
gog_view_class_init(GogViewClass * view_klass)677 gog_view_class_init (GogViewClass *view_klass)
678 {
679 GObjectClass *gobject_klass = (GObjectClass *) view_klass;
680
681 parent_klass = g_type_class_peek_parent (view_klass);
682 gobject_klass->set_property = gog_view_set_property;
683 gobject_klass->finalize = gog_view_finalize;
684 view_klass->padding_request = gog_view_padding_request_real;
685 view_klass->size_request = gog_view_size_request_real;
686 view_klass->size_allocate = gog_view_size_allocate_real;
687 view_klass->render = gog_view_render_real;
688 view_klass->build_toolkit = gog_view_build_toolkit;
689 view_klass->clip = FALSE;
690
691 g_object_class_install_property (gobject_klass, GOG_VIEW_PROP_PARENT,
692 g_param_spec_object ("parent",
693 _("Parent"),
694 _("the GogView parent"),
695 GOG_TYPE_VIEW,
696 GSF_PARAM_STATIC | G_PARAM_WRITABLE));
697 g_object_class_install_property (gobject_klass, GOG_VIEW_PROP_MODEL,
698 g_param_spec_object ("model",
699 _("Model"),
700 _("The GogObject this view displays"),
701 GOG_TYPE_OBJECT,
702 GSF_PARAM_STATIC | G_PARAM_WRITABLE));
703 }
704
705 static void
gog_view_init(GogView * view)706 gog_view_init (GogView *view)
707 {
708 view->allocation_valid = FALSE;
709 view->child_allocations_valid = FALSE;
710 view->being_updated = FALSE;
711 view->model = NULL;
712 view->parent = NULL;
713 view->children = NULL;
714 view->toolkit = NULL;
715 }
716
GSF_CLASS_ABSTRACT(GogView,gog_view,gog_view_class_init,gog_view_init,G_TYPE_OBJECT)717 GSF_CLASS_ABSTRACT (GogView, gog_view,
718 gog_view_class_init, gog_view_init,
719 G_TYPE_OBJECT)
720
721 /**
722 * gog_view_get_model:
723 * @view: #GogView
724 *
725 * Returns: (transfer none): the #GogObject owning the view.
726 **/
727 GogObject *
728 gog_view_get_model (GogView const *view)
729 {
730 return view->model;
731 }
732
733 /**
734 * gog_view_queue_redraw:
735 * @view: a #GogView
736 *
737 * Requests a redraw for the entire graph.
738 **/
739 void
gog_view_queue_redraw(GogView * view)740 gog_view_queue_redraw (GogView *view)
741 {
742 g_return_if_fail (GOG_IS_VIEW (view));
743 g_return_if_fail (view->renderer != NULL);
744
745 gog_renderer_request_update (view->renderer);
746 }
747
748 /**
749 * gog_view_queue_resize:
750 * @view: a #GogView
751 *
752 * Flags a view to have its size renegotiated; should
753 * be called when a model for some reason has a new size request.
754 * For example, when you change the size of a legend.
755 **/
756 void
gog_view_queue_resize(GogView * view)757 gog_view_queue_resize (GogView *view)
758 {
759 g_return_if_fail (GOG_IS_VIEW (view));
760 g_return_if_fail (view->renderer != NULL);
761
762 gog_renderer_request_update (view->renderer);
763
764 do {
765 view->allocation_valid = FALSE; /* in case there is no parent */
766 } while (NULL != (view = view->parent) && view->allocation_valid);
767 }
768
769 void
gog_view_padding_request(GogView * view,GogViewAllocation const * bbox,GogViewPadding * padding)770 gog_view_padding_request (GogView *view, GogViewAllocation const *bbox, GogViewPadding *padding)
771 {
772 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
773
774 g_return_if_fail (klass != NULL);
775 g_return_if_fail (padding != NULL);
776 g_return_if_fail (bbox != NULL);
777
778 padding->wl = padding->wr = padding->ht = padding->hb = 0.;
779
780 if (klass->padding_request != NULL)
781 (klass->padding_request) (view, bbox, padding);
782 }
783
784
785 /**
786 * gog_view_size_request:
787 * @view: a #GogView
788 * @available: available space.
789 * @requisition: a #GogViewRequisition.
790 *
791 * Determines the desired size of a view.
792 *
793 * Note, that the virtual method deviates slightly from this function. This
794 * function will zero @requisition before calling the virtual method.
795 *
796 * Remember that the size request is not necessarily the size a view will
797 * actually be allocated.
798 *
799 **/
800 void
gog_view_size_request(GogView * view,GogViewRequisition const * available,GogViewRequisition * requisition)801 gog_view_size_request (GogView *view,
802 GogViewRequisition const *available,
803 GogViewRequisition *requisition)
804 {
805 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
806
807 g_return_if_fail (klass != NULL);
808 g_return_if_fail (requisition != NULL);
809 g_return_if_fail (available != NULL);
810
811 if (klass->size_request) {
812 requisition->w = requisition->h = 0;
813 klass->size_request (view, available, requisition);
814 } else
815 requisition->w = requisition->h = 1.;
816 }
817
818 /**
819 * gog_view_size_allocate:
820 * @view: a #GogView
821 * @allocation: position and size to be allocated to @view
822 *
823 * Assign a size and position to a GogView. Primarilly used by containers.
824 **/
825 void
gog_view_size_allocate(GogView * view,GogViewAllocation const * allocation)826 gog_view_size_allocate (GogView *view, GogViewAllocation const *allocation)
827 {
828 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
829
830 g_return_if_fail (allocation != NULL);
831 g_return_if_fail (klass != NULL);
832 g_return_if_fail (klass->size_allocate != NULL);
833 g_return_if_fail (!view->being_updated);
834
835 gog_debug (0, g_warning ("size_allocate %s %p : x = %g, y = %g w = %g, h = %g",
836 G_OBJECT_TYPE_NAME (view), view,
837 allocation->x, allocation->y, allocation->w, allocation->h););
838
839 view->being_updated = TRUE;
840 (klass->size_allocate) (view, allocation);
841 view->being_updated = FALSE;
842
843 if (&view->allocation != allocation)
844 view->allocation = *allocation;
845 view->allocation_valid = view->child_allocations_valid = TRUE;
846 }
847
848 /**
849 * gog_view_update_sizes:
850 * @view: #GogView
851 *
852 * Returns: %TRUE if a redraw is necessary.
853 **/
854 gboolean
gog_view_update_sizes(GogView * view)855 gog_view_update_sizes (GogView *view)
856 {
857 g_return_val_if_fail (GOG_IS_VIEW (view), TRUE);
858 g_return_val_if_fail (!view->being_updated, TRUE);
859
860 if (!view->allocation_valid)
861 gog_view_size_allocate (view, &view->allocation);
862 else if (!view->child_allocations_valid) {
863 GSList *ptr;
864
865 view->being_updated = TRUE;
866 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next)
867 gog_view_update_sizes (ptr->data);
868 view->being_updated = FALSE;
869
870 view->child_allocations_valid = TRUE;
871 } else
872 return FALSE;
873 return TRUE;
874 }
875
876 void
gog_view_render(GogView * view,GogViewAllocation const * bbox)877 gog_view_render (GogView *view, GogViewAllocation const *bbox)
878 {
879 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
880
881 g_return_if_fail (view->renderer != NULL);
882
883 /* In particular this is true for NaNs. */
884 if (view->model->invisible ||
885 !(view->allocation.w >= 0 && view->allocation.h >= 0))
886 return;
887
888 if (klass->clip) {
889 gog_renderer_push_clip_rectangle (view->renderer, view->allocation.x, view->allocation.y,
890 view->allocation.w, view->allocation.h);
891
892 klass->render (view, bbox);
893
894 gog_renderer_pop_clip (view->renderer);
895 }
896 else
897 klass->render (view, bbox);
898 }
899
900 /**
901 * gog_view_size_child_request:
902 * @view: a #GogView
903 * @available: the amount of space available in total
904 * @req: additionnal requisition
905 * @min_req: minimum size for displaying all children
906 *
907 * Computes additional requision in @req which must be added to parent requisition,
908 * and minimum requisition in @min_req which is minimum space for displaying all
909 * children.
910 *
911 **/
912 void
gog_view_size_child_request(GogView * view,GogViewRequisition const * available,GogViewRequisition * req,GogViewRequisition * min_req)913 gog_view_size_child_request (GogView *view,
914 GogViewRequisition const *available,
915 GogViewRequisition *req,
916 GogViewRequisition *min_req)
917 {
918 GSList *ptr, *list;
919 GogView *child;
920 GogObjectPosition pos;
921 GogViewRequisition child_req;
922 double const pad_h = gog_renderer_pt2r_y (view->renderer, PAD_HACK);
923 double const pad_w = gog_renderer_pt2r_x (view->renderer, PAD_HACK);
924
925 req->w = req->h = min_req->w = min_req->h = 0.;
926
927 /* walk the list in reverse */
928 list = g_slist_reverse (g_slist_copy (view->children));
929 for (ptr = list; ptr != NULL ; ptr = ptr->next) {
930 child = ptr->data;
931
932 pos = child->model->position;
933 if (pos & GOG_POSITION_MANUAL) {
934 g_warning ("manual is not supported yet");
935 } else if (pos & GOG_POSITION_COMPASS) {
936 /* Dead simple */
937 gog_view_size_request (child, available, &child_req);
938
939 if (pos & (GOG_POSITION_N|GOG_POSITION_S)) {
940 if (child_req.h > 0) {
941 req->h += child_req.h + pad_h;
942 min_req->h += child_req.h + pad_h;
943 }
944 } else if (min_req->h < child_req.h)
945 min_req->h = child_req.h;
946
947 if (pos & (GOG_POSITION_E|GOG_POSITION_W)) {
948 if (child_req.w > 0) {
949 req->w += child_req.w + pad_w;
950 min_req->w += child_req.w + pad_w;
951 }
952 } else if (min_req->w < child_req.w)
953 min_req->w = child_req.w;
954
955 } else if (!(GOG_POSITION_IS_SPECIAL (pos)))
956 g_warning ("[GogView::size_child_request] unexpected position %x for child %p of %p",
957 pos, child, view);
958 }
959 g_slist_free (list);
960 }
961
962 /**
963 * gog_view_find_child_view:
964 * @container: #GogView
965 * @target_model: #GogObject
966 *
967 * Find the GogView contained in @container that corresponds to @model.
968 *
969 * Returns: (transfer none): NULL on error or if @target_model has no view.
970 **/
971 GogView *
gog_view_find_child_view(GogView const * container,GogObject const * target_model)972 gog_view_find_child_view (GogView const *container, GogObject const *target_model)
973 {
974 GogObject const *obj, *old_target;
975 GSList *ptr;
976
977 g_return_val_if_fail (GOG_IS_VIEW (container), NULL);
978 g_return_val_if_fail (GOG_IS_OBJECT (target_model), NULL);
979
980 /* @container is a view for @target_models parent */
981 obj = target_model;
982 while (obj != NULL && container->model != obj)
983 obj = obj->parent;
984
985 g_return_val_if_fail (obj != NULL, NULL);
986
987 for ( ; obj != target_model ; container = ptr->data) {
988 /* find the parent of @target_object that should be a child of this view */
989 old_target = obj;
990 obj = target_model;
991 while (obj != NULL && obj->parent != old_target)
992 obj = obj->parent;
993
994 g_return_val_if_fail (obj != NULL, NULL);
995
996 for (ptr = container->children ; ptr != NULL ; ptr = ptr->next)
997 if (GOG_VIEW (ptr->data)->model == obj)
998 break;
999
1000 /* target_model doesn't have view */
1001 if (ptr == NULL)
1002 return NULL;
1003 }
1004
1005 return (GogView *)container;
1006 }
1007
1008 /**
1009 * gog_view_render_toolkit:
1010 * @view: #GogView
1011 *
1012 * Render toolkit elements.
1013 **/
1014 void
gog_view_render_toolkit(GogView * view)1015 gog_view_render_toolkit (GogView *view)
1016 {
1017 GogTool *tool;
1018 GSList const *ptr;
1019
1020 g_return_if_fail (GOG_IS_VIEW (view));
1021
1022 for (ptr = gog_view_get_toolkit (view); ptr != NULL; ptr = ptr->next) {
1023 tool = ptr->data;
1024 if (tool->render != NULL)
1025 (tool->render) (view);
1026 }
1027
1028 }
1029
1030 /**
1031 * gog_view_get_toolkit:
1032 * @view: #GogView
1033 *
1034 * Returns: (element-type GogTool) (transfer none): toolkit associated with given view.
1035 **/
1036 GSList const *
gog_view_get_toolkit(GogView * view)1037 gog_view_get_toolkit (GogView *view)
1038 {
1039 g_return_val_if_fail (GOG_IS_VIEW (view), NULL);
1040
1041 if (view->toolkit == NULL) {
1042 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
1043 if (klass->build_toolkit != NULL)
1044 (klass->build_toolkit) (view);
1045 }
1046
1047 return view->toolkit;
1048 }
1049
1050 /**
1051 * gog_view_get_tool_at_point:
1052 * @view: #GogView
1053 * @x: in coords
1054 * @y: in coords
1055 * @gobj: pointed object or %NULL
1056 *
1057 * Returns: (transfer none): tool under cursor for a given view, or %NULL
1058 **/
1059 GogTool *
gog_view_get_tool_at_point(GogView * view,double x,double y,GogObject ** gobj)1060 gog_view_get_tool_at_point (GogView *view, double x, double y, GogObject **gobj)
1061 {
1062 GogObject *current_gobj = NULL;
1063 GogTool *current_tool;
1064 GSList const *ptr;
1065
1066 for (ptr = gog_view_get_toolkit (view); ptr != NULL; ptr = ptr->next) {
1067 current_tool = ptr->data;
1068 if (current_tool->point != NULL &&
1069 (current_tool->point) (view, x, y, ¤t_gobj)) {
1070 if (gobj != NULL)
1071 *gobj = current_gobj == NULL ? view->model : current_gobj;
1072 return current_tool;
1073 }
1074 }
1075
1076 if (gobj != NULL)
1077 *gobj = NULL;
1078 return NULL;
1079 }
1080
1081 /**
1082 * gog_view_get_view_at_point:
1083 * @view: #GogView
1084 * @x: cursor x position
1085 * @y: cursor y position
1086 * @obj: pointed object or %NULL
1087 * @tool: pointed tool or %NULL
1088 *
1089 * Gets view under cursor, searching recursively from @view. Corresponding object
1090 * is stored in @obj. This object may or may not be @view->model of pointed view.
1091 * This function also stores tool under cursor, for the pointed view.
1092 *
1093 * Returns: (transfer none): the #GogView at x,y position
1094 **/
1095 GogView *
gog_view_get_view_at_point(GogView * view,double x,double y,GogObject ** obj,GogTool ** tool)1096 gog_view_get_view_at_point (GogView *view, double x, double y, GogObject **obj, GogTool **tool)
1097 {
1098 GogView *pointed_view;
1099 GSList const *ptr;
1100 GSList *list;
1101 GogTool *current_tool;
1102
1103 g_return_val_if_fail (GOG_IS_VIEW (view), NULL);
1104
1105 /* walk the list in reverse */
1106 list = g_slist_reverse (g_slist_copy (view->children));
1107 for (ptr = list; ptr != NULL; ptr = ptr->next) {
1108 pointed_view = gog_view_get_view_at_point (GOG_VIEW (ptr->data), x, y, obj, tool);
1109 if (pointed_view != NULL) {
1110 g_slist_free (list);
1111 return pointed_view;
1112 }
1113 }
1114 g_slist_free (list);
1115
1116 if ((current_tool = gog_view_get_tool_at_point (view, x, y, obj))) {
1117 if (tool != NULL)
1118 *tool = current_tool;
1119 return view;
1120 }
1121
1122 if (obj && *obj)
1123 *obj = NULL;
1124 return NULL;
1125 }
1126
1127 /**
1128 * gog_view_get_tip_at_point:
1129 * @view: #GogView
1130 * @x: x position
1131 * @y: y position
1132 *
1133 * Gets a tip string related to the position as defined by (@x,@y) in @view.
1134 *
1135 * return value: the newly allocated tip string if the view class supports
1136 * that or %NULL.
1137 **/
1138 char*
gog_view_get_tip_at_point(GogView * view,double x,double y)1139 gog_view_get_tip_at_point (GogView *view, double x, double y)
1140 {
1141 GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
1142 return (klass->get_tip_at_point != NULL)? (klass->get_tip_at_point) (view, x, y): NULL;
1143 }
1144
1145 void
gog_view_get_natural_size(GogView * view,GogViewRequisition * requisition)1146 gog_view_get_natural_size (GogView *view, GogViewRequisition *requisition)
1147 {
1148 requisition->w = requisition->h = 0.; /*FIXME!!!*/
1149 }
1150