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