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