Add linking to x11 for recent version of ld.
[lxde/gpicview.git] / src / main-win.c
CommitLineData
1d48a247 1/***************************************************************************
1c8a8ba7
HJYP
2 * Copyright (C) 2007, 2008 by PCMan (Hong Jen Yee) *
3 * pcman.tw@gmail.com *
1d48a247 4 * *
1c8a8ba7 5 * This 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 * *
1c8a8ba7 10 * This 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 *
1c8a8ba7 16 * along with this 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>
d2d3c393 34#include <stdlib.h>
1c8a8ba7 35#include <stdio.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"
701822a6 43#include "file-dlgs.h"
7c3b160c 44#include "jpeg-tran.h"
1d48a247 45
d8d44976 46/* For drag & drop */
cf15ca10 47static GtkTargetEntry drop_targets[] =
1d48a247
HJYP
48{
49 {"text/uri-list", 0, 0},
cf15ca10 50 {"text/plain", 0, 1}
1d48a247
HJYP
51};
52
ebee501a 53extern int ExifRotate(const char * fname, int new_angle);
54
cf15ca10
HJYP
55static void main_win_init( MainWin*mw );
56static void main_win_finalize( GObject* obj );
57
58static void create_nav_bar( MainWin* mw, GtkWidget* box);
59GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle);
ebee501a 60GtkWidget* add_nav_btn_img( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle, GtkWidget** ret_img);
cf15ca10 61// GtkWidget* add_menu_item( GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, gboolean toggle=FALSE );
da046c6e 62static void rotate_image( MainWin* mw, int angle );
cf15ca10
HJYP
63static void show_popup_menu( MainWin* mw, GdkEventButton* evt );
64
65/* signal handlers */
66static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
67static void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation );
86ebaeb3 68static gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state );
cf15ca10
HJYP
69static void on_zoom_fit( GtkToggleButton* btn, MainWin* mw );
70static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw );
71static void on_full_screen( GtkWidget* btn, MainWin* mw );
72static void on_next( GtkWidget* btn, MainWin* mw );
73static void on_orig_size( GtkToggleButton* btn, MainWin* mw );
74static void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw );
75static void on_prev( GtkWidget* btn, MainWin* mw );
1c8a8ba7 76static void on_rotate_auto_save( GtkWidget* btn, MainWin* mw );
cf15ca10
HJYP
77static void on_rotate_clockwise( GtkWidget* btn, MainWin* mw );
78static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw );
79static void on_save_as( GtkWidget* btn, MainWin* mw );
80static void on_save( GtkWidget* btn, MainWin* mw );
ebee501a 81static void cancel_slideshow(MainWin* mw);
82static gboolean next_slide(MainWin* mw);
83static void on_slideshow_menu( GtkMenuItem* item, MainWin* mw );
84static void on_slideshow( GtkToggleButton* btn, MainWin* mw );
cf15ca10
HJYP
85static void on_open( GtkWidget* btn, MainWin* mw );
86static void on_zoom_in( GtkWidget* btn, MainWin* mw );
87static void on_zoom_out( GtkWidget* btn, MainWin* mw );
88static void on_preference( GtkWidget* btn, MainWin* mw );
89static void on_quit( GtkWidget* btn, MainWin* mw );
90static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
91static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
92static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw );
93static gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw );
94static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
1c8a8ba7 95static gboolean save_confirm( MainWin* mw, const char* file_path );
cf15ca10 96static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1c8a8ba7 97 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw );
cf15ca10
HJYP
98static void on_delete( GtkWidget* btn, MainWin* mw );
99static void on_about( GtkWidget* menu, MainWin* mw );
1d48a247 100
d8d44976 101static void update_title(const char *filename, MainWin *mw );
da046c6e 102
103void on_flip_vertical( GtkWidget* btn, MainWin* mw );
104void on_flip_horizontal( GtkWidget* btn, MainWin* mw );
105static int trans_angle_to_id(int i);
106static int get_new_angle( int orig_angle, int rotate_angle );
1d48a247 107
cf15ca10 108// Begin of GObject-related stuff
1d48a247 109
cf15ca10 110G_DEFINE_TYPE( MainWin, main_win, GTK_TYPE_WINDOW )
1d48a247 111
cf15ca10
HJYP
112void main_win_class_init( MainWinClass* klass )
113{
1d48a247
HJYP
114 GObjectClass * obj_class;
115 GtkWidgetClass *widget_class;
116
117 obj_class = ( GObjectClass * ) klass;
118// obj_class->set_property = _set_property;
119// obj_class->get_property = _get_property;
cf15ca10 120 obj_class->finalize = main_win_finalize;
1d48a247
HJYP
121
122 widget_class = GTK_WIDGET_CLASS ( klass );
123 widget_class->delete_event = on_delete_event;
124 widget_class->size_allocate = on_size_allocate;
125 widget_class->key_press_event = on_key_press_event;
e216a767 126 widget_class->window_state_event = on_win_state_event;
1d48a247
HJYP
127}
128
cf15ca10
HJYP
129void main_win_finalize( GObject* obj )
130{
131 MainWin *mw = (MainWin*)obj;
132
133 main_win_close(mw);
134
135 if( G_LIKELY(mw->img_list) )
136 image_list_free( mw->img_list );
137 gdk_cursor_unref( mw->hand_cursor );
138
1c8a8ba7
HJYP
139 // FIXME: Put this here is weird
140 gtk_main_quit();
cf15ca10
HJYP
141}
142
143GtkWidget* main_win_new()
1d48a247 144{
cf15ca10 145 return (GtkWidget*)g_object_new ( MAIN_WIN_TYPE, NULL );
1d48a247
HJYP
146}
147
148// End of GObject-related stuff
149
cf15ca10 150void main_win_init( MainWin*mw )
1d48a247 151{
1c8a8ba7 152 gtk_window_set_title( (GtkWindow*)mw, _("Image Viewer"));
cf15ca10
HJYP
153 gtk_window_set_icon_from_file( (GtkWindow*)mw, PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL );
154 gtk_window_set_default_size( (GtkWindow*)mw, 640, 480 );
1d48a247
HJYP
155
156 GtkWidget* box = gtk_vbox_new( FALSE, 0 );
cf15ca10 157 gtk_container_add( (GtkContainer*)mw, box);
1d48a247
HJYP
158
159 // image area
cf15ca10
HJYP
160 mw->evt_box = gtk_event_box_new();
161 GTK_WIDGET_SET_FLAGS( mw->evt_box, GTK_CAN_FOCUS );
162 gtk_widget_add_events( mw->evt_box,
1d48a247
HJYP
163 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
164 GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
cf15ca10
HJYP
165 g_signal_connect( mw->evt_box, "button-press-event", G_CALLBACK(on_button_press), mw );
166 g_signal_connect( mw->evt_box, "button-release-event", G_CALLBACK(on_button_release), mw );
167 g_signal_connect( mw->evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), mw );
168 g_signal_connect( mw->evt_box, "scroll-event", G_CALLBACK(on_scroll_event), mw );
1c8a8ba7 169 // Set bg color to white
794cac1a
HJYP
170
171 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.bg );
1d48a247 172
cf15ca10
HJYP
173 mw->img_view = image_view_new();
174 gtk_container_add( (GtkContainer*)mw->evt_box, (GtkWidget*)mw->img_view);
1d48a247 175
2c34ffe0
HJYP
176 const char scroll_style[]=
177 "style \"gpicview-scroll\" {"
178 "GtkScrolledWindow::scrollbar-spacing=0"
179 "}"
180 "class \"GtkScrolledWindow\" style \"gpicview-scroll\"";
181 gtk_rc_parse_string( scroll_style );
cf15ca10
HJYP
182 mw->scroll = gtk_scrolled_window_new( NULL, NULL );
183 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)mw->scroll, GTK_SHADOW_NONE );
184 gtk_scrolled_window_set_policy((GtkScrolledWindow*)mw->scroll,
1d48a247
HJYP
185 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
186 GtkAdjustment *hadj, *vadj;
cf15ca10 187 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
188 hadj->page_increment = 10;
189 gtk_adjustment_changed(hadj);
cf15ca10 190 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
191 vadj->page_increment = 10;
192 gtk_adjustment_changed(vadj);
9c52ae58 193
d2d3c393 194 image_view_set_adjustments( IMAGE_VIEW(mw->img_view), hadj, vadj ); // dirty hack :-(
cf15ca10
HJYP
195 gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)mw->scroll, mw->evt_box );
196 GtkWidget* viewport = gtk_bin_get_child( (GtkBin*)mw->scroll );
2c34ffe0
HJYP
197 gtk_viewport_set_shadow_type( (GtkViewport*)viewport, GTK_SHADOW_NONE );
198 gtk_container_set_border_width( (GtkContainer*)viewport, 0 );
1d48a247 199
cf15ca10 200 gtk_box_pack_start( (GtkBox*)box, mw->scroll, TRUE, TRUE, 0 );
1d48a247
HJYP
201
202 // build toolbar
cf15ca10 203 create_nav_bar( mw, box );
1d48a247
HJYP
204 gtk_widget_show_all( box );
205
cf15ca10 206 mw->hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)mw), GDK_FLEUR );
1d48a247 207
5bfec971 208// zoom_mode = ZOOM_NONE;
cf15ca10 209 mw->zoom_mode = ZOOM_FIT;
1d48a247
HJYP
210
211 // Set up drag & drop
cf15ca10
HJYP
212 gtk_drag_dest_set( (GtkWidget*)mw, GTK_DEST_DEFAULT_ALL,
213 drop_targets,
214 G_N_ELEMENTS(drop_targets),
215 GDK_ACTION_COPY | GDK_ACTION_ASK );
216 g_signal_connect( mw, "drag-data-received", G_CALLBACK(on_drag_data_received), mw );
9c52ae58 217
cf15ca10 218 mw->img_list = image_list_new();
7c3b160c 219
7c3b160c
HJYP
220 // rotation angle is zero on startup
221 mw->rotation_angle = 0;
1d48a247
HJYP
222}
223
cf15ca10 224void create_nav_bar( MainWin* mw, GtkWidget* box )
1d48a247 225{
cf15ca10 226 mw->nav_bar = gtk_hbox_new( FALSE, 0 );
1d48a247 227
cf15ca10
HJYP
228 add_nav_btn( mw, GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev), FALSE );
229 add_nav_btn( mw, GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next), FALSE );
ebee501a 230 mw->btn_play_stop = add_nav_btn_img( mw, GTK_STOCK_MEDIA_PLAY, _("Start Slideshow"), G_CALLBACK(on_slideshow), TRUE, &mw->img_play_stop );
1d48a247 231
cf15ca10 232 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1c8a8ba7 233
cf15ca10
HJYP
234 add_nav_btn( mw, GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out), FALSE );
235 add_nav_btn( mw, GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in), FALSE );
5bfec971
HJYP
236
237// percent = gtk_entry_new(); // show scale (in percentage)
cf15ca10 238// g_signal_connect( percent, "activate", G_CALLBACK(on_percentage), mw );
5bfec971
HJYP
239// gtk_widget_set_size_request( percent, 45, -1 );
240// gtk_box_pack_start( (GtkBox*)nav_bar, percent, FALSE, FALSE, 2 );
241
cf15ca10
HJYP
242 mw->btn_fit = add_nav_btn( mw, GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
243 G_CALLBACK(on_zoom_fit), TRUE );
244 mw->btn_orig = add_nav_btn( mw, GTK_STOCK_ZOOM_100, _("Original Size"),
245 G_CALLBACK(on_orig_size), TRUE );
246 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
5bfec971 247
1d48a247
HJYP
248#ifndef GTK_STOCK_FULLSCREEN
249#define GTK_STOCK_FULLSCREEN "gtk-fullscreen"
250#endif
1c8a8ba7 251 add_nav_btn( mw, GTK_STOCK_FULLSCREEN, _("Full Screen"), G_CALLBACK(on_full_screen), FALSE ); // gtk+ 2.8+
1d48a247 252
cf15ca10 253 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 254
701822a6
HJYP
255 mw->btn_rotate_ccw = add_nav_btn( mw, "object-rotate-left", _("Rotate Counterclockwise"), G_CALLBACK(on_rotate_counterclockwise), FALSE );
256 mw->btn_rotate_cw = add_nav_btn( mw, "object-rotate-right", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise), FALSE );
1d48a247 257
701822a6
HJYP
258 mw->btn_flip_h = add_nav_btn( mw, "object-flip-horizontal", _("Flip Horizontal"), G_CALLBACK(on_flip_horizontal), FALSE );
259 mw->btn_flip_v = add_nav_btn( mw, "object-flip-vertical", _("Flip Vertical"), G_CALLBACK(on_flip_vertical), FALSE );
d8d44976 260
cf15ca10 261 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 262
cf15ca10
HJYP
263 add_nav_btn( mw, GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open), FALSE );
264 add_nav_btn( mw, GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save), FALSE );
265 add_nav_btn( mw, GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as), FALSE );
266 add_nav_btn( mw, GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete), FALSE );
1d48a247 267
cf15ca10 268 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
bb0f721e 269 add_nav_btn( mw, GTK_STOCK_PREFERENCES, _("Preferences"), G_CALLBACK(on_preference), FALSE );
1c8a8ba7 270 add_nav_btn( mw, GTK_STOCK_QUIT, _("Quit"), G_CALLBACK(on_quit), FALSE );
1d48a247
HJYP
271
272 GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
cf15ca10 273 gtk_container_add( (GtkContainer*)align, mw->nav_bar );
1d48a247
HJYP
274 gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
275}
276
cf15ca10 277gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt )
1d48a247
HJYP
278{
279 gtk_widget_destroy( widget );
280 return TRUE;
281}
282
d8d44976 283static void update_title(const char *filename, MainWin *mw )
da046c6e 284{
285 static char fname[50];
286 static int wid, hei;
794cac1a 287
da046c6e 288 char buf[100];
794cac1a 289
da046c6e 290 if(filename != NULL)
291 {
292 strncpy(fname, filename, 49);
293 fname[49] = '\0';
794cac1a 294
da046c6e 295 wid = gdk_pixbuf_get_width( mw->pix );
296 hei = gdk_pixbuf_get_height( mw->pix );
297 }
794cac1a 298
da046c6e 299 snprintf(buf, 100, "%s (%dx%d) %d%%", fname, wid, hei, (int)(mw->scale * 100));
300 gtk_window_set_title( (GtkWindow*)mw, buf );
794cac1a
HJYP
301
302 return;
da046c6e 303}
304
d8d44976
HJYP
305gboolean on_animation_timeout( MainWin* mw )
306{
307 int delay;
308 if ( gdk_pixbuf_animation_iter_advance( mw->animation_iter, NULL ) )
309 {
310 mw->pix = gdk_pixbuf_animation_iter_get_pixbuf( mw->animation_iter );
311 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
312 }
313 delay = gdk_pixbuf_animation_iter_get_delay_time( mw->animation_iter );
7e1dcec1 314 mw->animation_timeout = g_timeout_add(delay, (GSourceFunc) on_animation_timeout, mw );
d8d44976
HJYP
315 return FALSE;
316}
317
07db5c2f
HJYP
318static void update_btns(MainWin* mw)
319{
320 gboolean enable = (mw->animation == NULL);
321 gtk_widget_set_sensitive(mw->btn_rotate_cw, enable);
322 gtk_widget_set_sensitive(mw->btn_rotate_ccw, enable);
323 gtk_widget_set_sensitive(mw->btn_flip_v, enable);
324 gtk_widget_set_sensitive(mw->btn_flip_h, enable);
325}
326
cf15ca10 327gboolean main_win_open( MainWin* mw, const char* file_path, ZoomMode zoom )
1d48a247 328{
1d48a247 329 GError* err = NULL;
da046c6e 330 GdkPixbufFormat* info;
331 info = gdk_pixbuf_get_file_info( file_path, NULL, NULL );
fecf68e0 332 char* type = ((info != NULL) ? gdk_pixbuf_format_get_name(info) : "");
da046c6e 333
cf15ca10 334 main_win_close( mw );
c215db47 335
d8d44976
HJYP
336 /* grabs a file as if it were an animation */
337 mw->animation = gdk_pixbuf_animation_new_from_file( file_path, &err );
338 if( ! mw->animation )
1d48a247 339 {
cf15ca10 340 main_win_show_error( mw, err->message );
d8d44976 341 g_error_free(err);
07db5c2f 342 update_btns( mw );
cf15ca10 343 return FALSE;
1d48a247 344 }
d8d44976
HJYP
345
346 /* tests if the file is actually just a normal picture */
347 if ( gdk_pixbuf_animation_is_static_image( mw->animation ) )
348 {
349 mw->pix = gdk_pixbuf_animation_get_static_image( mw->animation );
350 g_object_ref(mw->pix);
351 g_object_unref(mw->animation);
352 mw->animation = NULL;
353 }
354 else
355 {
356 int delay;
357 /* we found an animation */
358 mw->animation_iter = gdk_pixbuf_animation_get_iter( mw->animation, NULL );
359 mw->pix = gdk_pixbuf_animation_iter_get_pixbuf( mw->animation_iter );
360 delay = gdk_pixbuf_animation_iter_get_delay_time( mw->animation_iter );
7e1dcec1 361 mw->animation_timeout = g_timeout_add( delay, (GSourceFunc) on_animation_timeout, mw );
d8d44976 362 }
07db5c2f 363 update_btns( mw );
d8d44976
HJYP
364
365 if(!strcmp(type,"jpeg"))
c215db47 366 {
701822a6 367 GdkPixbuf* tmp;
da046c6e 368 // Only jpeg should rotate by EXIF
701822a6
HJYP
369 tmp = gdk_pixbuf_apply_embedded_orientation(mw->pix);
370 g_object_unref(mw->pix);
371 mw->pix = tmp;
c215db47 372 }
794cac1a 373
cf15ca10 374 mw->zoom_mode = zoom;
da8bd946 375
5bfec971 376 // select most suitable viewing mode
d8ce3af1 377 if( zoom == ZOOM_NONE )
1d48a247 378 {
cf15ca10
HJYP
379 int w = gdk_pixbuf_get_width( mw->pix );
380 int h = gdk_pixbuf_get_height( mw->pix );
1d48a247 381
1d48a247 382 GdkRectangle area;
cf15ca10 383 get_working_area( gtk_widget_get_screen((GtkWidget*)mw), &area );
c325c989
HJYP
384 // g_debug("determine best zoom mode: orig size: w=%d, h=%d", w, h);
385 // FIXME: actually this is a little buggy :-(
da8bd946 386 if( w < area.width && h < area.height && (w >= 640 || h >= 480) )
1d48a247 387 {
cf15ca10
HJYP
388 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER );
389 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, w, h );
5bfec971 390 GtkRequisition req;
cf15ca10 391 gtk_widget_size_request ( (GtkWidget*)mw, &req );
9c52ae58
HJYP
392 if( req.width < 640 ) req.width = 640;
393 if( req.height < 480 ) req.height = 480;
cf15ca10
HJYP
394 gtk_window_resize( (GtkWindow*)mw, req.width, req.height );
395 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, -1, -1 );
396 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
397 mw->zoom_mode = ZOOM_ORIG;
da046c6e 398 mw->scale = 1.0;
1d48a247
HJYP
399 }
400 else
cf15ca10 401 mw->zoom_mode = ZOOM_FIT;
5bfec971 402 }
5bfec971 403
cf15ca10 404 if( mw->zoom_mode == ZOOM_FIT )
d8ce3af1 405 {
cf15ca10 406 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
c325c989 407 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
d8ce3af1 408 }
cf15ca10 409 else if( mw->zoom_mode == ZOOM_SCALE ) // scale
d8ce3af1 410 {
cf15ca10
HJYP
411 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
412 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
413 main_win_scale_image( mw, mw->scale, GDK_INTERP_BILINEAR );
d8ce3af1 414 }
cf15ca10 415 else if( mw->zoom_mode == ZOOM_ORIG ) // original size
5bfec971 416 {
cf15ca10 417 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
da046c6e 418 image_view_set_scale( (ImageView*)mw->img_view, mw->scale, GDK_INTERP_BILINEAR );
cf15ca10 419 main_win_center_image( mw );
1d48a247
HJYP
420 }
421
cf15ca10 422 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
9c52ae58 423
1d48a247
HJYP
424// while (gtk_events_pending ())
425// gtk_main_iteration ();
426
427 // build file list
428 char* dir_path = g_path_get_dirname( file_path );
cf15ca10 429 image_list_open_dir( mw->img_list, dir_path, NULL );
f214531d 430 image_list_sort_by_name( mw->img_list, GTK_SORT_DESCENDING );
1d48a247 431 g_free( dir_path );
76e88fcf 432
1d48a247 433 char* base_name = g_path_get_basename( file_path );
cf15ca10 434 image_list_set_current( mw->img_list, base_name );
76e88fcf
HJYP
435
436 char* disp_name = g_filename_display_name( base_name );
1d48a247
HJYP
437 g_free( base_name );
438
d8d44976 439 update_title( disp_name, mw );
76e88fcf 440 g_free( disp_name );
1d48a247 441
cf15ca10 442 return TRUE;
1d48a247
HJYP
443}
444
ebee501a 445void main_win_start_slideshow( MainWin* mw )
446{
447 on_slideshow_menu(NULL, mw);
448}
449
cf15ca10 450void main_win_close( MainWin* mw )
1d48a247 451{
d8d44976
HJYP
452 if( mw->animation )
453 {
454 g_object_unref( mw->animation );
455 mw->animation = NULL;
456 if( mw->animation_timeout );
457 {
458 g_source_remove( mw->animation_timeout );
459 mw->animation_timeout = 0;
460 }
461 }
462 else if( mw->pix )
9c52ae58 463 {
cf15ca10 464 g_object_unref( mw->pix );
1d48a247 465 }
d8d44976 466 mw->pix = NULL;
1d48a247
HJYP
467}
468
cf15ca10 469void main_win_show_error( MainWin* mw, const char* message )
1d48a247 470{
cf15ca10 471 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
472 GTK_DIALOG_MODAL,
473 GTK_MESSAGE_ERROR,
474 GTK_BUTTONS_OK,
a574ce8f 475 "%s", message );
1d48a247
HJYP
476 gtk_dialog_run( (GtkDialog*)dlg );
477 gtk_widget_destroy( dlg );
478}
479
cf15ca10 480void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation )
1d48a247 481{
cf15ca10 482 GTK_WIDGET_CLASS(main_win_parent_class)->size_allocate( widget, allocation );
52c867e9 483 if( GTK_WIDGET_REALIZED (widget) )
1d48a247 484 {
cf15ca10 485 MainWin* mw = (MainWin*)widget;
9c52ae58 486
cf15ca10 487 if( mw->zoom_mode == ZOOM_FIT )
52c867e9
HJYP
488 {
489 while(gtk_events_pending ())
490 gtk_main_iteration(); // makes it more fluid
1d48a247 491
c325c989 492 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
52c867e9 493 }
1d48a247
HJYP
494 }
495}
496
86ebaeb3 497gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state )
e216a767
HJYP
498{
499 MainWin* mw = (MainWin*)widget;
1c8a8ba7 500 if( state->new_window_state == GDK_WINDOW_STATE_FULLSCREEN )
e216a767 501 {
794cac1a 502 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.bg_full );
1c8a8ba7
HJYP
503 gtk_widget_hide( gtk_widget_get_parent(mw->nav_bar) );
504 mw->full_screen = TRUE;
e216a767 505 }
1c8a8ba7 506 else
e216a767 507 {
794cac1a 508 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.bg );
1c8a8ba7
HJYP
509 gtk_widget_show( gtk_widget_get_parent(mw->nav_bar) );
510 mw->full_screen = FALSE;
e216a767 511 }
b0fd70a0 512
513 int previous = pref.open_maximized;
514 pref.open_maximized = (state->new_window_state == GDK_WINDOW_STATE_MAXIMIZED);
515 if (previous != pref.open_maximized)
516 save_preferences();
86ebaeb3 517 return TRUE;
e216a767
HJYP
518}
519
cf15ca10 520void main_win_fit_size( MainWin* mw, int width, int height, gboolean can_strech, GdkInterpType type )
1d48a247 521{
cf15ca10 522 if( ! mw->pix )
1d48a247
HJYP
523 return;
524
cf15ca10
HJYP
525 int orig_w = gdk_pixbuf_get_width( mw->pix );
526 int orig_h = gdk_pixbuf_get_height( mw->pix );
1d48a247 527
da8bd946
HJYP
528 if( can_strech || (orig_w > width || orig_h > height) )
529 {
cf15ca10
HJYP
530 gdouble xscale = ((gdouble)width) / orig_w;
531 gdouble yscale = ((gdouble)height)/ orig_h;
532 gdouble final_scale = xscale < yscale ? xscale : yscale;
1d48a247 533
cf15ca10 534 main_win_scale_image( mw, final_scale, type );
da8bd946
HJYP
535 }
536 else // use original size if the image is smaller than the window
537 {
cf15ca10
HJYP
538 mw->scale = 1.0;
539 image_view_set_scale( (ImageView*)mw->img_view, 1.0, type );
794cac1a 540
d8d44976 541 update_title(NULL, mw);
da8bd946 542 }
1d48a247
HJYP
543}
544
cf15ca10 545void main_win_fit_window_size( MainWin* mw, gboolean can_strech, GdkInterpType type )
1d48a247 546{
cf15ca10 547 mw->zoom_mode = ZOOM_FIT;
94fcf707 548
cf15ca10 549 if( mw->pix == NULL )
da8bd946 550 return;
94fcf707 551
cf15ca10 552 main_win_fit_size( mw, mw->scroll->allocation.width, mw->scroll->allocation.height, can_strech, type );
1d48a247
HJYP
553}
554
ebee501a 555GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle)
556{
557 GtkWidget* unused;
558 return add_nav_btn_img(mw, icon, tip, cb, toggle, &unused);
559}
560
561GtkWidget* add_nav_btn_img( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle, GtkWidget** ret_img )
1d48a247 562{
701822a6
HJYP
563 GtkWidget* img;
564 if( g_str_has_prefix(icon, "gtk-") )
565 img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
566 else
567 img = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
1d48a247
HJYP
568 GtkWidget* btn;
569 if( G_UNLIKELY(toggle) )
570 {
571 btn = gtk_toggle_button_new();
cf15ca10 572 g_signal_connect( btn, "toggled", cb, mw );
1d48a247
HJYP
573 }
574 else
575 {
576 btn = gtk_button_new();
cf15ca10 577 g_signal_connect( btn, "clicked", cb, mw );
1d48a247
HJYP
578 }
579 gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
580 gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
581 gtk_container_add( (GtkContainer*)btn, img );
d8d44976 582 gtk_widget_set_tooltip_text( btn, tip );
cf15ca10 583 gtk_box_pack_start( (GtkBox*)mw->nav_bar, btn, FALSE, FALSE, 0 );
ebee501a 584 *ret_img = img;
1d48a247
HJYP
585 return btn;
586}
587
cf15ca10 588void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw )
1d48a247 589{
cf15ca10 590 gtk_button_clicked( (GtkButton*)mw->btn_fit );
1d48a247
HJYP
591}
592
cf15ca10 593void on_zoom_fit( GtkToggleButton* btn, MainWin* mw )
1d48a247
HJYP
594{
595 if( ! btn->active )
596 {
cf15ca10 597 if( mw->zoom_mode == ZOOM_FIT )
1d48a247
HJYP
598 gtk_toggle_button_set_active( btn, TRUE );
599 return;
600 }
cf15ca10 601 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1d48a247 602
c325c989 603 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
604}
605
cf15ca10 606void on_full_screen( GtkWidget* btn, MainWin* mw )
1d48a247 607{
cf15ca10 608 if( ! mw->full_screen )
cf15ca10 609 gtk_window_fullscreen( (GtkWindow*)mw );
1d48a247 610 else
cf15ca10 611 gtk_window_unfullscreen( (GtkWindow*)mw );
1d48a247
HJYP
612}
613
cf15ca10 614void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw )
1d48a247 615{
cf15ca10 616 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
617}
618
cf15ca10 619void on_orig_size( GtkToggleButton* btn, MainWin* mw )
1d48a247 620{
cf15ca10 621 // this callback could be called from activate signal of menu item.
1d48a247
HJYP
622 if( GTK_IS_MENU_ITEM(btn) )
623 {
cf15ca10 624 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
625 return;
626 }
627
628 if( ! btn->active )
629 {
cf15ca10 630 if( mw->zoom_mode == ZOOM_ORIG )
1d48a247
HJYP
631 gtk_toggle_button_set_active( btn, TRUE );
632 return;
633 }
cf15ca10
HJYP
634 mw->zoom_mode = ZOOM_ORIG;
635 mw->scale = 1.0;
636// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 637// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 638
cf15ca10 639 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1d48a247 640
cf15ca10 641 if( ! mw->pix )
1d48a247
HJYP
642 return;
643
1c8a8ba7 644 // update scale
d8d44976 645 update_title(NULL, mw);
794cac1a 646
cf15ca10 647 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
1d48a247 648
9c52ae58
HJYP
649 while (gtk_events_pending ())
650 gtk_main_iteration ();
651
cf15ca10 652 main_win_center_image( mw ); // FIXME: mw doesn't work well. Why?
1d48a247
HJYP
653}
654
f34b3b31 655void on_prev( GtkWidget* btn, MainWin* mw )
1d48a247 656{
cf15ca10
HJYP
657 const char* name;
658 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
659 return;
660
cf15ca10 661 name = image_list_get_prev( mw->img_list );
1d48a247 662
cf15ca10 663 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
664 {
665 // FIXME: need to ask user first?
cf15ca10 666 name = image_list_get_last( mw->img_list );
1d48a247
HJYP
667 }
668
669 if( name )
670 {
cf15ca10
HJYP
671 char* file_path = image_list_get_current_file_path( mw->img_list );
672 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
673 g_free( file_path );
674 }
675}
676
f34b3b31 677void on_next( GtkWidget* btn, MainWin* mw )
1d48a247 678{
cf15ca10 679 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
680 return;
681
cf15ca10 682 const char* name = image_list_get_next( mw->img_list );
1d48a247 683
cf15ca10 684 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
685 {
686 // FIXME: need to ask user first?
cf15ca10 687 name = image_list_get_first( mw->img_list );
1d48a247
HJYP
688 }
689
690 if( name )
691 {
cf15ca10
HJYP
692 char* file_path = image_list_get_current_file_path( mw->img_list );
693 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
694 g_free( file_path );
695 }
696}
697
ebee501a 698void cancel_slideshow(MainWin* mw)
699{
700 mw->slideshow_cancelled = TRUE;
701 mw->slideshow_running = FALSE;
702 if (mw->slide_timeout != 0)
703 g_source_remove(mw->slide_timeout);
704 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mw->btn_play_stop), FALSE );
705}
706
707gboolean next_slide(MainWin* mw)
708{
709 /* Timeout causes an implicit "Next". */
710 if (mw->slideshow_running)
711 on_next( NULL, mw );
712
713 return mw->slideshow_running;
714}
715
716void on_slideshow_menu( GtkMenuItem* item, MainWin* mw )
717{
718 gtk_button_clicked( (GtkButton*)mw->btn_play_stop );
719}
720
721void on_slideshow( GtkToggleButton* btn, MainWin* mw )
722{
723 if ((mw->slideshow_running) || (mw->slideshow_cancelled))
724 {
725 mw->slideshow_running = FALSE;
726 mw->slideshow_cancelled = FALSE;
727 gtk_image_set_from_stock( GTK_IMAGE(mw->img_play_stop), GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_SMALL_TOOLBAR );
728 gtk_widget_set_tooltip_text( GTK_WIDGET(btn), _("Start Slideshow") );
729 gtk_toggle_button_set_active( btn, FALSE );
730 }
731 else
732 {
733 gtk_toggle_button_set_active( btn, TRUE );
734 mw->slideshow_running = TRUE;
735 gtk_image_set_from_stock( GTK_IMAGE(mw->img_play_stop), GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_SMALL_TOOLBAR );
736 gtk_widget_set_tooltip_text( GTK_WIDGET(btn), _("Stop Slideshow") );
737 mw->slide_timeout = g_timeout_add(1000 * pref.slide_delay, (GSourceFunc) next_slide, mw);
738 }
739}
740
da046c6e 741//////////////////// rotate & flip
742
743static int trans_angle_to_id(int i)
744{
745 if(i == 0) return 1;
746 else if(i == 90) return 6;
747 else if(i == 180) return 3;
748 else if(i == 270) return 8;
749 else if(i == -45) return 7;
750 else if(i == -90) return 2;
751 else if(i == -135) return 5;
ed14be05 752 else /* -180 */ return 4;
da046c6e 753}
754
755static int get_new_angle( int orig_angle, int rotate_angle )
756{
757 // defined in exif.c
758 extern int ExifRotateFlipMapping[9][9];
759 static int angle_trans_back[] = {0, 0, -90, 180, -180, -135, 90, -45, 270};
794cac1a 760
da046c6e 761 orig_angle = trans_angle_to_id(orig_angle);
762 rotate_angle = trans_angle_to_id(rotate_angle);
794cac1a 763
da046c6e 764 return angle_trans_back[ ExifRotateFlipMapping[orig_angle][rotate_angle] ];
765}
766
1c8a8ba7 767void on_rotate_auto_save( GtkWidget* btn, MainWin* mw )
aab0b120 768{
1c8a8ba7
HJYP
769 if(pref.auto_save_rotated){
770// gboolean ask_before_save = pref.ask_before_save;
771// pref.ask_before_save = FALSE;
772 on_save(btn,mw);
773// pref.ask_before_save = ask_before_save;
827117ce 774 }
aab0b120 775}
776
1c8a8ba7 777void on_rotate_clockwise( GtkWidget* btn, MainWin* mw )
1d48a247 778{
ebee501a 779 cancel_slideshow(mw);
1c8a8ba7
HJYP
780 rotate_image( mw, GDK_PIXBUF_ROTATE_CLOCKWISE );
781 mw->rotation_angle = get_new_angle(mw->rotation_angle, 90);
782 on_rotate_auto_save(btn, mw);
783}
827117ce 784
1c8a8ba7
HJYP
785void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw )
786{
ebee501a 787 cancel_slideshow(mw);
1c8a8ba7
HJYP
788 rotate_image( mw, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
789 mw->rotation_angle = get_new_angle(mw->rotation_angle, 270);
790 on_rotate_auto_save(btn, mw);
da046c6e 791}
792
793void on_flip_vertical( GtkWidget* btn, MainWin* mw )
794{
ebee501a 795 cancel_slideshow(mw);
1c8a8ba7
HJYP
796 rotate_image( mw, -180 );
797 mw->rotation_angle = get_new_angle(mw->rotation_angle, -180);
798 on_rotate_auto_save(btn, mw);
da046c6e 799}
800
801void on_flip_horizontal( GtkWidget* btn, MainWin* mw )
802{
ebee501a 803 cancel_slideshow(mw);
1c8a8ba7
HJYP
804 rotate_image( mw, -90 );
805 mw->rotation_angle = get_new_angle(mw->rotation_angle, -90);
806 on_rotate_auto_save(btn, mw);
1d48a247
HJYP
807}
808
d8d44976 809/* end of rotate & flip */
da046c6e 810
cf15ca10 811void on_save_as( GtkWidget* btn, MainWin* mw )
1d48a247 812{
701822a6
HJYP
813 char *file, *type;
814
ebee501a 815 cancel_slideshow(mw);
cf15ca10 816 if( ! mw->pix )
1d48a247 817 return;
827117ce 818
7e1dcec1 819 file = get_save_filename( GTK_WINDOW(mw), image_list_get_dir( mw->img_list ), &type );
701822a6 820 if( file )
1d48a247 821 {
701822a6
HJYP
822 char* dir;
823 main_win_save( mw, file, type, TRUE );
824 dir = g_path_get_dirname(file);
825 const char* name = file + strlen(dir) + 1;
827117ce 826
701822a6 827 if( strcmp( image_list_get_dir(mw->img_list), dir ) == 0 )
1d48a247 828 {
701822a6
HJYP
829 /* if the saved file is located in the same dir */
830 /* simply add it to image list */
831 image_list_add_sorted( mw->img_list, name, TRUE );
1d48a247 832 }
701822a6 833 else /* otherwise reload the whole image list. */
1d48a247 834 {
701822a6
HJYP
835 /* switch to the dir containing the saved file. */
836 image_list_open_dir( mw->img_list, dir, NULL );
1d48a247 837 }
701822a6
HJYP
838 update_title( name, mw );
839 g_free( dir );
1c8a8ba7
HJYP
840 g_free( file );
841 g_free( type );
1d48a247 842 }
1d48a247
HJYP
843}
844
cf15ca10 845void on_save( GtkWidget* btn, MainWin* mw )
1d48a247 846{
ebee501a 847 cancel_slideshow(mw);
cf15ca10 848 if( ! mw->pix )
1d48a247
HJYP
849 return;
850
cf15ca10
HJYP
851 char* file_name = g_build_filename( image_list_get_dir( mw->img_list ),
852 image_list_get_current( mw->img_list ), NULL );
1d48a247
HJYP
853 GdkPixbufFormat* info;
854 info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
855 char* type = gdk_pixbuf_format_get_name( info );
da046c6e 856
1c8a8ba7
HJYP
857 /* Confirm save if requested. */
858 if ((pref.ask_before_save) && ( ! save_confirm(mw, file_name)))
701822a6 859 return;
1c8a8ba7
HJYP
860
861 if(strcmp(type,"jpeg")==0)
da046c6e 862 {
1c8a8ba7 863 if(!pref.rotate_exif_only || ExifRotate(file_name, mw->rotation_angle) == FALSE)
da046c6e 864 {
865 // hialan notes:
794cac1a 866 // ExifRotate retrun FALSE when
da046c6e 867 // 1. Can not read file
868 // 2. Exif do not have TAG_ORIENTATION tag
869 // 3. Format unknown
794cac1a 870 // And then we apply rotate_and_save_jpeg_lossless() ,
da046c6e 871 // the result would not effected by EXIF Orientation...
7c3b160c 872#ifdef HAVE_LIBJPEG
1c8a8ba7
HJYP
873 int status = rotate_and_save_jpeg_lossless(file_name,mw->rotation_angle);
874 if(status != 0)
da046c6e 875 {
1c8a8ba7 876 main_win_show_error( mw, g_strerror(status) );
da046c6e 877 }
878#else
1c8a8ba7 879 main_win_save( mw, file_name, type, pref.ask_before_save );
7c3b160c 880#endif
da046c6e 881 }
1c8a8ba7
HJYP
882 } else
883 main_win_save( mw, file_name, type, pref.ask_before_save );
7c3b160c 884 mw->rotation_angle = 0;
1d48a247
HJYP
885 g_free( file_name );
886 g_free( type );
887}
888
cf15ca10 889void on_open( GtkWidget* btn, MainWin* mw )
1d48a247 890{
ebee501a 891 cancel_slideshow(mw);
701822a6 892 char* file = get_open_filename( (GtkWindow*)mw, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
893 if( file )
894 {
cf15ca10 895 main_win_open( mw, file, ZOOM_NONE );
1d48a247
HJYP
896 g_free( file );
897 }
898}
899
cf15ca10 900void on_zoom_in( GtkWidget* btn, MainWin* mw )
1d48a247 901{
cf15ca10
HJYP
902 mw->zoom_mode = ZOOM_SCALE;
903 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
904 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
905// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 906// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 907
cf15ca10
HJYP
908 double scale = mw->scale;
909 if( mw->pix && scale < 20.0 )
1d48a247 910 {
cf15ca10 911// busy(TRUE);
9c52ae58
HJYP
912 scale *= 1.05;
913 if( scale > 20.0 )
914 scale = 20.0;
cf15ca10
HJYP
915 if( mw->scale != scale )
916 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 917// adjust_adjustment_on_zoom(oldscale);
cf15ca10 918// busy(FALSE);
1d48a247
HJYP
919 }
920}
921
cf15ca10 922void on_zoom_out( GtkWidget* btn, MainWin* mw )
1d48a247 923{
cf15ca10
HJYP
924 mw->zoom_mode = ZOOM_SCALE;
925 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
926 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
927// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 928// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 929
cf15ca10
HJYP
930 double scale = mw->scale;
931 if( mw->pix && scale > 0.02 )
1d48a247 932 {
cf15ca10 933// busy(TRUE);
da8bd946 934
9c52ae58
HJYP
935 scale /= 1.05;
936 if( scale < 0.02 )
937 scale = 0.02;
cf15ca10
HJYP
938 if( mw->scale != scale )
939 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 940// adjust_adjustment_on_zoom(oldscale);
cf15ca10 941// busy(FALSE);
1d48a247
HJYP
942 }
943}
944
cf15ca10 945void on_preference( GtkWidget* btn, MainWin* mw )
1d48a247 946{
1c8a8ba7 947 edit_preferences( (GtkWindow*)mw );
1d48a247
HJYP
948}
949
cf15ca10 950void on_quit( GtkWidget* btn, MainWin* mw )
1d48a247 951{
ebee501a 952 cancel_slideshow(mw);
cf15ca10 953 gtk_widget_destroy( (GtkWidget*)mw );
1d48a247
HJYP
954}
955
cf15ca10 956gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 957{
9c52ae58
HJYP
958 if( ! GTK_WIDGET_HAS_FOCUS( widget ) )
959 gtk_widget_grab_focus( widget );
5bfec971 960
1d48a247
HJYP
961 if( evt->type == GDK_BUTTON_PRESS)
962 {
963 if( evt->button == 1 ) // left button
964 {
cf15ca10 965 if( ! mw->pix )
1d48a247 966 return FALSE;
cf15ca10
HJYP
967 mw->dragging = TRUE;
968 gtk_widget_get_pointer( (GtkWidget*)mw, &mw->drag_old_x ,&mw->drag_old_y );
969 gdk_window_set_cursor( widget->window, mw->hand_cursor );
1d48a247
HJYP
970 }
971 else if( evt->button == 3 ) // right button
972 {
cf15ca10 973 show_popup_menu( mw, evt );
1d48a247
HJYP
974 }
975 }
976 else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 ) // double clicked
977 {
1c8a8ba7 978 on_full_screen( NULL, mw );
1d48a247 979 }
1d48a247
HJYP
980 return FALSE;
981}
982
cf15ca10 983gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw )
1d48a247 984{
cf15ca10 985 if( ! mw->dragging )
1d48a247 986 return FALSE;
9c52ae58 987
1d48a247 988 int cur_x, cur_y;
cf15ca10 989 gtk_widget_get_pointer( (GtkWidget*)mw, &cur_x ,&cur_y );
1d48a247 990
cf15ca10
HJYP
991 int dx = (mw->drag_old_x - cur_x);
992 int dy = (mw->drag_old_y - cur_y);
1d48a247
HJYP
993
994 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
995 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
996 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 997
9c52ae58 998 GtkRequisition req;
cf15ca10 999 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247
HJYP
1000
1001 if( ABS(dx) > 4 )
1002 {
cf15ca10 1003 mw->drag_old_x = cur_x;
9c52ae58 1004 if( req.width > hadj->page_size )
1d48a247
HJYP
1005 {
1006 gdouble x = gtk_adjustment_get_value (hadj) + dx;
1007 if( x < hadj->lower )
1008 x = hadj->lower;
1009 else if( (x + hadj->page_size) > hadj->upper )
1010 x = hadj->upper - hadj->page_size;
1011
1012 if( x != hadj->value )
1013 gtk_adjustment_set_value (hadj, x );
1014 }
1015 }
1016
1017 if( ABS(dy) > 4 )
1018 {
9c52ae58 1019 if( req.height > vadj->page_size )
1d48a247 1020 {
cf15ca10 1021 mw->drag_old_y = cur_y;
1d48a247
HJYP
1022 gdouble y = gtk_adjustment_get_value (vadj) + dy;
1023 if( y < vadj->lower )
1024 y = vadj->lower;
1025 else if( (y + vadj->page_size) > vadj->upper )
1026 y = vadj->upper - vadj->page_size;
1027
1028 if( y != vadj->value )
1029 gtk_adjustment_set_value (vadj, y );
1030 }
1031 }
1032 return FALSE;
1033}
1034
cf15ca10 1035gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 1036{
cf15ca10 1037 mw->dragging = FALSE;
1d48a247
HJYP
1038 gdk_window_set_cursor( widget->window, NULL );
1039 return FALSE;
1040}
1041
cf15ca10 1042gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw )
5bfec971 1043{
58e1b106 1044 guint modifiers = gtk_accelerator_get_default_mod_mask();
5bfec971
HJYP
1045 switch( evt->direction )
1046 {
1047 case GDK_SCROLL_UP:
58e1b106
JH
1048 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1049 on_zoom_in( NULL, mw );
1050 else
1051 on_prev( NULL, mw );
5bfec971
HJYP
1052 break;
1053 case GDK_SCROLL_DOWN:
58e1b106
JH
1054 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1055 on_zoom_out( NULL, mw );
1056 else
1057 on_next( NULL, mw );
5bfec971
HJYP
1058 break;
1059 case GDK_SCROLL_LEFT:
1c8a8ba7
HJYP
1060 if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1061 on_next( NULL, mw );
1062 else
1063 on_prev( NULL, mw );
5bfec971
HJYP
1064 break;
1065 case GDK_SCROLL_RIGHT:
1c8a8ba7
HJYP
1066 if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1067 on_prev( NULL, mw );
1068 else
1069 on_next( NULL, mw );
5bfec971
HJYP
1070 break;
1071 }
da8bd946 1072 return TRUE;
5bfec971
HJYP
1073}
1074
cf15ca10 1075gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key)
1d48a247 1076{
cf15ca10 1077 MainWin* mw = (MainWin*)widget;
1d48a247
HJYP
1078 switch( key->keyval )
1079 {
7c3b160c
HJYP
1080 case GDK_Right:
1081 case GDK_KP_Right:
1082 case GDK_rightarrow:
1c8a8ba7
HJYP
1083 if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1084 on_prev( NULL, mw );
1085 else
1086 on_next( NULL, mw );
1087 break;
1d48a247
HJYP
1088 case GDK_Return:
1089 case GDK_space:
1090 case GDK_Next:
5bfec971
HJYP
1091 case GDK_KP_Down:
1092 case GDK_Down:
1093 case GDK_downarrow:
cf15ca10 1094 on_next( NULL, mw );
1d48a247 1095 break;
7c3b160c
HJYP
1096 case GDK_Left:
1097 case GDK_KP_Left:
1098 case GDK_leftarrow:
1c8a8ba7
HJYP
1099 if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1100 on_next( NULL, mw );
1101 else
1102 on_prev( NULL, mw );
1103 break;
1d48a247
HJYP
1104 case GDK_Prior:
1105 case GDK_BackSpace:
5bfec971
HJYP
1106 case GDK_KP_Up:
1107 case GDK_Up:
1108 case GDK_uparrow:
cf15ca10 1109 on_prev( NULL, mw );
1d48a247 1110 break;
ebee501a 1111 case GDK_w:
1112 case GDK_W:
1113 on_slideshow_menu( NULL, mw );
1114 break;
1d48a247
HJYP
1115 case GDK_KP_Add:
1116 case GDK_plus:
641adad3 1117 case GDK_equal:
cf15ca10 1118 on_zoom_in( NULL, mw );
1d48a247
HJYP
1119 break;
1120 case GDK_KP_Subtract:
1121 case GDK_minus:
cf15ca10 1122 on_zoom_out( NULL, mw );
1d48a247 1123 break;
1d48a247 1124 case GDK_s:
d8d44976 1125 case GDK_S:
cf15ca10 1126 on_save( NULL, mw );
1d48a247 1127 break;
c9c60499 1128 case GDK_a:
1129 case GDK_A:
1130 on_save_as( NULL, mw );
1131 break;
1d48a247 1132 case GDK_l:
d8d44976 1133 case GDK_L:
cf15ca10 1134 on_rotate_counterclockwise( NULL, mw );
1d48a247
HJYP
1135 break;
1136 case GDK_r:
d8d44976 1137 case GDK_R:
cf15ca10 1138 on_rotate_clockwise( NULL, mw );
1d48a247
HJYP
1139 break;
1140 case GDK_f:
d8d44976 1141 case GDK_F:
cf15ca10
HJYP
1142 if( mw->zoom_mode != ZOOM_FIT )
1143 gtk_button_clicked((GtkButton*)mw->btn_fit );
1d48a247
HJYP
1144 break;
1145 case GDK_g:
d8d44976 1146 case GDK_G:
cf15ca10
HJYP
1147 if( mw->zoom_mode != ZOOM_ORIG )
1148 gtk_button_clicked((GtkButton*)mw->btn_orig );
1d48a247 1149 break;
d8d44976
HJYP
1150 case GDK_h:
1151 case GDK_H:
1152 on_flip_horizontal( NULL, mw );
1153 break;
1154 case GDK_v:
1155 case GDK_V:
1156 on_flip_vertical( NULL, mw );
1157 break;
1d48a247 1158 case GDK_o:
d8d44976 1159 case GDK_O:
cf15ca10 1160 on_open( NULL, mw );
5bfec971
HJYP
1161 break;
1162 case GDK_Delete:
1163 case GDK_d:
d8d44976 1164 case GDK_D:
cf15ca10 1165 on_delete( NULL, mw );
1d48a247 1166 break;
d8d44976
HJYP
1167 case GDK_p:
1168 case GDK_P:
1169 on_preference( NULL, mw );
1c8a8ba7 1170 break;
1d48a247 1171 case GDK_Escape:
cf15ca10
HJYP
1172 if( mw->full_screen )
1173 on_full_screen( NULL, mw );
1d48a247 1174 else
cf15ca10 1175 on_quit( NULL, mw );
1d48a247 1176 break;
1c8a8ba7 1177 case GDK_q:
f7401dea 1178 case GDK_Q:
1c8a8ba7
HJYP
1179 on_quit( NULL, mw );
1180 break;
1d48a247 1181 case GDK_F11:
cf15ca10 1182 on_full_screen( NULL, mw );
1d48a247
HJYP
1183 break;
1184
1185 default:
cf15ca10 1186 GTK_WIDGET_CLASS(main_win_parent_class)->key_press_event( widget, key );
1d48a247
HJYP
1187 }
1188 return FALSE;
1189}
1190
cf15ca10 1191void main_win_center_image( MainWin* mw )
1d48a247
HJYP
1192{
1193 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1194 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1195 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1196
9c52ae58 1197 GtkRequisition req;
cf15ca10 1198 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247 1199
9c52ae58
HJYP
1200 if( req.width > hadj->page_size )
1201 gtk_adjustment_set_value(hadj, ( hadj->upper - hadj->page_size ) / 2 );
1d48a247 1202
9c52ae58
HJYP
1203 if( req.height > vadj->page_size )
1204 gtk_adjustment_set_value(vadj, ( vadj->upper - vadj->page_size ) / 2 );
1d48a247
HJYP
1205}
1206
da046c6e 1207void rotate_image( MainWin* mw, int angle )
1d48a247 1208{
da046c6e 1209 GdkPixbuf* rpix = NULL;
1210
cf15ca10 1211 if( ! mw->pix )
1d48a247
HJYP
1212 return;
1213
da046c6e 1214 if(angle > 0)
1215 {
1216 rpix = gdk_pixbuf_rotate_simple( mw->pix, angle );
1217 }
1218 else
1219 {
1220 if(angle == -90)
1221 rpix = gdk_pixbuf_flip( mw->pix, TRUE );
1222 else if(angle == -180)
1223 rpix = gdk_pixbuf_flip( mw->pix, FALSE );
1224 }
1c8a8ba7
HJYP
1225
1226 if (!rpix) {
1227 return;
1228 }
1229
cf15ca10 1230 g_object_unref( mw->pix );
794cac1a 1231
cf15ca10 1232 mw->pix = rpix;
0c0c8179 1233 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
1d48a247 1234
cf15ca10 1235 if( mw->zoom_mode == ZOOM_FIT )
c325c989 1236 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
1237}
1238
cf15ca10 1239gboolean main_win_scale_image( MainWin* mw, double new_scale, GdkInterpType type )
1d48a247
HJYP
1240{
1241 if( G_UNLIKELY( new_scale == 1.0 ) )
1242 {
cf15ca10 1243 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
da046c6e 1244 mw->scale = 1.0;
cf15ca10 1245 return TRUE;
1d48a247 1246 }
cf15ca10 1247 mw->scale = new_scale;
0c0c8179 1248 image_view_set_scale( (ImageView*)mw->img_view, new_scale, type );
794cac1a 1249
d8d44976 1250 update_title( NULL, mw );
794cac1a 1251
cf15ca10 1252 return TRUE;
1d48a247
HJYP
1253}
1254
1c8a8ba7 1255gboolean save_confirm( MainWin* mw, const char* file_path )
aab0b120 1256{
1c8a8ba7
HJYP
1257 if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
1258 {
1259 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1260 GTK_DIALOG_MODAL,
1261 GTK_MESSAGE_QUESTION,
1262 GTK_BUTTONS_YES_NO,
1263 _("The file name you selected already exists.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
1264 if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
827117ce 1265 {
827117ce 1266 gtk_widget_destroy( dlg );
1c8a8ba7 1267 return FALSE;
827117ce 1268 }
1c8a8ba7 1269 gtk_widget_destroy( dlg );
827117ce 1270 }
1c8a8ba7
HJYP
1271 return TRUE;
1272}
1273
1274gboolean main_win_save( MainWin* mw, const char* file_path, const char* type, gboolean confirm )
1275{
1276 gboolean result1,gdk_save_supported;
1277 GSList *gdk_formats;
1278 GSList *gdk_formats_i;
1279 if( ! mw->pix )
1280 return FALSE;
827117ce 1281
1c8a8ba7 1282 /* detect if the current type can be save by gdk_pixbuf_save() */
3d62c4bb
YCLP
1283 gdk_save_supported = FALSE;
1284 gdk_formats = gdk_pixbuf_get_formats();
1285 for (gdk_formats_i = gdk_formats; gdk_formats_i;
1286 gdk_formats_i = g_slist_next(gdk_formats_i))
1287 {
1288 GdkPixbufFormat *data;
1289 data = gdk_formats_i->data;
1290 if (gdk_pixbuf_format_is_writable(data))
1291 {
1292 if ( strcmp(type, gdk_pixbuf_format_get_name(data))==0)
1293 {
1294 gdk_save_supported = TRUE;
1295 break;
1296 }
1297 }
1298 }
1299 g_slist_free (gdk_formats);
1300
1d48a247 1301 GError* err = NULL;
3d62c4bb
YCLP
1302 if (!gdk_save_supported)
1303 {
701822a6 1304 main_win_show_error( mw, _("Writing this image format is not supported.") );
3d62c4bb
YCLP
1305 return FALSE;
1306 }
701822a6
HJYP
1307 if( strcmp( type, "jpeg" ) == 0 )
1308 {
1309 char tmp[32];
1310 g_sprintf(tmp, "%d", pref.jpg_quality);
1311 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, "quality", tmp, NULL );
1312 }
1313 else if( strcmp( type, "png" ) == 0 )
1314 {
1315 char tmp[32];
1316 g_sprintf(tmp, "%d", pref.png_compression);
1317 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, "compression", tmp, NULL );
1318 }
1319 else
1320 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL );
3d62c4bb 1321 if( ! result1 )
1d48a247 1322 {
cf15ca10
HJYP
1323 main_win_show_error( mw, err->message );
1324 return FALSE;
1d48a247 1325 }
cf15ca10 1326 return TRUE;
1d48a247
HJYP
1327}
1328
cf15ca10 1329void on_delete( GtkWidget* btn, MainWin* mw )
1d48a247 1330{
ebee501a 1331 cancel_slideshow(mw);
cf15ca10 1332 char* file_path = image_list_get_current_file_path( mw->img_list );
1d48a247
HJYP
1333 if( file_path )
1334 {
f7401dea 1335 int resp = GTK_RESPONSE_YES;
1336 if ( pref.ask_before_delete )
1337 {
1338 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1339 GTK_DIALOG_MODAL,
1340 GTK_MESSAGE_QUESTION,
1341 GTK_BUTTONS_YES_NO,
1342 _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
1343 resp = gtk_dialog_run( (GtkDialog*)dlg );
1344 gtk_widget_destroy( dlg );
1345 }
1d48a247 1346
f7401dea 1347 if( resp == GTK_RESPONSE_YES )
1d48a247 1348 {
fa27691a 1349 const char* name = image_list_get_current( mw->img_list );
794cac1a 1350
fa27691a
HJYP
1351 if( g_unlink( file_path ) != 0 )
1352 main_win_show_error( mw, g_strerror(errno) );
1353 else
1354 {
1355 const char* next_name = image_list_get_next( mw->img_list );
1356 if( ! next_name )
1357 next_name = image_list_get_prev( mw->img_list );
794cac1a 1358
fa27691a
HJYP
1359 if( next_name )
1360 {
1361 char* next_file_path = image_list_get_current_file_path( mw->img_list );
1362 main_win_open( mw, next_file_path, ZOOM_FIT );
1363 g_free( next_file_path );
1364 }
1365
1366 image_list_remove ( mw->img_list, name );
1367
1368 if ( ! next_name )
1c8a8ba7
HJYP
1369 {
1370 main_win_close( mw );
f7401dea 1371 image_list_close( mw->img_list );
1c8a8ba7
HJYP
1372 image_view_set_pixbuf( (ImageView*)mw->img_view, NULL );
1373 gtk_window_set_title( (GtkWindow*) mw, _("Image Viewer"));
1374 }
fa27691a 1375 }
1d48a247 1376 }
fa27691a 1377 g_free( file_path );
1d48a247
HJYP
1378 }
1379}
1380
cf15ca10 1381void show_popup_menu( MainWin* mw, GdkEventButton* evt )
1d48a247 1382{
5bfec971
HJYP
1383 static PtkMenuItemEntry menu_def[] =
1384 {
1385 PTK_IMG_MENU_ITEM( N_( "Previous" ), GTK_STOCK_GO_BACK, on_prev, GDK_leftarrow, 0 ),
1386 PTK_IMG_MENU_ITEM( N_( "Next" ), GTK_STOCK_GO_FORWARD, on_next, GDK_rightarrow, 0 ),
ebee501a 1387 PTK_IMG_MENU_ITEM( N_( "Start/Stop Slideshow" ), GTK_STOCK_MEDIA_PLAY, on_slideshow_menu, GDK_W, 0 ),
5bfec971
HJYP
1388 PTK_SEPARATOR_MENU_ITEM,
1389 PTK_IMG_MENU_ITEM( N_( "Zoom Out" ), GTK_STOCK_ZOOM_OUT, on_zoom_out, GDK_minus, 0 ),
1390 PTK_IMG_MENU_ITEM( N_( "Zoom In" ), GTK_STOCK_ZOOM_IN, on_zoom_in, GDK_plus, 0 ),
1391 PTK_IMG_MENU_ITEM( N_( "Fit Image To Window Size" ), GTK_STOCK_ZOOM_FIT, on_zoom_fit_menu, GDK_F, 0 ),
1392 PTK_IMG_MENU_ITEM( N_( "Original Size" ), GTK_STOCK_ZOOM_100, on_orig_size_menu, GDK_G, 0 ),
1393 PTK_SEPARATOR_MENU_ITEM,
0c8ac162 1394 PTK_IMG_MENU_ITEM( N_( "Full Screen" ), GTK_STOCK_FULLSCREEN, on_full_screen, GDK_F11, 0 ),
5bfec971 1395 PTK_SEPARATOR_MENU_ITEM,
701822a6
HJYP
1396 PTK_IMG_MENU_ITEM( N_( "Rotate Counterclockwise" ), "object-rotate-left", on_rotate_counterclockwise, GDK_L, 0 ),
1397 PTK_IMG_MENU_ITEM( N_( "Rotate Clockwise" ), "object-rotate-right", on_rotate_clockwise, GDK_R, 0 ),
1398 PTK_IMG_MENU_ITEM( N_( "Flip Horizontal" ), "object-flip-horizontal", on_flip_horizontal, GDK_H, 0 ),
1399 PTK_IMG_MENU_ITEM( N_( "Flip Vertical" ), "object-flip-vertical", on_flip_vertical, GDK_V, 0 ),
5bfec971
HJYP
1400 PTK_SEPARATOR_MENU_ITEM,
1401 PTK_IMG_MENU_ITEM( N_("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open), GDK_O, 0 ),
1402 PTK_IMG_MENU_ITEM( N_("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save), GDK_S, 0 ),
1403 PTK_IMG_MENU_ITEM( N_("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
da046c6e 1404// PTK_IMG_MENU_ITEM( N_("Save As Other Size"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
5bfec971
HJYP
1405 PTK_IMG_MENU_ITEM( N_("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete), GDK_Delete, 0 ),
1406 PTK_SEPARATOR_MENU_ITEM,
1c8a8ba7 1407 PTK_IMG_MENU_ITEM( N_("Preferences"), GTK_STOCK_PREFERENCES, G_CALLBACK(on_preference), GDK_P, 0 ),
1c8a8ba7 1408 PTK_STOCK_MENU_ITEM( GTK_STOCK_ABOUT, on_about ),
701822a6
HJYP
1409 PTK_SEPARATOR_MENU_ITEM,
1410 PTK_IMG_MENU_ITEM( N_("Quit"), GTK_STOCK_QUIT, G_CALLBACK(on_quit), GDK_Q, 0 ),
5bfec971
HJYP
1411 PTK_MENU_END
1412 };
07db5c2f
HJYP
1413 GtkWidget* rotate_cw;
1414 GtkWidget* rotate_ccw;
1415 GtkWidget* flip_v;
1416 GtkWidget* flip_h;
1417
1418 menu_def[10].ret = &rotate_ccw;
1419 menu_def[11].ret = &rotate_cw;
1420 menu_def[12].ret = &flip_h;
1421 menu_def[13].ret = &flip_v;
5bfec971 1422
cf15ca10 1423 // mw accel group is useless. It's only used to display accels in popup menu
5bfec971 1424 GtkAccelGroup* accel_group = gtk_accel_group_new();
cf15ca10 1425 GtkMenuShell* popup = (GtkMenuShell*)ptk_menu_new_from_data( menu_def, mw, accel_group );
1d48a247 1426
07db5c2f
HJYP
1427 if( mw->animation )
1428 {
1429 gtk_widget_set_sensitive( rotate_ccw, FALSE );
1430 gtk_widget_set_sensitive( rotate_cw, FALSE );
1431 gtk_widget_set_sensitive( flip_h, FALSE );
1432 gtk_widget_set_sensitive( flip_v, FALSE );
1433 }
1434
1d48a247
HJYP
1435 gtk_widget_show_all( (GtkWidget*)popup );
1436 g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1437 gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
1438}
1439
1c8a8ba7 1440/* callback used to open default browser when URLs got clicked */
7c3b160c 1441static void open_url( GtkAboutDialog *dlg, const gchar *url, gpointer data)
1d48a247 1442{
1c8a8ba7 1443 /* FIXME: is there any better way to do this? */
7e1dcec1 1444 char* programs[] = { "xdg-open", "gnome-open", "exo-open" };
7c3b160c
HJYP
1445 int i;
1446 for( i = 0; i < G_N_ELEMENTS(programs); ++i)
1d48a247 1447 {
d8d44976 1448 gchar* open_cmd = NULL;
7c3b160c 1449 if( (open_cmd = g_find_program_in_path( programs[i] )) )
1d48a247 1450 {
d8d44976 1451 char* argv [3];
ff032ffe 1452 argv [0] = programs[i];
1c8a8ba7
HJYP
1453 argv [1] = (gchar *) url;
1454 argv [2] = NULL;
794cac1a 1455 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
7c3b160c
HJYP
1456 g_free( open_cmd );
1457 break;
1d48a247 1458 }
1c8a8ba7 1459 }
1d48a247
HJYP
1460}
1461
cf15ca10 1462void on_about( GtkWidget* menu, MainWin* mw )
1d48a247
HJYP
1463{
1464 GtkWidget * about_dlg;
1465 const gchar *authors[] =
1466 {
1467 "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
7c3b160c 1468 "Martin Siggel <martinsiggel@googlemail.com>",
da046c6e 1469 "Hialan Liu <hialan.liu@gmail.com>",
d8d44976
HJYP
1470 "Marty Jack <martyj19@comcast.net>",
1471 "Louis Casillas <oxaric@gmail.com>",
e56253ea 1472 "Will Davies",
7c3b160c 1473 _(" * Refer to source code of EOG image viewer and GThumb"),
1d48a247
HJYP
1474 NULL
1475 };
1c8a8ba7 1476 /* TRANSLATORS: Replace this string with your names, one name per line. */
1d48a247
HJYP
1477 gchar *translators = _( "translator-credits" );
1478
7c3b160c
HJYP
1479 gtk_about_dialog_set_url_hook( open_url, mw, NULL);
1480
1d48a247 1481 about_dlg = gtk_about_dialog_new ();
7c3b160c
HJYP
1482
1483 gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 );
1484 gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION );
1485 gtk_about_dialog_set_name ( (GtkAboutDialog*)about_dlg, _( "GPicView" ) );
1486 gtk_about_dialog_set_logo( (GtkAboutDialog*)about_dlg, gdk_pixbuf_new_from_file( PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL ) );
701822a6 1487 gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2007 - 2009" ) );
7c3b160c 1488 gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Lightweight image viewer from LXDE project" ) );
1c8a8ba7 1489 gtk_about_dialog_set_license ( (GtkAboutDialog*)about_dlg, "GPicView\n\nCopyright (C) 2007 Hong Jen Yee (PCMan)\n\nThis 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\nThis 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 this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." );
d8d44976 1490 gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://wiki.lxde.org/en/GPicView" );
7c3b160c
HJYP
1491 gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors );
1492 gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators );
1493 gtk_window_set_transient_for( (GtkWindow*) about_dlg, GTK_WINDOW( mw ) );
1494
1495 gtk_dialog_run( ( GtkDialog*)about_dlg );
1d48a247
HJYP
1496 gtk_widget_destroy( about_dlg );
1497}
1498
cf15ca10
HJYP
1499void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1500 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw )
1d48a247
HJYP
1501{
1502 if( ! data || data->length <= 0)
1503 return;
1504
1d48a247
HJYP
1505 char* file = NULL;
1506 if( info == 0 ) // text/uri-list
1507 {
1508 char** uris = gtk_selection_data_get_uris( data );
1509 if( uris )
1510 {
1511 file = g_filename_from_uri(*uris, NULL, NULL);
1512 g_strfreev( uris );
1513 }
1514 }
1515 else if( info == 1 ) // text/plain
1516 {
1517 file = (char*)gtk_selection_data_get_text( data );
1518 }
1519 if( file )
1520 {
cf15ca10 1521 main_win_open( mw, file, ZOOM_FIT );
1d48a247
HJYP
1522 g_free( file );
1523 }
1524}