Make gpicview compatible with fullscreen mode triggered by window managers.
[lxde/gpicview.git] / src / main-win.c
CommitLineData
1d48a247 1/***************************************************************************
d9f34bf9 2 * Copyright (C) 2007, 2008 by PCMan (Hong Jen Yee) *
1d48a247
HJYP
3 * pcman.tw@gmail.com *
4 * *
cf15ca10 5 * mw program is free software; you can redistribute it and/or modify *
1d48a247
HJYP
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
cf15ca10 10 * mw program is distributed in the hope that it will be useful, *
1d48a247
HJYP
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
cf15ca10 16 * along with mw program; if not, write to the *
1d48a247
HJYP
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
cf15ca10 25#include "main-win.h"
bb0f721e 26
1d48a247
HJYP
27#include <glib/gi18n.h>
28#include <glib/gstdio.h>
29#include <gdk/gdkkeysyms.h>
30
0ed1aa41 31#include <unistd.h>
1d48a247
HJYP
32#include <string.h>
33#include <errno.h>
da8bd946 34#include <math.h>
d2d3c393 35#include <stdlib.h>
1d48a247 36
bb0f721e
HJYP
37#include "pref.h"
38
9c52ae58 39#include "image-view.h"
0c0c8179 40#include "image-list.h"
1d48a247 41#include "working-area.h"
cf15ca10 42#include "ptk-menu.h"
7c3b160c 43#include "jpeg-tran.h"
1d48a247
HJYP
44
45// For drag & drop
cf15ca10 46static GtkTargetEntry drop_targets[] =
1d48a247
HJYP
47{
48 {"text/uri-list", 0, 0},
cf15ca10 49 {"text/plain", 0, 1}
1d48a247
HJYP
50};
51
cf15ca10
HJYP
52static void main_win_init( MainWin*mw );
53static void main_win_finalize( GObject* obj );
54
55static void create_nav_bar( MainWin* mw, GtkWidget* box);
56GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle);
57// GtkWidget* add_menu_item( GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, gboolean toggle=FALSE );
58static void rotate_image( MainWin* mw, GdkPixbufRotation angle );
59static void show_popup_menu( MainWin* mw, GdkEventButton* evt );
60
61/* signal handlers */
62static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
63static void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation );
e216a767 64static void on_win_state_event( GtkWidget* widget, GdkEventWindowState* state );
cf15ca10
HJYP
65static void on_zoom_fit( GtkToggleButton* btn, MainWin* mw );
66static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw );
67static void on_full_screen( GtkWidget* btn, MainWin* mw );
68static void on_next( GtkWidget* btn, MainWin* mw );
69static void on_orig_size( GtkToggleButton* btn, MainWin* mw );
70static void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw );
71static void on_prev( GtkWidget* btn, MainWin* mw );
72static void on_rotate_clockwise( GtkWidget* btn, MainWin* mw );
73static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw );
74static void on_save_as( GtkWidget* btn, MainWin* mw );
75static void on_save( GtkWidget* btn, MainWin* mw );
76static void on_open( GtkWidget* btn, MainWin* mw );
77static void on_zoom_in( GtkWidget* btn, MainWin* mw );
78static void on_zoom_out( GtkWidget* btn, MainWin* mw );
79static void on_preference( GtkWidget* btn, MainWin* mw );
80static void on_quit( GtkWidget* btn, MainWin* mw );
81static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
82static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
83static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw );
84static gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw );
85static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
86static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
87 int x, int y, GtkSelectionData* data, guint info,
88 guint time, MainWin* mw );
89static void on_delete( GtkWidget* btn, MainWin* mw );
90static void on_about( GtkWidget* menu, MainWin* mw );
1d48a247 91
1d48a247 92
cf15ca10 93// Begin of GObject-related stuff
1d48a247 94
cf15ca10 95G_DEFINE_TYPE( MainWin, main_win, GTK_TYPE_WINDOW )
1d48a247 96
cf15ca10
HJYP
97void main_win_class_init( MainWinClass* klass )
98{
1d48a247
HJYP
99 GObjectClass * obj_class;
100 GtkWidgetClass *widget_class;
101
102 obj_class = ( GObjectClass * ) klass;
103// obj_class->set_property = _set_property;
104// obj_class->get_property = _get_property;
cf15ca10 105 obj_class->finalize = main_win_finalize;
1d48a247
HJYP
106
107 widget_class = GTK_WIDGET_CLASS ( klass );
108 widget_class->delete_event = on_delete_event;
109 widget_class->size_allocate = on_size_allocate;
110 widget_class->key_press_event = on_key_press_event;
e216a767 111 widget_class->window_state_event = on_win_state_event;
1d48a247
HJYP
112}
113
cf15ca10
HJYP
114void main_win_finalize( GObject* obj )
115{
116 MainWin *mw = (MainWin*)obj;
117
118 main_win_close(mw);
119
120 if( G_LIKELY(mw->img_list) )
121 image_list_free( mw->img_list );
122 gdk_cursor_unref( mw->hand_cursor );
123
124 g_object_unref( mw->tooltips );
125
126 // FIXME: Put this here is weird
127 gtk_main_quit();
128}
129
130GtkWidget* main_win_new()
1d48a247 131{
cf15ca10 132 return (GtkWidget*)g_object_new ( MAIN_WIN_TYPE, NULL );
1d48a247
HJYP
133}
134
135// End of GObject-related stuff
136
cf15ca10 137void main_win_init( MainWin*mw )
1d48a247 138{
cf15ca10
HJYP
139 gtk_window_set_title( (GtkWindow*)mw, _("Image Viewer"));
140 gtk_window_set_icon_from_file( (GtkWindow*)mw, PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL );
141 gtk_window_set_default_size( (GtkWindow*)mw, 640, 480 );
1d48a247
HJYP
142
143 GtkWidget* box = gtk_vbox_new( FALSE, 0 );
cf15ca10 144 gtk_container_add( (GtkContainer*)mw, box);
1d48a247
HJYP
145
146 // image area
cf15ca10
HJYP
147 mw->evt_box = gtk_event_box_new();
148 GTK_WIDGET_SET_FLAGS( mw->evt_box, GTK_CAN_FOCUS );
149 gtk_widget_add_events( mw->evt_box,
1d48a247
HJYP
150 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
151 GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
cf15ca10
HJYP
152 g_signal_connect( mw->evt_box, "button-press-event", G_CALLBACK(on_button_press), mw );
153 g_signal_connect( mw->evt_box, "button-release-event", G_CALLBACK(on_button_release), mw );
154 g_signal_connect( mw->evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), mw );
155 g_signal_connect( mw->evt_box, "scroll-event", G_CALLBACK(on_scroll_event), mw );
5bfec971
HJYP
156 // Set bg color to white
157 GdkColor white = {0, 65535, 65535, 65535};
cf15ca10 158 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &white );
1d48a247 159
cf15ca10
HJYP
160 mw->img_view = image_view_new();
161 gtk_container_add( (GtkContainer*)mw->evt_box, (GtkWidget*)mw->img_view);
1d48a247 162
2c34ffe0
HJYP
163 const char scroll_style[]=
164 "style \"gpicview-scroll\" {"
165 "GtkScrolledWindow::scrollbar-spacing=0"
166 "}"
167 "class \"GtkScrolledWindow\" style \"gpicview-scroll\"";
168 gtk_rc_parse_string( scroll_style );
cf15ca10
HJYP
169 mw->scroll = gtk_scrolled_window_new( NULL, NULL );
170 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)mw->scroll, GTK_SHADOW_NONE );
171 gtk_scrolled_window_set_policy((GtkScrolledWindow*)mw->scroll,
1d48a247
HJYP
172 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
173 GtkAdjustment *hadj, *vadj;
cf15ca10 174 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
175 hadj->page_increment = 10;
176 gtk_adjustment_changed(hadj);
cf15ca10 177 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
178 vadj->page_increment = 10;
179 gtk_adjustment_changed(vadj);
9c52ae58 180
d2d3c393 181 image_view_set_adjustments( IMAGE_VIEW(mw->img_view), hadj, vadj ); // dirty hack :-(
cf15ca10
HJYP
182 gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)mw->scroll, mw->evt_box );
183 GtkWidget* viewport = gtk_bin_get_child( (GtkBin*)mw->scroll );
2c34ffe0
HJYP
184 gtk_viewport_set_shadow_type( (GtkViewport*)viewport, GTK_SHADOW_NONE );
185 gtk_container_set_border_width( (GtkContainer*)viewport, 0 );
1d48a247 186
cf15ca10 187 gtk_box_pack_start( (GtkBox*)box, mw->scroll, TRUE, TRUE, 0 );
1d48a247
HJYP
188
189 // build toolbar
cf15ca10 190 mw->tooltips = gtk_tooltips_new();
d9f34bf9 191#if GTK_CHECK_VERSION(2, 8, 0)
cf15ca10 192 g_object_ref_sink(mw->tooltips);
1d48a247 193#else
cf15ca10 194 gtk_object_sink((GtkObject*)mw->tooltips);
1d48a247 195#endif
1d48a247 196
cf15ca10 197 create_nav_bar( mw, box );
1d48a247
HJYP
198 gtk_widget_show_all( box );
199
cf15ca10 200 mw->hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)mw), GDK_FLEUR );
1d48a247 201
5bfec971 202// zoom_mode = ZOOM_NONE;
cf15ca10 203 mw->zoom_mode = ZOOM_FIT;
1d48a247
HJYP
204
205 // Set up drag & drop
cf15ca10
HJYP
206 gtk_drag_dest_set( (GtkWidget*)mw, GTK_DEST_DEFAULT_ALL,
207 drop_targets,
208 G_N_ELEMENTS(drop_targets),
209 GDK_ACTION_COPY | GDK_ACTION_ASK );
210 g_signal_connect( mw, "drag-data-received", G_CALLBACK(on_drag_data_received), mw );
9c52ae58 211
cf15ca10 212 mw->img_list = image_list_new();
7c3b160c 213
7c3b160c
HJYP
214 // rotation angle is zero on startup
215 mw->rotation_angle = 0;
1d48a247
HJYP
216}
217
cf15ca10 218void create_nav_bar( MainWin* mw, GtkWidget* box )
1d48a247 219{
cf15ca10 220 mw->nav_bar = gtk_hbox_new( FALSE, 0 );
1d48a247 221
cf15ca10
HJYP
222 add_nav_btn( mw, GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev), FALSE );
223 add_nav_btn( mw, GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next), FALSE );
1d48a247 224
cf15ca10 225 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 226
cf15ca10
HJYP
227 add_nav_btn( mw, GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out), FALSE );
228 add_nav_btn( mw, GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in), FALSE );
5bfec971
HJYP
229
230// percent = gtk_entry_new(); // show scale (in percentage)
cf15ca10 231// g_signal_connect( percent, "activate", G_CALLBACK(on_percentage), mw );
5bfec971
HJYP
232// gtk_widget_set_size_request( percent, 45, -1 );
233// gtk_box_pack_start( (GtkBox*)nav_bar, percent, FALSE, FALSE, 2 );
234
cf15ca10
HJYP
235 mw->btn_fit = add_nav_btn( mw, GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
236 G_CALLBACK(on_zoom_fit), TRUE );
237 mw->btn_orig = add_nav_btn( mw, GTK_STOCK_ZOOM_100, _("Original Size"),
238 G_CALLBACK(on_orig_size), TRUE );
239 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
5bfec971 240
1d48a247
HJYP
241#ifndef GTK_STOCK_FULLSCREEN
242#define GTK_STOCK_FULLSCREEN "gtk-fullscreen"
243#endif
cf15ca10 244 add_nav_btn( mw, GTK_STOCK_FULLSCREEN, _(" Full Screen"), G_CALLBACK(on_full_screen), FALSE ); // gtk+ 2.8+
1d48a247 245
cf15ca10 246 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 247
cf15ca10
HJYP
248 add_nav_btn( mw, "gtk-counterclockwise", _("Rotate Counterclockwise"),
249 G_CALLBACK(on_rotate_counterclockwise), FALSE );
250 add_nav_btn( mw, "gtk-clockwise", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise), FALSE );
1d48a247 251
cf15ca10 252 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 253
cf15ca10
HJYP
254 add_nav_btn( mw, GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open), FALSE );
255 add_nav_btn( mw, GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save), FALSE );
256 add_nav_btn( mw, GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as), FALSE );
257 add_nav_btn( mw, GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete), FALSE );
1d48a247 258
cf15ca10 259 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
bb0f721e 260 add_nav_btn( mw, GTK_STOCK_PREFERENCES, _("Preferences"), G_CALLBACK(on_preference), FALSE );
1d48a247
HJYP
261
262 GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
cf15ca10 263 gtk_container_add( (GtkContainer*)align, mw->nav_bar );
1d48a247
HJYP
264 gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
265}
266
cf15ca10 267gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt )
1d48a247
HJYP
268{
269 gtk_widget_destroy( widget );
270 return TRUE;
271}
272
cf15ca10 273gboolean main_win_open( MainWin* mw, const char* file_path, ZoomMode zoom )
1d48a247 274{
1d48a247 275 GError* err = NULL;
cf15ca10
HJYP
276 main_win_close( mw );
277 mw->pix = gdk_pixbuf_new_from_file( file_path, &err );
c215db47 278
cf15ca10 279 if( ! mw->pix )
1d48a247 280 {
cf15ca10
HJYP
281 main_win_show_error( mw, err->message );
282 return FALSE;
1d48a247 283 }
c215db47
HJYP
284#if GTK_CHECK_VERSION( 2, 12, 0 )
285 else
286 {
287 /* apply orientation provided by EXIF (Use gtk+ 2.12 specific API) */
288 GdkPixbuf* tmp = gdk_pixbuf_apply_embedded_orientation(mw->pix);
289 g_object_unref( mw->pix );
290 mw->pix = tmp;
291 }
292#endif
d8ce3af1 293
cf15ca10 294 mw->zoom_mode = zoom;
da8bd946 295
5bfec971 296 // select most suitable viewing mode
d8ce3af1 297 if( zoom == ZOOM_NONE )
1d48a247 298 {
cf15ca10
HJYP
299 int w = gdk_pixbuf_get_width( mw->pix );
300 int h = gdk_pixbuf_get_height( mw->pix );
1d48a247 301
1d48a247 302 GdkRectangle area;
cf15ca10 303 get_working_area( gtk_widget_get_screen((GtkWidget*)mw), &area );
c325c989
HJYP
304 // g_debug("determine best zoom mode: orig size: w=%d, h=%d", w, h);
305 // FIXME: actually this is a little buggy :-(
da8bd946 306 if( w < area.width && h < area.height && (w >= 640 || h >= 480) )
1d48a247 307 {
cf15ca10
HJYP
308 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER );
309 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, w, h );
5bfec971 310 GtkRequisition req;
cf15ca10 311 gtk_widget_size_request ( (GtkWidget*)mw, &req );
9c52ae58
HJYP
312 if( req.width < 640 ) req.width = 640;
313 if( req.height < 480 ) req.height = 480;
cf15ca10
HJYP
314 gtk_window_resize( (GtkWindow*)mw, req.width, req.height );
315 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, -1, -1 );
316 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
317 mw->zoom_mode = ZOOM_ORIG;
1d48a247
HJYP
318 }
319 else
cf15ca10 320 mw->zoom_mode = ZOOM_FIT;
5bfec971 321 }
5bfec971 322
cf15ca10 323 if( mw->zoom_mode == ZOOM_FIT )
d8ce3af1 324 {
cf15ca10 325 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
c325c989 326 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
d8ce3af1 327 }
cf15ca10 328 else if( mw->zoom_mode == ZOOM_SCALE ) // scale
d8ce3af1 329 {
cf15ca10
HJYP
330 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
331 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
332 main_win_scale_image( mw, mw->scale, GDK_INTERP_BILINEAR );
d8ce3af1 333 }
cf15ca10 334 else if( mw->zoom_mode == ZOOM_ORIG ) // original size
5bfec971 335 {
cf15ca10
HJYP
336 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
337 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
338 main_win_center_image( mw );
1d48a247
HJYP
339 }
340
cf15ca10 341 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
9c52ae58 342
1d48a247
HJYP
343// while (gtk_events_pending ())
344// gtk_main_iteration ();
345
346 // build file list
347 char* dir_path = g_path_get_dirname( file_path );
cf15ca10
HJYP
348 image_list_open_dir( mw->img_list, dir_path, NULL );
349 image_list_sort_by_name( mw->img_list, GTK_SORT_ASCENDING );
1d48a247 350 g_free( dir_path );
76e88fcf 351
1d48a247 352 char* base_name = g_path_get_basename( file_path );
cf15ca10 353 image_list_set_current( mw->img_list, base_name );
76e88fcf
HJYP
354
355 char* disp_name = g_filename_display_name( base_name );
1d48a247
HJYP
356 g_free( base_name );
357
cf15ca10 358 gtk_window_set_title( (GtkWindow*)mw, disp_name );
76e88fcf 359 g_free( disp_name );
1d48a247 360
cf15ca10 361 return TRUE;
1d48a247
HJYP
362}
363
cf15ca10 364void main_win_close( MainWin* mw )
1d48a247 365{
cf15ca10 366 if( mw->pix )
9c52ae58 367 {
cf15ca10
HJYP
368 g_object_unref( mw->pix );
369 mw->pix = NULL;
1d48a247
HJYP
370 }
371}
372
cf15ca10 373void main_win_show_error( MainWin* mw, const char* message )
1d48a247 374{
cf15ca10 375 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
376 GTK_DIALOG_MODAL,
377 GTK_MESSAGE_ERROR,
378 GTK_BUTTONS_OK,
379 message );
380 gtk_dialog_run( (GtkDialog*)dlg );
381 gtk_widget_destroy( dlg );
382}
383
cf15ca10 384void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation )
1d48a247 385{
cf15ca10 386 GTK_WIDGET_CLASS(main_win_parent_class)->size_allocate( widget, allocation );
52c867e9 387 if( GTK_WIDGET_REALIZED (widget) )
1d48a247 388 {
cf15ca10 389 MainWin* mw = (MainWin*)widget;
9c52ae58 390
cf15ca10 391 if( mw->zoom_mode == ZOOM_FIT )
52c867e9
HJYP
392 {
393 while(gtk_events_pending ())
394 gtk_main_iteration(); // makes it more fluid
1d48a247 395
c325c989 396 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
52c867e9 397 }
1d48a247
HJYP
398 }
399}
400
e216a767
HJYP
401void on_win_state_event( GtkWidget* widget, GdkEventWindowState* state )
402{
403 MainWin* mw = (MainWin*)widget;
404 if( state->new_window_state == GDK_WINDOW_STATE_FULLSCREEN )
405 {
406 static GdkColor black = {0};
407 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &black );
408 gtk_widget_hide( gtk_widget_get_parent(mw->nav_bar) );
409 mw->full_screen = TRUE;
410 }
411 else
412 {
413// gtk_widget_reset_rc_styles( mw->evt_box );
414 static GdkColor white = {0, 65535, 65535, 65535};
415 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &white );
416 gtk_widget_show( gtk_widget_get_parent(mw->nav_bar) );
417 mw->full_screen = FALSE;
418 }
419}
420
cf15ca10 421void main_win_fit_size( MainWin* mw, int width, int height, gboolean can_strech, GdkInterpType type )
1d48a247 422{
cf15ca10 423 if( ! mw->pix )
1d48a247
HJYP
424 return;
425
cf15ca10
HJYP
426 int orig_w = gdk_pixbuf_get_width( mw->pix );
427 int orig_h = gdk_pixbuf_get_height( mw->pix );
1d48a247 428
da8bd946
HJYP
429 if( can_strech || (orig_w > width || orig_h > height) )
430 {
cf15ca10
HJYP
431 gdouble xscale = ((gdouble)width) / orig_w;
432 gdouble yscale = ((gdouble)height)/ orig_h;
433 gdouble final_scale = xscale < yscale ? xscale : yscale;
1d48a247 434
cf15ca10 435 main_win_scale_image( mw, final_scale, type );
da8bd946
HJYP
436 }
437 else // use original size if the image is smaller than the window
438 {
cf15ca10
HJYP
439 mw->scale = 1.0;
440 image_view_set_scale( (ImageView*)mw->img_view, 1.0, type );
da8bd946 441 }
1d48a247
HJYP
442}
443
cf15ca10 444void main_win_fit_window_size( MainWin* mw, gboolean can_strech, GdkInterpType type )
1d48a247 445{
cf15ca10 446 mw->zoom_mode = ZOOM_FIT;
94fcf707 447
cf15ca10 448 if( mw->pix == NULL )
da8bd946 449 return;
94fcf707 450
cf15ca10 451 main_win_fit_size( mw, mw->scroll->allocation.width, mw->scroll->allocation.height, can_strech, type );
1d48a247
HJYP
452}
453
cf15ca10 454GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle )
1d48a247
HJYP
455{
456 GtkWidget* img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
457 GtkWidget* btn;
458 if( G_UNLIKELY(toggle) )
459 {
460 btn = gtk_toggle_button_new();
cf15ca10 461 g_signal_connect( btn, "toggled", cb, mw );
1d48a247
HJYP
462 }
463 else
464 {
465 btn = gtk_button_new();
cf15ca10 466 g_signal_connect( btn, "clicked", cb, mw );
1d48a247
HJYP
467 }
468 gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
469 gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
470 gtk_container_add( (GtkContainer*)btn, img );
cf15ca10
HJYP
471 gtk_tooltips_set_tip( mw->tooltips, btn, tip, NULL );
472 gtk_box_pack_start( (GtkBox*)mw->nav_bar, btn, FALSE, FALSE, 0 );
1d48a247
HJYP
473 return btn;
474}
475
cf15ca10 476void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw )
1d48a247 477{
cf15ca10 478 gtk_button_clicked( (GtkButton*)mw->btn_fit );
1d48a247
HJYP
479}
480
cf15ca10 481void on_zoom_fit( GtkToggleButton* btn, MainWin* mw )
1d48a247
HJYP
482{
483 if( ! btn->active )
484 {
cf15ca10 485 if( mw->zoom_mode == ZOOM_FIT )
1d48a247
HJYP
486 gtk_toggle_button_set_active( btn, TRUE );
487 return;
488 }
cf15ca10 489 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1d48a247 490
c325c989 491 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
492}
493
cf15ca10 494void on_full_screen( GtkWidget* btn, MainWin* mw )
1d48a247 495{
cf15ca10 496 if( ! mw->full_screen )
cf15ca10 497 gtk_window_fullscreen( (GtkWindow*)mw );
1d48a247 498 else
cf15ca10 499 gtk_window_unfullscreen( (GtkWindow*)mw );
1d48a247
HJYP
500}
501
cf15ca10 502void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw )
1d48a247 503{
cf15ca10 504 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
505}
506
cf15ca10 507void on_orig_size( GtkToggleButton* btn, MainWin* mw )
1d48a247 508{
cf15ca10 509 // this callback could be called from activate signal of menu item.
1d48a247
HJYP
510 if( GTK_IS_MENU_ITEM(btn) )
511 {
cf15ca10 512 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
513 return;
514 }
515
516 if( ! btn->active )
517 {
cf15ca10 518 if( mw->zoom_mode == ZOOM_ORIG )
1d48a247
HJYP
519 gtk_toggle_button_set_active( btn, TRUE );
520 return;
521 }
cf15ca10
HJYP
522 mw->zoom_mode = ZOOM_ORIG;
523 mw->scale = 1.0;
524// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 525// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 526
cf15ca10 527 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1d48a247 528
cf15ca10 529 if( ! mw->pix )
1d48a247
HJYP
530 return;
531
cf15ca10 532 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
1d48a247 533
9c52ae58
HJYP
534 while (gtk_events_pending ())
535 gtk_main_iteration ();
536
cf15ca10 537 main_win_center_image( mw ); // FIXME: mw doesn't work well. Why?
1d48a247
HJYP
538}
539
f34b3b31 540void on_prev( GtkWidget* btn, MainWin* mw )
1d48a247 541{
cf15ca10
HJYP
542 const char* name;
543 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
544 return;
545
cf15ca10 546 name = image_list_get_prev( mw->img_list );
1d48a247 547
cf15ca10 548 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
549 {
550 // FIXME: need to ask user first?
cf15ca10 551 name = image_list_get_last( mw->img_list );
1d48a247
HJYP
552 }
553
554 if( name )
555 {
cf15ca10
HJYP
556 char* file_path = image_list_get_current_file_path( mw->img_list );
557 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
558 g_free( file_path );
559 }
560}
561
f34b3b31 562void on_next( GtkWidget* btn, MainWin* mw )
1d48a247 563{
cf15ca10 564 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
565 return;
566
cf15ca10 567 const char* name = image_list_get_next( mw->img_list );
1d48a247 568
cf15ca10 569 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
570 {
571 // FIXME: need to ask user first?
cf15ca10 572 name = image_list_get_first( mw->img_list );
1d48a247
HJYP
573 }
574
575 if( name )
576 {
cf15ca10
HJYP
577 char* file_path = image_list_get_current_file_path( mw->img_list );
578 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
579 g_free( file_path );
580 }
581}
582
cf15ca10 583void on_rotate_clockwise( GtkWidget* btn, MainWin* mw )
1d48a247 584{
cf15ca10 585 rotate_image( mw, GDK_PIXBUF_ROTATE_CLOCKWISE );
7c3b160c 586 mw->rotation_angle += 90;
bb0f721e
HJYP
587 if(pref.auto_save_rotated){
588 pref.ask_before_save = FALSE;
7c3b160c 589 on_save(btn,mw);
bb0f721e 590 pref.ask_before_save = TRUE;
7c3b160c 591 }
1d48a247
HJYP
592}
593
cf15ca10 594void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw )
1d48a247 595{
cf15ca10 596 rotate_image( mw, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
7c3b160c 597 mw->rotation_angle += 270;
bb0f721e
HJYP
598 if(pref.auto_save_rotated){
599 pref.ask_before_save = FALSE;
7c3b160c 600 on_save(btn,mw);
bb0f721e 601 pref.ask_before_save = TRUE;
7c3b160c 602 }
1d48a247
HJYP
603}
604
605static void on_update_preview( GtkFileChooser *chooser, GtkImage* img )
606{
607 char* file = gtk_file_chooser_get_preview_filename( chooser );
608 GdkPixbuf* pix = NULL;
609 if( file )
610 {
611 pix = gdk_pixbuf_new_from_file_at_scale( file, 128, 128, TRUE, NULL );
612 g_free( file );
613 }
1d48a247 614 if( pix )
9c52ae58
HJYP
615 {
616 gtk_image_set_from_pixbuf( img, pix );
617 g_object_unref( pix );
618 }
1d48a247
HJYP
619}
620
cf15ca10 621void on_save_as( GtkWidget* btn, MainWin* mw )
1d48a247 622{
cf15ca10 623 if( ! mw->pix )
1d48a247
HJYP
624 return;
625
cf15ca10 626 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
1d48a247
HJYP
627 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
628 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL );
629
cf15ca10 630 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
631
632 GtkWidget* img = gtk_image_new();
633 gtk_widget_set_size_request( img, 128, 128 );
634 gtk_file_chooser_set_preview_widget( dlg, img );
635 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
636
637 GtkFileFilter *filter;
638
639 /*
640 /// TODO: determine file type from file name
641 filter = gtk_file_filter_new();
642 gtk_file_filter_set_name( filter, _("Determined by File Name") );
643 gtk_file_filter_add_pixbuf_formats( filter );
644 gtk_file_chooser_add_filter( dlg, filter );
645 */
646
647 GSList* modules = gdk_pixbuf_get_formats();
648 GSList* module;
649 for( module = modules; module; module = module->next )
650 {
651 GdkPixbufFormat* format = (GdkPixbufFormat*)module->data;
652 if( ! gdk_pixbuf_format_is_writable( format ) )
653 continue;
654
655 filter = gtk_file_filter_new();
656
657 char* desc = gdk_pixbuf_format_get_description( format );
658 char* name = gdk_pixbuf_format_get_name( format );
659 char* tmp = g_strjoin( ": ", name, desc, NULL );
660 g_free( desc );
661 g_free( name );
662 gtk_file_filter_set_name( filter, tmp );
663 g_free( tmp );
664
665 char** mimes = gdk_pixbuf_format_get_mime_types( format ), **mime;
666 for( mime = mimes; *mime ; ++mime )
667 gtk_file_filter_add_mime_type( filter, *mime );
668 g_strfreev( mimes );
669 gtk_file_chooser_add_filter( dlg, filter );
670 }
671
672 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
673 {
674 filter = gtk_file_chooser_get_filter( dlg );
675 const char* filter_name = gtk_file_filter_get_name( filter );
676 char* p = strstr( filter_name, ": " );
677 char* type = NULL;
678 if( ! p ) // auto detection
679 {
680 /// TODO: auto file type
681 }
682 else
683 {
684 type = g_strndup( filter_name, (p - filter_name) );
685 }
686 char* file = gtk_file_chooser_get_filename( dlg );
687 // g_debug("type = %s", type);
cf15ca10 688 main_win_save( mw, file, type, TRUE );
1d48a247
HJYP
689 g_free( file );
690 g_free( type );
691 }
692 gtk_widget_destroy( (GtkWidget*)dlg );
693}
694
7c3b160c
HJYP
695#ifdef HAVE_LIBJPEG
696int rotate_and_save_jpeg_lossless(char * filename,int angle){
697 JXFORM_CODE code = JXFORM_NONE;
698
699 angle = angle % 360;
700
701 if(angle == 90)
702 code = JXFORM_ROT_90;
703 else if(angle == 180)
704 code = JXFORM_ROT_180;
705 else if(angle == 270)
706 code = JXFORM_ROT_270;
707
708 //rotate the image and save it to /tmp/rot.jpg
709 int error = jpegtran (filename, "/tmp/rot.jpg" , code);
710 if(error)
711 return error;
712
713 //now copy /tmp/rot.jpg back to the original file
714 char command[strlen(filename)+50]; //this should not generate buffer owerflow
715 // MS: didn't know, how to make it better, maybe an own copy routine
716 sprintf(command,"cp /tmp/rot.jpg \"%s\"",filename);
717 system(command);
718
719 return 0;
720}
721#endif
722
cf15ca10 723void on_save( GtkWidget* btn, MainWin* mw )
1d48a247 724{
cf15ca10 725 if( ! mw->pix )
1d48a247
HJYP
726 return;
727
cf15ca10
HJYP
728 char* file_name = g_build_filename( image_list_get_dir( mw->img_list ),
729 image_list_get_current( mw->img_list ), NULL );
1d48a247
HJYP
730 GdkPixbufFormat* info;
731 info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
732 char* type = gdk_pixbuf_format_get_name( info );
7c3b160c
HJYP
733#ifdef HAVE_LIBJPEG
734 if(strcmp(type,"jpeg")==0){
735 if(rotate_and_save_jpeg_lossless(file_name,mw->rotation_angle)!=0)
736 main_win_show_error(mw, "Save failed! Check permissions.");
737 } else
738#endif
bb0f721e 739 main_win_save( mw, file_name, type, pref.ask_before_save );
7c3b160c 740 mw->rotation_angle = 0;
1d48a247
HJYP
741 g_free( file_name );
742 g_free( type );
743}
744
cf15ca10 745void on_open( GtkWidget* btn, MainWin* mw )
1d48a247 746{
cf15ca10 747 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
1d48a247
HJYP
748 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
749 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
750
cf15ca10
HJYP
751 if( image_list_get_dir( mw->img_list ) )
752 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
753
754 GtkWidget* img = gtk_image_new();
755 gtk_widget_set_size_request( img, 128, 128 );
756 gtk_file_chooser_set_preview_widget( dlg, img );
757 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
758
759 GtkFileFilter *filter = gtk_file_filter_new();
760 gtk_file_filter_set_name( filter, _("All Supported Images") );
761 gtk_file_filter_add_pixbuf_formats( filter );
762 gtk_file_chooser_add_filter( dlg, filter );
763
764 filter = gtk_file_filter_new();
765 gtk_file_filter_set_name( filter, _("All Files") );
766 gtk_file_filter_add_pattern( filter, "*" );
767 gtk_file_chooser_add_filter( dlg, filter );
768
769 char* file = NULL;
770 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
771 file = gtk_file_chooser_get_filename( dlg );
772 gtk_widget_destroy( (GtkWidget*)dlg );
773
774 if( file )
775 {
cf15ca10 776 main_win_open( mw, file, ZOOM_NONE );
1d48a247
HJYP
777 g_free( file );
778 }
779}
780
cf15ca10 781void on_zoom_in( GtkWidget* btn, MainWin* mw )
1d48a247 782{
cf15ca10
HJYP
783 mw->zoom_mode = ZOOM_SCALE;
784 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
785 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
786// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 787// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 788
cf15ca10
HJYP
789 double scale = mw->scale;
790 if( mw->pix && scale < 20.0 )
1d48a247 791 {
cf15ca10 792// busy(TRUE);
9c52ae58
HJYP
793 scale *= 1.05;
794 if( scale > 20.0 )
795 scale = 20.0;
cf15ca10
HJYP
796 if( mw->scale != scale )
797 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 798// adjust_adjustment_on_zoom(oldscale);
cf15ca10 799// busy(FALSE);
1d48a247
HJYP
800 }
801}
802
cf15ca10 803void on_zoom_out( GtkWidget* btn, MainWin* mw )
1d48a247 804{
cf15ca10
HJYP
805 mw->zoom_mode = ZOOM_SCALE;
806 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
807 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
808// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 809// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 810
cf15ca10
HJYP
811 double scale = mw->scale;
812 if( mw->pix && scale > 0.02 )
1d48a247 813 {
cf15ca10 814// busy(TRUE);
da8bd946 815
9c52ae58
HJYP
816 scale /= 1.05;
817 if( scale < 0.02 )
818 scale = 0.02;
cf15ca10
HJYP
819 if( mw->scale != scale )
820 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 821// adjust_adjustment_on_zoom(oldscale);
cf15ca10 822// busy(FALSE);
1d48a247
HJYP
823 }
824}
825
cf15ca10 826void on_preference( GtkWidget* btn, MainWin* mw )
1d48a247 827{
bb0f721e 828 edit_preferences( (GtkWindow*)mw );
1d48a247
HJYP
829}
830
cf15ca10 831void on_quit( GtkWidget* btn, MainWin* mw )
1d48a247 832{
cf15ca10 833 gtk_widget_destroy( (GtkWidget*)mw );
1d48a247
HJYP
834}
835
cf15ca10 836gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 837{
9c52ae58
HJYP
838 if( ! GTK_WIDGET_HAS_FOCUS( widget ) )
839 gtk_widget_grab_focus( widget );
5bfec971 840
1d48a247
HJYP
841 if( evt->type == GDK_BUTTON_PRESS)
842 {
843 if( evt->button == 1 ) // left button
844 {
cf15ca10 845 if( ! mw->pix )
1d48a247 846 return FALSE;
cf15ca10
HJYP
847 mw->dragging = TRUE;
848 gtk_widget_get_pointer( (GtkWidget*)mw, &mw->drag_old_x ,&mw->drag_old_y );
849 gdk_window_set_cursor( widget->window, mw->hand_cursor );
1d48a247
HJYP
850 }
851 else if( evt->button == 3 ) // right button
852 {
cf15ca10 853 show_popup_menu( mw, evt );
1d48a247
HJYP
854 }
855 }
856 else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 ) // double clicked
857 {
cf15ca10 858 on_full_screen( NULL, mw );
1d48a247 859 }
1d48a247
HJYP
860 return FALSE;
861}
862
cf15ca10 863gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw )
1d48a247 864{
cf15ca10 865 if( ! mw->dragging )
1d48a247 866 return FALSE;
9c52ae58 867
1d48a247 868 int cur_x, cur_y;
cf15ca10 869 gtk_widget_get_pointer( (GtkWidget*)mw, &cur_x ,&cur_y );
1d48a247 870
cf15ca10
HJYP
871 int dx = (mw->drag_old_x - cur_x);
872 int dy = (mw->drag_old_y - cur_y);
1d48a247
HJYP
873
874 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
875 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
876 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 877
9c52ae58 878 GtkRequisition req;
cf15ca10 879 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247
HJYP
880
881 if( ABS(dx) > 4 )
882 {
cf15ca10 883 mw->drag_old_x = cur_x;
9c52ae58 884 if( req.width > hadj->page_size )
1d48a247
HJYP
885 {
886 gdouble x = gtk_adjustment_get_value (hadj) + dx;
887 if( x < hadj->lower )
888 x = hadj->lower;
889 else if( (x + hadj->page_size) > hadj->upper )
890 x = hadj->upper - hadj->page_size;
891
892 if( x != hadj->value )
893 gtk_adjustment_set_value (hadj, x );
894 }
895 }
896
897 if( ABS(dy) > 4 )
898 {
9c52ae58 899 if( req.height > vadj->page_size )
1d48a247 900 {
cf15ca10 901 mw->drag_old_y = cur_y;
1d48a247
HJYP
902 gdouble y = gtk_adjustment_get_value (vadj) + dy;
903 if( y < vadj->lower )
904 y = vadj->lower;
905 else if( (y + vadj->page_size) > vadj->upper )
906 y = vadj->upper - vadj->page_size;
907
908 if( y != vadj->value )
909 gtk_adjustment_set_value (vadj, y );
910 }
911 }
912 return FALSE;
913}
914
cf15ca10 915gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 916{
cf15ca10 917 mw->dragging = FALSE;
1d48a247
HJYP
918 gdk_window_set_cursor( widget->window, NULL );
919 return FALSE;
920}
921
cf15ca10 922gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw )
5bfec971 923{
58e1b106 924 guint modifiers = gtk_accelerator_get_default_mod_mask();
5bfec971
HJYP
925 switch( evt->direction )
926 {
927 case GDK_SCROLL_UP:
58e1b106
JH
928 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
929 on_zoom_in( NULL, mw );
930 else
931 on_prev( NULL, mw );
5bfec971
HJYP
932 break;
933 case GDK_SCROLL_DOWN:
58e1b106
JH
934 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
935 on_zoom_out( NULL, mw );
936 else
937 on_next( NULL, mw );
5bfec971
HJYP
938 break;
939 case GDK_SCROLL_LEFT:
cf15ca10 940 on_prev( NULL, mw );
5bfec971
HJYP
941 break;
942 case GDK_SCROLL_RIGHT:
cf15ca10 943 on_next( NULL, mw );
5bfec971
HJYP
944 break;
945 }
da8bd946 946 return TRUE;
5bfec971
HJYP
947}
948
cf15ca10 949gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key)
1d48a247 950{
cf15ca10 951 MainWin* mw = (MainWin*)widget;
1d48a247
HJYP
952 switch( key->keyval )
953 {
7c3b160c
HJYP
954 case GDK_Right:
955 case GDK_KP_Right:
956 case GDK_rightarrow:
1d48a247
HJYP
957 case GDK_Return:
958 case GDK_space:
959 case GDK_Next:
5bfec971
HJYP
960 case GDK_KP_Down:
961 case GDK_Down:
962 case GDK_downarrow:
cf15ca10 963 on_next( NULL, mw );
1d48a247 964 break;
7c3b160c
HJYP
965 case GDK_Left:
966 case GDK_KP_Left:
967 case GDK_leftarrow:
1d48a247
HJYP
968 case GDK_Prior:
969 case GDK_BackSpace:
5bfec971
HJYP
970 case GDK_KP_Up:
971 case GDK_Up:
972 case GDK_uparrow:
cf15ca10 973 on_prev( NULL, mw );
1d48a247
HJYP
974 break;
975 case GDK_KP_Add:
976 case GDK_plus:
641adad3 977 case GDK_equal:
cf15ca10 978 on_zoom_in( NULL, mw );
1d48a247
HJYP
979 break;
980 case GDK_KP_Subtract:
981 case GDK_minus:
cf15ca10 982 on_zoom_out( NULL, mw );
1d48a247 983 break;
1d48a247 984 case GDK_s:
641adad3 985// case GDK_S:
cf15ca10 986 on_save( NULL, mw );
1d48a247
HJYP
987 break;
988 case GDK_l:
5bfec971 989// case GDK_L:
cf15ca10 990 on_rotate_counterclockwise( NULL, mw );
1d48a247
HJYP
991 break;
992 case GDK_r:
5bfec971 993// case GDK_R:
cf15ca10 994 on_rotate_clockwise( NULL, mw );
1d48a247
HJYP
995 break;
996 case GDK_f:
5bfec971 997// case GDK_F:
cf15ca10
HJYP
998 if( mw->zoom_mode != ZOOM_FIT )
999 gtk_button_clicked((GtkButton*)mw->btn_fit );
1d48a247
HJYP
1000 break;
1001 case GDK_g:
5bfec971 1002// case GDK_G:
cf15ca10
HJYP
1003 if( mw->zoom_mode != ZOOM_ORIG )
1004 gtk_button_clicked((GtkButton*)mw->btn_orig );
1d48a247
HJYP
1005 break;
1006 case GDK_o:
5bfec971 1007// case GDK_O:
cf15ca10 1008 on_open( NULL, mw );
5bfec971
HJYP
1009 break;
1010 case GDK_Delete:
1011 case GDK_d:
1012// case GDK_D:
cf15ca10 1013 on_delete( NULL, mw );
1d48a247
HJYP
1014 break;
1015 case GDK_Escape:
cf15ca10
HJYP
1016 if( mw->full_screen )
1017 on_full_screen( NULL, mw );
1d48a247 1018 else
cf15ca10 1019 on_quit( NULL, mw );
1d48a247
HJYP
1020 break;
1021 case GDK_F11:
cf15ca10 1022 on_full_screen( NULL, mw );
1d48a247
HJYP
1023 break;
1024
1025 default:
cf15ca10 1026 GTK_WIDGET_CLASS(main_win_parent_class)->key_press_event( widget, key );
1d48a247
HJYP
1027 }
1028 return FALSE;
1029}
1030
cf15ca10 1031void main_win_center_image( MainWin* mw )
1d48a247
HJYP
1032{
1033 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1034 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1035 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1036
9c52ae58 1037 GtkRequisition req;
cf15ca10 1038 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247 1039
9c52ae58
HJYP
1040 if( req.width > hadj->page_size )
1041 gtk_adjustment_set_value(hadj, ( hadj->upper - hadj->page_size ) / 2 );
1d48a247 1042
9c52ae58
HJYP
1043 if( req.height > vadj->page_size )
1044 gtk_adjustment_set_value(vadj, ( vadj->upper - vadj->page_size ) / 2 );
1d48a247
HJYP
1045}
1046
cf15ca10 1047void rotate_image( MainWin* mw, GdkPixbufRotation angle )
1d48a247 1048{
cf15ca10 1049 if( ! mw->pix )
1d48a247
HJYP
1050 return;
1051
9c52ae58 1052 GdkPixbuf* rpix = NULL;
cf15ca10
HJYP
1053 rpix = gdk_pixbuf_rotate_simple( mw->pix, angle );
1054 g_object_unref( mw->pix );
1055 mw->pix = rpix;
0c0c8179 1056 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
1d48a247 1057
cf15ca10 1058 if( mw->zoom_mode == ZOOM_FIT )
c325c989 1059 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
1060}
1061
cf15ca10 1062gboolean main_win_scale_image( MainWin* mw, double new_scale, GdkInterpType type )
1d48a247
HJYP
1063{
1064 if( G_UNLIKELY( new_scale == 1.0 ) )
1065 {
cf15ca10
HJYP
1066 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
1067 return TRUE;
1d48a247 1068 }
cf15ca10 1069 mw->scale = new_scale;
0c0c8179 1070 image_view_set_scale( (ImageView*)mw->img_view, new_scale, type );
2c34ffe0 1071
cf15ca10 1072 return TRUE;
1d48a247
HJYP
1073}
1074
cf15ca10 1075gboolean main_win_save( MainWin* mw, const char* file_path, const char* type, gboolean confirm )
1d48a247 1076{
cf15ca10
HJYP
1077 if( ! mw->pix )
1078 return FALSE;
1d48a247
HJYP
1079
1080 if( confirm ) // check existing file
1081 {
1082 if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
1083 {
cf15ca10 1084 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
1085 GTK_DIALOG_MODAL,
1086 GTK_MESSAGE_QUESTION,
1087 GTK_BUTTONS_YES_NO,
1088 _("The file name you selected already exist.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
1089 if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
1090 {
1091 gtk_widget_destroy( dlg );
cf15ca10 1092 return FALSE;
1d48a247
HJYP
1093 }
1094 gtk_widget_destroy( dlg );
1095 }
1096 }
1097
1098 GError* err = NULL;
cf15ca10 1099 if( ! gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL ) )
1d48a247 1100 {
cf15ca10
HJYP
1101 main_win_show_error( mw, err->message );
1102 return FALSE;
1d48a247
HJYP
1103 }
1104
cf15ca10 1105 return TRUE;
1d48a247
HJYP
1106}
1107
cf15ca10 1108void on_delete( GtkWidget* btn, MainWin* mw )
1d48a247 1109{
cf15ca10 1110 char* file_path = image_list_get_current_file_path( mw->img_list );
1d48a247
HJYP
1111 if( file_path )
1112 {
cf15ca10 1113 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
1114 GTK_DIALOG_MODAL,
1115 GTK_MESSAGE_QUESTION,
1116 GTK_BUTTONS_YES_NO,
1117 _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
1118 int resp = gtk_dialog_run( (GtkDialog*)dlg );
1119 gtk_widget_destroy( dlg );
1120
1121 if( resp == GTK_RESPONSE_YES )
1122 {
1123 g_unlink( file_path );
1124 if( errno )
cf15ca10 1125 main_win_show_error( mw, g_strerror(errno) );
1d48a247
HJYP
1126 g_free( file_path );
1127 }
1128 }
1129}
1130
cf15ca10 1131void show_popup_menu( MainWin* mw, GdkEventButton* evt )
1d48a247 1132{
5bfec971
HJYP
1133 static PtkMenuItemEntry menu_def[] =
1134 {
1135 PTK_IMG_MENU_ITEM( N_( "Previous" ), GTK_STOCK_GO_BACK, on_prev, GDK_leftarrow, 0 ),
1136 PTK_IMG_MENU_ITEM( N_( "Next" ), GTK_STOCK_GO_FORWARD, on_next, GDK_rightarrow, 0 ),
1137 PTK_SEPARATOR_MENU_ITEM,
1138 PTK_IMG_MENU_ITEM( N_( "Zoom Out" ), GTK_STOCK_ZOOM_OUT, on_zoom_out, GDK_minus, 0 ),
1139 PTK_IMG_MENU_ITEM( N_( "Zoom In" ), GTK_STOCK_ZOOM_IN, on_zoom_in, GDK_plus, 0 ),
1140 PTK_IMG_MENU_ITEM( N_( "Fit Image To Window Size" ), GTK_STOCK_ZOOM_FIT, on_zoom_fit_menu, GDK_F, 0 ),
1141 PTK_IMG_MENU_ITEM( N_( "Original Size" ), GTK_STOCK_ZOOM_100, on_orig_size_menu, GDK_G, 0 ),
1142 PTK_SEPARATOR_MENU_ITEM,
0c8ac162 1143 PTK_IMG_MENU_ITEM( N_( "Full Screen" ), GTK_STOCK_FULLSCREEN, on_full_screen, GDK_F11, 0 ),
5bfec971
HJYP
1144 PTK_SEPARATOR_MENU_ITEM,
1145 PTK_IMG_MENU_ITEM( N_( "Rotate Counterclockwise" ), "gtk-counterclockwise", on_rotate_counterclockwise, GDK_L, 0 ),
1146 PTK_IMG_MENU_ITEM( N_( "Rotate Clockwise" ), "gtk-clockwise", on_rotate_clockwise, GDK_R, 0 ),
1147 PTK_SEPARATOR_MENU_ITEM,
1148 PTK_IMG_MENU_ITEM( N_("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open), GDK_O, 0 ),
1149 PTK_IMG_MENU_ITEM( N_("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save), GDK_S, 0 ),
1150 PTK_IMG_MENU_ITEM( N_("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
1151 PTK_IMG_MENU_ITEM( N_("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete), GDK_Delete, 0 ),
1152 PTK_SEPARATOR_MENU_ITEM,
1153 PTK_STOCK_MENU_ITEM( GTK_STOCK_ABOUT, on_about ),
1154 PTK_MENU_END
1155 };
1156
cf15ca10 1157 // mw accel group is useless. It's only used to display accels in popup menu
5bfec971 1158 GtkAccelGroup* accel_group = gtk_accel_group_new();
cf15ca10 1159 GtkMenuShell* popup = (GtkMenuShell*)ptk_menu_new_from_data( menu_def, mw, accel_group );
1d48a247
HJYP
1160
1161 gtk_widget_show_all( (GtkWidget*)popup );
1162 g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1163 gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
1164}
1165
7c3b160c
HJYP
1166/* callback used to open default browser when URLs got clicked */
1167static void open_url( GtkAboutDialog *dlg, const gchar *url, gpointer data)
1d48a247 1168{
7c3b160c
HJYP
1169 /* FIXME: is there any better way to do this? */
1170 char* programs[] = { "xdg-open", "gnome-open" /* Sorry, KDE users. :-P */, "exo-open" };
1171 int i;
1172 for( i = 0; i < G_N_ELEMENTS(programs); ++i)
1d48a247 1173 {
7c3b160c
HJYP
1174 char* open_cmd = NULL;
1175 if( (open_cmd = g_find_program_in_path( programs[i] )) )
1d48a247 1176 {
7c3b160c
HJYP
1177 char* cmd = g_strdup_printf( "%s \'%s\'", open_cmd, url );
1178 g_spawn_command_line_async( cmd, NULL );
1179 g_free( cmd );
1180 g_free( open_cmd );
1181 break;
1d48a247 1182 }
1d48a247 1183 }
1d48a247
HJYP
1184}
1185
cf15ca10 1186void on_about( GtkWidget* menu, MainWin* mw )
1d48a247
HJYP
1187{
1188 GtkWidget * about_dlg;
1189 const gchar *authors[] =
1190 {
1191 "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
7c3b160c
HJYP
1192 "Martin Siggel <martinsiggel@googlemail.com>",
1193 _(" * Refer to source code of EOG image viewer and GThumb"),
1d48a247
HJYP
1194 NULL
1195 };
cf15ca10 1196 /* TRANSLATORS: Replace mw string with your names, one name per line. */
1d48a247
HJYP
1197 gchar *translators = _( "translator-credits" );
1198
7c3b160c
HJYP
1199 gtk_about_dialog_set_url_hook( open_url, mw, NULL);
1200
1d48a247 1201 about_dlg = gtk_about_dialog_new ();
7c3b160c
HJYP
1202
1203 gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 );
1204 gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION );
1205 gtk_about_dialog_set_name ( (GtkAboutDialog*)about_dlg, _( "GPicView" ) );
1206 gtk_about_dialog_set_logo( (GtkAboutDialog*)about_dlg, gdk_pixbuf_new_from_file( PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL ) );
1207 gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2007" ) );
1208 gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Lightweight image viewer from LXDE project" ) );
1209 gtk_about_dialog_set_license ( (GtkAboutDialog*)about_dlg, "GPicView\n\nCopyright (C) 2007 Hong Jen Yee (PCMan)\n\nmw program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nmw program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with mw program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." );
c5a539c7 1210 gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://lxde.org/gpicview/" );
7c3b160c
HJYP
1211 gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors );
1212 gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators );
1213 gtk_window_set_transient_for( (GtkWindow*) about_dlg, GTK_WINDOW( mw ) );
1214
1215 gtk_dialog_run( ( GtkDialog*)about_dlg );
1d48a247
HJYP
1216 gtk_widget_destroy( about_dlg );
1217}
1218
cf15ca10
HJYP
1219void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1220 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw )
1d48a247
HJYP
1221{
1222 if( ! data || data->length <= 0)
1223 return;
1224
1d48a247
HJYP
1225 char* file = NULL;
1226 if( info == 0 ) // text/uri-list
1227 {
1228 char** uris = gtk_selection_data_get_uris( data );
1229 if( uris )
1230 {
1231 file = g_filename_from_uri(*uris, NULL, NULL);
1232 g_strfreev( uris );
1233 }
1234 }
1235 else if( info == 1 ) // text/plain
1236 {
1237 file = (char*)gtk_selection_data_get_text( data );
1238 }
1239 if( file )
1240 {
cf15ca10 1241 main_win_open( mw, file, ZOOM_FIT );
1d48a247
HJYP
1242 g_free( file );
1243 }
1244}