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 <guichaz@yahoo.fr>
21 /*************************
22 * The top level widgets *
23 *************************/
25 #include <gdk/gdkx.h> /* gdk_net_wm_supports() */
30 #include "gliv-image.h"
31 #include "callbacks.h"
33 #include "math_floats.h"
34 #include "str_utils.h"
36 #include "scrollbars.h"
37 #include "gl_widget.h"
42 extern options_struct
*options
;
43 extern GlivImage
*current_image
;
44 extern GtkWidget
*gl_widget
;
45 extern GtkMenuBar
*menu_bar
;
47 /* What is in the window. */
48 static GtkVBox
*widgets
;
50 static GtkWindow
*main_window
;
51 static GtkWindow
*fs_window
;
53 /* The main status bar and its subdivisions. */
54 static GtkHBox
*status_bar
;
55 static GtkEntry
*entry_ident
;
56 static GtkEntry
*entry_size
;
57 static GtkEntry
*entry_state
;
60 * Called when resizing the window because the image changed or a
61 * widget has been toggled.
63 void resize_window(gint width
, gint height
, gboolean force
)
65 if (options
->resize_win
== FALSE
&& force
== FALSE
)
69 width
= rt
->wid_size
->width
;
72 height
= rt
->wid_size
->height
;
74 width
= MAX(width
, 320);
75 height
= MAX(height
, 240);
78 * First, we let GTK resize the window around the
79 * width x height widget,
81 gtk_widget_set_size_request(gl_widget
, width
, height
);
82 gtk_window_resize(main_window
, 1, 1);
86 /* then, we make the widget downsizable. */
87 gtk_widget_set_size_request(gl_widget
, 1, 1);
90 void show_dialog(GtkWidget
* widget
, const gchar
* name
)
92 /* In windowed mode we let the WM choose the position. */
93 if (options
->fullscreen
)
94 gtk_window_set_position(GTK_WINDOW(widget
), GTK_WIN_POS_MOUSE
);
97 gtk_window_set_title(GTK_WINDOW(widget
), name
);
99 gtk_widget_show_all(widget
);
102 /* To add an entry in the status bar. */
103 static GtkEntry
*add_entry(gboolean grow
, const gchar
* init
)
107 entry
= GTK_ENTRY(gtk_entry_new());
108 gtk_editable_set_editable(GTK_EDITABLE(entry
), FALSE
);
109 gtk_box_pack_start(GTK_BOX(status_bar
), GTK_WIDGET(entry
), grow
, TRUE
, 0);
111 /* We don't want the blinking cursor. */
112 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(entry
), GTK_CAN_FOCUS
);
114 gtk_entry_set_text(entry
, init
);
119 static void create_status_bar(void)
121 status_bar
= GTK_HBOX(gtk_hbox_new(FALSE
, 0));
123 entry_ident
= add_entry(TRUE
, _("No image loaded"));
124 entry_size
= add_entry(FALSE
, _("width x height"));
125 entry_state
= add_entry(FALSE
, _("zoom% (angle)"));
128 void set_loading_entry(const gchar
* filename
)
130 static gchar
*old_ident
= NULL
;
132 if (filename
== NULL
) {
133 /* Revert to the previous text without the loading information. */
134 gtk_entry_set_text(entry_ident
, old_ident
);
137 /* Add a loading information. */
140 old_ident
= gtk_editable_get_chars(GTK_EDITABLE(entry_ident
), 0, -1);
142 new_ident
= g_strconcat(old_ident
, " ", _("loading"), ":",
143 filename_to_utf8(filename
), NULL
);
145 gtk_entry_set_text(entry_ident
, new_ident
);
149 if (options
->status_bar
) {
150 /* Make sure the widget is updated now. */
151 gtk_widget_queue_draw(GTK_WIDGET(status_bar
));
152 gdk_window_process_updates(GTK_WIDGET(status_bar
)->window
, TRUE
);
156 /* Returns TRUE if the widget has been toggled. */
157 gboolean
toggle_widget(GtkWidget
* widget
, gboolean
* flag
)
159 /* We are not reentrant. */
160 static gboolean toggling
= FALSE
;
170 gtk_widget_show(widget
);
173 gtk_widget_hide(widget
);
177 if (options
->fullscreen
== FALSE
)
178 resize_window(-1, -1, FALSE
);
185 void toggle_menu_bar(void)
187 toggle_widget(GTK_WIDGET(menu_bar
), &options
->menu_bar
);
190 void toggle_status_bar(void)
192 toggle_widget(GTK_WIDGET(status_bar
), &options
->status_bar
);
195 /* To take into account the new filename, zoom, angle, symmetry. */
196 void update_status_bar(void)
198 gchar
*size_str
, *state_str
;
202 if (current_image
== NULL
)
205 if (current_image
->ident
!= NULL
) {
206 /* Filename and dimensions status, only the first time. */
207 gtk_entry_set_text(entry_ident
, current_image
->ident
);
208 gtk_editable_set_position(GTK_EDITABLE(entry_ident
), 0);
210 size_str
= g_strdup_printf("%dx%d",
211 current_image
->width
, current_image
->height
);
212 gtk_entry_set_text(entry_size
, size_str
);
218 zoom
= get_matrix_zoom() * 100.0;
219 angle
= get_matrix_angle() * 180.0 / PI
;
220 sym
= is_matrix_symmetry()? " /" : "";
222 /* We don't want -0.000 */
223 if (float_equal(angle
, 0.0))
226 state_str
= g_strdup_printf(_("%.3f%% (%.3f deg%s)"), zoom
, angle
, sym
);
227 gtk_entry_set_text(entry_state
, state_str
);
232 /* Does the WM support the EWMH protocol? */
233 static gboolean
has_net_wm(void)
235 return gdk_net_wm_supports(gdk_atom_intern("_NET_WM_STATE_FULLSCREEN",
240 * On the first map, all widgets are shown, we have to hide
241 * those that are not requested.
243 static gboolean
first_map(void)
245 if (options
->menu_bar
== FALSE
)
246 gtk_widget_hide(GTK_WIDGET(menu_bar
));
248 if (options
->status_bar
== FALSE
)
249 gtk_widget_hide(GTK_WIDGET(status_bar
));
251 if (options
->scrollbars
== FALSE
)
254 if (options
->fullscreen
&& has_net_wm())
255 toggle_fullscreen(TRUE
);
260 void create_windows(void)
262 GtkWindow
*first_window
;
263 GtkAccelGroup
*accel_group
;
267 * The main window: seen when not in fullscreen,
268 * or when using the NET WM protocol.
270 main_window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
271 gtk_window_set_title(main_window
, "GLiv");
272 install_callbacks(main_window
);
274 /* The window seen in fullscreen mode. */
275 fs_window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
276 gtk_window_set_title(fs_window
, _("GLiv in fullscreen"));
278 /* Make it fullscreen. */
279 gtk_window_move(fs_window
, 0, 0);
280 gtk_window_set_default_size(fs_window
, rt
->scr_width
, rt
->scr_height
);
281 gtk_window_set_decorated(fs_window
, FALSE
);
282 install_callbacks(fs_window
);
286 accel_group
= create_menus();
289 /* Bind keyboard accelerators to windows. */
290 gtk_window_add_accel_group(main_window
, accel_group
);
291 gtk_window_add_accel_group(fs_window
, accel_group
);
294 widgets
= GTK_VBOX(gtk_vbox_new(FALSE
, 0));
296 /* The menu bar on the top. */
297 gtk_box_pack_start(GTK_BOX(widgets
), GTK_WIDGET(menu_bar
), FALSE
, FALSE
, 0);
299 /* The OpenGL widget and the vertical scrollbar under the menu bar. */
300 hbox
= GTK_HBOX(gtk_hbox_new(FALSE
, 0));
301 gtk_box_pack_start_defaults(GTK_BOX(hbox
), gl_widget
);
302 gtk_box_pack_start(GTK_BOX(hbox
), get_new_scrollbar(FALSE
), FALSE
, FALSE
,
304 gtk_box_pack_start_defaults(GTK_BOX(widgets
), GTK_WIDGET(hbox
));
306 /* The horizontal scrollbar under them. */
307 gtk_box_pack_start(GTK_BOX(widgets
), get_new_scrollbar(TRUE
), FALSE
, FALSE
,
310 /* The status bar in the bottom. */
311 gtk_box_pack_end(GTK_BOX(widgets
), GTK_WIDGET(status_bar
), FALSE
, FALSE
, 0);
313 first_window
= (options
->fullscreen
&& !has_net_wm())?
314 fs_window
: main_window
;
316 gtk_container_add(GTK_CONTAINER(first_window
), GTK_WIDGET(widgets
));
317 g_signal_connect_after(first_window
, "map", G_CALLBACK(first_map
), NULL
);
318 gtk_widget_show_all(GTK_WIDGET(first_window
));
322 * Called when going from fullscreen mode to window mode, or when
323 * the displayed image changed in window mode.
325 void goto_window(GlivImage
* im
)
327 gint new_width
, new_height
;
336 new_width
= im
->width
;
337 new_height
= im
->height
;
340 if (new_width
>= rt
->scr_width
)
341 new_width
= 3 * rt
->scr_width
/ 4;
343 if (new_height
>= rt
->scr_height
)
344 new_height
= 3 * rt
->scr_height
/ 4;
346 resize_window(new_width
, new_height
, FALSE
);
350 * Return TRUE if we managed to toggle the fullscreen mode using the
353 static gboolean
net_wm_toggle_fullscreen(gboolean enable
)
355 if (get_current_window() != main_window
|| has_net_wm() == FALSE
)
359 gtk_window_fullscreen(main_window
);
361 gtk_window_unfullscreen(main_window
);
366 void toggle_fullscreen(gboolean enable
)
368 /* We first try using the NET WM protocol, then with the old method. */
369 if (net_wm_toggle_fullscreen(enable
) == FALSE
) {
370 GtkWindow
*new, *old
;
373 /* Go to fullscreen mode. */
377 /* Go to window mode. */
382 gtk_widget_show(GTK_WIDGET(new));
383 gtk_widget_hide(GTK_WIDGET(old
));
385 gtk_widget_reparent(GTK_WIDGET(widgets
), GTK_WIDGET(new));
392 options
->fullscreen
= enable
;
393 schedule_hide_cursor();
396 void update_window_title(void)
400 title
= filename_to_utf8(current_image
->node
->data
);
401 gtk_window_set_title(main_window
, title
);
404 GtkWindow
*get_current_window(void)
406 if (GTK_IS_WIDGET(gl_widget
) && GTK_WIDGET_VISIBLE(gl_widget
))
407 return GTK_WINDOW(gtk_widget_get_toplevel(gl_widget
));