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"
41 extern options_struct
*options
;
42 extern gliv_image
*current_image
;
43 extern GtkWidget
*gl_widget
;
44 extern GtkMenuBar
*menu_bar
;
46 /* What is in the window. */
47 static GtkVBox
*widgets
;
49 static GtkWindow
*main_window
;
50 static GtkWindow
*fs_window
;
52 /* The main status bar and its subdivisions. */
53 static GtkHBox
*status_bar
;
54 static GtkEntry
*entry_ident
;
55 static GtkEntry
*entry_size
;
56 static GtkEntry
*entry_state
;
59 * Called when resizing the window because the image changed or a
60 * widget has been toggled.
62 void resize_window(gint width
, gint height
, gboolean force
)
64 if (options
->resize_win
== FALSE
&& force
== FALSE
)
68 width
= rt
->wid_size
->width
;
71 height
= rt
->wid_size
->height
;
73 width
= MAX(width
, 320);
74 height
= MAX(height
, 240);
77 * First, we let GTK resize the window around the
78 * width x height widget,
80 gtk_widget_set_size_request(gl_widget
, width
, height
);
81 gtk_window_resize(main_window
, width
, height
);
85 /* then, we make the widget downsizable. */
86 gtk_widget_set_size_request(gl_widget
, 1, 1);
89 void show_dialog(GtkWidget
* widget
, const gchar
* name
)
91 /* In windowed mode we let the WM choose the position. */
92 if (options
->fullscreen
)
93 gtk_window_set_position(GTK_WINDOW(widget
), GTK_WIN_POS_MOUSE
);
96 gtk_window_set_title(GTK_WINDOW(widget
), name
);
98 gtk_widget_show_all(widget
);
101 /* To add an entry in the status bar. */
102 static GtkEntry
*add_entry(gboolean grow
, const gchar
* init
)
106 entry
= GTK_ENTRY(gtk_entry_new());
107 gtk_editable_set_editable(GTK_EDITABLE(entry
), FALSE
);
108 gtk_box_pack_start(GTK_BOX(status_bar
), GTK_WIDGET(entry
), grow
, TRUE
, 0);
110 /* We don't want the blinking cursor. */
111 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(entry
), GTK_CAN_FOCUS
);
113 gtk_entry_set_text(entry
, init
);
118 static void create_status_bar(void)
120 status_bar
= GTK_HBOX(gtk_hbox_new(FALSE
, 0));
122 entry_ident
= add_entry(TRUE
, _("No image loaded"));
123 entry_size
= add_entry(FALSE
, _("width x height"));
124 entry_state
= add_entry(FALSE
, _("zoom% (angle)"));
127 void set_loading_entry(const gchar
* filename
)
129 static gchar
*old_ident
= NULL
;
131 if (filename
== NULL
) {
132 /* Revert to the previous text without the loading information. */
133 gtk_entry_set_text(entry_ident
, old_ident
);
136 /* Add a loading information. */
139 old_ident
= gtk_editable_get_chars(GTK_EDITABLE(entry_ident
), 0, -1);
141 new_ident
= g_strconcat(old_ident
, " ", _("loading"), ":",
142 filename_to_utf8(filename
), NULL
);
144 gtk_entry_set_text(entry_ident
, new_ident
);
148 if (options
->status_bar
) {
149 /* Make sure the widget is updated now. */
150 gtk_widget_queue_draw(GTK_WIDGET(status_bar
));
151 gdk_window_process_updates(GTK_WIDGET(status_bar
)->window
, TRUE
);
155 /* Returns TRUE if the widget has been toggled. */
156 gboolean
toggle_widget(GtkWidget
* widget
, gboolean
* flag
)
160 gtk_widget_show(widget
);
163 gtk_widget_hide(widget
);
167 if (options
->fullscreen
== FALSE
)
168 resize_window(-1, -1, FALSE
);
173 void toggle_menu_bar(void)
175 toggle_widget(GTK_WIDGET(menu_bar
), &options
->menu_bar
);
178 void toggle_status_bar(void)
180 toggle_widget(GTK_WIDGET(status_bar
), &options
->status_bar
);
183 /* To take into account the new filename, zoom, angle, symmetry. */
184 void update_status_bar(void)
186 gchar
*size_str
, *state_str
;
190 if (current_image
== NULL
)
193 if (current_image
->ident
!= NULL
) {
194 /* Filename and dimensions status, only the first time. */
195 gtk_entry_set_text(entry_ident
, current_image
->ident
);
196 gtk_editable_set_position(GTK_EDITABLE(entry_ident
), 0);
198 size_str
= g_strdup_printf("%dx%d",
199 current_image
->width
, current_image
->height
);
200 gtk_entry_set_text(entry_size
, size_str
);
206 zoom
= get_matrix_zoom() * 100.0;
207 angle
= get_matrix_angle() * 180.0 / PI
;
208 sym
= is_matrix_symmetry()? " /" : "";
210 /* We don't want -0.000 */
211 if (float_equal(angle
, 0.0))
214 state_str
= g_strdup_printf(_("%.3f%% (%.3f deg%s)"), zoom
, angle
, sym
);
215 gtk_entry_set_text(entry_state
, state_str
);
220 /* Does the WM support the EWMH protocol? */
221 static gboolean
has_net_wm(void)
223 return gdk_net_wm_supports(gdk_atom_intern("_NET_WM_STATE_FULLSCREEN",
228 * On the first map, all widgets are shown, we have to hide
229 * those that are not requested.
231 static gboolean
first_map(void)
233 if (options
->menu_bar
== FALSE
)
234 gtk_widget_hide(GTK_WIDGET(menu_bar
));
236 if (options
->status_bar
== FALSE
)
237 gtk_widget_hide(GTK_WIDGET(status_bar
));
239 if (options
->scrollbars
== FALSE
)
242 if (options
->fullscreen
&& has_net_wm())
243 toggle_fullscreen(TRUE
);
248 void create_windows(void)
250 GtkWindow
*first_window
;
251 GtkAccelGroup
*accel_group
;
255 * The main window: seen when not in fullscreen,
256 * or when using the NET WM protocol.
258 main_window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
259 gtk_window_set_title(main_window
, "GLiv");
260 install_callbacks(main_window
);
262 /* The window seen in fullscreen mode. */
263 fs_window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
264 gtk_window_set_title(fs_window
, _("GLiv in fullscreen"));
266 /* Make it fullscreen. */
267 gtk_window_move(fs_window
, 0, 0);
268 gtk_window_set_default_size(fs_window
, rt
->scr_width
, rt
->scr_height
);
269 gtk_window_set_decorated(fs_window
, FALSE
);
270 install_callbacks(fs_window
);
274 accel_group
= create_menus();
277 /* Bind keyboard accelerators to windows. */
278 gtk_window_add_accel_group(main_window
, accel_group
);
279 gtk_window_add_accel_group(fs_window
, accel_group
);
282 widgets
= GTK_VBOX(gtk_vbox_new(FALSE
, 0));
284 /* The menu bar on the top. */
285 gtk_box_pack_start(GTK_BOX(widgets
), GTK_WIDGET(menu_bar
), FALSE
, FALSE
, 0);
287 /* The OpenGL widget and the vertical scrollbar under the menu bar. */
288 hbox
= GTK_HBOX(gtk_hbox_new(FALSE
, 0));
289 gtk_box_pack_start_defaults(GTK_BOX(hbox
), gl_widget
);
290 gtk_box_pack_start(GTK_BOX(hbox
), get_new_scrollbar(FALSE
), FALSE
, FALSE
,
292 gtk_box_pack_start_defaults(GTK_BOX(widgets
), GTK_WIDGET(hbox
));
294 /* The horizontal scrollbar under them. */
295 gtk_box_pack_start(GTK_BOX(widgets
), get_new_scrollbar(TRUE
), FALSE
, FALSE
,
298 /* The status bar in the bottom. */
299 gtk_box_pack_end(GTK_BOX(widgets
), GTK_WIDGET(status_bar
), FALSE
, FALSE
, 0);
301 first_window
= (options
->fullscreen
&& !has_net_wm())?
302 fs_window
: main_window
;
304 gtk_container_add(GTK_CONTAINER(first_window
), GTK_WIDGET(widgets
));
305 g_signal_connect_after(first_window
, "map", G_CALLBACK(first_map
), NULL
);
306 gtk_widget_show_all(GTK_WIDGET(first_window
));
310 * Called when going from fullscreen mode to window mode, or when
311 * the displayed image changed in window mode.
313 void goto_window(void)
315 gint new_width
, new_height
;
317 if (current_image
== NULL
) {
321 new_width
= current_image
->width
;
322 new_height
= current_image
->height
;
325 if (new_width
>= rt
->scr_width
)
326 new_width
= 3 * rt
->scr_width
/ 4;
328 if (new_height
>= rt
->scr_height
)
329 new_height
= 3 * rt
->scr_height
/ 4;
331 resize_window(new_width
, new_height
, FALSE
);
335 * Return TRUE if we managed to toggle the fullscreen mode using the
338 static gboolean
net_wm_toggle_fullscreen(gboolean enable
)
340 if (get_current_window() != main_window
|| has_net_wm() == FALSE
)
344 gtk_window_fullscreen(main_window
);
346 gtk_window_unfullscreen(main_window
);
351 void toggle_fullscreen(gboolean enable
)
353 /* We first try using the NET WM protocol, then with the old method. */
354 if (net_wm_toggle_fullscreen(enable
) == FALSE
) {
355 GtkWindow
*new, *old
;
358 /* Go to fullscreen mode. */
362 /* Go to window mode. */
367 gtk_widget_show(GTK_WIDGET(new));
368 gtk_widget_hide(GTK_WIDGET(old
));
370 gtk_widget_reparent(GTK_WIDGET(widgets
), GTK_WIDGET(new));
377 options
->fullscreen
= enable
;
378 schedule_hide_cursor();
381 void update_window_title(void)
385 title
= filename_to_utf8(current_image
->node
->data
);
386 gtk_window_set_title(main_window
, title
);
389 GtkWindow
*get_current_window(void)
391 if (GTK_IS_WIDGET(gl_widget
) && GTK_WIDGET_VISIBLE(gl_widget
))
392 return GTK_WINDOW(gtk_widget_get_toplevel(gl_widget
));