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