Skip to content

Commit 57c0e2e

Browse files
jcupittangstyloop
andauthored
Save options (#20)
Add a save options dialog Co-authored-by: Sean Allen <[email protected]>
1 parent 94b9f5a commit 57c0e2e

File tree

13 files changed

+790
-65
lines changed

13 files changed

+790
-65
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## master
22

3+
- save options [angstyloop]
4+
35
## 2.4.1, 21/8/22
46

57
- remove stray printfs

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,10 @@ $ vipsdisp ~/pics/k2.jpg
133133
- gtk 4.10 added GSK_SCALING_FILTER_NEAREST and GskTextureScale ... use this
134134
for the main window
135135

136-
- fix deprecation warnings
136+
- animate kep press zooms, eg, "1" then "0"
137137

138-
at least hide them!
138+
- use eg. alt-left, alt-right to flip between images in "vipsdisp a.jpg b.jpg"
139+
or maybe shift-<, shift->?
139140

140141
- allow eg. "vipsdisp x.svg[scale=10]", the load dialog should have a
141142
"load options" expander, and save should have "save options"

src/gtk/saveoptions.ui

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<interface>
3+
4+
<template class="SaveOptions" parent="GtkDialog">
5+
<property name="title" translatable="yes">Save options</property>
6+
<property name="can-focus">True</property>
7+
<property name="modal">True</property>
8+
<property name="default-width">400</property>
9+
<property name="default-height">500</property>
10+
11+
<style>
12+
<class name="saveoptions"/>
13+
</style>
14+
15+
<child internal-child="content_area">
16+
<object class="GtkBox">
17+
<property name="orientation">1</property>
18+
<property name="spacing">2</property>
19+
<property name="margin-start">5</property>
20+
<property name="margin-end">5</property>
21+
<property name="margin-top">5</property>
22+
<property name="margin-bottom">5</property>
23+
24+
<child>
25+
<object class="GtkActionBar" id="progress_bar">
26+
<property name="revealed">false</property>
27+
28+
<child type="center">
29+
<object class="GtkProgressBar" id="progress">
30+
<property name="hexpand">true</property>
31+
<property name="show-text">true</property>
32+
</object>
33+
</child>
34+
35+
<child type="end">
36+
<object class="GtkButton" id="progress_cancel">
37+
<property name="label">Cancel</property>
38+
</object>
39+
</child>
40+
</object>
41+
</child>
42+
43+
<child>
44+
<object class="GtkInfoBar" id="error_bar">
45+
<property name="message-type">error</property>
46+
<property name="show-close-button">true</property>
47+
<property name="revealed">false</property>
48+
<child>
49+
<object class="GtkLabel" id="error_label">
50+
<attributes>
51+
<attribute name="weight" value="bold"/>
52+
</attributes>
53+
</object>
54+
</child>
55+
</object>
56+
</child>
57+
58+
<child>
59+
<object class="GtkScrolledWindow">
60+
<property name="hexpand">1</property>
61+
<property name="vexpand">1</property>
62+
<property name="min-content-height">600</property>
63+
64+
<child>
65+
<object class="GtkGrid" id="options_grid">
66+
<property name="row-homogeneous">1</property>
67+
<property name="column-spacing">5</property>
68+
<property name="row-spacing">3</property>
69+
</object>
70+
</child>
71+
72+
</object>
73+
</child>
74+
75+
</object>
76+
</child>
77+
78+
<child type="action">
79+
<object class="GtkButton" id="cancel_button">
80+
<property name="use-underline">1</property>
81+
<property name="label" translatable="yes">_Cancel</property>
82+
</object>
83+
</child>
84+
85+
<child type="action">
86+
<object class="GtkButton" id="ok_button">
87+
<property name="use-underline">1</property>
88+
<property name="label" translatable="yes">_Save</property>
89+
</object>
90+
</child>
91+
92+
<action-widgets>
93+
<action-widget response="cancel">cancel_button</action-widget>
94+
<action-widget response="ok" default="true">ok_button</action-widget>
95+
</action-widgets>
96+
97+
</template>
98+
99+
</interface>
100+

src/gtk/vipsdisp.gresources.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
<file preprocess="xml-stripblanks">infobar.ui</file>
77
<file preprocess="xml-stripblanks">displaybar.ui</file>
88
<file preprocess="xml-stripblanks">tslider.ui</file>
9+
<file preprocess="xml-stripblanks">saveoptions.ui</file>
910
</gresource>
1011
</gresources>

src/gtkutil.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,25 @@ copy_state( GtkWidget *to, GtkWidget *from, const char *name )
153153
g_variant_unref( state );
154154
}
155155
}
156+
157+
/* A 'safe' way to run a few events.
158+
*/
159+
void
160+
process_events( void )
161+
{
162+
/* Max events we process before signalling a timeout. Without this we
163+
* can get stuck in event loops in some circumstances.
164+
*/
165+
static const int max_events = 100;
166+
167+
/* Block too much recursion. 0 is from the top-level, 1 is from a
168+
* callback, we don't want any more than that.
169+
*/
170+
if( g_main_depth() < 2 ) {
171+
int n;
172+
173+
for( n = 0; n < max_events &&
174+
g_main_context_iteration( NULL, FALSE ); n++ )
175+
;
176+
}
177+
}

src/gtkutil.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ void copy_adj( GtkAdjustment *to, GtkAdjustment *from );
1010
void change_state( GtkWidget *widget, const char *name, GVariant *state );
1111
GVariant *get_state( GtkWidget *widget, const char *name );
1212
void copy_state( GtkWidget *to, GtkWidget *from, const char *name );
13+
14+
void process_events( void );

src/imagewindow.c

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,8 @@ image_window_eval( VipsImage *image,
324324
printf( "image_window_eval: %d%%\n", progress->percent );
325325
#endif /*DEBUG_VERBOSE*/
326326

327-
/* You'd think we could just update the progress bar now, but it
328-
* seems to trigger a lot of races. Instead, set an idle handler and
329-
* do the update there.
327+
/* This can come from the background load thread, so we can't update
328+
* the UI directly.
330329
*/
331330

332331
update = g_new( EvalUpdate, 1 );
@@ -420,7 +419,7 @@ image_window_tile_source_changed( TileSource *tile_source, ImageWindow *win )
420419
static void
421420
image_window_error_response( GtkWidget *button, int response, ImageWindow *win )
422421
{
423-
gtk_info_bar_set_revealed( GTK_INFO_BAR( win->error_bar ), FALSE );
422+
image_window_error_hide( win );
424423
}
425424

426425
static void
@@ -573,25 +572,59 @@ image_window_replace_action( GSimpleAction *action,
573572
}
574573

575574
static void
576-
image_window_saveas_response( GtkDialog *dialog,
577-
gint response_id, gpointer user_data )
575+
image_window_saveas_options_response( GtkDialog *dialog,
576+
gint response, gpointer user_data )
577+
{
578+
GtkWidget *file_chooser = GTK_WIDGET( user_data );
579+
580+
// final save and everything worked OK, we can all pop down
581+
if( response == GTK_RESPONSE_ACCEPT ) {
582+
gtk_window_destroy( GTK_WINDOW( dialog ) );
583+
gtk_window_destroy( GTK_WINDOW( file_chooser ) );
584+
}
585+
586+
// save options was cancelled, just pop that down
587+
if( response == GTK_RESPONSE_CANCEL )
588+
gtk_window_destroy( GTK_WINDOW( dialog ) );
589+
590+
// other return codes are intermediate stages of processing and we
591+
// should do nothing
592+
}
593+
594+
static void
595+
image_window_saveas_response( GtkDialog *dialog,
596+
gint response, gpointer user_data )
578597
{
579598
ImageWindow *win = VIPSDISP_IMAGE_WINDOW( user_data );
580599

581-
GFile *file;
600+
if( response == GTK_RESPONSE_ACCEPT ) {
601+
GFile *file;
602+
char *filename;
603+
SaveOptions *options;
582604

583-
/* We need to pop down immediately so we expose the cancel
584-
* button.
585-
*/
586-
file = gtk_file_chooser_get_file( GTK_FILE_CHOOSER( dialog ) );
587-
image_window_error_hide( win );
588-
gtk_window_destroy( GTK_WINDOW( dialog ) );
605+
file = gtk_file_chooser_get_file( GTK_FILE_CHOOSER( dialog ) );
606+
filename = g_file_get_path( file );
607+
VIPS_UNREF( file );
608+
609+
options = save_options_new( GTK_WINDOW( dialog ),
610+
win->tile_source->image, filename );
611+
612+
g_free( filename );
613+
614+
if( !options ) {
615+
image_window_error( win );
616+
return;
617+
}
589618

590-
if( response_id == GTK_RESPONSE_ACCEPT &&
591-
tile_source_write_to_file( win->tile_source, file ) )
592-
image_window_error( win );
619+
g_signal_connect_object( options, "response",
620+
G_CALLBACK( image_window_saveas_options_response ),
621+
dialog, 0 );
593622

594-
VIPS_UNREF( file );
623+
gtk_window_present( GTK_WINDOW( options ) );
624+
}
625+
626+
if( response == GTK_RESPONSE_CANCEL )
627+
gtk_window_destroy( GTK_WINDOW( dialog ) );
595628
}
596629

597630
static void
@@ -601,27 +634,29 @@ image_window_saveas_action( GSimpleAction *action,
601634
ImageWindow *win = VIPSDISP_IMAGE_WINDOW( user_data );
602635

603636
if( win->tile_source ) {
604-
GtkWidget *dialog;
637+
GtkWidget *file_chooser;
605638
GFile *file;
606639

607-
dialog = gtk_file_chooser_dialog_new( "Save file",
640+
file_chooser = gtk_file_chooser_dialog_new( "Save file",
608641
GTK_WINDOW( win ) ,
609642
GTK_FILE_CHOOSER_ACTION_SAVE,
610643
"_Cancel", GTK_RESPONSE_CANCEL,
611644
"_Save", GTK_RESPONSE_ACCEPT,
612645
NULL );
613-
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
646+
647+
gtk_window_set_modal( GTK_WINDOW( file_chooser ), true );
614648

615649
if( (file = tile_source_get_file( win->tile_source )) ) {
616-
gtk_file_chooser_set_file( GTK_FILE_CHOOSER( dialog ),
650+
gtk_file_chooser_set_file(
651+
GTK_FILE_CHOOSER( file_chooser ),
617652
file, NULL );
618653
VIPS_UNREF( file );
619654
}
620655

621-
g_signal_connect( dialog, "response",
656+
g_signal_connect( file_chooser, "response",
622657
G_CALLBACK( image_window_saveas_response ), win );
623658

624-
gtk_widget_show( dialog );
659+
gtk_widget_show( file_chooser );
625660
}
626661
}
627662

@@ -909,6 +944,7 @@ image_window_scale_begin( GtkGesture* self,
909944
imagedisplay_gtk_to_image( VIPSDISP_IMAGEDISPLAY( win->imagedisplay ),
910945
finger_cx, finger_cy, &win->scale_cx, &win->scale_cy );
911946
}
947+
912948
static void
913949
image_window_scale_changed( GtkGestureZoom *self,
914950
gdouble scale, gpointer user_data )
@@ -1343,7 +1379,6 @@ image_window_init( ImageWindow *win )
13431379
g_settings_get_value( win->settings, "control" ) );
13441380
change_state( GTK_WIDGET( win ), "info",
13451381
g_settings_get_value( win->settings, "info" ) );
1346-
13471382
}
13481383

13491384
static void
@@ -1493,6 +1528,10 @@ image_window_open( ImageWindow *win, GFile *file )
14931528
{
14941529
TileSource *tile_source;
14951530

1531+
// show the progress bar ... if we don't, it can take so many events
1532+
// to show that we never see it
1533+
gtk_action_bar_set_revealed( GTK_ACTION_BAR( win->progress_bar ),
1534+
TRUE );
14961535
if( !(tile_source = tile_source_new_from_file( file )) ) {
14971536
image_window_error( win );
14981537
return;

src/meson.build

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,21 @@ resources = gnome.compile_resources(
1111
)
1212

1313
executable('vipsdisp', [
14-
marshal,
15-
resources,
16-
'displaybar.c',
17-
'gtkutil.c',
18-
'imagedisplay.c',
19-
'imagewindow.c',
20-
'infobar.c',
21-
'main.c',
22-
'tile.c',
23-
'tilecache.c',
24-
'tilesource.c',
25-
'tslider.c',
26-
'vipsdispapp.c',
27-
],
14+
marshal,
15+
resources,
16+
'displaybar.c',
17+
'gtkutil.c',
18+
'imagedisplay.c',
19+
'imagewindow.c',
20+
'infobar.c',
21+
'main.c',
22+
'tile.c',
23+
'tilecache.c',
24+
'tilesource.c',
25+
'tslider.c',
26+
'vipsdispapp.c',
27+
'saveoptions.c',
28+
],
2829
dependencies: vipsdisp_deps,
2930
install: true,
3031
)

0 commit comments

Comments
 (0)