30
use POSIX qw/setlocale/;
30
use POSIX qw/setlocale strftime/;
31
31
use Locale::gettext;
32
32
use Glib qw/TRUE FALSE/;
33
33
use FindBin '$Bin'; #path where plugin is located
34
34
use File::Temp qw/ tempfile tempdir /;
35
use Time::HiRes qw/usleep/;
37
#load modules at custom path at runtime
38
#--------------------------------------
40
import lib $ENV{'SHUTTER_ROOT'}."/share/shutter/resources/modules";
42
#proc (Thanks to Michael Schilli)
45
#load shutter's modules
46
#--------------------------------------
47
require Shutter::App::SimpleDialogs;
36
49
#configure gettext using ENV Variable (setup during shutter start)
37
50
setlocale( LC_MESSAGES, "" );
38
51
my $d = Locale::gettext->domain("shutter-plugins");
39
52
$d->dir( $ENV{'SHUTTER_INTL'} );
54
#shutter will ask for some infos
55
my %plugin_info = ( 'name' => $d->get( "polaroid" ),
56
'sort' => $d->get( "effect" ),
57
'tip' => $d->get("make your screenshot look like a polaroid photo, add a caption, and even rotate it a little"),
41
60
binmode( STDOUT, ":utf8" );
42
#gscrot will ask for some infos - no need of changing anything
43
if ( $ARGV[ 0 ] eq "name" ) {
44
print $d->get( "polaroid" );
46
} elsif ( $ARGV[ 0 ] eq "sort" ) {
47
print $d->get( "effect" );
49
} elsif ( $ARGV[ 0 ] eq "tip" ) {
51
"make your screenshot look like a polaroid photo, add a caption, and even rotate it a little"
54
} elsif ( $ARGV[ 0 ] eq "ext" ) {
57
} elsif ( $ARGV[ 0 ] eq "lang" ) {
61
if ( exists $plugin_info{$ARGV[ 0 ]} ) {
62
print $plugin_info{$ARGV[ 0 ]};
75
82
my $plug = Gtk2::Plug->new( $socket_id );
84
$plug->set_default_icon_from_file( $ENV{'SHUTTER_ICON'} );
76
86
$plug->set_border_width( 10 );
78
88
$plug->signal_connect( destroy => sub { Gtk2->main_quit } );
80
#variables used in this plugin
81
my $width_preview = 0;
82
my $height_preview = 0;
91
my $tooltips = Gtk2::Tooltips->new;
84
93
#configure buttons and other needed controls
85
my $caption_frame = Gtk2::Frame->new( $d->get( "Caption:" ) );
87
94
my $caption_label = Gtk2::Label->new( $d->get( "Text:" ) );
88
95
my $caption_entry = Gtk2::Entry->new();
89
$caption_entry->set_text( "$ENV{ 'USER' }" );
96
$caption_entry->set_text( "%c - $filename" );
101
"There are several wild-cards available, like\n%Y = year\n%m = month\n%d = day\n%T = time"
107
"There are several wild-cards available, like\n%Y = year\n%m = month\n%d = day\n%T = time"
91
111
my $pointsize_label = Gtk2::Label->new( $d->get( "Font size:" ) );
92
112
my $pointsize_sbutton = Gtk2::SpinButton->new_with_range( 1, 200, 1 );
93
$pointsize_sbutton->set_value( 40 );
113
$pointsize_sbutton->set_value( 20 );
95
115
my $gravity_label = Gtk2::Label->new( $d->get( "Gravity:" ) );
96
116
my $gravity_combo = Gtk2::ComboBox->new_text;
109
129
my $angle_sbutton = Gtk2::SpinButton->new_with_range( -90, 90, 1 );
110
130
$angle_sbutton->set_value( -5 );
132
my $stroke_color_label = Gtk2::Label->new( $d->get("Stroke color"). ":" );
133
my $stroke_color = Gtk2::ColorButton->new();
134
$stroke_color->set_color( Gtk2::Gdk::Color->parse('black') );
135
$stroke_color->set_alpha( int( 1.0 * 65535 ) );
136
$stroke_color->set_use_alpha(TRUE);
137
$stroke_color->set_title( $d->get("Choose stroke color") );
139
#we define two Gtk2::Image widgets
140
#to store the screenshot
141
#and a throbber that is shown while the changes are processed
143
Gtk2::Image->new_from_pixbuf(
144
Gtk2::Gdk::Pixbuf->new_from_file_at_scale( $filename, 300, 300, TRUE ) );
147
Gtk2::Image->new_from_file( $ENV{'SHUTTER_ROOT'}."/share/shutter/resources/icons/throbber.gif" );
149
my $sg = Gtk2::SizeGroup->new ('both');
150
$sg->set ('ignore-hidden' => FALSE);
151
$sg->add_widget($preview);
152
$sg->add_widget($preview_throb);
155
#we define three Gtk2::Button widgets
156
#to refresh, save and cancel the plugin's work
112
157
my $refresh_btn = Gtk2::Button->new_from_stock( 'gtk-refresh' );
113
158
$refresh_btn->signal_connect( 'clicked', \&fct_imagemagick_polaroid,
117
Gtk2::Image->new_from_pixbuf(
118
Gtk2::Gdk::Pixbuf->new_from_file_at_scale( $filename, 300, 300, TRUE ) );
121
161
my $save_btn = Gtk2::Button->new_from_stock( 'gtk-save' );
122
162
$save_btn->signal_connect( 'clicked', \&fct_imagemagick_polaroid, 'save' );
124
164
my $cancel_btn = Gtk2::Button->new_from_stock( 'gtk-cancel' );
125
$cancel_btn->signal_connect( 'clicked' => sub { Gtk2->main_quit }, 'cancel' );
165
$cancel_btn->signal_connect( 'clicked' => sub { Gtk2->main_quit; exit 2; }, 'cancel' );
127
167
#define the gui layout
128
168
my $vbox_param = Gtk2::VBox->new( TRUE, 10 );
152
193
$hbox4->pack_start( $angle_label, FALSE, TRUE, 5 );
153
194
$hbox4->pack_start( $angle_sbutton, TRUE, TRUE, 5 );
196
$hbox5->pack_start( $stroke_color_label, FALSE, TRUE, 5 );
197
$hbox5->pack_start( $stroke_color, FALSE, TRUE, 5 );
155
199
$hbox_row1->pack_start_defaults( $hbox1 );
156
200
$hbox_row1->pack_start_defaults( $hbox2 );
158
202
$hbox_row2->pack_start_defaults( $hbox3 );
159
203
$hbox_row2->pack_start_defaults( $hbox4 );
204
$hbox_row2->pack_start_defaults( $hbox5 );
161
206
$vbox_param->pack_start( $hbox_row1, TRUE, TRUE, 5 );
173
218
$vbox_main->pack_start( $vbox_param, FALSE, TRUE, 5 );
175
$vbox_main->pack_start( $preview, TRUE, TRUE, 5 );
220
$vbox_main->pack_start( $preview, TRUE, TRUE, 5 );
221
$vbox_main->pack_start( $preview_throb, TRUE, TRUE, 5 );
176
222
$vbox_main->pack_start( $refresh_btn, TRUE, TRUE, 5 );
178
224
$hbox_btn->pack_start( $cancel_btn, TRUE, TRUE, 5 );
186
#generate first preview
187
&fct_imagemagick_polaroid( undef, 'refresh' );
232
#hide the preview widget at startup
236
my ( $tmpfh, $tmpfilename ) = tempfile();
238
$tmpfilename .= ".png";
240
#we fork a child process to do the work
241
my $process = Proc::Simple->new;
242
#create tempfiles for subprocess outputs
243
my ( $tmpfh_stdout, $tmpfilename_stdout ) = tempfile(UNLINK => 1);
244
my ( $tmpfh_stderr, $tmpfilename_sterr ) = tempfile(UNLINK => 1);
245
$process->redirect_output ($tmpfilename_stdout, $tmpfilename_sterr);
247
#generate first preview at startup
250
&fct_imagemagick_polaroid( undef, 'refresh' );
195
261
sub fct_imagemagick_polaroid {
196
262
my ( $widget, $data ) = @_;
198
my $image = Image::Magick->new;
264
$save_btn->set_sensitive(FALSE);
200
266
if ( $data eq 'save' ) {
202
my ( $tmpfh, $tmpfilename ) = tempfile();
203
my $convert_output = undef;
204
if ( $use_caption ) {
206
system( "convert -caption '"
207
. $caption_entry->get_text
208
. "' '$filename' -pointsize "
209
. $pointsize_sbutton->get_value
211
. $gravity_combo->get_active_text
212
. " -bordercolor snow "
213
. " -background black "
215
. $angle_sbutton->get_value
218
$convert_output = system( "
219
convert '$filename' -bordercolor white -border 6 -bordercolor grey60 -border 1 -background none -rotate "
220
. $angle_sbutton->get_value
221
. " -background black \\( +clone -shadow 60x4+4+4 \\) +swap -background none -flatten $tmpfilename"
268
my $image = Image::Magick->new;
224
269
$image->ReadImage( $tmpfilename );
225
270
$image->WriteImage( filename => $filename );
231
my ( $tmpfh, $tmpfilename ) = tempfile();
232
my $convert_output = undef;
233
if ( $use_caption ) {
235
system( "convert -caption '"
236
. $caption_entry->get_text
237
. "' '$filename' -pointsize "
238
. $pointsize_sbutton->get_value
240
. $gravity_combo->get_active_text
241
. " -bordercolor snow "
242
. " -background black "
244
. $angle_sbutton->get_value
247
$convert_output = system( "
248
convert '$filename' -bordercolor white -border 6 -bordercolor grey60 -border 1 -background none -rotate "
249
. $angle_sbutton->get_value
250
. " -background black \\( +clone -shadow 60x4+4+4 \\) +swap -background none -flatten $tmpfilename"
280
$preview_throb->show_all;
289
#so we can update the gui
290
while ( $process->poll ) {
296
$preview->set_from_pixbuf( Gtk2::Gdk::Pixbuf->new_from_file_at_scale(
297
$tmpfilename, 300, 300, TRUE)
300
#error -> read output from tempfiles
303
my $error_string = "Plugin error:\n";
305
#reading stdout from file
306
while (<$tmpfh_stdout>){
309
#reading stderr from file
310
while (<$tmpfh_stderr>){
314
#get the parent window of the plug
315
require X11::Protocol;
316
my $x11 = X11::Protocol->new( $ENV{ 'DISPLAY' } );
318
my $plugp = Gtk2::Gdk::Window->foreign_new( &find_wm_window( $x11, $plug->get_id ));
321
my $shutter_dialog = Shutter::App::SimpleDialogs->new($plug, $plugp);
322
$shutter_dialog->dlg_error_message(
323
sprintf ( $d->get( "Error while executing plugin %s." ), "'" . $plugin_info{'name'} . "'" ) ,
324
$d->get( "There was an error executing the plugin." ),
253
$preview->set_from_pixbuf(
254
Gtk2::Gdk::Pixbuf->new_from_file_at_scale(
255
$tmpfilename, 300, 300, TRUE
337
$save_btn->set_sensitive(TRUE);
339
$preview_throb->hide_all;
262
####define your functions here
347
my $convert_output = undef;
348
if ( $use_caption ) {
350
my $text = $caption_entry->get_text;
352
$text = strftime $text, localtime;
353
$text = quotemeta $text;
355
my $color = $stroke_color->get_color;
357
system( "convert -caption "
360
. sprintf( "#%04x%04x%04x%04x", $color->red, $color->green, $color->blue, $stroke_color->get_alpha )
361
. "' '$filename' -pointsize "
362
. $pointsize_sbutton->get_value
364
. $gravity_combo->get_active_text
365
. " -bordercolor snow "
366
. " -background black "
368
. $angle_sbutton->get_value
374
convert '$filename' -bordercolor white -border 6 -bordercolor grey60 -border 1 -background none -rotate "
375
. $angle_sbutton->get_value
376
. " -background black \\( +clone -shadow 60x4+4+4 \\) +swap -background none -flatten $tmpfilename"
385
while ( Gtk2->events_pending ) {
386
Gtk2->main_iteration;
398
my ( $qroot, $qparent, @qkids ) = $x11->QueryTree($xid);
399
return undef unless ( $qroot || $qparent );
400
return $xid if ( $qroot == $qparent );