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