Fix minor warnings
[lxde/gpicview.git] / src / main-win.c
CommitLineData
1d48a247 1/***************************************************************************
827117ce
LC
2 * Copyright (C) 2007, 2008 by PCMan (Hong Jen Yee) *
3 * pcman.tw@gmail.com *
1d48a247 4 * *
827117ce 5 * mw program is free software; you can redistribute it and/or modify *
1d48a247
HJYP
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
827117ce 10 * mw program is distributed in the hope that it will be useful, *
1d48a247
HJYP
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
827117ce 16 * along with mw program; if not, write to the *
1d48a247
HJYP
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
cf15ca10 25#include "main-win.h"
bb0f721e 26
1d48a247
HJYP
27#include <glib/gi18n.h>
28#include <glib/gstdio.h>
29#include <gdk/gdkkeysyms.h>
30
0ed1aa41 31#include <unistd.h>
1d48a247
HJYP
32#include <string.h>
33#include <errno.h>
d2d3c393 34#include <stdlib.h>
1d48a247 35
bb0f721e
HJYP
36#include "pref.h"
37
9c52ae58 38#include "image-view.h"
0c0c8179 39#include "image-list.h"
1d48a247 40#include "working-area.h"
cf15ca10 41#include "ptk-menu.h"
7c3b160c 42#include "jpeg-tran.h"
1d48a247
HJYP
43
44// For drag & drop
cf15ca10 45static GtkTargetEntry drop_targets[] =
1d48a247
HJYP
46{
47 {"text/uri-list", 0, 0},
cf15ca10 48 {"text/plain", 0, 1}
1d48a247
HJYP
49};
50
cf15ca10
HJYP
51static void main_win_init( MainWin*mw );
52static void main_win_finalize( GObject* obj );
53
54static void create_nav_bar( MainWin* mw, GtkWidget* box);
55GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle);
56// GtkWidget* add_menu_item( GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, gboolean toggle=FALSE );
da046c6e 57static void rotate_image( MainWin* mw, int angle );
cf15ca10
HJYP
58static void show_popup_menu( MainWin* mw, GdkEventButton* evt );
59
60/* signal handlers */
61static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
62static void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation );
86ebaeb3 63static gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state );
cf15ca10
HJYP
64static void on_zoom_fit( GtkToggleButton* btn, MainWin* mw );
65static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw );
66static void on_full_screen( GtkWidget* btn, MainWin* mw );
67static void on_next( GtkWidget* btn, MainWin* mw );
68static void on_orig_size( GtkToggleButton* btn, MainWin* mw );
69static void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw );
70static void on_prev( GtkWidget* btn, MainWin* mw );
71static void on_rotate_clockwise( GtkWidget* btn, MainWin* mw );
72static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw );
73static void on_save_as( GtkWidget* btn, MainWin* mw );
74static void on_save( GtkWidget* btn, MainWin* mw );
75static void on_open( GtkWidget* btn, MainWin* mw );
76static void on_zoom_in( GtkWidget* btn, MainWin* mw );
77static void on_zoom_out( GtkWidget* btn, MainWin* mw );
827117ce 78static void on_tools( GtkWidget* btn, MainWin* mw );
cf15ca10
HJYP
79static void on_preference( GtkWidget* btn, MainWin* mw );
80static void on_quit( GtkWidget* btn, MainWin* mw );
81static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
82static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
83static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw );
84static gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw );
85static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
86static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
827117ce
LC
87 int x, int y, GtkSelectionData* data, guint info,
88 guint time, MainWin* mw );
cf15ca10
HJYP
89static void on_delete( GtkWidget* btn, MainWin* mw );
90static void on_about( GtkWidget* menu, MainWin* mw );
1d48a247 91
da046c6e 92static GdkPixbuf* RotateByEXIF(const char* FileName, GdkPixbuf* pix);
93static void updateTitle(const char *filename, MainWin *mw );
94
95void on_flip_vertical( GtkWidget* btn, MainWin* mw );
96void on_flip_horizontal( GtkWidget* btn, MainWin* mw );
97static int trans_angle_to_id(int i);
98static int get_new_angle( int orig_angle, int rotate_angle );
1d48a247 99
cf15ca10 100// Begin of GObject-related stuff
1d48a247 101
cf15ca10 102G_DEFINE_TYPE( MainWin, main_win, GTK_TYPE_WINDOW )
1d48a247 103
cf15ca10
HJYP
104void main_win_class_init( MainWinClass* klass )
105{
1d48a247
HJYP
106 GObjectClass * obj_class;
107 GtkWidgetClass *widget_class;
108
109 obj_class = ( GObjectClass * ) klass;
110// obj_class->set_property = _set_property;
111// obj_class->get_property = _get_property;
cf15ca10 112 obj_class->finalize = main_win_finalize;
1d48a247
HJYP
113
114 widget_class = GTK_WIDGET_CLASS ( klass );
115 widget_class->delete_event = on_delete_event;
116 widget_class->size_allocate = on_size_allocate;
117 widget_class->key_press_event = on_key_press_event;
e216a767 118 widget_class->window_state_event = on_win_state_event;
1d48a247
HJYP
119}
120
cf15ca10
HJYP
121void main_win_finalize( GObject* obj )
122{
123 MainWin *mw = (MainWin*)obj;
124
125 main_win_close(mw);
126
127 if( G_LIKELY(mw->img_list) )
128 image_list_free( mw->img_list );
129 gdk_cursor_unref( mw->hand_cursor );
130
131 g_object_unref( mw->tooltips );
132
827117ce 133 g_main_loop_quit( mw->loop );
cf15ca10
HJYP
134}
135
136GtkWidget* main_win_new()
1d48a247 137{
cf15ca10 138 return (GtkWidget*)g_object_new ( MAIN_WIN_TYPE, NULL );
1d48a247
HJYP
139}
140
141// End of GObject-related stuff
142
cf15ca10 143void main_win_init( MainWin*mw )
1d48a247 144{
827117ce 145 gtk_window_set_title( (GtkWindow*)mw, _("GPicView - Simple Image Viewer"));
cf15ca10
HJYP
146 gtk_window_set_icon_from_file( (GtkWindow*)mw, PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL );
147 gtk_window_set_default_size( (GtkWindow*)mw, 640, 480 );
1d48a247 148
827117ce
LC
149// mw->img_buffer = gdk_pixbuf_loader_new();
150
1d48a247 151 GtkWidget* box = gtk_vbox_new( FALSE, 0 );
cf15ca10 152 gtk_container_add( (GtkContainer*)mw, box);
1d48a247
HJYP
153
154 // image area
cf15ca10
HJYP
155 mw->evt_box = gtk_event_box_new();
156 GTK_WIDGET_SET_FLAGS( mw->evt_box, GTK_CAN_FOCUS );
157 gtk_widget_add_events( mw->evt_box,
1d48a247
HJYP
158 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
159 GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
cf15ca10
HJYP
160 g_signal_connect( mw->evt_box, "button-press-event", G_CALLBACK(on_button_press), mw );
161 g_signal_connect( mw->evt_box, "button-release-event", G_CALLBACK(on_button_release), mw );
162 g_signal_connect( mw->evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), mw );
163 g_signal_connect( mw->evt_box, "scroll-event", G_CALLBACK(on_scroll_event), mw );
827117ce
LC
164
165 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.background_color );
1d48a247 166
cf15ca10
HJYP
167 mw->img_view = image_view_new();
168 gtk_container_add( (GtkContainer*)mw->evt_box, (GtkWidget*)mw->img_view);
1d48a247 169
2c34ffe0
HJYP
170 const char scroll_style[]=
171 "style \"gpicview-scroll\" {"
172 "GtkScrolledWindow::scrollbar-spacing=0"
173 "}"
174 "class \"GtkScrolledWindow\" style \"gpicview-scroll\"";
175 gtk_rc_parse_string( scroll_style );
cf15ca10
HJYP
176 mw->scroll = gtk_scrolled_window_new( NULL, NULL );
177 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)mw->scroll, GTK_SHADOW_NONE );
178 gtk_scrolled_window_set_policy((GtkScrolledWindow*)mw->scroll,
1d48a247
HJYP
179 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
180 GtkAdjustment *hadj, *vadj;
cf15ca10 181 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
182 hadj->page_increment = 10;
183 gtk_adjustment_changed(hadj);
cf15ca10 184 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
185 vadj->page_increment = 10;
186 gtk_adjustment_changed(vadj);
9c52ae58 187
d2d3c393 188 image_view_set_adjustments( IMAGE_VIEW(mw->img_view), hadj, vadj ); // dirty hack :-(
cf15ca10
HJYP
189 gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)mw->scroll, mw->evt_box );
190 GtkWidget* viewport = gtk_bin_get_child( (GtkBin*)mw->scroll );
2c34ffe0
HJYP
191 gtk_viewport_set_shadow_type( (GtkViewport*)viewport, GTK_SHADOW_NONE );
192 gtk_container_set_border_width( (GtkContainer*)viewport, 0 );
1d48a247 193
cf15ca10 194 gtk_box_pack_start( (GtkBox*)box, mw->scroll, TRUE, TRUE, 0 );
1d48a247
HJYP
195
196 // build toolbar
cf15ca10 197 mw->tooltips = gtk_tooltips_new();
d9f34bf9 198#if GTK_CHECK_VERSION(2, 8, 0)
cf15ca10 199 g_object_ref_sink(mw->tooltips);
1d48a247 200#else
cf15ca10 201 gtk_object_sink((GtkObject*)mw->tooltips);
1d48a247 202#endif
1d48a247 203
cf15ca10 204 create_nav_bar( mw, box );
1d48a247
HJYP
205 gtk_widget_show_all( box );
206
cf15ca10 207 mw->hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)mw), GDK_FLEUR );
1d48a247 208
5bfec971 209// zoom_mode = ZOOM_NONE;
cf15ca10 210 mw->zoom_mode = ZOOM_FIT;
1d48a247
HJYP
211
212 // Set up drag & drop
cf15ca10
HJYP
213 gtk_drag_dest_set( (GtkWidget*)mw, GTK_DEST_DEFAULT_ALL,
214 drop_targets,
215 G_N_ELEMENTS(drop_targets),
216 GDK_ACTION_COPY | GDK_ACTION_ASK );
217 g_signal_connect( mw, "drag-data-received", G_CALLBACK(on_drag_data_received), mw );
9c52ae58 218
cf15ca10 219 mw->img_list = image_list_new();
7c3b160c 220
7c3b160c
HJYP
221 // rotation angle is zero on startup
222 mw->rotation_angle = 0;
1d48a247
HJYP
223}
224
cf15ca10 225void create_nav_bar( MainWin* mw, GtkWidget* box )
1d48a247 226{
cf15ca10 227 mw->nav_bar = gtk_hbox_new( FALSE, 0 );
1d48a247 228
cf15ca10
HJYP
229 add_nav_btn( mw, GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev), FALSE );
230 add_nav_btn( mw, GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next), FALSE );
1d48a247 231
cf15ca10 232 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
cf15ca10
HJYP
233 add_nav_btn( mw, GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out), FALSE );
234 add_nav_btn( mw, GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in), FALSE );
5bfec971
HJYP
235
236// percent = gtk_entry_new(); // show scale (in percentage)
cf15ca10 237// g_signal_connect( percent, "activate", G_CALLBACK(on_percentage), mw );
5bfec971
HJYP
238// gtk_widget_set_size_request( percent, 45, -1 );
239// gtk_box_pack_start( (GtkBox*)nav_bar, percent, FALSE, FALSE, 2 );
240
cf15ca10
HJYP
241 mw->btn_fit = add_nav_btn( mw, GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
242 G_CALLBACK(on_zoom_fit), TRUE );
243 mw->btn_orig = add_nav_btn( mw, GTK_STOCK_ZOOM_100, _("Original Size"),
244 G_CALLBACK(on_orig_size), TRUE );
245 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
5bfec971 246
1d48a247
HJYP
247#ifndef GTK_STOCK_FULLSCREEN
248#define GTK_STOCK_FULLSCREEN "gtk-fullscreen"
249#endif
827117ce 250 add_nav_btn( mw, GTK_STOCK_FULLSCREEN, _(" Full Screen"), G_CALLBACK(on_full_screen), FALSE ); // gtk+ 2.8+
1d48a247 251
cf15ca10 252 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 253
cf15ca10
HJYP
254 add_nav_btn( mw, "gtk-counterclockwise", _("Rotate Counterclockwise"),
255 G_CALLBACK(on_rotate_counterclockwise), FALSE );
256 add_nav_btn( mw, "gtk-clockwise", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise), FALSE );
827117ce
LC
257 add_nav_btn( mw, "gtk-horizontal", _("Flip Horizontal"), G_CALLBACK(on_flip_horizontal), FALSE );
258 add_nav_btn( mw, "gtk-vertical", _("Flip Vertical"), G_CALLBACK(on_flip_vertical), FALSE );
1d48a247 259
cf15ca10 260 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 261
cf15ca10
HJYP
262 add_nav_btn( mw, GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open), FALSE );
263 add_nav_btn( mw, GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save), FALSE );
264 add_nav_btn( mw, GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as), FALSE );
265 add_nav_btn( mw, GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete), FALSE );
1d48a247 266
cf15ca10 267 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
827117ce
LC
268
269// add_nav_btn( mw, "gtk-tools", _("Image Tools"), G_CALLBACK(on_tools), FALSE );
270
271// gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
272
bb0f721e 273 add_nav_btn( mw, GTK_STOCK_PREFERENCES, _("Preferences"), G_CALLBACK(on_preference), FALSE );
827117ce 274 add_nav_btn( mw, GTK_STOCK_ABOUT, _("About"), G_CALLBACK(on_about), FALSE );
1d48a247
HJYP
275
276 GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
cf15ca10 277 gtk_container_add( (GtkContainer*)align, mw->nav_bar );
1d48a247
HJYP
278 gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
279}
280
cf15ca10 281gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt )
1d48a247
HJYP
282{
283 gtk_widget_destroy( widget );
284 return TRUE;
285}
286
da046c6e 287static GdkPixbuf* RotateByEXIF(const char* FileName, GdkPixbuf* pix)
288{
289 GdkPixbuf* tmppixbuf = pix;
290#if GTK_CHECK_VERSION( 2, 12, 0 )
291 // apply orientation provided by EXIF (Use gtk+ 2.12 specific API)
292 tmppixbuf = gdk_pixbuf_apply_embedded_orientation(pix);
293 g_object_unref( pix );
294#else
295 // use jhead functions
296 ResetJpgfile();
297
298 // Start with an empty image information structure.
299 memset(&ImageInfo, 0, sizeof(ImageInfo));
300
301 if (!ReadJpegFile( FileName, READ_METADATA)) return;
302
303 // Do Rotate
304 switch(ImageInfo.Orientation)
305 {
306 case 0: // Undefined
307 case 1: // Normal
308 break;
309 case 2: // flip horizontal: left right reversed mirror
310 tmppixbuf = gdk_pixbuf_flip(pix, TRUE);
311 g_object_unref( pix );
312 break;
313 case 3: // rotate 180
314 tmppixbuf = gdk_pixbuf_rotate_simple(pix, 180);
315 g_object_unref( pix );
316 break;
317 case 4: // flip vertical: upside down mirror
318 tmppixbuf = gdk_pixbuf_flip(pix, FALSE);
319 g_object_unref( pix );
320 break;
321 case 5: // transpose: Flipped about top-left <--> bottom-right axis.
322 tmppixbuf = gtk_pixbuf_flip(pix, FALSE);
323 g_object_unref( pix );
324 pix = tmppixbuf;
325 tmppixbuf = gtk_pixbuf_rotate_simple(pix, 270);
326 g_object_unref( pix );
327 break;
328 case 6: // rotate 90: rotate 90 cw to right it.
329 tmppixbuf = gdk_pixbuf_rotate_simple(pix, 270);
330 g_object_unref( pix );
331 break;
332 case 7: // transverse: flipped about top-right <--> bottom-left axis
333 tmppixbuf = gtk_pixbuf_flip(pix, FALSE);
334 g_object_unref( pix );
335 pix = tmppixbuf;
336 tmppixbuf = gtk_pixbuf_rotate_simple(pix, 90);
337 g_object_unref( pix );
338 break;
339 case 8: // rotate 270: rotate 270 to right it.
340 tmppixbuf = gdk_pixbuf_rotate_simple(pix, 90);
341 g_object_unref( pix );
342 break;
343 default:
344 break;
345 }
346
347 DiscardData();
348#endif
349
350 return tmppixbuf;
351}
352
353static void updateTitle(const char *filename, MainWin *mw )
354{
355 static char fname[50];
356 static int wid, hei;
357
358 char buf[100];
359
360 if(filename != NULL)
361 {
362 strncpy(fname, filename, 49);
363 fname[49] = '\0';
364
365 wid = gdk_pixbuf_get_width( mw->pix );
366 hei = gdk_pixbuf_get_height( mw->pix );
367 }
368
369 snprintf(buf, 100, "%s (%dx%d) %d%%", fname, wid, hei, (int)(mw->scale * 100));
370 gtk_window_set_title( (GtkWindow*)mw, buf );
371
372 return;
373}
374
827117ce
LC
375gboolean on_idle( MainWin* mw )
376{
377 // make sure the main loop is running before displaying the animation
378 if ( ( mw->loop != NULL ) && (g_main_loop_is_running( mw->loop ) ) )
379 {
380 if ( mw->is_animation )
381 {
382 if ( gdk_pixbuf_animation_iter_advance( mw->animation_iter, NULL ) )
383 {
384 //g_object_unref( mw->pix ); // free the memory from the last frame
385 mw->pix = gdk_pixbuf_animation_iter_get_pixbuf( mw->animation_iter );
386 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
387 }
388 }
389 else
390 { // returning FALSE stops the g_timeout_add that calls this function
391 return FALSE;
392 }
393 }
394
395 return TRUE;
396}
397
cf15ca10 398gboolean main_win_open( MainWin* mw, const char* file_path, ZoomMode zoom )
1d48a247 399{
1d48a247 400 GError* err = NULL;
da046c6e 401 GdkPixbufFormat* info;
827117ce 402
da046c6e 403 info = gdk_pixbuf_get_file_info( file_path, NULL, NULL );
404 char* type = gdk_pixbuf_format_get_name( info );
405
827117ce
LC
406 // setup so that if the user saves an image as a new file then the image list will be freed
407 // and a new one will be created for the directory the file was saved in
408 if ( mw->img_list != NULL )
409 {
410 image_list_free( mw->img_list );
411 mw->img_list = image_list_new();
412 }
413
cf15ca10 414 main_win_close( mw );
827117ce
LC
415
416 // grabs a file as if it were an animation
417 mw->animation = gdk_pixbuf_animation_new_from_file( file_path, NULL );
418
419 // tests if the file is actually just a normal picture
420 if ( gdk_pixbuf_animation_is_static_image( mw->animation ) )
421 {
422 mw->pix = gdk_pixbuf_animation_get_static_image( mw->animation );
423 mw->animation = NULL;
424
425 mw->is_animation = FALSE;
426 }
427 else
428 { // we found an animation
429 mw->animation_iter = gdk_pixbuf_animation_get_iter( mw->animation, NULL );
430 mw->pix = gdk_pixbuf_animation_iter_get_pixbuf( mw->animation_iter );
431 mw->is_animation = TRUE;
432
433 g_timeout_add( 30, on_idle, mw ); // set for 30 fps
434 }
435
436 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
c215db47 437
cf15ca10 438 if( ! mw->pix )
1d48a247 439 {
cf15ca10 440 main_win_show_error( mw, err->message );
827117ce 441
cf15ca10 442 return FALSE;
1d48a247 443 }
da046c6e 444 else if(!strcmp(type,"jpeg"))
c215db47 445 {
da046c6e 446 // Only jpeg should rotate by EXIF
447 mw->pix = RotateByEXIF( file_path, mw->pix);
c215db47 448 }
da046c6e 449
cf15ca10 450 mw->zoom_mode = zoom;
da8bd946 451
5bfec971 452 // select most suitable viewing mode
d8ce3af1 453 if( zoom == ZOOM_NONE )
1d48a247 454 {
cf15ca10
HJYP
455 int w = gdk_pixbuf_get_width( mw->pix );
456 int h = gdk_pixbuf_get_height( mw->pix );
1d48a247 457
1d48a247 458 GdkRectangle area;
cf15ca10 459 get_working_area( gtk_widget_get_screen((GtkWidget*)mw), &area );
c325c989
HJYP
460 // g_debug("determine best zoom mode: orig size: w=%d, h=%d", w, h);
461 // FIXME: actually this is a little buggy :-(
da8bd946 462 if( w < area.width && h < area.height && (w >= 640 || h >= 480) )
1d48a247 463 {
cf15ca10
HJYP
464 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER );
465 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, w, h );
5bfec971 466 GtkRequisition req;
cf15ca10 467 gtk_widget_size_request ( (GtkWidget*)mw, &req );
9c52ae58
HJYP
468 if( req.width < 640 ) req.width = 640;
469 if( req.height < 480 ) req.height = 480;
cf15ca10
HJYP
470 gtk_window_resize( (GtkWindow*)mw, req.width, req.height );
471 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, -1, -1 );
472 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
473 mw->zoom_mode = ZOOM_ORIG;
da046c6e 474 mw->scale = 1.0;
1d48a247
HJYP
475 }
476 else
827117ce 477 {
cf15ca10 478 mw->zoom_mode = ZOOM_FIT;
827117ce 479 }
5bfec971 480 }
5bfec971 481
cf15ca10 482 if( mw->zoom_mode == ZOOM_FIT )
d8ce3af1 483 {
cf15ca10 484 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
c325c989 485 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
d8ce3af1 486 }
cf15ca10 487 else if( mw->zoom_mode == ZOOM_SCALE ) // scale
d8ce3af1 488 {
cf15ca10
HJYP
489 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
490 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
491 main_win_scale_image( mw, mw->scale, GDK_INTERP_BILINEAR );
d8ce3af1 492 }
cf15ca10 493 else if( mw->zoom_mode == ZOOM_ORIG ) // original size
5bfec971 494 {
cf15ca10 495 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
da046c6e 496 image_view_set_scale( (ImageView*)mw->img_view, mw->scale, GDK_INTERP_BILINEAR );
cf15ca10 497 main_win_center_image( mw );
1d48a247
HJYP
498 }
499
cf15ca10 500 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
9c52ae58 501
1d48a247
HJYP
502// while (gtk_events_pending ())
503// gtk_main_iteration ();
504
505 // build file list
506 char* dir_path = g_path_get_dirname( file_path );
cf15ca10 507 image_list_open_dir( mw->img_list, dir_path, NULL );
f214531d 508 image_list_sort_by_name( mw->img_list, GTK_SORT_DESCENDING );
1d48a247 509 g_free( dir_path );
76e88fcf 510
1d48a247 511 char* base_name = g_path_get_basename( file_path );
827117ce 512
cf15ca10 513 image_list_set_current( mw->img_list, base_name );
76e88fcf
HJYP
514
515 char* disp_name = g_filename_display_name( base_name );
1d48a247
HJYP
516 g_free( base_name );
517
da046c6e 518 updateTitle( disp_name, mw );
76e88fcf 519 g_free( disp_name );
1d48a247 520
cf15ca10 521 return TRUE;
1d48a247
HJYP
522}
523
cf15ca10 524void main_win_close( MainWin* mw )
1d48a247 525{
cf15ca10 526 if( mw->pix )
9c52ae58 527 {
cf15ca10
HJYP
528 g_object_unref( mw->pix );
529 mw->pix = NULL;
1d48a247
HJYP
530 }
531}
532
cf15ca10 533void main_win_show_error( MainWin* mw, const char* message )
1d48a247 534{
cf15ca10 535 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
536 GTK_DIALOG_MODAL,
537 GTK_MESSAGE_ERROR,
538 GTK_BUTTONS_OK,
a574ce8f 539 "%s", message );
1d48a247
HJYP
540 gtk_dialog_run( (GtkDialog*)dlg );
541 gtk_widget_destroy( dlg );
542}
543
cf15ca10 544void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation )
1d48a247 545{
cf15ca10 546 GTK_WIDGET_CLASS(main_win_parent_class)->size_allocate( widget, allocation );
52c867e9 547 if( GTK_WIDGET_REALIZED (widget) )
1d48a247 548 {
cf15ca10 549 MainWin* mw = (MainWin*)widget;
9c52ae58 550
cf15ca10 551 if( mw->zoom_mode == ZOOM_FIT )
52c867e9
HJYP
552 {
553 while(gtk_events_pending ())
554 gtk_main_iteration(); // makes it more fluid
1d48a247 555
c325c989 556 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
52c867e9 557 }
1d48a247
HJYP
558 }
559}
560
86ebaeb3 561gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state )
e216a767
HJYP
562{
563 MainWin* mw = (MainWin*)widget;
827117ce
LC
564
565 if ( state->new_window_state == GDK_WINDOW_STATE_MAXIMIZED )
e216a767 566 {
827117ce 567 pref.open_maximized = TRUE;
e216a767 568 }
827117ce 569 else if ( !mw->full_screen && state->new_window_state == 0 ) // if new_window_state == 0 then window is not maximized
e216a767 570 {
827117ce 571 pref.open_maximized = FALSE;
e216a767 572 }
827117ce 573
86ebaeb3 574 return TRUE;
e216a767
HJYP
575}
576
cf15ca10 577void main_win_fit_size( MainWin* mw, int width, int height, gboolean can_strech, GdkInterpType type )
1d48a247 578{
cf15ca10 579 if( ! mw->pix )
1d48a247
HJYP
580 return;
581
cf15ca10
HJYP
582 int orig_w = gdk_pixbuf_get_width( mw->pix );
583 int orig_h = gdk_pixbuf_get_height( mw->pix );
1d48a247 584
da8bd946
HJYP
585 if( can_strech || (orig_w > width || orig_h > height) )
586 {
cf15ca10
HJYP
587 gdouble xscale = ((gdouble)width) / orig_w;
588 gdouble yscale = ((gdouble)height)/ orig_h;
589 gdouble final_scale = xscale < yscale ? xscale : yscale;
1d48a247 590
cf15ca10 591 main_win_scale_image( mw, final_scale, type );
da8bd946
HJYP
592 }
593 else // use original size if the image is smaller than the window
594 {
cf15ca10
HJYP
595 mw->scale = 1.0;
596 image_view_set_scale( (ImageView*)mw->img_view, 1.0, type );
da046c6e 597
598 updateTitle(NULL, mw);
da8bd946 599 }
1d48a247
HJYP
600}
601
cf15ca10 602void main_win_fit_window_size( MainWin* mw, gboolean can_strech, GdkInterpType type )
1d48a247 603{
cf15ca10 604 mw->zoom_mode = ZOOM_FIT;
94fcf707 605
cf15ca10 606 if( mw->pix == NULL )
da8bd946 607 return;
94fcf707 608
cf15ca10 609 main_win_fit_size( mw, mw->scroll->allocation.width, mw->scroll->allocation.height, can_strech, type );
1d48a247
HJYP
610}
611
cf15ca10 612GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle )
1d48a247
HJYP
613{
614 GtkWidget* img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
615 GtkWidget* btn;
616 if( G_UNLIKELY(toggle) )
617 {
618 btn = gtk_toggle_button_new();
cf15ca10 619 g_signal_connect( btn, "toggled", cb, mw );
1d48a247
HJYP
620 }
621 else
622 {
623 btn = gtk_button_new();
cf15ca10 624 g_signal_connect( btn, "clicked", cb, mw );
1d48a247
HJYP
625 }
626 gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
627 gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
628 gtk_container_add( (GtkContainer*)btn, img );
cf15ca10
HJYP
629 gtk_tooltips_set_tip( mw->tooltips, btn, tip, NULL );
630 gtk_box_pack_start( (GtkBox*)mw->nav_bar, btn, FALSE, FALSE, 0 );
1d48a247
HJYP
631 return btn;
632}
633
cf15ca10 634void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw )
1d48a247 635{
cf15ca10 636 gtk_button_clicked( (GtkButton*)mw->btn_fit );
1d48a247
HJYP
637}
638
827117ce
LC
639void on_tools( GtkWidget* btn, MainWin* mw )
640{
641}
642
cf15ca10 643void on_zoom_fit( GtkToggleButton* btn, MainWin* mw )
1d48a247
HJYP
644{
645 if( ! btn->active )
646 {
cf15ca10 647 if( mw->zoom_mode == ZOOM_FIT )
1d48a247
HJYP
648 gtk_toggle_button_set_active( btn, TRUE );
649 return;
650 }
cf15ca10 651 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1d48a247 652
c325c989 653 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
654}
655
cf15ca10 656void on_full_screen( GtkWidget* btn, MainWin* mw )
1d48a247 657{
827117ce 658
cf15ca10 659 if( ! mw->full_screen )
827117ce 660 {
cf15ca10 661 gtk_window_fullscreen( (GtkWindow*)mw );
827117ce
LC
662 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.background_color );
663 gtk_widget_hide( gtk_widget_get_parent(mw->nav_bar) );
664 mw->full_screen = TRUE;
665 }
1d48a247 666 else
827117ce 667 {
cf15ca10 668 gtk_window_unfullscreen( (GtkWindow*)mw );
827117ce
LC
669 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.background_color );
670 gtk_widget_show( gtk_widget_get_parent(mw->nav_bar) );
671 mw->full_screen = FALSE;
672 }
1d48a247
HJYP
673}
674
cf15ca10 675void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw )
1d48a247 676{
cf15ca10 677 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
678}
679
cf15ca10 680void on_orig_size( GtkToggleButton* btn, MainWin* mw )
1d48a247 681{
cf15ca10 682 // this callback could be called from activate signal of menu item.
1d48a247
HJYP
683 if( GTK_IS_MENU_ITEM(btn) )
684 {
cf15ca10 685 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
686 return;
687 }
688
689 if( ! btn->active )
690 {
cf15ca10 691 if( mw->zoom_mode == ZOOM_ORIG )
1d48a247
HJYP
692 gtk_toggle_button_set_active( btn, TRUE );
693 return;
694 }
cf15ca10
HJYP
695 mw->zoom_mode = ZOOM_ORIG;
696 mw->scale = 1.0;
697// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 698// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 699
827117ce
LC
700 // update scale
701 updateTitle(NULL, mw);
702
cf15ca10 703 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1d48a247 704
cf15ca10 705 if( ! mw->pix )
1d48a247
HJYP
706 return;
707
cf15ca10 708 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
1d48a247 709
9c52ae58
HJYP
710 while (gtk_events_pending ())
711 gtk_main_iteration ();
712
cf15ca10 713 main_win_center_image( mw ); // FIXME: mw doesn't work well. Why?
1d48a247
HJYP
714}
715
f34b3b31 716void on_prev( GtkWidget* btn, MainWin* mw )
1d48a247 717{
cf15ca10
HJYP
718 const char* name;
719 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
720 return;
721
cf15ca10 722 name = image_list_get_prev( mw->img_list );
1d48a247 723
cf15ca10 724 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
725 {
726 // FIXME: need to ask user first?
cf15ca10 727 name = image_list_get_last( mw->img_list );
1d48a247
HJYP
728 }
729
730 if( name )
731 {
cf15ca10
HJYP
732 char* file_path = image_list_get_current_file_path( mw->img_list );
733 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
734 g_free( file_path );
735 }
736}
737
f34b3b31 738void on_next( GtkWidget* btn, MainWin* mw )
1d48a247 739{
cf15ca10 740 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
741 return;
742
cf15ca10 743 const char* name = image_list_get_next( mw->img_list );
1d48a247 744
cf15ca10 745 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
746 {
747 // FIXME: need to ask user first?
cf15ca10 748 name = image_list_get_first( mw->img_list );
1d48a247
HJYP
749 }
750
751 if( name )
752 {
cf15ca10
HJYP
753 char* file_path = image_list_get_current_file_path( mw->img_list );
754 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
755 g_free( file_path );
756 }
757}
758
da046c6e 759//////////////////// rotate & flip
760
761static int trans_angle_to_id(int i)
762{
763 if(i == 0) return 1;
764 else if(i == 90) return 6;
765 else if(i == 180) return 3;
766 else if(i == 270) return 8;
767 else if(i == -45) return 7;
768 else if(i == -90) return 2;
769 else if(i == -135) return 5;
770 else if(i == -180) return 4;
771}
772
773static int get_new_angle( int orig_angle, int rotate_angle )
774{
775 // defined in exif.c
776 extern int ExifRotateFlipMapping[9][9];
777 static int angle_trans_back[] = {0, 0, -90, 180, -180, -135, 90, -45, 270};
778
779 orig_angle = trans_angle_to_id(orig_angle);
780 rotate_angle = trans_angle_to_id(rotate_angle);
781
782 return angle_trans_back[ ExifRotateFlipMapping[orig_angle][rotate_angle] ];
783}
784
aab0b120 785void on_rotate_clockwise( GtkWidget* btn, MainWin* mw )
786{
827117ce
LC
787 if ( ! mw->is_animation )
788 {
789
790 rotate_image( mw, GDK_PIXBUF_ROTATE_CLOCKWISE );
791 mw->rotation_angle = get_new_angle(mw->rotation_angle, 90);
792
793 if ( pref.auto_save_rotated )
794 {
795 gboolean current_ask_before_save_preference = pref.ask_before_save;
796
797 pref.ask_before_save = FALSE;
798 on_save(btn,mw);
799 pref.ask_before_save = current_ask_before_save_preference;
800 }
801 }
aab0b120 802}
803
cf15ca10 804void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw )
1d48a247 805{
827117ce
LC
806 if ( ! mw->is_animation )
807 {
808
809 rotate_image( mw, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
810 mw->rotation_angle = get_new_angle(mw->rotation_angle, 270);
811
812 if ( pref.auto_save_rotated )
813 {
814 gboolean current_ask_before_save_preference = pref.ask_before_save;
815
816 pref.ask_before_save = FALSE;
817 on_save(btn,mw);
818 pref.ask_before_save = current_ask_before_save_preference;
819 }
820 }
da046c6e 821}
822
823void on_flip_vertical( GtkWidget* btn, MainWin* mw )
824{
827117ce
LC
825 if ( ! mw->is_animation )
826 {
827
828 rotate_image( mw, -180 );
829 mw->rotation_angle = get_new_angle(mw->rotation_angle, -180);
830
831 if ( pref.auto_save_rotated )
832 {
833 gboolean current_ask_before_save_preference = pref.ask_before_save;
834
835 pref.ask_before_save = FALSE;
836 on_save(btn,mw);
837 pref.ask_before_save = current_ask_before_save_preference;
838 }
839 }
da046c6e 840}
841
842void on_flip_horizontal( GtkWidget* btn, MainWin* mw )
843{
827117ce
LC
844 if ( ! mw->is_animation )
845 {
846 rotate_image( mw, -90 );
847 mw->rotation_angle = get_new_angle(mw->rotation_angle, -90);
848
849 if( pref.auto_save_rotated )
850 {
851 gboolean current_ask_before_save_preference = pref.ask_before_save;
852
853 pref.ask_before_save = FALSE;
854 on_save(btn,mw);
855 pref.ask_before_save = current_ask_before_save_preference;
856 }
857 }
1d48a247
HJYP
858}
859
da046c6e 860///////////////////// end of rotate & flip
861
1d48a247
HJYP
862static void on_update_preview( GtkFileChooser *chooser, GtkImage* img )
863{
864 char* file = gtk_file_chooser_get_preview_filename( chooser );
865 GdkPixbuf* pix = NULL;
866 if( file )
867 {
868 pix = gdk_pixbuf_new_from_file_at_scale( file, 128, 128, TRUE, NULL );
869 g_free( file );
870 }
1d48a247 871 if( pix )
9c52ae58
HJYP
872 {
873 gtk_image_set_from_pixbuf( img, pix );
874 g_object_unref( pix );
875 }
1d48a247
HJYP
876}
877
cf15ca10 878void on_save_as( GtkWidget* btn, MainWin* mw )
1d48a247 879{
cf15ca10 880 if( ! mw->pix )
827117ce 881 {
1d48a247 882 return;
827117ce
LC
883 }
884
885 gint compression_level = -1;
886 char* original_file_name = g_build_filename( image_list_get_dir( mw->img_list ),
887 image_list_get_current( mw->img_list ), NULL );
888 GdkPixbufFormat* info;
889 info = gdk_pixbuf_get_file_info( original_file_name, NULL, NULL );
890 char* original_file_type = gdk_pixbuf_format_get_name( info );
1d48a247 891
8ff83a68
YCLP
892 GtkWidget *file_save_as_dlg = gtk_dialog_new_with_buttons( _("Save As..."), GTK_WINDOW(mw), GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
893 gtk_dialog_set_has_separator( GTK_DIALOG(file_save_as_dlg), FALSE );
1d48a247 894
827117ce 895 //g_object_set_property( file_save_as_dlg, "content-area-border", &spacing );
1d48a247 896
827117ce
LC
897 GtkWidget* file_chooser_widget = gtk_file_chooser_widget_new( GTK_FILE_CHOOSER_ACTION_SAVE );
898 GtkWidget* compression_widget = gtk_vbox_new( FALSE, 0 );
899
8ff83a68 900 gtk_dialog_set_default_response( GTK_DIALOG(file_save_as_dlg), GTK_RESPONSE_ACCEPT );
827117ce
LC
901 gtk_widget_set_size_request( file_save_as_dlg, 900, 560 );
902
903 GtkWidget *file_save_as_content_area = gtk_dialog_get_content_area( (GtkDialog*)file_save_as_dlg );
904
905 GtkWidget *compression_scale = gtk_hscale_new( NULL );
8ff83a68 906 gtk_range_set_increments( GTK_RANGE(compression_scale), (gdouble)1.0, 0 );
827117ce
LC
907 g_object_set( G_OBJECT(compression_scale), "digits", 0, NULL );
908
909 GtkWidget *compression_label = gtk_label_new( NULL );
910
911// gtk_box_set_homogeneous( file_save_as_content_area, TRUE );
912 // gtk_widget_set_size_request( quality_label, 0, 0 );
913 // gtk_widget_set_size_request( horizontal_scale, 0, 0 );
914
8ff83a68
YCLP
915 gtk_container_add( GTK_CONTAINER(compression_widget), gtk_hseparator_new() );
916 gtk_container_add( GTK_CONTAINER(compression_widget), compression_label );
917 gtk_container_add( GTK_CONTAINER(compression_widget), compression_scale );
827117ce 918
8ff83a68
YCLP
919 gtk_container_add( GTK_CONTAINER(file_save_as_content_area), file_chooser_widget );
920 gtk_container_add( GTK_CONTAINER(file_save_as_content_area), compression_widget );
827117ce
LC
921
922 gtk_widget_show_all( file_chooser_widget );
923
924 // gint result = gtk_dialog_run( (GtkDialog*)quality_dlg );
925 // gint compression_level = (gint)gtk_range_get_value( horizontal_scale );
926
927 // gtk_widget_destroy( quality_dlg );
928
929
930// GtkFileChooser* file_chooser_dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
931// GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
932// GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL );
933
8ff83a68
YCLP
934 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(file_chooser_widget), image_list_get_dir( mw->img_list ) );
935 gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file_chooser_widget), original_file_name ); // TODO: figure out why if you select the filename
827117ce 936 //the filter is unselected
1d48a247
HJYP
937
938 GtkFileFilter *filter;
939
1d48a247 940 filter = gtk_file_filter_new();
827117ce
LC
941 gtk_file_filter_add_pattern( filter, "*" );
942 gtk_file_filter_set_name( filter, _("All Files") );
8ff83a68 943 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(file_chooser_widget), filter );
1d48a247 944
827117ce
LC
945 GSList *gdk_formats;
946 GSList *gdk_formats_i;
947
948 char filter_pattern[20];
949
950 gdk_formats = gdk_pixbuf_get_formats();
951
952 for (gdk_formats_i = gdk_formats; gdk_formats_i;
953 gdk_formats_i = g_slist_next(gdk_formats_i))
1d48a247 954 {
827117ce
LC
955 GdkPixbufFormat *data;
956 data = gdk_formats_i->data;
1d48a247 957
827117ce
LC
958 if ( gdk_pixbuf_format_is_writable(data) )
959 {
960 char *file_type_name = gdk_pixbuf_format_get_name(data);
961
962 filter = gtk_file_filter_new();
963
964 strcpy( filter_pattern, "*." );
965 strcat( filter_pattern, file_type_name );
966
967 gtk_file_filter_add_pattern( filter, filter_pattern );
968 gtk_file_filter_set_name( filter, file_type_name );
969
970 if ( strcmp( file_type_name, "jpeg" ) == 0 )
971 {
972 gtk_file_filter_add_pattern( filter, "*.jpg" );
973 }
974
975 if ( strcmp( file_type_name, "tiff" ) == 0 )
976 {
977 gtk_file_filter_add_pattern( filter, "*.tif" );
978 }
1d48a247 979
8ff83a68 980 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(file_chooser_widget), filter );
1d48a247 981
827117ce
LC
982 if ( strcmp( file_type_name, original_file_type ) == 0 )
983 {
8ff83a68 984 gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(file_chooser_widget), filter );
827117ce
LC
985 }
986 }
1d48a247
HJYP
987 }
988
827117ce 989
8ff83a68 990 if ( strcmp( gtk_file_filter_get_name( gtk_file_chooser_get_filter( GTK_FILE_CHOOSER(file_chooser_widget) ) ), "jpeg" ) == 0 )
1d48a247 991 {
8ff83a68 992 gtk_label_set_text( GTK_LABEL(compression_label), _("JPEG Compression Level:") );
827117ce 993
8ff83a68
YCLP
994 gtk_range_set_range( GTK_RANGE(compression_scale), (gdouble)0.0, (gdouble)100.0 );
995 gtk_range_set_value( GTK_RANGE(compression_scale), (gdouble)100.0 );
827117ce
LC
996
997 gtk_widget_set_tooltip_text( compression_scale, _("Lower compression values create smaller file sizes but cause the image to lose quality.") );
998
999 gtk_widget_show_all( compression_widget );
1000 }
1001
8ff83a68 1002 if ( strcmp( gtk_file_filter_get_name( gtk_file_chooser_get_filter( GTK_FILE_CHOOSER(file_chooser_widget) ) ), "png" ) == 0 )
827117ce 1003 {
8ff83a68 1004 gtk_label_set_text( GTK_LABEL(compression_label), _("PNG Compression Level:") );
827117ce 1005
8ff83a68
YCLP
1006 gtk_range_set_range( GTK_RANGE(compression_scale), (gdouble)0.0, (gdouble)9.0 );
1007 gtk_range_set_value( GTK_RANGE(compression_scale), (gdouble)9.0 );
827117ce
LC
1008 gtk_widget_set_tooltip_text( compression_scale, _("Higher compression values create smaller file sizes but cause the image to lose quality.") );
1009
1010 gtk_widget_show_all( compression_widget );
1011 }
1012
1013 g_slist_free (gdk_formats);
1014
1015 if( gtk_dialog_run( (GtkDialog*)file_save_as_dlg ) == GTK_RESPONSE_ACCEPT )
1016 {
1017 char* new_file_type = NULL;
8ff83a68 1018 char* new_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file_chooser_widget) );
827117ce
LC
1019
1020 char* file_extension_start = strrchr( new_file_name, '.' ); // finds last '.' in the string
8ff83a68 1021 gint compression_level = (gint)gtk_range_get_value( GTK_RANGE(compression_scale) );
827117ce
LC
1022
1023 if( file_extension_start == NULL ) // if the image file name has no '.'
1d48a247 1024 {
827117ce 1025 new_file_type = original_file_type;
1d48a247
HJYP
1026 }
1027 else
1028 {
827117ce
LC
1029 char extension[5]; // buffer for the file extension, 4 for the extension + 1 for '\0'
1030
1031 int i = 0;
1032 file_extension_start++;
1033
1034 while ( (file_extension_start != '\0') && (i < 4) )
1035 {
1036 extension[i++] = *file_extension_start;
1037 file_extension_start++;
1038 }
1039
1040 extension[4] = '\0';
1041 new_file_type = extension;
1042
1043 gboolean file_extension_matches_known_type = FALSE;
1044
1045 if ( strcmp( new_file_type, "jpg" ) == 0 )
1046 {
1047 new_file_type = "jpeg";
1048 file_extension_matches_known_type = TRUE;
1049 }
1050 else if (strcmp( new_file_type, "tif" ) == 0 )
1051 {
1052 new_file_type = "tiff";
1053 file_extension_matches_known_type = TRUE;
1054 }
1055 else
1056 {
1057 gdk_formats = gdk_pixbuf_get_formats();
1058 for (gdk_formats_i = gdk_formats; gdk_formats_i;
1059 gdk_formats_i = g_slist_next(gdk_formats_i))
1060 {
1061 GdkPixbufFormat *data = gdk_formats_i->data;
1062
1063 if ( strcmp( gdk_pixbuf_format_get_name( data ), new_file_type) == 0)
1064 {
1065 file_extension_matches_known_type = TRUE;
1066 break;
1067 }
1068 }
1069
1070 g_slist_free (gdk_formats);
1071 }
1072
1073 // if a user is trying to save an image file with an unkown extension then save the image
1074 // as the original file type
1075 if ( ! file_extension_matches_known_type )
1076 {
1077 new_file_type = original_file_type;
1078 }
1d48a247 1079 }
827117ce
LC
1080
1081 // currently the compression selection widget only opens for the initial file
1082 // so if a png is opened then the compression selection is from 0 to 9 and if the file
1083 // is saved as a jpg the compression will automatically be set between 0 to 9 which is
1084 // extremely low compression for jpeg. this is not needed if we can find a way to automatically
1085 // update the compression select bar by looking at the file name being typed by the user
1086 if ( strcmp( original_file_type, new_file_type ) != 0 )
1087 {
1088 compression_level = -1;
1089 }
1090
1091 if ( main_win_save( mw, new_file_name, new_file_type, TRUE, compression_level ) )
1092 {
1093 main_win_open( mw, new_file_name, ZOOM_FIT );
1094 }
1095
1d48a247 1096 }
827117ce
LC
1097
1098 gtk_widget_destroy( (GtkWidget*)file_save_as_dlg );
1099
1100 g_free( original_file_name );
1d48a247
HJYP
1101}
1102
827117ce 1103//TODO: replace
7c3b160c 1104#ifdef HAVE_LIBJPEG
827117ce 1105int rotate_and_save_jpeg_lossless(char *filename, int angle){
da046c6e 1106
827117ce 1107 char tmpfilename[] = {"/tmp/rot.jpg.XXXXXX"};
c9bcabc8 1108 int tmpfilefd;
827117ce
LC
1109 FILE *cp_in;
1110 FILE *cp_out;
1111 char cp_buf[512];
da046c6e 1112
7c3b160c
HJYP
1113 JXFORM_CODE code = JXFORM_NONE;
1114
1115 angle = angle % 360;
1116
827117ce 1117
7c3b160c
HJYP
1118 if(angle == 90)
1119 code = JXFORM_ROT_90;
1120 else if(angle == 180)
1121 code = JXFORM_ROT_180;
1122 else if(angle == 270)
1123 code = JXFORM_ROT_270;
827117ce
LC
1124 else if(angle == -90)
1125 code = JXFORM_FLIP_H;
1126 else if(angle == -180)
1127 code = JXFORM_FLIP_V;
7c3b160c 1128
827117ce 1129 /* create tmp file */
c9bcabc8
YCLP
1130 tmpfilefd = mkstemp(tmpfilename);
1131 if (tmpfilefd == -1) {
827117ce 1132 return -1;
c9bcabc8
YCLP
1133 }
1134 close(tmpfilefd);
1135
827117ce
LC
1136 //rotate the image and save it to /tmp/rot.jpg
1137 int error = jpegtran (filename, tmpfilename , code);
1138 if (error)
1139 return error;
1140
1141 //now copy /tmp/rot.jpg back to the original file
1142 cp_in = fopen(tmpfilename,"rb");
1143 cp_out = fopen(filename,"wb");
1144 while (!feof(cp_in)) {
1145 int n;
1146 n = fread(cp_buf,1,sizeof(cp_buf),cp_in);
1147 if (n<=0) {
1148 break;
1149 }
1150 fwrite (cp_buf,1,n,cp_out);
aab0b120 1151 }
827117ce
LC
1152 fclose(cp_in);
1153 fclose(cp_out);
1154
1155 /* remove tmp file */
1156 unlink(tmpfilename);
1157 return 0;
1158}
1159#endif
1160
1161//TODO: replace
1162#ifdef HAVE_LIBJPEG
1163int rotate_and_save_jpeg_lossless2(char *filename, int angle){
1164
1165 char tmpfilename[] = {"/tmp/rot.jpg.XXXXXX"};
1166 int tmpfilefd;
1167 FILE *cp_in;
1168 FILE *cp_out;
1169 char cp_buf[512];
1170
1171 JXFORM_CODE code = JXFORM_NONE;
1172
1173 angle = angle % 360;
7c3b160c 1174
827117ce
LC
1175 if(angle == 90)
1176 code = JXFORM_ROT_90;
1177 else if(angle == 180)
1178 code = JXFORM_ROT_180;
1179 else if(angle == 270)
1180 code = JXFORM_ROT_270;
1181 else if(angle == -90)
1182 code = JXFORM_FLIP_H;
1183 else if(angle == -180)
1184 code = JXFORM_FLIP_V;
1185
1186 /* create tmp file */
1187 tmpfilefd = mkstemp(tmpfilename);
1188 if (tmpfilefd == -1) {
1189 return -1;
15e3d132 1190 }
827117ce
LC
1191 close(tmpfilefd);
1192
1193 //rotate the image and save it to /tmp/rot.jpg
1194 int error = jpegtran (filename, tmpfilename , code);
1195 if (error)
1196 return error;
1197
1198 //now copy /tmp/rot.jpg back to the original file
1199 cp_in = fopen(tmpfilename,"rb");
1200 cp_out = fopen(filename,"wb");
1201 while (!feof(cp_in))
1202 {
1203 int n;
1204 n = fread(cp_buf,1,sizeof(cp_buf),cp_in);
1205 if (n<=0)
1206 {
1207 break;
1208 }
1209 fwrite (cp_buf,1,n,cp_out);
1210 }
1211 fclose(cp_in);
1212 fclose(cp_out);
1213
1214 /* remove tmp file */
1215 unlink(tmpfilename);
7c3b160c
HJYP
1216 return 0;
1217}
1218#endif
1219
cf15ca10 1220void on_save( GtkWidget* btn, MainWin* mw )
1d48a247 1221{
cf15ca10 1222 if( ! mw->pix )
1d48a247
HJYP
1223 return;
1224
cf15ca10
HJYP
1225 char* file_name = g_build_filename( image_list_get_dir( mw->img_list ),
1226 image_list_get_current( mw->img_list ), NULL );
827117ce 1227
1d48a247
HJYP
1228 GdkPixbufFormat* info;
1229 info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
1230 char* type = gdk_pixbuf_format_get_name( info );
da046c6e 1231
827117ce 1232 if (strcmp(type,"jpeg")==0)
da046c6e 1233 {
827117ce 1234 if ( ExifRotate(file_name, mw->rotation_angle) != FALSE )
da046c6e 1235 {
1236 // hialan notes:
1237 // ExifRotate retrun FALSE when
1238 // 1. Can not read file
1239 // 2. Exif do not have TAG_ORIENTATION tag
1240 // 3. Format unknown
1241 // And then we apply rotate_and_save_jpeg_lossless() ,
1242 // the result would not effected by EXIF Orientation...
7c3b160c 1243#ifdef HAVE_LIBJPEG
827117ce
LC
1244
1245 if (rotate_and_save_jpeg_lossless(file_name,mw->rotation_angle)!=0)
da046c6e 1246 {
827117ce 1247 main_win_show_error(mw, "Save failed! Check permissions.");
da046c6e 1248 }
1249#else
827117ce 1250 main_win_save( mw, file_name, type, pref.ask_before_save, -1 );
7c3b160c 1251#endif
da046c6e 1252 }
827117ce
LC
1253 else
1254 {
1255 main_win_save( mw, file_name, type, pref.ask_before_save, -1 );
1256 }
1257
1258 }
1259 else
1260 {
1261 main_win_save( mw, file_name, type, pref.ask_before_save, -1 );
1262 }
1263
7c3b160c 1264 mw->rotation_angle = 0;
1d48a247
HJYP
1265 g_free( file_name );
1266 g_free( type );
1267}
1268
cf15ca10 1269void on_open( GtkWidget* btn, MainWin* mw )
1d48a247 1270{
827117ce 1271 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( _("Open"), (GtkWindow*)mw,
1d48a247
HJYP
1272 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
1273 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
1274
827117ce 1275 if ( image_list_get_dir( mw->img_list ) )
cf15ca10 1276 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
1277
1278 GtkWidget* img = gtk_image_new();
1279 gtk_widget_set_size_request( img, 128, 128 );
1280 gtk_file_chooser_set_preview_widget( dlg, img );
1281 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
1282
1283 GtkFileFilter *filter = gtk_file_filter_new();
1284 gtk_file_filter_set_name( filter, _("All Supported Images") );
1285 gtk_file_filter_add_pixbuf_formats( filter );
1286 gtk_file_chooser_add_filter( dlg, filter );
1287
1288 filter = gtk_file_filter_new();
1289 gtk_file_filter_set_name( filter, _("All Files") );
1290 gtk_file_filter_add_pattern( filter, "*" );
1291 gtk_file_chooser_add_filter( dlg, filter );
1292
1293 char* file = NULL;
1294 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
1295 file = gtk_file_chooser_get_filename( dlg );
1296 gtk_widget_destroy( (GtkWidget*)dlg );
1297
1298 if( file )
1299 {
cf15ca10 1300 main_win_open( mw, file, ZOOM_NONE );
1d48a247
HJYP
1301 g_free( file );
1302 }
1303}
1304
cf15ca10 1305void on_zoom_in( GtkWidget* btn, MainWin* mw )
1d48a247 1306{
cf15ca10
HJYP
1307 mw->zoom_mode = ZOOM_SCALE;
1308 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1309 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1310// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 1311// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 1312
cf15ca10
HJYP
1313 double scale = mw->scale;
1314 if( mw->pix && scale < 20.0 )
1d48a247 1315 {
cf15ca10 1316// busy(TRUE);
9c52ae58
HJYP
1317 scale *= 1.05;
1318 if( scale > 20.0 )
1319 scale = 20.0;
cf15ca10
HJYP
1320 if( mw->scale != scale )
1321 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 1322// adjust_adjustment_on_zoom(oldscale);
cf15ca10 1323// busy(FALSE);
1d48a247
HJYP
1324 }
1325}
1326
cf15ca10 1327void on_zoom_out( GtkWidget* btn, MainWin* mw )
1d48a247 1328{
cf15ca10
HJYP
1329 mw->zoom_mode = ZOOM_SCALE;
1330 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1331 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1332// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 1333// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 1334
cf15ca10
HJYP
1335 double scale = mw->scale;
1336 if( mw->pix && scale > 0.02 )
1d48a247 1337 {
cf15ca10 1338// busy(TRUE);
da8bd946 1339
9c52ae58
HJYP
1340 scale /= 1.05;
1341 if( scale < 0.02 )
1342 scale = 0.02;
cf15ca10
HJYP
1343 if( mw->scale != scale )
1344 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 1345// adjust_adjustment_on_zoom(oldscale);
cf15ca10 1346// busy(FALSE);
1d48a247
HJYP
1347 }
1348}
1349
cf15ca10 1350void on_preference( GtkWidget* btn, MainWin* mw )
1d48a247 1351{
827117ce 1352 edit_preferences( mw );
1d48a247
HJYP
1353}
1354
cf15ca10 1355void on_quit( GtkWidget* btn, MainWin* mw )
1d48a247 1356{
cf15ca10 1357 gtk_widget_destroy( (GtkWidget*)mw );
1d48a247
HJYP
1358}
1359
cf15ca10 1360gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 1361{
9c52ae58
HJYP
1362 if( ! GTK_WIDGET_HAS_FOCUS( widget ) )
1363 gtk_widget_grab_focus( widget );
5bfec971 1364
1d48a247
HJYP
1365 if( evt->type == GDK_BUTTON_PRESS)
1366 {
1367 if( evt->button == 1 ) // left button
1368 {
cf15ca10 1369 if( ! mw->pix )
1d48a247 1370 return FALSE;
cf15ca10
HJYP
1371 mw->dragging = TRUE;
1372 gtk_widget_get_pointer( (GtkWidget*)mw, &mw->drag_old_x ,&mw->drag_old_y );
1373 gdk_window_set_cursor( widget->window, mw->hand_cursor );
1d48a247
HJYP
1374 }
1375 else if( evt->button == 3 ) // right button
1376 {
cf15ca10 1377 show_popup_menu( mw, evt );
1d48a247
HJYP
1378 }
1379 }
1380 else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 ) // double clicked
1381 {
827117ce 1382 on_full_screen( NULL, mw );
1d48a247 1383 }
827117ce 1384
1d48a247
HJYP
1385 return FALSE;
1386}
1387
cf15ca10 1388gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw )
1d48a247 1389{
cf15ca10 1390 if( ! mw->dragging )
1d48a247 1391 return FALSE;
9c52ae58 1392
1d48a247 1393 int cur_x, cur_y;
cf15ca10 1394 gtk_widget_get_pointer( (GtkWidget*)mw, &cur_x ,&cur_y );
1d48a247 1395
cf15ca10
HJYP
1396 int dx = (mw->drag_old_x - cur_x);
1397 int dy = (mw->drag_old_y - cur_y);
1d48a247
HJYP
1398
1399 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1400 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1401 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1402
9c52ae58 1403 GtkRequisition req;
cf15ca10 1404 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247
HJYP
1405
1406 if( ABS(dx) > 4 )
1407 {
cf15ca10 1408 mw->drag_old_x = cur_x;
9c52ae58 1409 if( req.width > hadj->page_size )
1d48a247
HJYP
1410 {
1411 gdouble x = gtk_adjustment_get_value (hadj) + dx;
1412 if( x < hadj->lower )
1413 x = hadj->lower;
1414 else if( (x + hadj->page_size) > hadj->upper )
1415 x = hadj->upper - hadj->page_size;
1416
1417 if( x != hadj->value )
1418 gtk_adjustment_set_value (hadj, x );
1419 }
1420 }
1421
1422 if( ABS(dy) > 4 )
1423 {
9c52ae58 1424 if( req.height > vadj->page_size )
1d48a247 1425 {
cf15ca10 1426 mw->drag_old_y = cur_y;
1d48a247
HJYP
1427 gdouble y = gtk_adjustment_get_value (vadj) + dy;
1428 if( y < vadj->lower )
1429 y = vadj->lower;
1430 else if( (y + vadj->page_size) > vadj->upper )
1431 y = vadj->upper - vadj->page_size;
1432
1433 if( y != vadj->value )
1434 gtk_adjustment_set_value (vadj, y );
1435 }
1436 }
1437 return FALSE;
1438}
1439
cf15ca10 1440gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 1441{
cf15ca10 1442 mw->dragging = FALSE;
1d48a247
HJYP
1443 gdk_window_set_cursor( widget->window, NULL );
1444 return FALSE;
1445}
1446
cf15ca10 1447gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw )
5bfec971 1448{
58e1b106 1449 guint modifiers = gtk_accelerator_get_default_mod_mask();
5bfec971
HJYP
1450 switch( evt->direction )
1451 {
1452 case GDK_SCROLL_UP:
58e1b106
JH
1453 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1454 on_zoom_in( NULL, mw );
1455 else
1456 on_prev( NULL, mw );
5bfec971
HJYP
1457 break;
1458 case GDK_SCROLL_DOWN:
58e1b106
JH
1459 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1460 on_zoom_out( NULL, mw );
1461 else
1462 on_next( NULL, mw );
5bfec971
HJYP
1463 break;
1464 case GDK_SCROLL_LEFT:
827117ce 1465 on_prev( NULL, mw );
5bfec971
HJYP
1466 break;
1467 case GDK_SCROLL_RIGHT:
827117ce 1468 on_next( NULL, mw );
5bfec971
HJYP
1469 break;
1470 }
da8bd946 1471 return TRUE;
5bfec971
HJYP
1472}
1473
cf15ca10 1474gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key)
1d48a247 1475{
cf15ca10 1476 MainWin* mw = (MainWin*)widget;
1d48a247
HJYP
1477 switch( key->keyval )
1478 {
7c3b160c
HJYP
1479 case GDK_Right:
1480 case GDK_KP_Right:
1481 case GDK_rightarrow:
1d48a247
HJYP
1482 case GDK_Return:
1483 case GDK_space:
1484 case GDK_Next:
5bfec971
HJYP
1485 case GDK_KP_Down:
1486 case GDK_Down:
1487 case GDK_downarrow:
cf15ca10 1488 on_next( NULL, mw );
1d48a247 1489 break;
7c3b160c
HJYP
1490 case GDK_Left:
1491 case GDK_KP_Left:
1492 case GDK_leftarrow:
1d48a247
HJYP
1493 case GDK_Prior:
1494 case GDK_BackSpace:
5bfec971
HJYP
1495 case GDK_KP_Up:
1496 case GDK_Up:
1497 case GDK_uparrow:
cf15ca10 1498 on_prev( NULL, mw );
1d48a247
HJYP
1499 break;
1500 case GDK_KP_Add:
1501 case GDK_plus:
641adad3 1502 case GDK_equal:
cf15ca10 1503 on_zoom_in( NULL, mw );
1d48a247
HJYP
1504 break;
1505 case GDK_KP_Subtract:
1506 case GDK_minus:
cf15ca10 1507 on_zoom_out( NULL, mw );
1d48a247 1508 break;
1d48a247 1509 case GDK_s:
827117ce 1510 case GDK_S:
cf15ca10 1511 on_save( NULL, mw );
1d48a247
HJYP
1512 break;
1513 case GDK_l:
827117ce 1514 case GDK_L:
cf15ca10 1515 on_rotate_counterclockwise( NULL, mw );
1d48a247
HJYP
1516 break;
1517 case GDK_r:
827117ce 1518 case GDK_R:
cf15ca10 1519 on_rotate_clockwise( NULL, mw );
1d48a247
HJYP
1520 break;
1521 case GDK_f:
827117ce 1522 case GDK_F:
cf15ca10
HJYP
1523 if( mw->zoom_mode != ZOOM_FIT )
1524 gtk_button_clicked((GtkButton*)mw->btn_fit );
1d48a247
HJYP
1525 break;
1526 case GDK_g:
827117ce 1527 case GDK_G:
cf15ca10
HJYP
1528 if( mw->zoom_mode != ZOOM_ORIG )
1529 gtk_button_clicked((GtkButton*)mw->btn_orig );
1d48a247 1530 break;
827117ce
LC
1531 case GDK_h:
1532 case GDK_H:
1533 on_flip_horizontal( NULL, mw );
1534 break;
1535 case GDK_v:
1536 case GDK_V:
1537 on_flip_vertical( NULL, mw );
1538 break;
1d48a247 1539 case GDK_o:
827117ce 1540 case GDK_O:
cf15ca10 1541 on_open( NULL, mw );
5bfec971
HJYP
1542 break;
1543 case GDK_Delete:
1544 case GDK_d:
827117ce 1545 case GDK_D:
cf15ca10 1546 on_delete( NULL, mw );
1d48a247 1547 break;
827117ce
LC
1548 case GDK_a:
1549 case GDK_A:
1550 on_about( NULL, mw );
1551 break;
1552 case GDK_p:
1553 case GDK_P:
1554 on_preference( NULL, mw );
1555 break;
1d48a247 1556 case GDK_Escape:
cf15ca10
HJYP
1557 if( mw->full_screen )
1558 on_full_screen( NULL, mw );
1d48a247 1559 else
cf15ca10 1560 on_quit( NULL, mw );
1d48a247
HJYP
1561 break;
1562 case GDK_F11:
cf15ca10 1563 on_full_screen( NULL, mw );
1d48a247
HJYP
1564 break;
1565
1566 default:
cf15ca10 1567 GTK_WIDGET_CLASS(main_win_parent_class)->key_press_event( widget, key );
1d48a247
HJYP
1568 }
1569 return FALSE;
1570}
1571
cf15ca10 1572void main_win_center_image( MainWin* mw )
1d48a247
HJYP
1573{
1574 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1575 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1576 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1577
9c52ae58 1578 GtkRequisition req;
cf15ca10 1579 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247 1580
9c52ae58
HJYP
1581 if( req.width > hadj->page_size )
1582 gtk_adjustment_set_value(hadj, ( hadj->upper - hadj->page_size ) / 2 );
1d48a247 1583
9c52ae58
HJYP
1584 if( req.height > vadj->page_size )
1585 gtk_adjustment_set_value(vadj, ( vadj->upper - vadj->page_size ) / 2 );
1d48a247
HJYP
1586}
1587
da046c6e 1588void rotate_image( MainWin* mw, int angle )
1d48a247 1589{
da046c6e 1590 GdkPixbuf* rpix = NULL;
1591
cf15ca10 1592 if( ! mw->pix )
1d48a247
HJYP
1593 return;
1594
da046c6e 1595 if(angle > 0)
1596 {
1597 rpix = gdk_pixbuf_rotate_simple( mw->pix, angle );
1598 }
1599 else
1600 {
1601 if(angle == -90)
1602 rpix = gdk_pixbuf_flip( mw->pix, TRUE );
1603 else if(angle == -180)
1604 rpix = gdk_pixbuf_flip( mw->pix, FALSE );
1605 }
cf15ca10 1606 g_object_unref( mw->pix );
da046c6e 1607
cf15ca10 1608 mw->pix = rpix;
0c0c8179 1609 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
1d48a247 1610
cf15ca10 1611 if( mw->zoom_mode == ZOOM_FIT )
c325c989 1612 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
1613}
1614
cf15ca10 1615gboolean main_win_scale_image( MainWin* mw, double new_scale, GdkInterpType type )
1d48a247
HJYP
1616{
1617 if( G_UNLIKELY( new_scale == 1.0 ) )
1618 {
cf15ca10 1619 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
da046c6e 1620 mw->scale = 1.0;
cf15ca10 1621 return TRUE;
1d48a247 1622 }
cf15ca10 1623 mw->scale = new_scale;
0c0c8179 1624 image_view_set_scale( (ImageView*)mw->img_view, new_scale, type );
da046c6e 1625
1626 updateTitle( NULL, mw );
1627
cf15ca10 1628 return TRUE;
1d48a247
HJYP
1629}
1630
827117ce 1631gboolean main_win_save( MainWin* mw, const char* file_path, const char* type, gboolean confirm, gdouble compression_level )
aab0b120 1632{
827117ce 1633 gboolean result1, gdk_save_supported;
3d62c4bb
YCLP
1634 GSList *gdk_formats;
1635 GSList *gdk_formats_i;
827117ce 1636
cf15ca10
HJYP
1637 if( ! mw->pix )
1638 return FALSE;
1d48a247 1639
827117ce
LC
1640 if( confirm ) // check existing file
1641 {
1642 if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
1643 {
1644 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1645 GTK_DIALOG_MODAL,
1646 GTK_MESSAGE_QUESTION,
1647 GTK_BUTTONS_YES_NO,
1648 _("The file name you selected already exist.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
1649 if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
1650 {
1651 gtk_widget_destroy( dlg );
1652 return FALSE;
1653 }
1654 gtk_widget_destroy( dlg );
1655 }
1656 }
1657
1658 // detect if the current type can be saved by gdk_pixbuf_save()
3d62c4bb
YCLP
1659 gdk_save_supported = FALSE;
1660 gdk_formats = gdk_pixbuf_get_formats();
827117ce 1661
3d62c4bb
YCLP
1662 for (gdk_formats_i = gdk_formats; gdk_formats_i;
1663 gdk_formats_i = g_slist_next(gdk_formats_i))
1664 {
1665 GdkPixbufFormat *data;
1666 data = gdk_formats_i->data;
1667 if (gdk_pixbuf_format_is_writable(data))
1668 {
1669 if ( strcmp(type, gdk_pixbuf_format_get_name(data))==0)
1670 {
1671 gdk_save_supported = TRUE;
1672 break;
1673 }
1674 }
1675 }
827117ce 1676
3d62c4bb
YCLP
1677 g_slist_free (gdk_formats);
1678
1d48a247 1679 GError* err = NULL;
3d62c4bb
YCLP
1680 if (!gdk_save_supported)
1681 {
827117ce
LC
1682 main_win_show_error( mw, _("Sorry but the image you are trying to save has a format not supported by the GDK.") );
1683
3d62c4bb
YCLP
1684 return FALSE;
1685 }
827117ce
LC
1686
1687 if ( compression_level == -1 )
1688 {
1689 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL );
1690 }
1691 else
1692 {
1693 gchar compression_level_string[4];
1694 g_ascii_dtostr( compression_level_string, 4, compression_level ); // convert the gdouble compression_level to a string so it can be passed as an option to gdk_pixbuf_save()
1695
1696 if ( strcmp( type, "jpeg" ) == 0 )
1697 {
1698 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, "quality", compression_level_string, NULL );
1699 }
1700 else if ( strcmp( type, "png" ) == 0 )
1701 {
1702 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, "compression", compression_level_string, NULL );
1703 }
1704 else
1705 {
1706 result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL );
1707 }
1708 }
1709
3d62c4bb 1710 if( ! result1 )
1d48a247 1711 {
cf15ca10
HJYP
1712 main_win_show_error( mw, err->message );
1713 return FALSE;
1d48a247 1714 }
827117ce 1715
cf15ca10 1716 return TRUE;
1d48a247
HJYP
1717}
1718
cf15ca10 1719void on_delete( GtkWidget* btn, MainWin* mw )
1d48a247 1720{
cf15ca10 1721 char* file_path = image_list_get_current_file_path( mw->img_list );
1d48a247
HJYP
1722 if( file_path )
1723 {
827117ce
LC
1724 int resp = 0;
1725
1726 if ( pref.ask_before_delete )
1727 {
1728 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1729 GTK_DIALOG_MODAL,
1730 GTK_MESSAGE_QUESTION,
1731 GTK_BUTTONS_YES_NO,
1732 _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
1733 resp = gtk_dialog_run( (GtkDialog*)dlg );
1734 gtk_widget_destroy( dlg );
1735 }
1d48a247 1736
827117ce 1737 if( ! pref.ask_before_delete || (pref.ask_before_delete && resp == GTK_RESPONSE_YES) )
1d48a247 1738 {
fa27691a
HJYP
1739 const char* name = image_list_get_current( mw->img_list );
1740
1741 if( g_unlink( file_path ) != 0 )
1742 main_win_show_error( mw, g_strerror(errno) );
1743 else
1744 {
1745 const char* next_name = image_list_get_next( mw->img_list );
1746 if( ! next_name )
1747 next_name = image_list_get_prev( mw->img_list );
1748
1749 if( next_name )
1750 {
1751 char* next_file_path = image_list_get_current_file_path( mw->img_list );
1752 main_win_open( mw, next_file_path, ZOOM_FIT );
1753 g_free( next_file_path );
1754 }
1755
1756 image_list_remove ( mw->img_list, name );
1757
1758 if ( ! next_name )
827117ce 1759 g_main_loop_quit( mw->loop );
fa27691a 1760 }
1d48a247 1761 }
827117ce 1762
fa27691a 1763 g_free( file_path );
1d48a247
HJYP
1764 }
1765}
1766
cf15ca10 1767void show_popup_menu( MainWin* mw, GdkEventButton* evt )
1d48a247 1768{
5bfec971
HJYP
1769 static PtkMenuItemEntry menu_def[] =
1770 {
1771 PTK_IMG_MENU_ITEM( N_( "Previous" ), GTK_STOCK_GO_BACK, on_prev, GDK_leftarrow, 0 ),
1772 PTK_IMG_MENU_ITEM( N_( "Next" ), GTK_STOCK_GO_FORWARD, on_next, GDK_rightarrow, 0 ),
1773 PTK_SEPARATOR_MENU_ITEM,
1774 PTK_IMG_MENU_ITEM( N_( "Zoom Out" ), GTK_STOCK_ZOOM_OUT, on_zoom_out, GDK_minus, 0 ),
1775 PTK_IMG_MENU_ITEM( N_( "Zoom In" ), GTK_STOCK_ZOOM_IN, on_zoom_in, GDK_plus, 0 ),
1776 PTK_IMG_MENU_ITEM( N_( "Fit Image To Window Size" ), GTK_STOCK_ZOOM_FIT, on_zoom_fit_menu, GDK_F, 0 ),
1777 PTK_IMG_MENU_ITEM( N_( "Original Size" ), GTK_STOCK_ZOOM_100, on_orig_size_menu, GDK_G, 0 ),
1778 PTK_SEPARATOR_MENU_ITEM,
0c8ac162 1779 PTK_IMG_MENU_ITEM( N_( "Full Screen" ), GTK_STOCK_FULLSCREEN, on_full_screen, GDK_F11, 0 ),
5bfec971
HJYP
1780 PTK_SEPARATOR_MENU_ITEM,
1781 PTK_IMG_MENU_ITEM( N_( "Rotate Counterclockwise" ), "gtk-counterclockwise", on_rotate_counterclockwise, GDK_L, 0 ),
1782 PTK_IMG_MENU_ITEM( N_( "Rotate Clockwise" ), "gtk-clockwise", on_rotate_clockwise, GDK_R, 0 ),
827117ce
LC
1783 PTK_IMG_MENU_ITEM( N_( "Flip Horizontal" ), "gtk-horizontal", on_flip_horizontal, GDK_H, 0 ),
1784 PTK_IMG_MENU_ITEM( N_( "Flip Vertical" ), "gtk-vertical", on_flip_vertical, GDK_V, 0 ),
5bfec971
HJYP
1785 PTK_SEPARATOR_MENU_ITEM,
1786 PTK_IMG_MENU_ITEM( N_("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open), GDK_O, 0 ),
1787 PTK_IMG_MENU_ITEM( N_("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save), GDK_S, 0 ),
1788 PTK_IMG_MENU_ITEM( N_("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
da046c6e 1789// PTK_IMG_MENU_ITEM( N_("Save As Other Size"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
5bfec971
HJYP
1790 PTK_IMG_MENU_ITEM( N_("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete), GDK_Delete, 0 ),
1791 PTK_SEPARATOR_MENU_ITEM,
827117ce
LC
1792 PTK_IMG_MENU_ITEM( N_( "Preferences" ), GTK_STOCK_PREFERENCES, on_preference, GDK_P, 0 ),
1793 PTK_IMG_MENU_ITEM( N_( "About" ), GTK_STOCK_ABOUT, on_about, GDK_A, 0 ),
5bfec971
HJYP
1794 PTK_MENU_END
1795 };
1796
cf15ca10 1797 // mw accel group is useless. It's only used to display accels in popup menu
5bfec971 1798 GtkAccelGroup* accel_group = gtk_accel_group_new();
cf15ca10 1799 GtkMenuShell* popup = (GtkMenuShell*)ptk_menu_new_from_data( menu_def, mw, accel_group );
1d48a247
HJYP
1800
1801 gtk_widget_show_all( (GtkWidget*)popup );
1802 g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1803 gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
1804}
1805
827117ce 1806// callback used to open default browser when URLs got clicked
7c3b160c 1807static void open_url( GtkAboutDialog *dlg, const gchar *url, gpointer data)
1d48a247 1808{
827117ce
LC
1809// GError *error = NULL;
1810// gtk_show_uri( gdk_screen_get_default (),
1811// "http://google.com",
1812// gtk_get_current_event_time (),
1813// &error);
1814
1815/*
1816 // FIXME: is there any better way to do this?
1817 char* programs[] = { "xdg-open", "gnome-open", "exo-open" };
7c3b160c
HJYP
1818 int i;
1819 for( i = 0; i < G_N_ELEMENTS(programs); ++i)
1d48a247 1820 {
7c3b160c
HJYP
1821 char* open_cmd = NULL;
1822 if( (open_cmd = g_find_program_in_path( programs[i] )) )
1d48a247 1823 {
ff032ffe
JH
1824 gchar* argv [3];
1825 argv [0] = programs[i];
827117ce
LC
1826 // *******************************************************************************************
1827 argv [1] = url; // FIXME: gcc gives warning: assignment discards qualifiers from pointer target type
1828 // *******************************************************************************************
1829 //argv [2] = NULL;
ff032ffe 1830 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
7c3b160c
HJYP
1831 g_free( open_cmd );
1832 break;
1d48a247 1833 }
827117ce 1834 }*/
1d48a247
HJYP
1835}
1836
cf15ca10 1837void on_about( GtkWidget* menu, MainWin* mw )
1d48a247
HJYP
1838{
1839 GtkWidget * about_dlg;
1840 const gchar *authors[] =
1841 {
1842 "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
827117ce 1843 "Louis Casillas <oxaric@gmail.com>",
7c3b160c 1844 "Martin Siggel <martinsiggel@googlemail.com>",
da046c6e 1845 "Hialan Liu <hialan.liu@gmail.com>",
827117ce 1846 "",
7c3b160c 1847 _(" * Refer to source code of EOG image viewer and GThumb"),
1d48a247
HJYP
1848 NULL
1849 };
827117ce 1850 // TRANSLATORS: Replace mw string with your names, one name per line.
1d48a247
HJYP
1851 gchar *translators = _( "translator-credits" );
1852
7c3b160c
HJYP
1853 gtk_about_dialog_set_url_hook( open_url, mw, NULL);
1854
1d48a247 1855 about_dlg = gtk_about_dialog_new ();
7c3b160c
HJYP
1856
1857 gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 );
1858 gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION );
1859 gtk_about_dialog_set_name ( (GtkAboutDialog*)about_dlg, _( "GPicView" ) );
1860 gtk_about_dialog_set_logo( (GtkAboutDialog*)about_dlg, gdk_pixbuf_new_from_file( PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL ) );
1861 gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2007" ) );
1862 gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Lightweight image viewer from LXDE project" ) );
827117ce
LC
1863 gtk_about_dialog_set_license ( (GtkAboutDialog*)about_dlg, "GPicView\n\nCopyright (C) 2007 Hong Jen Yee (PCMan)\n\nmw program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nmw program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with mw program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." );
1864 gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://wiki.lxde.org/en/GPicView" );
7c3b160c
HJYP
1865 gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors );
1866 gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators );
1867 gtk_window_set_transient_for( (GtkWindow*) about_dlg, GTK_WINDOW( mw ) );
1868
1869 gtk_dialog_run( ( GtkDialog*)about_dlg );
1d48a247
HJYP
1870 gtk_widget_destroy( about_dlg );
1871}
1872
cf15ca10
HJYP
1873void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1874 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw )
1d48a247
HJYP
1875{
1876 if( ! data || data->length <= 0)
1877 return;
1878
1d48a247
HJYP
1879 char* file = NULL;
1880 if( info == 0 ) // text/uri-list
1881 {
1882 char** uris = gtk_selection_data_get_uris( data );
1883 if( uris )
1884 {
1885 file = g_filename_from_uri(*uris, NULL, NULL);
1886 g_strfreev( uris );
1887 }
1888 }
1889 else if( info == 1 ) // text/plain
1890 {
1891 file = (char*)gtk_selection_data_get_text( data );
1892 }
1893 if( file )
1894 {
cf15ca10 1895 main_win_open( mw, file, ZOOM_FIT );
1d48a247
HJYP
1896 g_free( file );
1897 }
1898}