2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <gfc@altern.org>
21 /*************************
22 * The top level widgets. *
23 *************************/
27 #include <gdk/gdkx.h> /* gdk_net_wm_supports() */
30 extern options_struct
*options
;
31 extern gliv_image
*current_image
;
32 extern GtkWidget
*gl_widget
;
33 extern GtkMenuBar
*menu_bar
;
35 static GtkVBox
*widgets
;
37 static GtkWindow
*main_window
;
38 static GtkWindow
*fs_window
;
40 /* The main status bar and its subdivisions. */
41 static GtkHBox
*status_bar
;
42 static GtkEntry
*entry_ident
;
43 static GtkEntry
*entry_size
;
44 static GtkEntry
*entry_state
;
46 static GtkHScrollbar
*hscroll
;
47 static GtkVScrollbar
*vscroll
;
49 static gboolean window_resized
= FALSE
;
50 static gint last_width
= 0;
51 static gint last_height
= 0;
54 * We determine whether the window has been manually resized in order
55 * to prevent automatic resizing in this case.
57 static gboolean
check_window_resized(void)
59 gint current_width
, current_height
;
65 gtk_window_get_size(main_window
, ¤t_width
, ¤t_height
);
67 window_resized
= last_width
!= 0 &&
68 (last_width
!= current_width
|| last_height
!= current_height
);
70 return window_resized
;
73 static void resize_window(gint width
, gint height
)
75 gtk_window_resize(main_window
, width
, height
);
83 void show_dialog(GtkWidget
* widget
, const gchar
* name
, gboolean unused
)
85 /* In windowed mode we let the WM choose the position. */
86 if (options
->fullscreen
)
87 gtk_window_set_position(GTK_WINDOW(widget
), GTK_WIN_POS_MOUSE
);
90 gtk_window_set_title(GTK_WINDOW(widget
), name
);
92 gtk_widget_show_all(widget
);
95 static GtkEntry
*add_entry(gboolean grow
, const gchar
* init
)
99 entry
= GTK_ENTRY(gtk_entry_new());
100 gtk_editable_set_editable(GTK_EDITABLE(entry
), FALSE
);
101 gtk_box_pack_start(GTK_BOX(status_bar
), GTK_WIDGET(entry
), grow
, TRUE
, 0);
103 /* We don't want the blinking cursor. */
104 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(entry
), GTK_CAN_FOCUS
);
106 gtk_entry_set_text(entry
, init
);
111 static void create_status_bar(void)
113 status_bar
= GTK_HBOX(gtk_hbox_new(FALSE
, 0));
115 entry_ident
= add_entry(TRUE
, _("No image loaded"));
116 entry_size
= add_entry(FALSE
, _("width x height"));
117 entry_state
= add_entry(FALSE
, _("zoom% (angle)"));
120 void set_loading_entry(const gchar
* filename
)
122 static gchar
*old_ident
= NULL
;
124 if (filename
== NULL
) {
125 gtk_entry_set_text(entry_ident
, old_ident
);
130 old_ident
= gtk_editable_get_chars(GTK_EDITABLE(entry_ident
), 0, -1);
132 new_ident
= g_strconcat(old_ident
, " ", _("loading"), ":",
133 locale_to_utf8(filename
), NULL
);
135 gtk_entry_set_text(entry_ident
, new_ident
);
139 if (options
->status_bar
) {
140 /* Make sure the widget is updated now. */
141 gtk_widget_queue_draw(GTK_WIDGET(entry_ident
));
142 gdk_window_process_updates(GTK_WIDGET(entry_ident
)->window
, TRUE
);
146 /* Returns TRUE if the widget has been toggled. */
147 static gboolean
toggle_widget(GtkWidget
* widget
, gboolean
* flag
,
150 gint new_width
, new_height
, coeff
;
152 /* We are not reentrant. */
153 static gboolean toggling
= FALSE
;
161 gtk_window_get_size(main_window
, &new_width
, &new_height
);
163 if (*flag
== FALSE
) {
165 gtk_widget_show_all(widget
);
169 gtk_widget_hide_all(widget
);
174 new_width
+= coeff
* widget
->allocation
.width
;
176 new_height
+= coeff
* widget
->allocation
.height
;
180 if (options
->fullscreen
== FALSE
)
181 resize_window(new_width
, new_height
);
187 void toggle_menu_bar(void)
189 toggle_widget(GTK_WIDGET(menu_bar
), &options
->menu_bar
, FALSE
);
192 void toggle_status_bar(void)
194 toggle_widget(GTK_WIDGET(status_bar
), &options
->status_bar
, FALSE
);
197 void toggle_scrollbars(void)
199 if (!toggle_widget(GTK_WIDGET(hscroll
), &options
->scrollbars
, FALSE
))
202 /* To undo the toggle_widget() side effect. */
203 options
->scrollbars
^= TRUE
;
205 if (!toggle_widget(GTK_WIDGET(vscroll
), &options
->scrollbars
, TRUE
))
208 if (options
->scrollbars
)
209 update_scrollbars(TRUE
);
212 void update_status_bar(void)
214 gchar
*size_str
, *state_str
;
218 if (current_image
== NULL
)
221 if (current_image
->ident
!= NULL
) {
222 /* Filename and dimensions status, only the first time. */
223 gtk_entry_set_text(entry_ident
, current_image
->ident
);
224 gtk_editable_set_position(GTK_EDITABLE(entry_ident
), 0);
226 size_str
= g_strdup_printf("%dx%d",
227 current_image
->width
, current_image
->height
);
228 gtk_entry_set_text(entry_size
, size_str
);
234 zoom
= get_matrix_zoom() * 100.0;
235 angle
= get_matrix_angle() * 180.0 / PI
;
236 sym
= is_matrix_symmetry()? " /" : "";
238 /* We don't want -0.000 */
239 if (float_equal(angle
, 0.0))
242 state_str
= g_strdup_printf(_("%.3f%% (%.3f deg%s)"), zoom
, angle
, sym
);
243 gtk_entry_set_text(entry_state
, state_str
);
248 static void update_scroll(GtkScrollbar
* scroll
, gfloat min
, gfloat max
,
249 gfloat dim
, gboolean redraw
)
252 gboolean update_needed
;
254 adj
= gtk_range_get_adjustment(GTK_RANGE(scroll
));
256 update_needed
= (adj
->lower
< 0.0 || adj
->upper
> dim
||
257 float_equal(adj
->value
, 0.0) == FALSE
);
259 /* smallest length containing the image and the window = window U image. */
260 adj
->lower
= MIN(min
, 0.0);
261 adj
->upper
= MAX(max
, dim
);
264 adj
->page_size
= dim
;
266 /* the offset is actually computed with adj->lower and adj->upper. */
269 if (update_needed
== FALSE
&& (adj
->lower
< 0.0 || adj
->upper
> dim
))
270 update_needed
= TRUE
;
272 adj
->step_increment
= MOVE_OFFSET
;
273 adj
->page_increment
= dim
;
275 gtk_range_set_adjustment(GTK_RANGE(scroll
), adj
);
277 if (options
->scrollbars
&& update_needed
&& redraw
&&
278 GTK_WIDGET_REALIZED(GTK_WIDGET(scroll
))) {
280 gtk_widget_queue_draw(GTK_WIDGET(scroll
));
281 gdk_window_process_updates(GTK_WIDGET(scroll
)->window
, FALSE
);
285 void update_scrollbars(gboolean redraw
)
287 gfloat min_x
, max_x
, min_y
, max_y
;
289 if (current_image
== NULL
)
292 get_matrix_bounding_box(&min_x
, &max_x
, &min_y
, &max_y
);
294 update_scroll(GTK_SCROLLBAR(hscroll
), min_x
, max_x
,
295 (gfloat
) rt
->wid_size
->width
, redraw
);
297 update_scroll(GTK_SCROLLBAR(vscroll
), min_y
, max_y
,
298 (gfloat
) rt
->wid_size
->height
, redraw
);
301 static void scroll_val(GtkRange
* range
)
305 offset
= -1.0 * gtk_range_get_value(range
);
307 if (range
== GTK_RANGE(hscroll
))
308 matrix_move(offset
, 0.0);
310 /* range == GTK_RANGE(vscroll) */
311 matrix_move(0.0, offset
);
313 update_scrollbars(FALSE
);
314 refresh(REFRESH_IMAGE
| APPEND_HISTORY
);
317 static gboolean
has_net_wm(void)
319 return gdk_net_wm_supports(gdk_atom_intern("_NET_WM_STATE_FULLSCREEN",
323 static GtkRange
*scroll_new(gboolean horiz
)
328 range
= GTK_RANGE(gtk_hscrollbar_new(NULL
));
330 range
= GTK_RANGE(gtk_vscrollbar_new(NULL
));
332 gtk_range_set_update_policy(range
, GTK_UPDATE_CONTINUOUS
);
334 g_signal_connect(range
, "value-changed", G_CALLBACK(scroll_val
), NULL
);
339 void refresh_all(void)
341 gtk_widget_queue_draw(GTK_WIDGET(widgets
));
342 gdk_window_process_updates(GTK_WIDGET(widgets
)->window
, TRUE
);
346 static gboolean
first_map(void)
348 if (options
->menu_bar
== FALSE
)
349 gtk_widget_hide(GTK_WIDGET(menu_bar
));
351 if (options
->status_bar
== FALSE
)
352 gtk_widget_hide(GTK_WIDGET(status_bar
));
354 if (options
->scrollbars
== FALSE
) {
355 gtk_widget_hide(GTK_WIDGET(hscroll
));
356 gtk_widget_hide(GTK_WIDGET(vscroll
));
359 if (options
->fullscreen
&& has_net_wm())
360 toggle_fullscreen(TRUE
);
365 void create_windows(void)
367 GtkWindow
*first_window
;
368 GtkAccelGroup
*accel_group
;
372 * The main window: seen when not in fullscreen,
373 * or when using the NET WM protocol.
375 main_window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
376 gtk_window_set_title(main_window
, "GLiv");
378 install_callbacks(main_window
);
380 /* The window seen in fullscreen mode. */
381 fs_window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
382 gtk_window_set_title(fs_window
, _("GLiv in fullscreen"));
384 gtk_window_move(fs_window
, 0, 0);
385 gtk_window_set_default_size(fs_window
, rt
->scr_width
, rt
->scr_height
);
386 gtk_window_set_decorated(fs_window
, FALSE
);
387 install_callbacks(fs_window
);
391 accel_group
= create_menus();
394 hscroll
= GTK_HSCROLLBAR(scroll_new(TRUE
));
395 vscroll
= GTK_VSCROLLBAR(scroll_new(FALSE
));
397 /* Bind keyboard accelerators to windows. */
398 gtk_window_add_accel_group(main_window
, accel_group
);
399 gtk_window_add_accel_group(fs_window
, accel_group
);
402 widgets
= GTK_VBOX(gtk_vbox_new(FALSE
, 0));
403 gtk_box_pack_start(GTK_BOX(widgets
), GTK_WIDGET(menu_bar
), FALSE
, FALSE
, 0);
405 hbox
= GTK_HBOX(gtk_hbox_new(FALSE
, 0));
406 gtk_box_pack_start_defaults(GTK_BOX(hbox
), gl_widget
);
407 gtk_box_pack_start(GTK_BOX(hbox
), GTK_WIDGET(vscroll
), FALSE
, FALSE
, 0);
409 gtk_box_pack_start_defaults(GTK_BOX(widgets
), GTK_WIDGET(hbox
));
410 gtk_box_pack_start(GTK_BOX(widgets
), GTK_WIDGET(hscroll
), FALSE
, FALSE
, 0);
411 gtk_box_pack_end(GTK_BOX(widgets
), GTK_WIDGET(status_bar
), FALSE
, FALSE
, 0);
413 gtk_widget_show_all(GTK_WIDGET(widgets
));
415 first_window
= (options
->fullscreen
&& !has_net_wm())?
416 fs_window
: main_window
;
418 gtk_container_add(GTK_CONTAINER(first_window
), GTK_WIDGET(widgets
));
419 g_signal_connect_after(first_window
, "map", G_CALLBACK(first_map
), NULL
);
420 gtk_widget_show_all(GTK_WIDGET(first_window
));
423 void goto_window(gboolean force_resize
)
425 gint new_width
, new_height
;
427 if (current_image
== NULL
)
431 window_resized
= FALSE
;
433 if (force_resize
== FALSE
&& check_window_resized())
436 new_width
= current_image
->width
;
437 new_height
= current_image
->height
;
439 if (options
->menu_bar
) {
440 new_height
+= GTK_WIDGET(menu_bar
)->requisition
.height
;
441 if (new_width
< GTK_WIDGET(menu_bar
)->requisition
.width
)
442 new_width
= GTK_WIDGET(menu_bar
)->requisition
.width
;
445 if (options
->status_bar
) {
446 new_height
+= GTK_WIDGET(status_bar
)->requisition
.height
;
447 if (new_width
< GTK_WIDGET(status_bar
)->requisition
.width
)
448 new_width
= GTK_WIDGET(status_bar
)->requisition
.width
;
451 if (options
->scrollbars
) {
452 new_height
+= GTK_WIDGET(hscroll
)->requisition
.height
;
453 new_width
+= GTK_WIDGET(vscroll
)->requisition
.width
;
456 if (new_width
>= rt
->scr_width
|| new_height
>= rt
->scr_height
) {
457 new_width
= 3 * rt
->scr_width
/ 4;
458 new_height
= 3 * rt
->scr_height
/ 4;
461 resize_window(new_width
, new_height
);
464 static void reparent(GtkWidget
* new_parent
)
466 /* Be sure all widgets are displayed. */
468 /* gtk_widget_show_all() would also show the '(Rebuilding images menus)'. */
469 gtk_widget_show(GTK_WIDGET(menu_bar
));
471 if (options
->status_bar
&& GTK_WIDGET_REALIZED(status_bar
) == FALSE
)
472 gtk_container_foreach(GTK_CONTAINER(status_bar
),
473 (GtkCallback
) gtk_widget_realize
, NULL
);
475 gtk_widget_show_all(GTK_WIDGET(status_bar
));
477 gtk_widget_show_all(GTK_WIDGET(hscroll
));
478 gtk_widget_show_all(GTK_WIDGET(vscroll
));
482 /* Then reparent them. */
483 gtk_widget_reparent(GTK_WIDGET(widgets
), new_parent
);
487 /* Finally, hide widgets previously hidden. */
489 if (options
->menu_bar
== FALSE
)
490 gtk_widget_hide(GTK_WIDGET(menu_bar
));
492 if (options
->status_bar
== FALSE
)
493 gtk_widget_hide(GTK_WIDGET(status_bar
));
495 if (options
->scrollbars
== FALSE
) {
496 gtk_widget_hide(GTK_WIDGET(hscroll
));
497 gtk_widget_hide(GTK_WIDGET(vscroll
));
501 static gboolean
net_wm_toggle_fullscreen(gboolean enable
)
503 if (get_current_window() != main_window
|| has_net_wm() == FALSE
)
507 gtk_window_fullscreen(main_window
);
508 gdk_window_raise(GTK_WIDGET(main_window
)->window
);
510 gtk_window_unfullscreen(main_window
);
515 void toggle_fullscreen(gboolean enable
)
517 /* We first try using the NET WM protocol, then with the old method. */
518 if (net_wm_toggle_fullscreen(enable
) == FALSE
) {
519 GtkWindow
*new, *old
;
522 /* Go to fullscreen mode. */
526 /* Go to window mode. */
531 gtk_widget_show_all(GTK_WIDGET(new));
532 gtk_widget_hide(GTK_WIDGET(old
));
534 reparent(GTK_WIDGET(new));
540 options
->fullscreen
= enable
;
541 schedule_hide_cursor();
544 void update_window_title(void)
548 title
= locale_to_utf8(current_image
->node
->data
);
549 gtk_window_set_title(main_window
, title
);
552 GtkWindow
*get_current_window(void)
554 return GTK_WINDOW(gtk_widget_get_toplevel(gl_widget
));