Merged older cs.po file with newest pot file.
[gliv/czech_localization.git] / src / windows.c
blob9b0fe0836f21e223a9385d938abd9f6af5777168
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 <string.h> /* memcpy() */
26 #include <gdk/gdkx.h> /* gdk_net_wm_supports() */
28 #include "gliv.h"
29 #include "windows.h"
30 #include "options.h"
31 #include "gliv-image.h"
32 #include "callbacks.h"
33 #include "messages.h"
34 #include "math_floats.h"
35 #include "str_utils.h"
36 #include "matrix.h"
37 #include "scrollbars.h"
38 #include "gl_widget.h"
39 #include "menus.h"
40 #include "cursors.h"
41 #include "loading.h"
42 #include "help.h"
44 extern rt_struct *rt;
45 extern options_struct *options;
46 extern GlivImage *current_image;
47 extern GtkWidget *gl_widget;
48 extern GtkMenuBar *menu_bar;
50 /* What is in the window. */
51 static GtkVBox *widgets;
53 static GtkWindow *main_window;
54 static GtkWindow *fs_window;
56 /* The main status bar and its subdivisions. */
57 static GtkHBox *status_bar;
58 static GtkEntry *entry_ident;
59 static GtkEntry *entry_size;
60 static GtkEntry *entry_state;
63 * Called when resizing the window because the image changed or a
64 * widget has been toggled.
66 static void resize_window(gint width, gint height, gboolean first_time)
68 GtkRequisition req;
70 if (options->resize_win == FALSE && first_time == FALSE)
71 return;
73 if (first_time && options->initial_geometry)
74 return;
76 width = MAX(width, 320);
77 height = MAX(height, 240);
79 if (width == rt->wid_size->width && height == rt->wid_size->height)
80 return;
83 * First, we let GTK resize the window around the
84 * width x height widget,
86 gtk_widget_set_size_request(gl_widget, width, height);
87 gtk_widget_size_request(GTK_WIDGET(main_window), &req);
88 gtk_window_resize(main_window, req.width, req.height);
90 /* then, we make the widget downsizable. */
91 gtk_widget_set_size_request(gl_widget, 1, 1);
94 void show_message(GtkWidget * widget, const gchar * name)
96 /* In windowed mode we let the WM choose the position. */
97 if (options->fullscreen)
98 gtk_window_set_position(GTK_WINDOW(widget), GTK_WIN_POS_MOUSE);
100 if (name != NULL)
101 gtk_window_set_title(GTK_WINDOW(widget), name);
103 gtk_widget_show_all(widget);
106 gint run_modal_dialog(GtkDialog * dialog)
108 gint response;
110 gtk_window_set_transient_for(GTK_WINDOW(dialog), get_current_window());
111 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
113 /* In windowed mode we let the WM choose the position. */
114 if (options->fullscreen)
115 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
117 /* We want the cursor to be visible when the dialog is shown. */
118 set_hide_cursor_enabled(FALSE);
119 response = gtk_dialog_run(GTK_DIALOG(dialog));
120 set_hide_cursor_enabled(TRUE);
122 return response;
125 static gint get_entry_border(GtkEntry * entry)
127 gint interior_focus, focus_width;
128 gint border;
130 gtk_widget_style_get(GTK_WIDGET(entry),
131 "interior-focus", &interior_focus,
132 "focus-line-width", &focus_width, NULL);
134 border = entry->has_frame ? GTK_WIDGET(entry)->style->xthickness : 0;
136 if (!interior_focus)
137 border += focus_width;
139 return border + 2; /* + 2 : INNER_BORDER */
142 static gint status_bar_free_space(void)
144 gint space = 0;
145 GtkEntry *entries[] = { entry_ident, entry_size, entry_state, NULL };
146 GtkEntry **ptr;
148 for (ptr = entries; *ptr != NULL; ptr++) {
149 GtkRequisition req;
151 gtk_widget_size_request(GTK_WIDGET(*ptr), &req);
152 space += GTK_WIDGET(*ptr)->allocation.width - req.width;
155 return space;
158 static void set_entry_text(GtkEntry * entry, const gchar * text)
160 GtkRequisition old;
161 GtkRequisition req;
162 gboolean accept_resize;
164 gtk_entry_set_text(entry, text);
166 /* Compute the full width */
167 gtk_widget_size_request(GTK_WIDGET(entry), &old);
168 memcpy(&req, &old, sizeof(GtkRequisition));
169 pango_layout_get_pixel_size(gtk_entry_get_layout(entry), &req.width, NULL);
170 req.width += 2 * get_entry_border(entry);
172 if (req.width > old.width) {
173 gint free_space = status_bar_free_space();
175 accept_resize = req.width - old.width <= free_space;
176 } else
177 accept_resize = old.width - req.width > 20;
179 if (accept_resize)
180 gtk_widget_set_size_request(GTK_WIDGET(entry), req.width, req.height);
183 static gint ident_basename_pos(const gchar * ident)
185 gint pos;
186 gint last1 = 0, last2 = 0;
188 for (pos = 0; *ident != '\0'; ident = g_utf8_next_char(ident)) {
189 if (g_utf8_get_char(ident) == '/') {
190 last2 = last1;
191 last1 = pos;
193 pos++;
196 return last2;
199 static void set_ident_text(const gchar * ident)
201 gint pos;
202 const gchar *loading = currently_loading();
204 pos = ident_basename_pos(ident);
206 if (loading) {
207 gchar *concat = g_strconcat(ident, " ", _("loading"), ":",
208 loading, NULL);
209 gtk_entry_set_text(entry_ident, concat);
210 g_free(concat);
211 } else
212 gtk_entry_set_text(entry_ident, ident);
214 gtk_editable_set_position(GTK_EDITABLE(entry_ident), pos);
217 /* To add an entry in the status bar. */
218 static GtkEntry *add_entry(gboolean grow, const gchar * init)
220 GtkEntry *entry;
222 entry = GTK_ENTRY(gtk_entry_new());
223 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
224 gtk_box_pack_start(GTK_BOX(status_bar), GTK_WIDGET(entry), grow, TRUE, 0);
226 /* We don't want the blinking cursor. */
227 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(entry), GTK_CAN_FOCUS);
229 set_entry_text(entry, init);
231 return entry;
234 static void create_status_bar(void)
236 status_bar = GTK_HBOX(gtk_hbox_new(FALSE, 0));
238 entry_ident = add_entry(TRUE, _("No image loaded"));
239 entry_size = add_entry(FALSE, _("width x height"));
240 entry_state = add_entry(FALSE, _("zoom% (angle)"));
243 /* Returns TRUE if the widgets has been toggled. */
244 gboolean toggle_widgets(GtkWidget ** widgets, gint nb_widgets, gboolean * flag)
246 /* We are not reentrant. */
247 static gboolean toggling = FALSE;
248 gint w, h;
249 gint i;
251 if (toggling)
252 return FALSE;
254 toggling = TRUE;
255 w = rt->wid_size->width;
256 h = rt->wid_size->height;
258 for (i = 0; i < nb_widgets; i++) {
259 if (*flag == FALSE)
260 gtk_widget_show(widgets[i]);
261 else
262 gtk_widget_hide(widgets[i]);
265 *flag ^= TRUE;
266 toggling = FALSE;
267 return TRUE;
270 static gboolean toggle_widget(GtkWidget * widget, gboolean * flag)
272 return toggle_widgets(&widget, 1, flag);
275 gboolean toggle_menu_bar(void)
277 toggle_widget(GTK_WIDGET(menu_bar), &options->menu_bar);
278 return FALSE;
281 gboolean toggle_status_bar(void)
283 toggle_widget(GTK_WIDGET(status_bar), &options->status_bar);
284 return FALSE;
287 /* To take into account the new filename, zoom, angle, symmetry. */
288 void update_status_bar(void)
290 gchar *size_str, *state_str;
291 const gchar *sym;
292 gfloat zoom, angle;
294 if (current_image == NULL)
295 /* Loading first image */
296 set_ident_text("");
297 else if (current_image->ident != NULL) {
298 /* Filename and dimensions status, only the first time. */
299 set_ident_text(current_image->ident);
300 g_free(current_image->ident);
301 current_image->ident = NULL;
303 size_str = g_strdup_printf("%dx%d",
304 current_image->width, current_image->height);
305 set_entry_text(entry_size, size_str);
306 g_free(size_str);
309 /* The state */
311 zoom = get_matrix_zoom() * 100.0;
312 angle = get_matrix_angle() * 180.0 / PI;
313 sym = is_matrix_symmetry()? " /" : "";
315 /* We don't want -0.000 */
316 if (float_equal(angle, 0.0))
317 angle = 0.0;
319 state_str = g_strdup_printf(_("%.3f%% (%.3f deg%s)"), zoom, angle, sym);
320 set_entry_text(entry_state, state_str);
322 g_free(state_str);
325 /* Does the WM support the EWMH protocol? */
326 static gboolean has_net_wm(void)
328 return gdk_net_wm_supports(gdk_atom_intern("_NET_WM_STATE_FULLSCREEN",
329 FALSE));
333 * On the first map, all widgets are shown, we have to hide
334 * those that are not requested.
336 static gboolean first_map(void)
338 if (options->menu_bar == FALSE)
339 gtk_widget_hide(GTK_WIDGET(menu_bar));
341 if (options->status_bar == FALSE)
342 gtk_widget_hide(GTK_WIDGET(status_bar));
344 if (options->scrollbars == FALSE)
345 hide_scrollbars();
347 if (options->fullscreen && has_net_wm())
348 toggle_fullscreen(TRUE);
350 return FALSE;
353 static void set_initial_geometry(void)
355 GdkGeometry size_hints = {
356 1, 1, 0, 0, 1, 1, 1, 1, 0.0, 0.0, GDK_GRAVITY_NORTH_WEST
359 if (!options->initial_geometry)
360 goto fail;
362 if (options->fullscreen) {
363 g_printerr(_("Ignoring initial geometry (%s) as"
364 " fullscreen mode is requested\n"),
365 options->initial_geometry);
366 goto fail;
369 gtk_window_set_geometry_hints(main_window, GTK_WIDGET(main_window),
370 &size_hints,
371 GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
372 GDK_HINT_RESIZE_INC);
374 if (!gtk_window_parse_geometry(main_window, options->initial_geometry)) {
375 g_printerr(_("Cannot parse geometry: %s\n"), options->initial_geometry);
376 goto fail;
379 return;
381 fail:
382 g_free(options->initial_geometry);
383 options->initial_geometry = NULL;
386 void create_windows(void)
388 GtkWindow *first_window;
389 GtkAccelGroup *accel_group;
390 GtkHBox *hbox;
391 GdkPixbuf *logo = get_gliv_logo();
393 if (logo != NULL)
394 gtk_window_set_default_icon(logo);
396 rt->scr_width = gdk_screen_width();
397 rt->scr_height = gdk_screen_height();
400 * The main window: seen when not in fullscreen,
401 * or when using the NET WM protocol.
403 main_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
404 gtk_window_set_title(main_window, "GLiv");
405 install_callbacks(main_window);
407 /* The window seen in fullscreen mode. */
408 fs_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
409 gtk_window_set_title(fs_window, _("GLiv in fullscreen"));
411 /* Make it fullscreen. */
412 gtk_window_move(fs_window, 0, 0);
413 gtk_window_set_default_size(fs_window, rt->scr_width, rt->scr_height);
414 gtk_window_set_decorated(fs_window, FALSE);
415 install_callbacks(fs_window);
417 /* The widgets. */
418 create_gl_widget();
419 accel_group = create_menus();
420 create_status_bar();
422 /* Bind keyboard accelerators to windows. */
423 gtk_window_add_accel_group(main_window, accel_group);
424 gtk_window_add_accel_group(fs_window, accel_group);
426 /* Collection. */
427 widgets = GTK_VBOX(gtk_vbox_new(FALSE, 0));
429 /* The menu bar on the top. */
430 gtk_box_pack_start(GTK_BOX(widgets), GTK_WIDGET(menu_bar), FALSE, FALSE, 0);
432 /* The OpenGL widget and the vertical scrollbar under the menu bar. */
433 hbox = GTK_HBOX(gtk_hbox_new(FALSE, 0));
434 gtk_box_pack_start_defaults(GTK_BOX(hbox), gl_widget);
435 gtk_box_pack_start(GTK_BOX(hbox), get_new_scrollbar(FALSE), FALSE, FALSE,
437 gtk_box_pack_start_defaults(GTK_BOX(widgets), GTK_WIDGET(hbox));
439 /* The horizontal scrollbar under them. */
440 gtk_box_pack_start(GTK_BOX(widgets), get_new_scrollbar(TRUE), FALSE, FALSE,
443 /* The status bar in the bottom. */
444 gtk_box_pack_end(GTK_BOX(widgets), GTK_WIDGET(status_bar), FALSE, FALSE, 0);
446 first_window = (options->fullscreen && !has_net_wm())?
447 fs_window : main_window;
449 gtk_container_add(GTK_CONTAINER(first_window), GTK_WIDGET(widgets));
450 g_signal_connect_after(first_window, "map", G_CALLBACK(first_map), NULL);
451 gtk_widget_show_all(GTK_WIDGET(widgets));
452 set_initial_geometry();
453 gtk_widget_show_all(GTK_WIDGET(first_window));
457 * Called when going from fullscreen mode to window mode, or when
458 * the displayed image changed in window mode.
460 void goto_window(GlivImage * im, gboolean first_time)
462 gint new_width, new_height;
464 if (im == NULL)
465 im = current_image;
467 if (im == NULL) {
468 new_width = 0;
469 new_height = 0;
470 } else {
471 new_width = im->width;
472 new_height = im->height;
475 if (new_width >= rt->scr_width)
476 new_width = 3 * rt->scr_width / 4;
478 if (new_height >= rt->scr_height)
479 new_height = 3 * rt->scr_height / 4;
481 resize_window(new_width, new_height, first_time);
485 * Return TRUE if we managed to toggle the fullscreen mode using the
486 * NET WM protocol.
488 static gboolean net_wm_toggle_fullscreen(gboolean enable)
490 if (get_current_window() != main_window || has_net_wm() == FALSE)
491 return FALSE;
493 if (enable)
494 gtk_window_fullscreen(main_window);
495 else
496 gtk_window_unfullscreen(main_window);
498 return TRUE;
501 void toggle_fullscreen(gboolean enable)
503 /* We first try using the NET WM protocol, then with the old method. */
504 if (net_wm_toggle_fullscreen(enable) == FALSE) {
505 GtkWindow *new, *old;
507 if (enable) {
508 /* Go to fullscreen mode. */
509 new = fs_window;
510 old = main_window;
511 } else {
512 /* Go to window mode. */
513 new = main_window;
514 old = fs_window;
517 gtk_widget_show(GTK_WIDGET(new));
518 gtk_widget_hide(GTK_WIDGET(old));
520 gtk_widget_reparent(GTK_WIDGET(widgets), GTK_WIDGET(new));
524 if (enable == FALSE)
525 goto_window(NULL, FALSE);
527 options->fullscreen = enable;
528 schedule_hide_cursor();
531 void update_window_title(void)
533 const gchar *path;
534 gchar *basename, *title;
536 if (current_image->node->data == NULL)
537 return;
539 path = filename_to_utf8(current_image->node->data);
540 basename = g_path_get_basename(path);
541 title = g_strdup_printf("GLiv - %s", basename);
542 gtk_window_set_title(main_window, title);
543 g_free(basename);
544 g_free(title);
547 GtkWindow *get_current_window(void)
549 if (GTK_IS_WIDGET(gl_widget) && GTK_WIDGET_VISIBLE(gl_widget))
550 return GTK_WINDOW(gtk_widget_get_toplevel(gl_widget));
552 return NULL;
555 GtkWindow *new_window(const gchar * title)
557 GtkWindow *win = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
558 GtkWindow *parent = get_current_window();
560 gtk_window_set_title(win, title);
561 gtk_window_set_transient_for(win, parent);
562 gtk_window_set_destroy_with_parent(win, TRUE);
563 gtk_window_set_type_hint(win, GDK_WINDOW_TYPE_HINT_DIALOG);
565 return win;
568 gboolean toggle_floating_windows(void)
570 static GList *hidden_windows;
571 GList *ptr;
573 if (hidden_windows) {
574 for (ptr = hidden_windows; ptr; ptr = ptr->next) {
575 gtk_widget_show(GTK_WIDGET(ptr->data));
576 g_object_unref(ptr->data);
579 g_list_free(hidden_windows);
580 hidden_windows = NULL;
581 } else {
582 GList *toplevels = gtk_window_list_toplevels();
584 /* First two windows are the main window and the full screen one */
585 for (ptr = toplevels->next->next; ptr; ptr = ptr->next) {
586 GtkWidget *widget = GTK_WIDGET(ptr->data);
587 if (GTK_WIDGET_VISIBLE(widget)) {
588 gtk_widget_hide(widget);
589 hidden_windows = g_list_prepend(hidden_windows, widget);
590 g_object_ref(widget);
593 g_list_free(toplevels);
596 return FALSE;