Misc mnemonics fixes.
[gliv.git] / src / windows.c
blob20112c4a013e6f95186fce4a8545d9e0970f93b8
1 /*
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() */
27 #include "gliv.h"
28 #include "windows.h"
29 #include "options.h"
30 #include "gliv-image.h"
31 #include "callbacks.h"
32 #include "messages.h"
33 #include "math_floats.h"
34 #include "str_utils.h"
35 #include "matrix.h"
36 #include "scrollbars.h"
37 #include "gl_widget.h"
38 #include "menus.h"
39 #include "cursors.h"
41 extern rt_struct *rt;
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)
66 return;
68 if (width < 0)
69 width = rt->wid_size->width;
71 if (height < 0)
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);
84 process_events();
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);
96 if (name != NULL)
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)
105 GtkEntry *entry;
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);
116 return entry;
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);
135 g_free(old_ident);
136 } else {
137 /* Add a loading information. */
138 gchar *new_ident;
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);
146 g_free(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;
162 if (toggling)
163 return FALSE;
165 toggling = TRUE;
166 process_events();
168 if (*flag == FALSE)
169 /* Show. */
170 gtk_widget_show(widget);
171 else
172 /* Hide. */
173 gtk_widget_hide(widget);
175 *flag ^= TRUE;
177 if (options->fullscreen == FALSE)
178 resize_window(-1, -1, FALSE);
180 process_events();
181 toggling = FALSE;
182 return TRUE;
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;
199 const gchar *sym;
200 gfloat zoom, angle;
202 if (current_image == NULL)
203 return;
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);
213 g_free(size_str);
216 /* The state */
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))
224 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);
229 g_free(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",
236 FALSE));
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)
252 hide_scrollbars();
254 if (options->fullscreen && has_net_wm())
255 toggle_fullscreen(TRUE);
257 return FALSE;
260 void create_windows(void)
262 GtkWindow *first_window;
263 GtkAccelGroup *accel_group;
264 GtkHBox *hbox;
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);
284 /* The widgets. */
285 create_gl_widget();
286 accel_group = create_menus();
287 create_status_bar();
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);
293 /* Collection. */
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;
329 if (im == NULL)
330 im = current_image;
332 if (im == NULL) {
333 new_width = 1;
334 new_height = 1;
335 } else {
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
351 * NET WM protocol.
353 static gboolean net_wm_toggle_fullscreen(gboolean enable)
355 if (get_current_window() != main_window || has_net_wm() == FALSE)
356 return FALSE;
358 if (enable)
359 gtk_window_fullscreen(main_window);
360 else
361 gtk_window_unfullscreen(main_window);
363 return TRUE;
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;
372 if (enable) {
373 /* Go to fullscreen mode. */
374 new = fs_window;
375 old = main_window;
376 } else {
377 /* Go to window mode. */
378 new = main_window;
379 old = fs_window;
382 gtk_widget_show(GTK_WIDGET(new));
383 gtk_widget_hide(GTK_WIDGET(old));
385 gtk_widget_reparent(GTK_WIDGET(widgets), GTK_WIDGET(new));
389 if (enable == FALSE)
390 goto_window(NULL);
392 options->fullscreen = enable;
393 schedule_hide_cursor();
396 void update_window_title(void)
398 const gchar *title;
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));
409 return NULL;