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, &current_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