1 /* Dia -- a diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * $Id: navigation.c,v 1.2 2004/01/06 19:37:31 hans Exp $
6 * navigation.c : a navigation popup window to browse large diagrams.
7 * Copyright (C) 2003 Luc Pionchon
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "diagdkrenderer.h"
31 #include "navigation.h"
34 #define THUMBNAIL_MAX_SIZE 150 /*(pixels) this may be a preference*/
37 struct _NavigationWindow
39 GtkWidget
* popup_window
;
41 /*popup size (drawing_area)*/
46 gboolean is_first_expose
;
54 /*factors to translate thumbnail coordinates to adjustement values*/
58 /*diagram thumbnail's buffer*/
61 /*display to navigate*/
65 typedef struct _NavigationWindow NavigationWindow
;
67 static NavigationWindow _nav
;
68 static NavigationWindow
* nav
= &_nav
;
71 #define DIAGRAM_OFFSET 1 /*(diagram's unit) so we can see the little green boxes :)*/
72 #define FRAME_THICKNESS 2 /*(pixels)*/
73 #define STD_CURSOR_MIN 16 /*(pixels)*/
76 static void on_button_navigation_popup_pressed (GtkButton
* button
, gpointer _ddisp
);
77 static void on_button_navigation_popup_released (GtkButton
* button
, gpointer unused
);
79 static void reset_sc_adj (GtkAdjustment
* adj
, gdouble lower
, gdouble upper
, gdouble page
);
81 static gboolean
on_da_expose_event (GtkWidget
* widget
, GdkEventExpose
* event
, gpointer unused
);
82 static gboolean
on_da_motion_notify_event (GtkWidget
* widget
, GdkEventMotion
* event
, gpointer unused
);
83 static gboolean
on_da_button_release_event (GtkWidget
* widget
, GdkEventButton
* event
, gpointer popup_window
);
86 static char * nav_xpm
[] = {
103 navigation_popup_new (DDisplay
*ddisp
)
109 GdkBitmap
* mask
= NULL
;
111 button
= gtk_button_new ();
112 gtk_container_set_border_width (GTK_CONTAINER (button
), 0);
113 gtk_button_set_relief (GTK_BUTTON(button
), GTK_RELIEF_NONE
);
114 g_signal_connect (G_OBJECT (button
), "pressed",
115 G_CALLBACK (on_button_navigation_popup_pressed
), ddisp
);
116 /*if you are fast, the button catches it before the drawing_area:*/
117 g_signal_connect (G_OBJECT (button
), "released",
118 G_CALLBACK (on_button_navigation_popup_released
), NULL
);
120 pixmap
= gdk_pixmap_colormap_create_from_xpm_d(NULL
,
121 gtk_widget_get_colormap(button
),
123 &(button
->style
->bg
[GTK_STATE_NORMAL
]),
126 image
= gtk_image_new_from_pixmap (pixmap
, mask
);
127 g_object_unref(pixmap
);
128 g_object_unref(mask
);
130 gtk_container_add (GTK_CONTAINER (button
), image
);
131 gtk_widget_show(image
);
139 on_button_navigation_popup_pressed (GtkButton
* button
, gpointer _ddisp
)
141 GtkWidget
* popup_window
;
144 GtkWidget
* drawing_area
;
148 Rectangle rect
;/*diagram's extents*/
149 real zoom
;/*zoom factor for thumbnail rendering*/
152 /*--Retrieve the diagram's data*/
153 nav
->ddisp
= (DDisplay
*) _ddisp
;
154 data
= nav
->ddisp
->diagram
->data
;
156 /*--Calculate sizes*/
158 int canvas_width
, canvas_height
;/*pixels*/
159 int diagram_width
, diagram_height
;/*pixels*/
162 nav
->max_size
= THUMBNAIL_MAX_SIZE
;
164 /*size: Diagram <--> thumbnail*/
165 rect
.top
= data
->extents
.top
- DIAGRAM_OFFSET
;
166 rect
.left
= data
->extents
.left
- DIAGRAM_OFFSET
;
167 rect
.bottom
= data
->extents
.bottom
+ DIAGRAM_OFFSET
+ 1;
168 rect
.right
= data
->extents
.right
+ DIAGRAM_OFFSET
+ 1;
170 zoom
= nav
->max_size
/ MAX( (rect
.right
- rect
.left
) , (rect
.bottom
- rect
.top
) );
172 nav
->width
= MIN( nav
->max_size
, (rect
.right
- rect
.left
) * zoom
);
173 nav
->height
= MIN( nav
->max_size
, (rect
.bottom
- rect
.top
) * zoom
);
175 /*size: display canvas <--> frame cursor*/
176 diagram_width
= (int) ddisplay_transform_length (nav
->ddisp
, (rect
.right
- rect
.left
));
177 diagram_height
= (int) ddisplay_transform_length (nav
->ddisp
, (rect
.bottom
- rect
.top
));
179 canvas_width
= nav
->ddisp
->canvas
->allocation
.width
;
180 canvas_height
= nav
->ddisp
->canvas
->allocation
.height
;
182 nav
->frame_w
= nav
->width
* canvas_width
/ diagram_width
;
183 nav
->frame_h
= nav
->height
* canvas_height
/ diagram_height
;
185 /*reset adjustements to diagram size,*/
186 /*(dia allows to grow the canvas bigger than the diagram's actual size) */
187 /*and store the ratio thumbnail/adjustement(speedup on motion)*/
188 adj
= nav
->ddisp
->hsbdata
;
189 reset_sc_adj (adj
, rect
.left
, rect
.right
, canvas_width
/ nav
->ddisp
->zoom_factor
);
190 nav
->hadj_coef
= (adj
->upper
- adj
->page_size
- adj
->lower
) / (nav
->width
- nav
->frame_w
);
192 adj
= nav
->ddisp
->vsbdata
;
193 reset_sc_adj (adj
, rect
.top
, rect
.bottom
, canvas_height
/ nav
->ddisp
->zoom_factor
);
194 nav
->vadj_coef
= (adj
->upper
- adj
->page_size
- adj
->lower
) / (nav
->height
- nav
->frame_h
);
198 /*popup window, and cute frame*/
199 popup_window
= gtk_window_new (GTK_WINDOW_POPUP
);
200 nav
->popup_window
= popup_window
;
201 gtk_window_set_position (GTK_WINDOW (popup_window
), GTK_WIN_POS_MOUSE
);
203 frame
= gtk_frame_new (NULL
);
204 gtk_frame_set_shadow_type (GTK_FRAME (frame
), GTK_SHADOW_ETCHED_OUT
);
207 drawing_area
= gtk_drawing_area_new ();
208 gtk_drawing_area_size (GTK_DRAWING_AREA(drawing_area
), nav
->width
, nav
->height
);
210 gtk_widget_set_events (drawing_area
, 0
212 | GDK_POINTER_MOTION_MASK
213 | GDK_BUTTON_RELEASE_MASK
216 g_signal_connect (G_OBJECT (drawing_area
), "expose_event",
217 G_CALLBACK (on_da_expose_event
), NULL
);
218 g_signal_connect (G_OBJECT (drawing_area
), "motion_notify_event",
219 G_CALLBACK (on_da_motion_notify_event
), NULL
);
220 g_signal_connect (G_OBJECT (drawing_area
), "button_release_event",
221 G_CALLBACK (on_da_button_release_event
), NULL
);
224 gtk_container_add (GTK_CONTAINER (frame
), drawing_area
);
225 gtk_container_add (GTK_CONTAINER (popup_window
), frame
);
226 gtk_widget_show (drawing_area
);
227 gtk_widget_show (frame
);
228 gtk_widget_show (popup_window
);
231 nav
->gc
= gdk_gc_new (drawing_area
->window
);
232 gdk_gc_set_line_attributes (nav
->gc
,
234 GDK_LINE_SOLID
, GDK_CAP_BUTT
, GDK_JOIN_MITER
);
237 if(MIN(nav
->frame_h
, nav
->frame_w
) > STD_CURSOR_MIN
){
238 nav
->cursor
= gdk_cursor_new (GDK_FLEUR
);
240 else{/*the miniframe is very small, so we use a minimalist cursor*/
241 unsigned char cursor_none_data
[] = { 0x00 };
243 GdkColor fg
= { 0, 65535, 65535, 65535};
244 GdkColor bg
= { 0, 0, 0, 0 };
246 bitmap
= gdk_bitmap_create_from_data(NULL
, cursor_none_data
, 1, 1);
247 nav
->cursor
= gdk_cursor_new_from_pixmap(bitmap
, bitmap
, &fg
, &bg
, 1, 1);
248 g_object_unref(bitmap
);
252 gdk_pointer_grab (drawing_area
->window
, TRUE
,
253 GDK_BUTTON_RELEASE_MASK
| GDK_BUTTON_MOTION_MASK
,
254 drawing_area
->window
,
258 /*buffer to draw the thumbnail on*/
259 nav
->buffer
= gdk_pixmap_new (drawing_area
->window
,
260 nav
->width
, nav
->height
, -1);
261 gdk_draw_rectangle (nav
->buffer
,
262 drawing_area
->style
->black_gc
, TRUE
,
263 0, 0, nav
->width
, nav
->height
);
265 {/*--Render the thumbnail*/
266 DiaGdkRenderer
*renderer
;
269 renderer
= g_object_new (DIA_TYPE_GDK_RENDERER
, NULL
);
270 renderer
->transform
= dia_transform_new (&rect
, &zoom
);
271 renderer
->pixmap
= nav
->buffer
;/*render on the thumbnail buffer*/
272 renderer
->gc
= gdk_gc_new (nav
->buffer
);
275 color_convert (&data
->bg_color
, &color
);
276 gdk_gc_set_foreground (renderer
->gc
, &color
);
277 gdk_draw_rectangle (renderer
->pixmap
, renderer
->gc
, 1, 0, 0, nav
->width
, nav
->height
);
280 data_render (data
, DIA_RENDERER (renderer
), NULL
, NULL
, NULL
);
282 g_object_ref (renderer
->pixmap
);
283 g_object_unref (renderer
);
286 nav
->is_first_expose
= TRUE
;/*set to request to draw the miniframe*/
290 /* resets adjustement to diagram size */
292 reset_sc_adj (GtkAdjustment
* adj
, gdouble lower
, gdouble upper
, gdouble page
)
294 adj
->page_size
= page
;
299 if (adj
->value
< lower
) adj
->value
= lower
;
300 if (adj
->value
> (upper
- page
)) adj
->value
= upper
- page
;
302 gtk_adjustment_changed(adj
);
307 on_da_expose_event (GtkWidget
* widget
, GdkEventExpose
* event
, gpointer unused
)
309 /*refresh the part outdated by the event*/
310 gdk_draw_pixmap (widget
->window
,
311 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
312 GDK_PIXMAP(nav
->buffer
),
313 event
->area
.x
, event
->area
.y
,
314 event
->area
.x
, event
->area
.y
,
315 event
->area
.width
, event
->area
.height
);
317 /*the first time, display the current display's state*/
318 if(nav
->is_first_expose
){
323 adj
= nav
->ddisp
->hsbdata
;
324 x
= (adj
->value
- adj
->lower
) / (adj
->upper
- adj
->lower
) * (nav
->width
) +1;
326 adj
= nav
->ddisp
->vsbdata
;
327 y
= (adj
->value
- adj
->lower
) / (adj
->upper
- adj
->lower
) * (nav
->height
) +1;
329 /*draw directly on the window, do not buffer the miniframe*/
330 gdk_draw_rectangle (widget
->window
,
332 x
, y
, nav
->frame_w
, nav
->frame_h
);
334 nav
->is_first_expose
= FALSE
;
341 on_da_motion_notify_event (GtkWidget
* drawing_area
, GdkEventMotion
* event
, gpointer unused
)
344 gboolean value_changed
;
346 int w
= nav
->frame_w
;
347 int h
= nav
->frame_h
;
349 int x
, y
;/*top left of the miniframe*/
351 /* Don't try to move if there's no room for it.*/
352 if (w
>= nav
->width
-1 && h
>= nav
->height
-1) return FALSE
;
354 x
= CLAMP (event
->x
- w
/2 , 0, nav
->width
- w
);
355 y
= CLAMP (event
->y
- h
/2 , 0, nav
->height
- h
);
357 adj
= nav
->ddisp
->hsbdata
;
358 value_changed
= FALSE
;
359 if (w
/2 <= event
->x
&& event
->x
<= (nav
->width
- w
/2)){
360 adj
->value
= adj
->lower
+ x
* nav
->hadj_coef
;
361 value_changed
= TRUE
;
363 else if (x
== 0 && adj
->value
!= adj
->lower
){/*you've been too fast! :)*/
364 adj
->value
= adj
->lower
;
365 value_changed
= TRUE
;
367 else if (x
== (nav
->width
- w
) && adj
->value
!= (adj
->upper
- adj
->page_size
)){/*idem*/
368 adj
->value
= adj
->upper
- adj
->page_size
;
369 value_changed
= TRUE
;
371 if (value_changed
) gtk_adjustment_value_changed(adj
);
373 adj
= nav
->ddisp
->vsbdata
;
374 value_changed
= FALSE
;
375 if (h
/2 <= event
->y
&& event
->y
<= (nav
->height
- h
/2)){
376 adj
->value
= adj
->lower
+ y
* nav
->vadj_coef
;
377 value_changed
= TRUE
;
379 else if (y
== 0 && adj
->value
!= adj
->lower
){/*you've been too fast! :)*/
380 adj
->value
= adj
->lower
;
381 value_changed
= TRUE
;
383 else if (y
== (nav
->height
- h
) && adj
->value
!= (adj
->upper
- adj
->page_size
)){/*idem*/
384 adj
->value
= adj
->upper
- adj
->page_size
;
385 value_changed
= TRUE
;
387 if (value_changed
) gtk_adjustment_value_changed(adj
);
390 /*--Draw the miniframe*/
391 /*refresh from the buffer*/
392 gdk_draw_pixmap (drawing_area
->window
,
393 drawing_area
->style
->fg_gc
[GTK_WIDGET_STATE (drawing_area
)],
394 GDK_PIXMAP(nav
->buffer
),
395 0, 0, 0, 0, nav
->width
, nav
->height
);
396 /*draw directly on the window, do not buffer the miniframe*/
397 gdk_draw_rectangle (drawing_area
->window
,
405 on_da_button_release_event (GtkWidget
* widget
, GdkEventButton
* event
, gpointer unused
)
407 g_object_unref (nav
->buffer
);
408 g_object_unref (nav
->gc
);
409 gdk_cursor_unref (nav
->cursor
);
411 gtk_widget_destroy (nav
->popup_window
);
413 /*returns the focus on the canvas*/
414 gtk_widget_grab_focus(nav
->ddisp
->canvas
);
419 on_button_navigation_popup_released (GtkButton
* button
, gpointer z
)
421 on_da_button_release_event (NULL
, NULL
, NULL
);