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