diff -Nru shutter-0.99.2/.bzrignore shutter-0.99.5/.bzrignore --- shutter-0.99.2/.bzrignore 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/.bzrignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -.project -.settings diff -Nru shutter-0.99.2/.github/ISSUE_TEMPLATE/bug_report.md shutter-0.99.5/.github/ISSUE_TEMPLATE/bug_report.md --- shutter-0.99.2/.github/ISSUE_TEMPLATE/bug_report.md 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/.github/ISSUE_TEMPLATE/bug_report.md 2024-03-31 16:18:07.000000000 +0000 @@ -19,4 +19,4 @@ #### Error output -#### Extra information, such as Shutter version, operating system and ideas for how to solve: +#### Extra information, such as Shutter version, display server in use (Xorg or Wayland), operating system and ideas for how to solve: diff -Nru shutter-0.99.2/.github/workflows/run-tests.yml shutter-0.99.5/.github/workflows/run-tests.yml --- shutter-0.99.2/.github/workflows/run-tests.yml 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/.github/workflows/run-tests.yml 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + run-tests: + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - uses: actions/checkout@v2 + + - name: Install deps + run: | + sudo apt update + sudo apt install cpanminus gir1.2-ayatanaappindicator3-0.1 gir1.2-wnck-3.0 imagemagick \ + libcarp-always-perl libfile-basedir-perl libfile-copy-recursive-perl libfile-which-perl \ + libglib-object-introspection-perl libglib-perl libgoocanvas2-perl libgoocanvas-2.0-dev \ + libgtk3-perl libimage-exiftool-perl libimage-magick-perl \ + libjson-maybexs-perl libjson-perl liblocale-gettext-perl liblwp-protocol-https-perl \ + libnet-dbus-perl libnet-dbus-glib-perl libnet-oauth-perl libnumber-bytes-human-perl \ + libpango-perl libpath-class-perl libproc-processtable-perl libproc-simple-perl \ + libreadonly-perl librsvg2-common libsort-naturally-perl libwww-mechanize-perl libwww-perl \ + libx11-protocol-other-perl libx11-protocol-perl libxml-simple-perl procps xdg-utils + + sudo cpanm --installdeps --notest . + sudo cpanm --sudo --notest Gtk3::ImageView + sudo cpanm --sudo --notest GooCanvas2::CairoTypes + + - name: Run tests + env: + TEST_APP_SHUTTER_PATH: ${{ env.GITHUB_WORKSPACE }} + uses: GabrielBB/xvfb-action@v1 + with: + run: prove -I share/shutter/resources/modules/ -I t/lib t -r diff -Nru shutter-0.99.2/.gitignore shutter-0.99.5/.gitignore --- shutter-0.99.2/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/.gitignore 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1 @@ +.vscode diff -Nru shutter-0.99.2/Makefile shutter-0.99.5/Makefile --- shutter-0.99.2/Makefile 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/Makefile 2024-03-31 16:18:07.000000000 +0000 @@ -15,10 +15,10 @@ uninstall: rm $(prefix)/bin/shutter - rm $(prefix)/share/appdata/shutter.appdata.xml + rm $(prefix)/share/metainfo/shutter.metainfo.xml rm $(prefix)/share/applications/shutter.desktop rm -r $(prefix)/share/doc/shutter/ - rm $(prefix)/share/man/man1/shutter.1.gz + rm $(prefix)/share/man/man1/shutter.1 rm $(prefix)/share/pixmaps/shutter.png rm -r $(prefix)/share/shutter/ rm $(prefix)/share/icons/HighContrast/scalable/apps/shutter.svg @@ -37,4 +37,4 @@ rm $$locale/LC_MESSAGES/$$mofile; \ fi \ done \ - done \ No newline at end of file + done diff -Nru shutter-0.99.2/bin/shutter shutter-0.99.5/bin/shutter --- shutter-0.99.2/bin/shutter 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/bin/shutter 2024-03-31 16:18:07.000000000 +0000 @@ -93,7 +93,7 @@ use XML::Simple; #DBus message system -use Net::DBus qw/ dbus_string dbus_boolean /; +use Net::DBus; #HTTP Status code processing use HTTP::Status; @@ -116,6 +116,8 @@ #sort lexically, but sort numeral parts numerically use Sort::Naturally; +use English; + sub escape_string { return $_[0]; @@ -164,9 +166,9 @@ #define constants #-------------------------------------- use constant MAX_ERROR => 5; -use constant SHUTTER_REV => 'Rev.1593'; +use constant SHUTTER_REV => 'Rev.1783'; use constant SHUTTER_NAME => 'Shutter'; -use constant SHUTTER_VERSION => '0.99.2'; +use constant SHUTTER_VERSION => '0.99.5'; #-------------------------------------- @@ -180,7 +182,6 @@ $shutter_root = $1; } -my $shutter_path = $Bin . "/shutter"; $ENV{'SHUTTER_ROOT'} = $shutter_root; #used by some plugins #-------------------------------------- @@ -245,14 +246,20 @@ #-------------------------------------- -my $sc = Shutter::App::Common->new($shutter_root, undef, SHUTTER_NAME, SHUTTER_VERSION, SHUTTER_REV, $$); +my $sc = Shutter::App::Common->new( + shutter_root => $shutter_root, + main_window => undef, + appname => SHUTTER_NAME, + version => SHUTTER_VERSION, + rev => SHUTTER_REV, + pid => $PID +); my $shf = Shutter::App::HelperFunctions->new($sc); my $so = Shutter::App::Options->new($sc, $shf); -my $sdir = Shutter::App::Directories->new(); #set home #-------------------------------------- -$ENV{'HOME'} = $sdir->get_home_dir; +$ENV{'HOME'} = Shutter::App::Directories::get_home_dir(); #-------------------------------------- @@ -433,7 +440,7 @@ }); #create needed directories etc. - fct_init_environment(); + Shutter::App::Directories::create_hidden_home_dir_if_not_exist(); #print debug output fct_init_debug_output() if $sc->get_debug; @@ -457,12 +464,22 @@ $window->set_default_size(-1, 500); #UPDATE WINDOW LIST (signal is connected when GUI is loaded) - my $wnck_screen = Wnck::Screen::get_default(); - $wnck_screen->force_update if $wnck_screen; - my $x11_supported = $wnck_screen ? 1 : 0; + + my $x11_supported = 1; + if ($ENV{XDG_SESSION_TYPE} eq "wayland") { + $x11_supported = 0; + } + + my $wnck_screen; + if ($x11_supported) { + $wnck_screen = Wnck::Screen::get_default(); + $wnck_screen->force_update if $wnck_screen; + } #TRAY ICON AND MENU my $tray = undef; + my $tray_libappindicator = undef; + my $tray_legacy = undef; my $tray_menu = fct_ret_tray_menu(); #SESSION NOTEBOOK @@ -644,35 +661,13 @@ #-------------------------------------- #command line param set to disable tray icon? unless ($sc->get_disable_systray) { - - $tray = Gtk3::StatusIcon->new(); - $tray->set_from_icon_name("shutter-panel"); - $tray->set_visible(1); - - fct_update_gui(); - - if ($tray->is_embedded) { - $tray->{'hid'} = $tray->signal_connect( - 'popup-menu' => sub { evt_show_systray_statusicon(@_); }, - $tray - ); - $tray->{'hid2'} = $tray->signal_connect( - 'activate' => sub { - evt_activate_systray_statusicon(@_); - $tray; - }, - $tray - ); - } else { - $tray = undef; - } - - if ($appindicator && !defined $tray) { - # Fallback to AppIndicator. This one doesn't allow left-click signal, but it seems to be the only option for Gnome on Ubuntu 21.04... - $tray = AppIndicator::Indicator->new("Shutter", "shutter-panel", 'application-status'); - $tray->set_menu($tray_menu); - $tray->set_status('active'); - } + fct_try_init_tray(); + Glib::Timeout->add(10000, sub { + if (!$tray_legacy || !$tray_legacy->is_embedded) { + fct_try_init_tray(); + } + return TRUE; + }); } #-------------------------------------- @@ -726,6 +721,7 @@ my $zoom_box = Gtk3::HBox->new(FALSE, 0); my $as_help_box = Gtk3::HBox->new(FALSE, 0); + my $as_confirmation_box = Gtk3::HBox->new(FALSE, 0); my $asel_isize_box = Gtk3::HBox->new(FALSE, 0); my $asel_isize_box2 = Gtk3::HBox->new(FALSE, 0); my $border_box = Gtk3::HBox->new(FALSE, 0); @@ -821,6 +817,13 @@ fct_check_installed_upload_plugins(); } + unless ($x11_supported) { + my $wayland_warning = Gtk3::Label->new; + $wayland_warning->set_line_wrap(TRUE); + $wayland_warning->set_markup($d->get("Wayland support is limited, for more advanced screenshots please switch back to Xorg. Click here for details.")); + $vbox->pack_start($wayland_warning, FALSE, TRUE, 0); + } + $vbox->pack_start($st->create_toolbar, FALSE, TRUE, 0); $st->{_redoshot}->signal_connect('clicked' => \&evt_take_screenshot, 'redoshot'); @@ -832,7 +835,9 @@ $st->{_full}->signal_connect('clicked' => \&evt_take_screenshot, 'full'); #init menus - $st->{_full}->set_menu(fct_ret_workspace_menu(TRUE)); + if ($x11_supported){ + $st->{_full}->set_menu(fct_ret_workspace_menu(TRUE)); + } $st->{_window}->set_menu(fct_ret_window_menu()); #and attach signal handlers @@ -885,7 +890,7 @@ my $combobox_settings_profiles = Gtk3::ComboBoxText->new; my @current_profiles; my $current_index = 0; - foreach my $pfile (sort bsd_glob("$ENV{'HOME'}/.shutter/profiles/*.xml")) { + foreach my $pfile (sort +bsd_glob("$ENV{'HOME'}/.shutter/profiles/*.xml")) { utf8::decode $pfile; next if $pfile =~ /\_accounts.xml/; #accounts file - we are looking for "real" profiles @@ -1072,35 +1077,10 @@ $filename->set_text("\$name_\%NNN"); } - #do some input validation - #here are all invalid char codes - my @invalid_codes = (47, 92, 63, 42, 58, 124, 34, 60, 62, 44, 59, 35, 38); my $filename_hint = Gtk3::Label->new; $filename_hint->set_no_show_all(TRUE); - $filename->signal_connect( - 'key-press-event' => sub { - my $filename = shift; - my $event = shift; - - my $input = Gtk3::Gdk::keyval_to_unicode($event->keyval); - - #invalid input - #~ print $input."\n"; - if (grep($input == $_, @invalid_codes)) { - my $char = chr($input); - $char = 'amp();' if $char eq '&'; - $filename_hint->set_markup("" . sprintf($d->get("Reserved character %s is not allowed to be in a filename."), "'" . $char . "'") . ""); - $filename_hint->show; - return TRUE; - } else { - - #clear possible message when valid char is entered - $filename_hint->set_markup(""); - $filename_hint->hide; - return FALSE; - } - }); + fct_validate_filename($filename, $filename_hint); my $filename_tooltip_string = $d->get("There are several wildcards available, like\n") @@ -1145,13 +1125,14 @@ #add compatile, writeable file types my $combobox_type = Gtk3::ComboBoxText->new; - my ($int_png, $int_jpeg, $int_bmp) = (-1, -1, -1); + my ($int_png, $int_jpeg, $int_bmp, $int_webp) = (-1, -1, -1, -1); my $format_counter = 0; foreach my $format (Gtk3::Gdk::Pixbuf::get_formats()) { if ( $format->get_name eq "jpeg" || $format->get_name eq "png" - || $format->get_name eq "bmp") + || $format->get_name eq "bmp" + || $format->get_name eq "webp") { #we want jpg not jpeg @@ -1169,6 +1150,8 @@ $int_png = $format_counter; } elsif ($format->get_name eq "bmp") { $int_bmp = $format_counter; + } elsif ($format->get_name eq "webp") { + $int_webp = $format_counter; } $format_counter++; @@ -1210,7 +1193,7 @@ } else { #we will try to set a default value in this order - foreach my $cformat (@{[$int_png, $int_jpeg, $int_bmp]}) { + foreach my $cformat (@{[$int_png, $int_jpeg, $int_bmp, $int_webp]}) { if ($cformat > -1) { $combobox_type->set_active($cformat); last; @@ -1681,6 +1664,23 @@ #end - show help text when using advanced selection tool #-------------------------------------- + + #confirmation is necessary in selection tool + #-------------------------------------- + my $as_confirmation_necessary = Gtk3::CheckButton->new_with_label($d->get("Confirmation necessary")); + + if (defined $settings_xml->{'general'}->{'as_confirmation_necessary'}) { + $as_confirmation_necessary->set_active($settings_xml->{'general'}->{'as_confirmation_necessary'}); + } else { + $as_confirmation_necessary->set_active(TRUE); + } + + $as_confirmation_necessary->set_tooltip_text($d->get("Pressing the enter key or doubleclicking is necessary to take the screenshot")); + + $as_confirmation_box->pack_start($as_confirmation_necessary, FALSE, TRUE, 12); + + #end - confirmation is necessary in selection tool + #-------------------------------------- #border #-------------------------------------- @@ -2382,10 +2382,11 @@ $sg_asel2->add_widget($asel_size_label2); $sg_asel2->add_widget($asel_size_label4); - $sel_capture_vbox->pack_start($zoom_box, FALSE, TRUE, 3); - $sel_capture_vbox->pack_start($as_help_box, FALSE, TRUE, 3); - $sel_capture_vbox->pack_start($asel_isize_box, FALSE, TRUE, 3); - $sel_capture_vbox->pack_start($asel_isize_box2, FALSE, TRUE, 3); + $sel_capture_vbox->pack_start($zoom_box, FALSE, TRUE, 3); + $sel_capture_vbox->pack_start($as_help_box, FALSE, TRUE, 3); + $sel_capture_vbox->pack_start($as_confirmation_box, FALSE, TRUE, 3); + $sel_capture_vbox->pack_start($asel_isize_box, FALSE, TRUE, 3); + $sel_capture_vbox->pack_start($asel_isize_box2, FALSE, TRUE, 3); $sel_capture_frame->add($sel_capture_vbox); $window_capture_vbox->pack_start($border_box, FALSE, TRUE, 3); @@ -2768,6 +2769,10 @@ #-------------------------------------- sub evt_value_changed { my ($widget, $data) = @_; + + # a small workaround when the widget is undef + $widget ||= "undef"; + print "\n$data was emitted by widget $widget\n" if $sc->get_debug; @@ -2869,7 +2874,9 @@ } elsif ($combobox_type->get_active_text =~ /jpg/) { $settings->set_jpg_quality($scale->get_value); } elsif ($combobox_type->get_active_text =~ /png/) { - $settings->set_png_quality($scale->get_value); + $settings->set_jpg_quality($scale->get_value); + } elsif ($combobox_type->get_active_text =~ /webp/) { + $settings->set_webp_quality($scale->get_value); } else { $settings->clear_quality_settings(); } @@ -2909,6 +2916,12 @@ $scale->set_range(0, 9); $scale->set_value(9); $scale_label->set_text($d->get("Compression") . ":"); + } elsif ($combobox_type->get_active_text =~ /webp/) { + $scale->set_sensitive(TRUE); + $scale_label->set_sensitive(TRUE); + $scale->set_range(0, 100); + $scale->set_value(98); + $scale_label->set_text($d->get("Quality") . ":"); } else { $scale->set_sensitive(FALSE); $scale_label->set_sensitive(FALSE); @@ -3186,7 +3199,12 @@ if $combobox_settings_profiles->get_active != -1; #autostart - $sas->create_autostart_file($sdir->get_autostart_dir, $fs_active->get_active, $fs_min_active->get_active, $fs_nonot_active->get_active); + $sas->create_autostart_file( + Shutter::App::Directories::get_autostart_dir(), + $fs_active->get_active, + $fs_min_active->get_active, + $fs_nonot_active->get_active + ); $app->quit; @@ -3748,7 +3766,12 @@ if $combobox_settings_profiles->get_active != -1; #autostart - $sas->create_autostart_file($sdir->get_autostart_dir, $fs_active->get_active, $fs_min_active->get_active, $fs_nonot_active->get_active); + $sas->create_autostart_file( + Shutter::App::Directories::get_autostart_dir(), + $fs_active->get_active, + $fs_min_active->get_active, + $fs_nonot_active->get_active + ); #we need to update the first tab here #because the profile might have changed @@ -3843,7 +3866,12 @@ fct_save_settings($new_profile_name); #autostart - $sas->create_autostart_file($sdir->get_autostart_dir, $fs_active->get_active, $fs_min_active->get_active, $fs_nonot_active->get_active); + $sas->create_autostart_file( + Shutter::App::Directories::get_autostart_dir(), + $fs_active->get_active, + $fs_min_active->get_active, + $fs_nonot_active->get_active + ); } return TRUE; } @@ -3859,7 +3887,7 @@ unless ($shf->file_exists("$ENV{'HOME'}/.shutter/profiles/" . $active_text . ".xml") || $shf->file_exists("$ENV{'HOME'}/.shutter/profiles/" . $active_text . "_accounts.xml")) { - $combobox_settings_profiles->remove_text($active_index); + $combobox_settings_profiles->remove($active_index); $combobox_settings_profiles->set_active($combobox_settings_profiles->get_active + 1); $current_profile_indx = $combobox_settings_profiles->get_active; @@ -3900,6 +3928,49 @@ #functions #-------------------------------------- + sub fct_try_init_tray { + $tray_legacy = Gtk3::StatusIcon->new(); + $tray_legacy->set_from_icon_name("shutter-panel"); + $tray_legacy->set_visible(1); + + fct_update_gui(); + + if ($tray_legacy->is_embedded) { + if ($tray_libappindicator) { + $tray_libappindicator->set_status('passive'); + $tray_libappindicator = undef; + } + $tray = $tray_legacy; + $tray->{'hid'} = $tray->signal_connect( + 'popup-menu' => sub { evt_show_systray_statusicon(@_); }, + $tray + ); + $tray->{'hid2'} = $tray->signal_connect( + 'activate' => sub { + evt_activate_systray_statusicon(@_); + $tray; + }, + $tray + ); + } else { + $tray_legacy->set_visible(0); + $tray_legacy = undef; + + if ($appindicator && !$tray_libappindicator) { + # Fallback to AppIndicator. This one doesn't allow left-click signal, but it seems to be the only option for Gnome on Ubuntu 21.04... + $tray_libappindicator = AppIndicator::Indicator->new("Shutter", "shutter-panel", 'application-status'); + $tray_libappindicator->set_menu($tray_menu); + $tray_libappindicator->set_status('active'); + } + + if ($tray_libappindicator) { + $tray = $tray_libappindicator; + } else { + $tray = undef; + } + } + } + sub fct_create_session_notebook { #~ $notebook->set( 'homogeneous' => TRUE ); @@ -4150,11 +4221,8 @@ $tray->signal_handler_block($tray->{'hid2'}); } } elsif ($tray && $tray->isa('AppIndicator::Indicator')) { - if (Gtk3::AppIndicator->VERSION > 0.12) { - $tray->set_passive(); - } + $tray->set_status('passive'); } - } elsif ($action eq 'unblock') { $sensitive = TRUE; @@ -4175,11 +4243,8 @@ $tray->signal_handler_unblock($tray->{'hid2'}); } } elsif ($tray && $tray->isa('AppIndicator::Indicator')) { - if (Gtk3::AppIndicator->VERSION > 0.12) { - $tray->set_active(); - } + $tray->set_status('active'); } - } #enable/disable controls @@ -4477,7 +4542,13 @@ #main $settings{'general'}->{'filetype'} = $combobox_type->get_active; $settings{'general'}->{'quality'} = $scale->get_value(); - $settings{'general'}->{'filename'} = $filename->get_text(); + if ($filename->get_text() =~ "\%NN" || $filename->get_text() =~ "\%T") { + $settings{'general'}->{'filename'} = $filename->get_text(); + } else { + $sd->dlg_error_message($@, $d->get("Settings could not be saved! Please make sure that the filename contains a wildcard like \%NN or \%T!")); + evt_show_settings(); + return 1; + } $settings{'general'}->{'folder'} = Glib::filename_to_unicode($saveDir_button->get_filename()); #~ print "Pfad ".$saveDir_button->get_filename()."\n"; @@ -4492,8 +4563,9 @@ $settings{'general'}->{'delay'} = $delay->get_value(); #wrksp -> submenu - $settings{'general'}->{'current_monitor_active'} = $current_monitor_active->get_active; - + if ($x11_supported) { + $settings{'general'}->{'current_monitor_active'} = $current_monitor_active->get_active; + } #determining timeout if ($gnome_web_photo) { my $web_menu = $st->{_web}->get_menu; @@ -4531,6 +4603,7 @@ #advanced $settings{'general'}->{'zoom_active'} = $zoom_active->get_active(); $settings{'general'}->{'as_help_active'} = $as_help_active->get_active(); + $settings{'general'}->{'as_confirmation_necessary'} = $as_confirmation_necessary->get_active(); $settings{'general'}->{'asel_x'} = $asel_size3->get_value(); $settings{'general'}->{'asel_y'} = $asel_size4->get_value(); $settings{'general'}->{'asel_w'} = $asel_size1->get_value(); @@ -4706,7 +4779,7 @@ $filename->set_text($settings_xml->{'general'}->{'filename'}); utf8::decode $settings_xml->{'general'}->{'folder'}; - $saveDir_button->set_current_folder($settings_xml->{'general'}->{'folder'}); + $saveDir_button->set_filename($settings_xml->{'general'}->{'folder'}); $save_auto_active->set_active($settings_xml->{'general'}->{'save_auto'}); $save_ask_active->set_active($settings_xml->{'general'}->{'save_ask'}); @@ -4763,8 +4836,8 @@ #advanced settings $zoom_active->set_active($settings_xml->{'general'}->{'zoom_active'}); - $as_help_active->set_active($settings_xml->{'general'}->{'as_help_active'}); + $as_confirmation_necessary->set_active($settings_xml->{'general'}->{'as_confirmation_necessary'}); $asel_size3->set_value($settings_xml->{'general'}->{'asel_x'}); $asel_size4->set_value($settings_xml->{'general'}->{'asel_y'}); @@ -4928,6 +5001,11 @@ #get applications my $apps = Glib::IO::AppInfo::get_recommended_for_type('image/png'); + # $apps is undefined if Glib::IO::AppInfo::get_recommended_for_type fails + unless (defined $apps) { + return $model; + } + #no apps determined! unless (scalar @$apps) { return $model; @@ -5457,6 +5535,12 @@ my $image = $clipboard->wait_for_image; if (defined $image) { + #folder to save + my $folder = Glib::filename_to_unicode($saveDir_button->get_filename) ; + if ($save_no_active->get_active) { + $folder = Shutter::App::Directories::get_cache_dir(); + } + #determine current file type (name - description) $combobox_type->get_active_text =~ /(.*) -/; my $filetype_value = $1; @@ -5465,14 +5549,20 @@ fct_control_main_window('show'); return FALSE; } - - #create tempfile - my ($tmpfh, $tmpfilename) = tempfile(UNLINK => 1); - $tmpfilename .= "." . $filetype_value; - + + #generate random filename + my $short = "clipboard-import-" . int(rand(10000000000)); + + #relative to abs + my $tmpfilename = $folder ."/" . $short ."." . $filetype_value; + unless (File::Spec->file_name_is_absolute($tmpfilename)) { + $tmpfilename = File::Spec->rel2abs($tmpfilename); + } + #save pixbuf to tempfile and integrate it if ($sp->save_pixbuf_to_file($image, $tmpfilename, $filetype_value)) { my $new_key = fct_integrate_screenshot_in_notebook(Glib::IO::File::new_for_path($tmpfilename), $image); + $session_screens{$new_key}->{'image'}->set_fitting(TRUE); } } else { @@ -5570,6 +5660,33 @@ return TRUE; } + sub fct_validate_filename{ + my $myfilename = shift; + my $myfilename_hint = shift; + my @invalid_codes = (47, 92); + $myfilename->signal_connect( + 'key-press-event' => sub { + shift; + my $event = shift; + + my $input = Gtk3::Gdk::keyval_to_unicode($event->keyval); + + #invalid input + #~ print $input."\n"; + if (grep($input == $_, @invalid_codes)) { + my $char = chr($input); + $char = 'amp();' if $char eq '&'; + $myfilename_hint->set_markup("" . sprintf($d->get("Reserved character %s is not allowed to be in a filename."), "'" . $char . "'") . ""); + return TRUE; + } else { + + #clear possible message when valid char is entered + $myfilename_hint->set_markup(""); + return FALSE; + } + }); + } + sub fct_undo { my $key = fct_get_current_file(); @@ -5856,7 +5973,7 @@ #folder to save my $folder = Glib::filename_to_unicode($saveDir_button->get_filename) || $folder_from_config; if ($save_no_active->get_active) { - $folder = $sdir->get_cache_dir; + $folder = Shutter::App::Directories::get_cache_dir(); } #screenshot(pixbuf) and screenshot name @@ -5910,22 +6027,20 @@ #fullscreen screenshot if ($data eq "full" || $data eq "tray_full") { - $screenshooter = Shutter::Screenshot::Workspace->new( - $sc, $include_cursor, $delay_value, - $notify_timeout_active->get_active, - $wnck_screen ? $wnck_screen->get_active_workspace : undef, - undef, undef, $current_monitor_active->get_active - ); - - if ($ENV{XDG_SESSION_TYPE} eq "wayland") { + if ($x11_supported) { + $screenshooter = Shutter::Screenshot::Workspace->new( + $sc, $include_cursor, $delay_value, + $notify_timeout_active->get_active, + $wnck_screen ? $wnck_screen->get_active_workspace : undef, + undef, undef, $current_monitor_active->get_active + ); + $screenshot = $screenshooter->workspace(); + } else { # TODO: support kwin directly, because it has more features than the xdg portal $screenshot = Shutter::Screenshot::Wayland::xdg_portal($screenshooter); - } else { - - $screenshot = $screenshooter->workspace(); } - #window + #window } elsif ($data eq "window" || $data eq "tray_window" || $data eq "awindow" @@ -5985,7 +6100,7 @@ $screenshooter = Shutter::Screenshot::SelectorAdvanced->new( $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, $zoom_active->get_active, $hide_time->get_value, $as_help_active->get_active, $asel_size3->get_value, - $asel_size4->get_value, $asel_size1->get_value, $asel_size2->get_value, + $asel_size4->get_value, $asel_size1->get_value, $asel_size2->get_value, $as_confirmation_necessary->get_active, ); $screenshot = $screenshooter->select_advanced(); @@ -6222,6 +6337,15 @@ #start postprocessing here #...successfully??? + + # $screenshot is undefined if the selection width or height is zero + # Actually this should be handled by Shutter::Screenshot::Error but fails for an area with zero width or height for some reason + unless ($screenshot) { + my $response = $sd->dlg_error_message($d->get("Error: selection width or height is zero, please retry!"), $d->get("Failed")); + fct_control_main_window('show'); + return FALSE; + } + my $giofile = undef; my $error = Shutter::Screenshot::Error->new($sc, $screenshot, $data, $extra); if ($error->is_error) { @@ -7142,14 +7266,14 @@ #tray menu foreach my $child ($tray_menu->get_children) { if ($child->get_name eq 'quicks') { - $child->remove_submenu; + $child->set_submenu(undef); $child->set_sensitive(FALSE); last; } } #main menu - $sm->{_menuitem_quicks}->remove_submenu; + $sm->{_menuitem_quicks}->set_submenu(undef); $sm->{_menuitem_quicks}->set_sensitive(FALSE); #and statusbar @@ -7301,7 +7425,7 @@ } #update is_unsaved flag - if ($session_screens{$key}->{'folder'} eq $sdir->get_cache_dir) { + if ($session_screens{$key}->{'folder'} eq Shutter::App::Directories::get_cache_dir()) { $session_screens{$key}->{'is_unsaved'} = TRUE; } else { $session_screens{$key}->{'is_unsaved'} = FALSE; @@ -7541,7 +7665,7 @@ $sd->dlg_error_message( sprintf($d->get("Error while saving the image %s."), "'" . $session_screens{$key}->{'short'} . "'"), - sprintf($d->get("There was an error saving the image to %s."), "'" . $sdir->get_cache_dir . "'"), + sprintf($d->get("There was an error saving the image to %s."), "'" . Shutter::App::Directories::get_cache_dir() . "'"), undef, undef, undef, undef, undef, undef, $@ ); @@ -7564,7 +7688,7 @@ #prepare new filename my ($short, $folder, $ext) = fileparse($session_screens{$key}->{'long'}, qr/\.[^.]*/); - my $new_giofile = fct_get_next_filename($short, $sdir->get_cache_dir, $ext); + my $new_giofile = fct_get_next_filename($short, Shutter::App::Directories::get_cache_dir(), $ext); my $new_filename = $shf->utf8_decode(unescape_string($new_giofile->get_path)); if ($sp->save_pixbuf_to_file($pixbuf, $new_filename, $session_screens{$key}->{'filetype'})) { @@ -7695,7 +7819,7 @@ print "Parsing wildcards for $screenshot_name\n" if $sc->get_debug; - + #parse width and height my $swidth = $screenshot->get_width; my $sheight = $screenshot->get_height; @@ -7715,14 +7839,18 @@ #set name #e.g. window or workspace name - if (my $action_name = $screenshooter->get_action_name) { - utf8::decode $action_name; - $action_name =~ s/(\/|\#|\>|\<|\%|\*)/-/g; - $screenshot_name =~ s/\$name/$action_name/g; - - #no blanks (special wildcard) - $action_name =~ s/\ //g; - $screenshot_name =~ s/\$nb_name/$action_name/g; + if ($x11_supported) { + if (my $action_name = $screenshooter->get_action_name) { + utf8::decode $action_name; + $action_name =~ s/(\/|\#|\>|\<|\%|\*)/-/g; + $screenshot_name =~ s/\$name/$action_name/g; + + #no blanks (special wildcard) + $action_name =~ s/\ //g; + $screenshot_name =~ s/\$nb_name/$action_name/g; + } else { + $screenshot_name =~ s/(\$name|\$nb_name)/unknown/g; + } } else { $screenshot_name =~ s/(\$name|\$nb_name)/unknown/g; } @@ -8154,6 +8282,9 @@ my $init = shift; my $menu_wrksp = Gtk3::Menu->new; + unless ($x11_supported) { + return $menu_wrksp; + } my $wnck_screen = Wnck::Screen::get_default(); unless ($wnck_screen) { @@ -8594,6 +8725,13 @@ # https://developer.gnome.org/gio/stable/GAppInfo.html my $apps = Glib::IO::AppInfo::get_recommended_for_type($mime_type); + # $apps is undefined if Glib::IO::AppInfo::get_recommended_for_type fails + unless (defined $apps) { + $sm->{_menuitem_reopen}->set_sensitive(FALSE); + $sm->{_menuitem_large_reopen}->set_sensitive(FALSE); + return $menu_programs; + } + #no apps determined! unless (scalar @$apps) { $sm->{_menuitem_reopen}->set_sensitive(FALSE); @@ -8661,11 +8799,11 @@ my $menu_web = Gtk3::Menu->new; - my $timeout0 = Gtk3::RadioMenuItem->new(undef, $d->get("Wait indefinitely")); - my $timeout1 = Gtk3::RadioMenuItem->new($timeout0, sprintf($d->nget("Wait max %d second", "Wait max %d seconds", 10), 10)); - my $timeout2 = Gtk3::RadioMenuItem->new($timeout0, sprintf($d->nget("Wait max %d second", "Wait max %d seconds", 10), 30)); - my $timeout3 = Gtk3::RadioMenuItem->new($timeout0, sprintf($d->nget("Wait max %d minute", "Wait max %d minutes", 1), 1)); - my $timeout4 = Gtk3::RadioMenuItem->new($timeout0, sprintf($d->nget("Wait max %d minute", "Wait max %d minutes", 2), 2)); + my $timeout0 = Gtk3::RadioMenuItem->new_with_label(undef, $d->get("Wait indefinitely")); + my $timeout1 = Gtk3::RadioMenuItem->new_with_label($timeout0, sprintf($d->nget("Wait max %d second", "Wait max %d seconds", 10), 10)); + my $timeout2 = Gtk3::RadioMenuItem->new_with_label($timeout0, sprintf($d->nget("Wait max %d second", "Wait max %d seconds", 10), 30)); + my $timeout3 = Gtk3::RadioMenuItem->new_with_label($timeout0, sprintf($d->nget("Wait max %d minute", "Wait max %d minutes", 1), 1)); + my $timeout4 = Gtk3::RadioMenuItem->new_with_label($timeout0, sprintf($d->nget("Wait max %d minute", "Wait max %d minutes", 2), 2)); $timeout0->set_name("timeout0"); $timeout1->set_name("timeout10"); @@ -8749,7 +8887,7 @@ my $group = undef; my $counter = 0; foreach my $profile (@{$current_profiles_ref}) { - my $profile_item = Gtk3::RadioMenuItem::new($group, $profile); + my $profile_item = Gtk3::RadioMenuItem->new_with_label($group, $profile); $profile_item->set_active(TRUE) if $profile eq $combobox_settings_profiles->get_active_text; $profile_item->signal_connect( @@ -9141,24 +9279,11 @@ return $curr_value; } - sub fct_init_environment { - - #is there already a .shutter folder? - mkdir("$ENV{ 'HOME' }/.shutter") - unless (-d "$ENV{ 'HOME' }/.shutter"); - - #...and a profiles folder? - mkdir "$ENV{'HOME'}/.shutter/profiles" - unless (-d "$ENV{'HOME'}/.shutter/profiles"); - - return TRUE; - } - sub fct_init_unsaved_files { #delete all files in this folder #except the ones that are in the current session - my @unsaved_files = bsd_glob($sdir->get_cache_dir . "/*"); + my @unsaved_files = bsd_glob(Shutter::App::Directories::get_cache_dir() . "/*"); foreach my $unsaved_file (@unsaved_files) { utf8::decode $unsaved_file; print $unsaved_file, " checking \n" if $sc->get_debug; @@ -9174,6 +9299,11 @@ print "\nINFO: gathering system information..."; print "\n"; print "\n"; + print "Shutter "; + print SHUTTER_VERSION; + print ' '; + print SHUTTER_REV; + print "\n"; #kernel info if (File::Which::which('uname')) { @@ -9236,29 +9366,7 @@ my $new_filename = Gtk3::Entry->new(); $new_filename->set_activates_default(TRUE); - #here are all invalid char codes - my @invalid_codes = (47, 92, 63, 37, 42, 58, 124, 34, 60, 62, 44, 59, 35, 38); - $new_filename->signal_connect( - 'key-press-event' => sub { - my $new_filename = shift; - my $event = shift; - - my $input = Gtk3::Gdk::keyval_to_unicode($event->keyval); - - #invalid input - #~ print $input."\n"; - if (grep($input == $_, @invalid_codes)) { - my $char = chr($input); - $char = 'amp();' if $char eq '&'; - $new_filename_hint->set_markup("" . sprintf($d->get("Reserved character %s is not allowed to be in a filename."), "'" . $char . "'") . ""); - return TRUE; - } else { - - #clear possible message when valid char is entered - $new_filename_hint->set_markup(""); - return FALSE; - } - }); + fct_validate_filename($new_filename, $new_filename_hint); #parse filename my ($short, $folder, $ext) = fileparse($session_screens{$key}->{'long'}, qr/\.[^.]*/); @@ -9664,6 +9772,8 @@ $combobox_save_as_type->set_active($counter); } elsif ($mime eq "image/bmp" && $ext eq ".bmp") { $combobox_save_as_type->set_active($counter); + } elsif ($mime eq "image/webp" && $ext eq ".webp") { + $combobox_save_as_type->set_active($counter); } } } @@ -10726,29 +10836,7 @@ my $new_profile_name = Gtk3::Entry->new(); $new_profile_name->set_activates_default(TRUE); - #here are all invalid char codes - my @invalid_codes = (47, 92, 63, 37, 42, 58, 124, 34, 60, 62, 44, 59, 35, 38); - $new_profile_name->signal_connect( - 'key-press-event' => sub { - my $new_profile_name = shift; - my $event = shift; - - my $input = Gtk3::Gdk::keyval_to_unicode($event->keyval); - - #invalid input - #~ print $input."\n"; - if (grep($input == $_, @invalid_codes)) { - my $char = chr($input); - $char = 'amp();' if $char eq '&'; - $new_profile_name_hint->set_markup("" . sprintf($d->get("Reserved character %s is not allowed to be in a filename."), "'" . $char . "'") . ""); - return TRUE; - } else { - - #clear possible message when valid char is entered - $new_profile_name_hint->set_markup(""); - return FALSE; - } - }); + fct_validate_filename($new_profile_name, $new_profile_name_hint); #show name of current profile $new_profile_name->set_text($curr_profile_name) diff -Nru shutter-0.99.2/cpanfile shutter-0.99.5/cpanfile --- shutter-0.99.2/cpanfile 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/cpanfile 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,20 @@ +requires "Gtk3"; +requires "Pango"; +requires "Glib"; +requires "Gtk3::ImageView"; +requires "Number::Bytes::Human"; +requires "XML::Simple"; +requires "Net::DBus"; +requires "HTTP::Status"; +requires "Digest::MD5"; +requires "Proc::Simple"; +requires "Sort::Naturally"; +requires "GooCanvas2"; +requires "Image::Magick"; +requires "Locale::gettext"; +requires "Proc::Killfam"; +requires "File::Which"; +requires "File::Copy::Recursive"; +requires "Moo", ">= 2.0"; + +test_requires "Test::MockModule"; diff -Nru shutter-0.99.2/debian/changelog shutter-0.99.5/debian/changelog --- shutter-0.99.2/debian/changelog 2022-02-20 21:58:56.000000000 +0000 +++ shutter-0.99.5/debian/changelog 2024-04-03 12:06:34.000000000 +0000 @@ -1,3 +1,24 @@ +shutter (0.99.5-1) unstable; urgency=medium + + [ Debian Janitor ] + * Update standards version to 4.6.2, no changes needed. + + [ gregor herrmann ] + * Import upstream version 0.99.5. + * Update Upstream-Contact. + * Update debian/copyright: new and removed files. + * Adjust some paths in debian/*. + * Add a lintian override. + * debian/control: update substitution variables. + * Fix hashbangs in modules. + * Update runtime dependencies from cpanfile. + * Run the new test suite during build. + Add Build-Depends-Indep to debian/control and + override_dh_auto_test to debian/rules. + * Fix and extend autopkgtests. + + -- Andrej Shadura Wed, 03 Apr 2024 14:06:34 +0200 + shutter (0.99.2-4) unstable; urgency=high * Rebuild with no source changes. diff -Nru shutter-0.99.2/debian/control shutter-0.99.5/debian/control --- shutter-0.99.2/debian/control 2022-02-20 21:58:56.000000000 +0000 +++ shutter-0.99.5/debian/control 2024-04-03 12:06:34.000000000 +0000 @@ -5,7 +5,50 @@ Priority: optional Build-Depends: debhelper-compat (= 13), -Standards-Version: 4.6.0 +Build-Depends-Indep: + gir1.2-ayatanaappindicator3-0.1 , + gir1.2-wnck-3.0 , + imagemagick , + libcarp-always-perl , + libfile-basedir-perl , + libfile-copy-recursive-perl , + libfile-which-perl , + libglib-object-introspection-perl , + libglib-perl , + libgoocanvas2-cairotypes-perl , + libgoocanvas2-perl , + libgtk3-imageview-perl (>= 10) , + libgtk3-perl , + libhttp-message-perl , + libimage-exiftool-perl , + libimage-magick-perl , + libjson-maybexs-perl , + libjson-perl , + liblocale-gettext-perl , + liblwp-protocol-https-perl , + libmoo-perl , + libnet-dbus-glib-perl , + libnet-dbus-perl , + libnet-oauth-perl , + libnumber-bytes-human-perl , + libpango-perl , + libpath-class-perl , + libproc-processtable-perl , + libproc-simple-perl , + libreadonly-perl , + librsvg2-common , + libsort-naturally-perl , + libtest-mockmodule-perl , + libwww-mechanize-perl , + libwww-perl , + libx11-protocol-other-perl , + libx11-protocol-perl , + libxml-simple-perl , + procps , + xauth , + xdg-utils , + xvfb , +Standards-Version: 4.6.2 Rules-Requires-Root: no Vcs-Browser: https://salsa.debian.org/perl-team/modules/packages/shutter Vcs-Git: https://salsa.debian.org/perl-team/modules/packages/shutter.git @@ -28,12 +71,14 @@ libgoocanvas2-perl, libgtk3-imageview-perl (>= 10), libgtk3-perl, + libhttp-message-perl, libimage-exiftool-perl, libimage-magick-perl, libjson-maybexs-perl, libjson-perl, liblocale-gettext-perl, liblwp-protocol-https-perl, + libmoo-perl, libnet-dbus-perl, libnet-dbus-glib-perl, libnet-oauth-perl, @@ -53,7 +98,7 @@ procps, xdg-utils, ${misc:Depends}, - ${shlibs:Depends}, + ${perl:Depends}, Suggests: nautilus-sendto, Description: feature-rich screenshot program diff -Nru shutter-0.99.2/debian/copyright shutter-0.99.5/debian/copyright --- shutter-0.99.2/debian/copyright 2022-02-20 21:58:56.000000000 +0000 +++ shutter-0.99.5/debian/copyright 2024-04-03 12:06:34.000000000 +0000 @@ -1,6 +1,6 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Shutter -Upstream-Contact: Alexey Sokolov +Upstream-Contact: Alexander Ruzhnikov Source: https://github.com/shutter-project/shutter/releases Files: * @@ -10,29 +10,14 @@ and Shutter Team License: GPL-3+ -Files: share/appdata/* +Files: share/metainfo/* Copyright: 2017 Matthias Mailänder -License: CC0-1.0 or GPL-3+ +License: CC0-1.0 Files: share/shutter/resources/modules/X11/Protocol/Ext/XFIXES.pm Copyright: 2011-2014 Kevin Ryde License: GPL-3+ -Files: share/shutter/resources/modules/WebService/Dropbox.pm -Copyright: 2011-2014 Shinichiro Aska -License: GPL-2+ - -Files: share/shutter/resources/system/upload_plugins/upload/vgyme.pm -Copyright: 2014 SwooshyCueb -License: GPL-3+ - -Files: share/shutter/resources/system/upload_plugins/upload/ToileLibre.pm -Copyright: - 2010-2011 Vadim Rutkovsky - Mario Kemper - and Shutter Team -License: GPL-3+ - Files: share/shutter/resources/po/* Copyright: 2008-2020 Rosetta Contributors and Canonical Ltd @@ -43,8 +28,6 @@ share/shutter/resources/icons/*.svg share/shutter/resources/icons/lpi-*.png share/shutter/resources/icons/drawing_tool/*.png - share/shutter/resources/icons/drawing_tool/draw-image.svg - share/shutter/resources/icons/drawing_tool/objects/*.svg share/shutter/resources/system/plugins/perl/spresize/*.svg share/shutter/resources/system/plugins/shell/sptrim/sptrim.svg Copyright: Tango Icons are released into Public Domain @@ -56,6 +39,21 @@ for some reason, but they are otherwise identical as those distributed by the Tango project, and hence are also in the public domain. +Files: + share/shutter/resources/modules/Shutter/App/Common.pm + share/shutter/resources/modules/Shutter/App/Directories.pm +Copyright: + 2008-2013 Mario Kemper + 2021 Alexander Ruzhnikov +License: GPL-3+ + +Files: + share/shutter/resources/modules/Shutter/Draw/Ellipse.pm + share/shutter/resources/modules/Shutter/Draw/UIManager.pm + share/shutter/resources/modules/Shutter/Draw/Utils.pm +Copyright: 2021 Alexander Ruzhnikov +License: GPL-3+ + Files: share/shutter/resources/icons/drawing_tool/cursor/* Copyright: Inkscape Project License: GPL-2+ diff -Nru shutter-0.99.2/debian/gbp.conf shutter-0.99.5/debian/gbp.conf --- shutter-0.99.2/debian/gbp.conf 2022-02-20 21:58:56.000000000 +0000 +++ shutter-0.99.5/debian/gbp.conf 2024-04-03 12:06:34.000000000 +0000 @@ -1,3 +1,4 @@ [DEFAULT] debian-branch = debian/unstable upstream-branch = upstream/latest +upstream-vcs-tag=v%(version)s diff -Nru shutter-0.99.2/debian/install shutter-0.99.5/debian/install --- shutter-0.99.2/debian/install 2022-02-20 21:58:56.000000000 +0000 +++ shutter-0.99.5/debian/install 2024-04-03 12:06:34.000000000 +0000 @@ -1,8 +1,8 @@ bin/* /usr/bin/ -share/appdata/* /usr/share/metainfo/ share/applications /usr/share/ share/icons /usr/share/ share/locale /usr/share/ share/man /usr/share/ +share/metainfo/* /usr/share/metainfo/ share/pixmaps /usr/share/ share/shutter /usr/share/ diff -Nru shutter-0.99.2/debian/lintian-overrides shutter-0.99.5/debian/lintian-overrides --- shutter-0.99.2/debian/lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/debian/lintian-overrides 2024-04-03 12:06:34.000000000 +0000 @@ -0,0 +1,2 @@ +# needed for About dialog +shutter: extra-license-file [usr/share/shutter/resources/license/gplv3] diff -Nru shutter-0.99.2/debian/rules shutter-0.99.5/debian/rules --- shutter-0.99.2/debian/rules 2022-02-20 21:58:56.000000000 +0000 +++ shutter-0.99.5/debian/rules 2024-04-03 12:06:34.000000000 +0000 @@ -1,17 +1,22 @@ #!/usr/bin/make -f # -*- makefile -*- +PACKAGE = $(shell dh_listpackages) +TMP = $(CURDIR)/debian/$(PACKAGE) + %: dh $@ -execute_after_dh_install override_dh_auto_install: PACKAGE := $(firstword $(shell dh_listpackages)) +override_dh_auto_test: + TEST_APP_SHUTTER_PATH=$(CURDIR) xvfb-run -a prove -I $(CURDIR)/share/shutter/resources/modules/ -I $(CURDIR)/t/lib -r -v t override_dh_auto_install: - dh_auto_install -- prefix=debian/$(PACKAGE)/usr + dh_auto_install -- prefix=$(TMP)/usr execute_after_dh_install: dh_install - mkdir -p debian/$(PACKAGE)/usr/share/perl5/ - mv debian/$(PACKAGE)/usr/share/shutter/resources/modules/Shutter debian/$(shell dh_listpackages)/usr/share/perl5/ - mv debian/$(PACKAGE)/usr/share/shutter/resources/modules/WebService debian/$(shell dh_listpackages)/usr/share/perl5/ - rm -fr debian/$(PACKAGE)/usr/share/shutter/resources/modules/ debian/$(PACKAGE)/usr/share/doc/*/COPYING + mkdir -p $(TMP)/usr/share/perl5/ + mv $(TMP)/usr/share/shutter/resources/modules/Shutter $(TMP)/usr/share/perl5/ + $(RM) -r $(TMP)/usr/share/shutter/resources/modules/ $(TMP)/usr/share/doc/*/COPYING + find $(TMP)/usr/share -type f -name '*.pm' -print0 | \ + xargs -r0 sed -i -e '1s|^#! /usr/bin/env perl|#!/usr/bin/perl|' diff -Nru shutter-0.99.2/debian/tests/pkg-perl/smoke-env shutter-0.99.5/debian/tests/pkg-perl/smoke-env --- shutter-0.99.2/debian/tests/pkg-perl/smoke-env 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/debian/tests/pkg-perl/smoke-env 2024-04-03 12:06:34.000000000 +0000 @@ -0,0 +1,2 @@ +PERL5LIB=${TDIR}/t/lib +TEST_APP_SHUTTER_PATH=/usr diff -Nru shutter-0.99.2/debian/tests/pkg-perl/syntax-skip shutter-0.99.5/debian/tests/pkg-perl/syntax-skip --- shutter-0.99.2/debian/tests/pkg-perl/syntax-skip 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/debian/tests/pkg-perl/syntax-skip 2024-04-03 12:06:34.000000000 +0000 @@ -0,0 +1 @@ +# Empty file to check everything despite Suggests diff -Nru shutter-0.99.2/debian/tests/pkg-perl/use-name shutter-0.99.5/debian/tests/pkg-perl/use-name --- shutter-0.99.2/debian/tests/pkg-perl/use-name 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/debian/tests/pkg-perl/use-name 2024-04-03 12:06:34.000000000 +0000 @@ -0,0 +1,2 @@ +# pick one +Shutter::App::Common diff -Nru shutter-0.99.2/share/appdata/shutter.appdata.xml shutter-0.99.5/share/appdata/shutter.appdata.xml --- shutter-0.99.2/share/appdata/shutter.appdata.xml 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/appdata/shutter.appdata.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ - - - org.shutterproject.shutter - shutter.desktop - CC0-1.0 - GPL-3.0+ - -

- Shutter is a feature-rich screenshot program. You can take a screenshot of a - specific area, window, your whole screen, or even of a website - apply different - effects to it, draw on it to highlight points, and then upload to an image - hosting site, all within one window. -

-

- Shutter allows you to capture nearly anything on your screen without losing - control over your screenshots (tabbed interface). - You don't need to open an external graphics editor like GIMP, because Shutter - ships with its own built-in editor. -

-
- https://shutter-project.org/ - https://github.com/shutter-project/shutter/issues - https://shutter-project.org/contact/ - - https://shutter-project.org/wp-content/uploads/key_feature_030.png - https://shutter-project.org/wp-content/uploads/key_feature_042.png - https://shutter-project.org/wp-content/uploads/key_feature_036.png - https://shutter-project.org/wp-content/uploads/key_feature_073.png - -
diff -Nru shutter-0.99.2/share/man/man1/shutter.1 shutter-0.99.5/share/man/man1/shutter.1 --- shutter-0.99.2/share/man/man1/shutter.1 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/man/man1/shutter.1 2024-03-31 16:18:07.000000000 +0000 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== @@ -38,27 +38,36 @@ . ds PI \(*p . ds L" `` . ds R" '' +. ds C` +. ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" -.\" If the F register is turned on, we'll generate index entries on stderr for +.\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. -.ie \nF \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX .. -. nr % 0 -. rr F -.\} -.el \{\ -. de IX +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" .. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} .\} +.rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. @@ -124,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "SHUTTER 1" -.TH SHUTTER 1 "2013-08-25" "perl v5.14.2" "User Contributed Perl Documentation" +.TH SHUTTER 1 "2022-02-19" "perl v5.34.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -154,7 +163,7 @@ .IP "Example 4" 8 .IX Item "Example 4" shutter \-\-web=http://shutter\-project.org/ \-e -.SS "\s-1CAPTURE\s0 \s-1MODE\s0 \s-1OPTIONS\s0" +.SS "\s-1CAPTURE MODE OPTIONS\s0" .IX Subsection "CAPTURE MODE OPTIONS" .IP "\fB\-s, \-\-select=[X,Y,WIDTH,HEIGHT]\fR" 8 .IX Item "-s, --select=[X,Y,WIDTH,HEIGHT]" @@ -168,9 +177,6 @@ .IP "\fB\-a, \-\-active\fR" 8 .IX Item "-a, --active" Capture the current active window. -.IP "\fB\-\-section\fR" 8 -.IX Item "--section" -Capture a section. You will be able to select any child window by moving the mouse over it. .IP "\fB\-m, \-\-menu\fR" 8 .IX Item "-m, --menu" Capture a menu. @@ -183,7 +189,7 @@ .IP "\fB\-r, \-\-redo\fR" 8 .IX Item "-r, --redo" Redo last screenshot. -.SS "\s-1SETTINGS\s0 \s-1OPTIONS\s0" +.SS "\s-1SETTINGS OPTIONS\s0" .IX Subsection "SETTINGS OPTIONS" .IP "\fB\-p, \-\-profile=NAME\fR" 8 .IX Item "-p, --profile=NAME" @@ -222,7 +228,7 @@ .IP "\fB\-C, \-\-remove_cursor\fR" 8 .IX Item "-C, --remove_cursor" Remove cursor when taking a screenshot. -.SS "\s-1APPLICATION\s0 \s-1OPTIONS\s0" +.SS "\s-1APPLICATION OPTIONS\s0" .IX Subsection "APPLICATION OPTIONS" .IP "\fB\-h, \-\-help\fR" 8 .IX Item "-h, --help" @@ -232,7 +238,7 @@ Prints version information. .IP "\fB\-\-debug\fR" 8 .IX Item "--debug" -Prints a lot of debugging information to \s-1STDOUT\s0. +Prints a lot of debugging information to \s-1STDOUT.\s0 .IP "\fB\-\-clear_cache\fR" 8 .IX Item "--clear_cache" Clears cache, e.g. installed plugins, at startup. @@ -253,7 +259,7 @@ If you find a bug in Shutter, you should report it. But first, you should make sure that it really is a bug, and that it appears in the latest version of Shutter. .PP The latest version is always available from: -\&\fBhttps://launchpad.net/shutter\fR +\&\fBhttps://github.com/shutter\-project/shutter/releases\fR .PP -Once you have determined that a bug actually exists, please report it at launchpad: -\&\fBhttps://bugs.launchpad.net/shutter/+filebug\fR +Once you have determined that a bug actually exists, please report it at github: +\&\fBhttps://github.com/shutter\-project/shutter/issues/new\fR diff -Nru shutter-0.99.2/share/metainfo/shutter.metainfo.xml shutter-0.99.5/share/metainfo/shutter.metainfo.xml --- shutter-0.99.2/share/metainfo/shutter.metainfo.xml 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/share/metainfo/shutter.metainfo.xml 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,40 @@ + + + org.shutterproject.shutter + Shutter + The feature-rich screenshot tool + shutter.desktop + CC0-1.0 + GPL-3.0+ + +

+ Shutter is a feature-rich screenshot program. You can take a screenshot of a + specific area, window, your whole screen, or even of a website - apply different + effects to it, draw on it to highlight points, and then upload to an image + hosting site, all within one window. +

+

+ Shutter allows you to capture nearly anything on your screen without losing + control over your screenshots (tabbed interface). + You don't need to open an external graphics editor like GIMP, because Shutter + ships with its own built-in editor. +

+
+ https://shutter-project.org/ + https://github.com/shutter-project/shutter/issues + https://shutter-project.org/contact/ + + + https://shutter-project.org/wp-content/uploads/key_feature_030.png + + + https://shutter-project.org/wp-content/uploads/key_feature_042.png + + + https://shutter-project.org/wp-content/uploads/key_feature_036.png + + + https://shutter-project.org/wp-content/uploads/key_feature_073.png + + +
diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/App/Autostart.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/App/Autostart.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/App/Autostart.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/App/Autostart.pm 2024-03-31 16:18:07.000000000 +0000 @@ -56,7 +56,10 @@ my $path = $dir . "/shutter.desktop"; - open FILE, ">:utf8", $path or die $!; + open FILE, ">:utf8", $path or do { + warn "WARNING: can not create/update file $path: $!\n"; + return FALSE; + }; foreach my $line (@data) { if ($line =~ /Exec=shutter/) { @@ -74,7 +77,7 @@ } print FILE $line; } - close FILE or die $!; + close FILE or warn "WARNING: close $path fail: $!\n"; return TRUE; } diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/App/Common.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/App/Common.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/App/Common.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/App/Common.pm 2024-03-31 16:18:07.000000000 +0000 @@ -1,6 +1,7 @@ ################################################### # # Copyright (C) 2008-2013 Mario Kemper +# Copyright (C) 2021 Alexander Ruzhnikov # # This file is part of Shutter. # @@ -22,419 +23,171 @@ package Shutter::App::Common; -#modules -#-------------------------------------- use utf8; -use strict; +use Moo; use Gtk3; #Gettext and filename parsing -use POSIX qw/setlocale/; +use POSIX qw/ setlocale /; use Locale::gettext; #Glib -use Glib qw/TRUE FALSE/; +use Glib qw/ TRUE FALSE /; -#File -use File::Spec; +has shutter_root => ( is => "ro", required => 1 ); +has main_window => ( is => "rw", required => 1 ); +has appname => ( is => "ro", required => 1 ); +has version => ( is => "ro", required => 1 ); +has rev => ( is => "ro", required => 1 ); +has pid => ( is => "ro", required => 1 ); -#-------------------------------------- +has debug => ( is => "rw", default => sub {TRUE} ); +has clear_cache => ( is => "rw", default => sub {FALSE} ); +has min => ( is => "rw", default => sub {FALSE} ); +has disable_systray => ( is => "rw", default => sub {FALSE} ); +has exit_after_capture => ( is => "rw", default => sub {FALSE} ); +has no_session => ( is => "rw", default => sub {FALSE} ); -##################public subs################## -sub new { - my $class = shift; - - #constructor - my $self = {_shutter_root => shift, _mainwindow => shift, _appname => shift, _version => shift, _rev => shift, _pid => shift}; - - #vars - $self->{_debug_cparam} = TRUE; - $self->{_clear_cache} = FALSE; - $self->{_min_cparam} = FALSE; - $self->{_disable_systray_cparam} = FALSE; - $self->{_exit_after_capture_cparam} = FALSE; - $self->{_no_session_cparam} = FALSE; - $self->{_start_with} = undef; - $self->{_start_with_extra} = undef; - $self->{_profile_to_start_with} = undef; - $self->{_export_filename} = undef; - $self->{_delay} = undef; - $self->{_include_cursor} = undef; - $self->{_remove_cursor} = undef; - - #Set LC_NUMERIC to C to prevent decimal commas (or anything else) - setlocale(LC_NUMERIC, "C"); - setlocale(LC_MESSAGES, ""); - - #gettext init - $self->{_gettext_object} = Locale::gettext->domain("shutter"); - $self->{_gettext_object}->dir($self->{_shutter_root} . "/share/locale"); - - #ENV needed by some plugins - $ENV{'SHUTTER_INTL'} = $self->{_shutter_root} . "/share/locale"; - - #notification object - $self->{_notification}; - - #globalsettings object - $self->{_globalsettings}; - - #icontheme to determine if icons exist or not - #in some cases we deliver fallback icons - $self->{_icontheme} = Gtk3::IconTheme::get_default(); - $self->{_icontheme}->append_search_path($self->{_shutter_root} . "/share/icons"); - - #recently used upload tab - $self->{_ruu_tab} = 0; - - #... and details - $self->{_ruu_hosting} = 0; - $self->{_ruu_places} = 0; - $self->{_ruu_u1} = 0; +# private attributes +has _start_with => ( is => "rw", lazy => 1 ); +has _start_with_extra => ( is => "rw", lazy => 1 ); - #recently used save folder - $self->{_rusf} = undef; +has profile_to_start_with => ( is => "rw", lazy => 1 ); +has export_filename => ( is => "rw", lazy => 1 ); +has delay => ( is => "rw", lazy => 1 ); +has include_cursor => ( is => "rw", lazy => 1 ); +has remove_cursor => ( is => "rw", lazy => 1 ); - #recently used open folder - $self->{_ruof} = undef; +has gettext_object => ( + is => "rw", + lazy => 1, + builder => sub { + my $self = shift; - bless $self, $class; - return $self; -} - -#getter / setter -sub get_root { - my $self = shift; - return $self->{_shutter_root}; -} - -sub get_pid { - my $self = shift; - return $self->{_pid}; -} - -sub set_pid { - my $self = shift; - if (@_) { - $self->{_pid} = shift; - } - return $self->{_pid}; -} - -sub get_appname { - my $self = shift; - return $self->{_appname}; -} - -sub get_version { - my $self = shift; - return $self->{_version}; -} + my $l = Locale::gettext->domain("shutter"); + $l->dir( $self->shutter_root . "/share/locale" ); -sub get_rev { - my $self = shift; - return $self->{_rev}; -} + return $l; + }, +); -sub get_gettext { - my $self = shift; - return $self->{_gettext_object}; -} +has notification => ( is => "rw", lazy => 1 ); +has global_settings => ( is => "rw", lazy => 1 ); -sub set_gettext { - my $self = shift; - if (@_) { - $self->{_gettext_object} = shift; - } - return $self->{_gettext_object}; -} +#icontheme to determine if icons exist or not +#in some cases we deliver fallback icons +has icontheme => ( + is => "rw", + lazy => 1, + builder => "_setup_icontheme", +); -sub get_theme { - my $self = shift; - return $self->{_icontheme}; -} +#recently used upload tab +has ruu_tab => ( is => "rw", default => sub {0} ); -sub get_notification_object { - my $self = shift; - return $self->{_notification}; -} +#... and details +has ruu_hosting => ( is => "rw", default => sub {0} ); +has ruu_places => ( is => "rw", default => sub {0} ); -sub set_notification_object { - my $self = shift; - if (@_) { - $self->{_notification} = shift; - } - return $self->{_notification}; -} +# TODO: this attribute looks like isn't used. Consider to remove it later +has ruu_u1 => ( is => "rw", default => sub {0} ); -sub get_globalsettings_object { - my $self = shift; - return $self->{_globalsettings}; -} +#recently used save folder +has rusf => ( is => "rw", lazy => 1 ); -sub set_globalsettings_object { - my $self = shift; - if (@_) { - $self->{_globalsettings} = shift; - } - return $self->{_globalsettings}; -} +#recently used open folder +has ruof => ( is => "rw", lazy => 1 ); -# filename -sub get_rusf { - my $self = shift; - return $self->{_rusf}; -} +sub BUILD { + my ( $self, $args ) = @_; -sub set_rusf { - my $self = shift; - if (@_) { - $self->{_rusf} = shift; - } - return $self->{_rusf}; -} + setlocale( LC_NUMERIC, "C" ); + setlocale( LC_MESSAGES, "" ); -# uri -sub get_ruof { - my $self = shift; - return $self->{_ruof}; + $ENV{'SHUTTER_INTL'} = $args->{shutter_root} . "/share/locale"; } -sub set_ruof { - my $self = shift; - if (@_) { - $self->{_ruof} = shift; - } - return $self->{_ruof}; -} +sub _setup_icontheme { + my $self = shift; -sub get_ruu_tab { - my $self = shift; - return $self->{_ruu_tab}; -} - -sub set_ruu_tab { - my $self = shift; - if (@_) { - $self->{_ruu_tab} = shift; - } - return $self->{_ruu_tab}; -} - -sub get_ruu_hosting { - my $self = shift; - return $self->{_ruu_hosting}; -} - -sub get_ruu_places { - my $self = shift; - return $self->{_ruu_places}; -} - -sub get_ruu_u1 { - my $self = shift; - return $self->{_ruu_u1}; -} - -sub set_ruu_hosting { - my $self = shift; - if (@_) { - $self->{_ruu_hosting} = shift; - } - return $self->{_ruu_hosting}; -} - -sub set_ruu_places { - my $self = shift; - if (@_) { - $self->{_ruu_places} = shift; - } - return $self->{_ruu_places}; -} - -sub set_ruu_u1 { - my $self = shift; - if (@_) { - $self->{_ruu_u1} = shift; - } - return $self->{_ruu_u1}; -} - -sub get_debug { - my $self = shift; - return $self->{_debug_cparam}; -} - -sub set_debug { - my $self = shift; - if (@_) { - $self->{_debug_cparam} = shift; - } - return $self->{_debug_cparam}; -} - -sub get_clear_cache { - my $self = shift; - return $self->{_clear_cache}; -} + my $theme = Gtk3::IconTheme::get_default(); + $theme->append_search_path( $self->shutter_root . "/share/icons" ); -sub set_clear_cache { - my $self = shift; - if (@_) { - $self->{_clear_cache} = shift; - } - return $self->{_clear_cache}; + return $theme; } -sub get_mainwindow { - my $self = shift; - return $self->{_mainwindow}; -} - -sub set_mainwindow { - my $self = shift; - if (@_) { - $self->{_mainwindow} = shift; - } - return $self->{_mainwindow}; -} - -sub get_min { - my $self = shift; - return $self->{_min_cparam}; -} - -sub set_min { - my $self = shift; - if (@_) { - $self->{_min_cparam} = shift; - } - return $self->{_min_cparam}; -} - -sub get_disable_systray { - my $self = shift; - return $self->{_disable_systray_cparam}; -} - -sub set_disable_systray { - my $self = shift; - if (@_) { - $self->{_disable_systray_cparam} = shift; - } - return $self->{_disable_systray_cparam}; -} - -sub get_exit_after_capture { - my $self = shift; - return $self->{_exit_after_capture_cparam}; -} - -sub set_exit_after_capture { - my $self = shift; - if (@_) { - $self->{_exit_after_capture_cparam} = shift; - } - return $self->{_exit_after_capture_cparam}; -} - -sub get_no_session { - my $self = shift; - return $self->{_no_session_cparam}; -} +sub get_current_monitor { + my $self = shift; -sub set_no_session { - my $self = shift; - if (@_) { - $self->{_no_session_cparam} = shift; - } - return $self->{_no_session_cparam}; -} + my ( $window_at_pointer, $x, $y, $mask ) = Gtk3::Gdk::get_default_root_window->get_pointer; + my $mon = Gtk3::Gdk::Screen::get_default->get_monitor_geometry( + Gtk3::Gdk::Screen::get_default->get_monitor_at_point( $x, $y ) ); + + return ($mon); +} + +# Methods that were used in the old implementation and needed for backward compatibility + +sub get_root { shift->shutter_root } +sub get_appname { shift->appname } +sub get_version { shift->version } +sub get_rev { shift->rev } +sub get_gettext { shift->gettext_object } +sub get_theme { shift->icontheme } +sub get_notification_object { shift->notification } +sub set_notification_object { shift->notification(shift) if @_ } +sub get_globalsettings_object { shift->global_settings } +sub set_globalsettings_object { shift->global_settings(shift) if @_ } +sub get_rusf { shift->rusf } +sub set_rusf { shift->rusf(shift) if @_ } +sub get_ruof { shift->ruof } +sub set_ruof { shift->ruof(shift) if @_ } +sub get_ruu_tab { shift->ruu_tab } +sub set_ruu_tab { shift->ruu_tab(shift) if @_ } +sub get_ruu_hosting { shift->ruu_hosting } +sub set_ruu_hosting { shift->ruu_hosting(shift) if @_ } +sub get_ruu_places { shift->ruu_places } +sub set_ruu_places { shift->ruu_places(shift) if @_ } +sub get_debug { shift->debug } +sub set_debug { shift->debug(shift) if @_ } +sub get_clear_cache { shift->clear_cache } +sub set_clear_cache { shift->clear_cache(shift) if @_ } +sub get_mainwindow { shift->main_window } +sub set_mainwindow { shift->main_window(shift) if @_ } +sub get_min { shift->min } +sub set_min { shift->min(shift) if @_ } +sub get_disable_systray { shift->disable_systray } +sub set_disable_systray { shift->disable_systray(shift) if @_ } +sub get_exit_after_capture { shift->exit_after_capture } +sub set_exit_after_capture { shift->exit_after_capture(shift) if @_ } +sub get_no_session { shift->no_session } +sub set_no_session { shift->no_session(shift) if @_ } sub get_start_with { - my $self = shift; - return ($self->{_start_with}, $self->{_start_with_extra}); + my $self = shift; + return ( $self->_start_with, $self->_start_with_extra ); } sub set_start_with { - my $self = shift; - if (@_) { - $self->{_start_with} = shift; - $self->{_start_with_extra} = shift; - } - return ($self->{_start_with}, $self->{_start_with_extra}); -} - -sub get_profile_to_start_with { - my $self = shift; - return $self->{_profile_to_start_with}; -} - -sub set_profile_to_start_with { - my $self = shift; - if (@_) { - $self->{_profile_to_start_with} = shift; - } - return $self->{_profile_to_start_with}; -} - -sub get_export_filename { - my $self = shift; - return $self->{_export_filename}; -} - -sub set_export_filename { - my $self = shift; - if (@_) { - $self->{_export_filename} = shift; - } - return $self->{_export_filename}; -} - -sub get_include_cursor { - my $self = shift; - return $self->{_include_cursor}; -} - -sub set_include_cursor { - my $self = shift; - if (@_) { - $self->{_include_cursor} = shift; - } - return $self->{_include_cursor}; -} + my $self = shift; -sub get_remove_cursor { - my $self = shift; - return $self->{_remove_cursor}; -} - -sub set_remove_cursor { - my $self = shift; - if (@_) { - $self->{_remove_cursor} = shift; - } - return $self->{_remove_cursor}; -} - -sub get_delay { - my $self = shift; - return $self->{_delay}; -} - -sub set_delay { - my $self = shift; - if (@_) { - $self->{_delay} = shift; - } - return $self->{_delay}; -} - -sub get_current_monitor { - my $self = shift; - my ($window_at_pointer, $x, $y, $mask) = Gtk3::Gdk::get_default_root_window->get_pointer; - my $mon = Gtk3::Gdk::Screen::get_default->get_monitor_geometry(Gtk3::Gdk::Screen::get_default->get_monitor_at_point($x, $y)); - return ($mon); -} + if (@_) { + $self->_start_with(shift); + $self->_start_with_extra(shift); + } + + return ( $self->_start_with, $self->_start_with_extra ); +} + +sub get_profile_to_start_with { shift->profile_to_start_with } +sub set_profile_to_start_with { shift->profile_to_start_with(shift) if @_ } +sub get_export_filename { shift->export_filename } +sub set_export_filename { shift->export_filename(shift) if @_ } +sub get_include_cursor { shift->include_cursor } +sub set_include_cursor { shift->include_cursor(shift) if @_ } +sub get_remove_cursor { shift->remove_cursor } +sub set_remove_cursor { shift->remove_cursor(shift) if @_ } +sub get_delay { shift->delay } +sub set_delay { shift->delay(shift) if @_ } 1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/App/Directories.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/App/Directories.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/App/Directories.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/App/Directories.pm 2024-03-31 16:18:07.000000000 +0000 @@ -1,6 +1,7 @@ ################################################### # # Copyright (C) 2008-2013 Mario Kemper +# Copyright (C) 2021 Alexander Ruzhnikov # # This file is part of Shutter. # @@ -22,62 +23,56 @@ package Shutter::App::Directories; -use utf8; +use 5.010; use strict; use warnings; -#Glib -use Glib qw/TRUE FALSE/; +use Glib qw/ TRUE /; -sub new { - my $class = shift; +use constant { + SHUTTER_DIR => "shutter", + UNSAVED_DIR => "unsaved", + TEMP_DIR => "temp", + AUTOSTART_DIR => "autostart", + HIDDEN_SHUTTER_DIR => ".shutter", + PROFILES_DIR => "profiles" +}; - my $self = {}; +sub create_if_not_exists { + my $dir = shift; - bless $self, $class; - return $self; -} + mkdir $dir unless -d $dir && -r $dir; -sub create_if_not_exists { - my $self = shift; - my $dir = shift; - mkdir($dir) unless (-d $dir && -r $dir); - return $dir; + return $dir; } sub get_root_dir { - my $self = shift; - return $self->create_if_not_exists(Glib::get_user_cache_dir . "/shutter"); + return create_if_not_exists( Glib::get_user_cache_dir . "/" . SHUTTER_DIR ); } sub get_cache_dir { - my $self = shift; - return $self->create_if_not_exists($self->get_root_dir . "/unsaved"); + return create_if_not_exists( get_root_dir() . "/" . UNSAVED_DIR ); } sub get_temp_dir { - my $self = shift; - return $self->create_if_not_exists($self->get_root_dir . "/temp"); + return create_if_not_exists( get_root_dir() . "/" . TEMP_DIR ); } sub get_autostart_dir { - my $self = shift; - return $self->create_if_not_exists(Glib::get_user_config_dir . "/autostart"); + return create_if_not_exists( Glib::get_user_config_dir . "/" . AUTOSTART_DIR ); } -sub get_home_dir { - my $self = shift; - return Glib::get_home_dir; -} +sub get_home_dir {Glib::get_home_dir} +sub get_config_dir {Glib::get_user_config_dir} -sub get_config_dir { - my $self = shift; - return Glib::get_user_config_dir; -} +sub create_hidden_home_dir_if_not_exist { + my $hidden_dir = $ENV{HOME} . "/" . HIDDEN_SHUTTER_DIR; + my $hidden_profiles_dir = "$hidden_dir" . "/" . PROFILES_DIR; -sub get_settings_dir { + mkdir $hidden_dir unless -d $hidden_dir; + mkdir $hidden_profiles_dir unless -d $hidden_profiles_dir; - #not implemented + return TRUE; } 1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/App/GlobalSettings.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/App/GlobalSettings.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/App/GlobalSettings.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/App/GlobalSettings.pm 2024-03-31 16:18:07.000000000 +0000 @@ -39,6 +39,7 @@ $self->{_png_quality} = undef; $self->{_jpg_quality} = undef; + $self->{_webp_quality} = undef; bless $self, $class; return $self; @@ -63,6 +64,15 @@ } } +sub get_webp_quality { + my $self = shift; + if (defined $self->{_webp_quality}) { + return $self->{_webp_quality}; + } else { + return 98; + } +} + sub set_png_quality { my $self = shift; if (@_) { @@ -79,10 +89,19 @@ return $self->{_jpg_quality}; } +sub set_webp_quality { + my $self = shift; + if (@_) { + $self->{_webp_quality} = shift; + } + return $self->{_webp_quality}; +} + sub clear_quality_settings { my $self = shift; $self->{_jpg_quality} = undef; $self->{_png_quality} = undef; + $self->{_webp_quality} = undef; } 1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm 2024-03-31 16:18:07.000000000 +0000 @@ -41,6 +41,10 @@ use strict; use warnings; +use constant { + DEFAULT_FONT => "Sans Regular 16" +}; + use Gtk3; use Exporter; @@ -60,6 +64,10 @@ #Glib use Glib qw/TRUE FALSE/; +require Shutter::Draw::Utils; +require Shutter::App::Directories; +require Shutter::Draw::UIManager; + #-------------------------------------- sub new { @@ -145,7 +153,7 @@ $self->{_stroke_color} = Gtk3::Gdk::RGBA::parse('#ff0000'); $self->{_stroke_color}->alpha(1); $self->{_line_width} = 3; - $self->{_font} = 'Sans Regular 16'; + $self->{_font} = DEFAULT_FONT; #obtain current colors and font_desc from the main window $self->{_style} = $self->{_sc}->get_mainwindow->get_style_context; @@ -160,7 +168,7 @@ $self->{_last_stroke_color} = Gtk3::Gdk::RGBA::parse('#ff0000'); $self->{_last_stroke_color}->alpha(1); $self->{_last_line_width} = 3; - $self->{_last_font} = 'Sans Regular 16'; + $self->{_last_font} = DEFAULT_FONT; #some status variables $self->{_busy} = undef; @@ -176,7 +184,7 @@ $self->{_start_time} = undef; - $self->{_stipple_pixbuf} = Gtk3::Gdk::Pixbuf->new_from_file($self->{_sc}{_shutter_root} . '/share/shutter/resources/gui/stipple.png'); + $self->{_stipple_pixbuf} = Gtk3::Gdk::Pixbuf->new_from_file($self->{_sc}->get_root . '/share/shutter/resources/gui/stipple.png'); bless $self, $class; @@ -1320,12 +1328,19 @@ my $minutes = int((time - $self->{_start_time}) / 60); $minutes = 1 if $minutes == 0; + + my $txt = $self->{_d}->nget( + "If you don't save the image, changes from the last minute will be lost", + "If you don't save the image, changes from the last %d minutes will be lost", + $minutes + ); + + $txt = sprintf($txt, $minutes) if $minutes > 1; + $warn_dialog->set( - 'secondary-text' => - sprintf($self->{_d}->nget("If you don't save the image, changes from the last minute will be lost", "If you don't save the image, changes from the last %d minutes will be lost", $minutes), - $minutes,) - . "." + 'secondary-text' => "$txt." ); + return TRUE; } @@ -1465,7 +1480,6 @@ ); my $shutter_hfunct = Shutter::App::HelperFunctions->new($self->{_sc}); - my $shutter_dir = Shutter::App::Directories->new(); #parse filename my ($short, $folder, $ext) = fileparse($self->{_filename}, qr/\.[^.]*/); @@ -1475,7 +1489,7 @@ $fs->set_current_folder($self->{_sc}->get_rusf); $fs->set_current_name($short . $ext); } elsif (defined $self->{_is_unsaved} && $self->{_is_unsaved}) { - $fs->set_current_folder($shutter_dir->get_home_dir); + $fs->set_current_folder(Shutter::App::Directories::get_home_dir()); $fs->set_current_name($short . $ext); } else { $fs->set_current_folder($folder); @@ -1966,7 +1980,7 @@ } - $self->{_items}{$item}->set('points' => points_to_canvas_points(@{$self->{_items}{$item}{'points'}})); + $self->{_items}{$item}->set('points' => Shutter::Draw::Utils::points_to_canvas_points(@{$self->{_items}{$item}{'points'}})); #new item is already on the canvas with small initial size #drawing is like resizing, so set up for resizing @@ -2305,7 +2319,7 @@ foreach (keys %{$self->{_items}{$item}}) { #fancy resizing using our little resize boxes - if ($rect == $self->{_items}{$item}{$_}) { + if ($rect eq $self->{_items}{$item}{$_}) { if ($_ eq 'top-left-corner') { @@ -4195,7 +4209,7 @@ #determine font description from string my ($attr_list, $text_raw, $accel_char) = Pango->parse_markup($item->get('text')); - my ($font_desc) = $attr_list->get_iterator->get_font; + my ($font_desc) = Pango::FontDescription->from_string($self->{_font}); $font_hbox->pack_start($font_label, FALSE, TRUE, 12); $font_hbox->pack_start($font_btn, TRUE, TRUE, 0); @@ -4872,7 +4886,7 @@ #arrow is always and end-arrow if ($self->{_items}{$item}{mirrored_w} < 0 && $self->{_items}{$item}{mirrored_h} < 0) { $self->{_items}{$item}{line}->set( - 'points' => points_to_canvas_points( + 'points' => Shutter::Draw::Utils::points_to_canvas_points( $self->{_items}{$item}->get('x') + $self->{_items}{$item}->get('width'), $self->{_items}{$item}->get('y') + $self->{_items}{$item}->get('height'), $self->{_items}{$item}->get('x'), $self->{_items}{$item}->get('y') ), @@ -4880,7 +4894,7 @@ ); } elsif ($self->{_items}{$item}{mirrored_w} < 0) { $self->{_items}{$item}{line}->set( - 'points' => points_to_canvas_points( + 'points' => Shutter::Draw::Utils::points_to_canvas_points( $self->{_items}{$item}->get('x') + $self->{_items}{$item}->get('width'), $self->{_items}{$item}->get('y'), $self->{_items}{$item}->get('x'), $self->{_items}{$item}->get('y') + $self->{_items}{$item}->get('height') ), @@ -4888,7 +4902,7 @@ ); } elsif ($self->{_items}{$item}{mirrored_h} < 0) { $self->{_items}{$item}{line}->set( - 'points' => points_to_canvas_points( + 'points' => Shutter::Draw::Utils::points_to_canvas_points( $self->{_items}{$item}->get('x'), $self->{_items}{$item}->get('y') + $self->{_items}{$item}->get('height'), $self->{_items}{$item}->get('x') + $self->{_items}{$item}->get('width'), $self->{_items}{$item}->get('y') ), @@ -4896,7 +4910,7 @@ ); } else { $self->{_items}{$item}{line}->set( - 'points' => points_to_canvas_points( + 'points' => Shutter::Draw::Utils::points_to_canvas_points( $self->{_items}{$item}->get('x'), $self->{_items}{$item}->get('y'), $self->{_items}{$item}->get('x') + $self->{_items}{$item}->get('width'), $self->{_items}{$item}->get('y') + $self->{_items}{$item}->get('height') ), @@ -5731,425 +5745,7 @@ sub setup_uimanager { my $self = shift; - $self->{_factory} = Gtk3::IconFactory->new(); - $self->{_factory}->add('shutter-ellipse', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-ellipse.png'))); - $self->{_factory}->add('shutter-eraser', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-eraser.png'))); - $self->{_factory}->add('shutter-freehand', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-freehand.png'))); - $self->{_factory}->add('shutter-highlighter', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-highlighter.png'))); - $self->{_factory}->add('shutter-pointer', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-pointer.png'))); - $self->{_factory}->add('shutter-rectangle', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-rectangle.png'))); - $self->{_factory}->add('shutter-line', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-line.png'))); - $self->{_factory}->add('shutter-arrow', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-arrow.png'))); - $self->{_factory}->add('shutter-text', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-text.png'))); - $self->{_factory}->add('shutter-censor', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-censor.png'))); - $self->{_factory}->add('shutter-pixelize', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-pixelize.png'))); - $self->{_factory}->add('shutter-number', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/draw-number.png'))); - $self->{_factory}->add('shutter-crop', Gtk3::IconSet->new_from_pixbuf(Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons} . '/transform-crop.png'))); - - #~ $self->{_factory}->add( 'shutter-mime-pdf', Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons}.'/mime-pdf.svg') ) ); - #~ $self->{_factory}->add( 'shutter-mime-svg', Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file($self->{_dicons}.'/mime-svg.svg') ) ); - $self->{_factory}->add_default(); - - my @main_actions = ( - ["File", undef, $self->{_d}->get("_File")], - ["Edit", undef, $self->{_d}->get("_Edit")], - ["Tools", undef, $self->{_d}->get("_Tools")], - ["View", undef, $self->{_d}->get("_View")], - [ - "Undo", - 'gtk-undo', - undef, - "Z", - $self->{_d}->get("Undo last action"), - sub { - $self->abort_current_mode; - $self->xdo('undo', 'ui'); - } - ], - [ - "Redo", - 'gtk-redo', - undef, - "Y", - $self->{_d}->get("Do again the last undone action"), - sub { - $self->abort_current_mode; - $self->xdo('redo', 'ui'); - } - ], - [ - "Copy", - 'gtk-copy', - undef, - "C", - $self->{_d}->get("Copy selection to clipboard"), - sub { - - #clear clipboard - $self->{_clipboard}->set_text(""); - $self->{_cut} = FALSE; - $self->{_current_copy_item} = $self->{_current_item}; - } - ], - [ - "Cut", - 'gtk-cut', - undef, - "X", - $self->{_d}->get("Cut selection to clipboard"), - sub { - - #clear clipboard - $self->{_clipboard}->set_text(""); - $self->{_cut} = TRUE; - $self->{_current_copy_item} = $self->{_current_item}; - $self->clear_item_from_canvas($self->{_current_copy_item}); - } - ], - [ - "Paste", - 'gtk-paste', - undef, - "V", - $self->{_d}->get("Paste objects from clipboard"), - sub { - $self->paste_item($self->{_current_copy_item}, $self->{_cut}); - $self->{_cut} = FALSE; - } - ], - [ - "Delete", - 'gtk-delete', - undef, "Delete", - $self->{_d}->get("Delete current object"), - sub { - $self->clear_item_from_canvas($self->{_current_item}); - } - ], - [ - "Clear", - 'gtk-clear', - undef, - "Delete", - $self->{_d}->get("Clear canvas"), - sub { - - #store items to delete in temporary hash - #sort them uid - my %time_hash; - foreach (keys %{$self->{_items}}) { - next if (exists $self->{_items}{$_}{image} && $self->{_items}{$_}{image} == $self->{_canvas_bg}); - $time_hash{$self->{_items}{$_}{uid}} = $self->{_items}{$_}; - } - - #delete items - foreach (sort keys %time_hash) { - $self->clear_item_from_canvas($time_hash{$_}); - } - } - ], - [ - "Stop", - 'gtk-stop', - undef, "Escape", - $self->{_d}->get("Abort current mode"), - sub { - $self->abort_current_mode; - } - ], - [ - "Close", - 'gtk-close', - undef, - "Q", - $self->{_d}->get("Close this window"), - sub { - $self->quit(TRUE); - } - ], - [ - "Save", - 'gtk-save', - undef, - "S", - $self->{_d}->get("Save image"), - sub { - $self->save(), $self->quit(FALSE); - } - ], - [ - "ExportTo", - 'gtk-save-as', - $self->{_d}->get("Export to _File..."), - "E", - $self->{_d}->get("Export to File..."), - sub { - $self->export_to_file(); - } - ], - [ - "ExportToSvg", - undef, - $self->{_d}->get("_Export to SVG..."), - "V", - $self->{_d}->get("Export to SVG..."), - sub { - $self->export_to_svg(); - } - ], - [ - "ExportToPdf", - undef, - $self->{_d}->get("E_xport to PDF..."), - "P", - $self->{_d}->get("Export to PDF..."), - sub { - $self->export_to_pdf(); - } - ], - [ - "ExportToPS", - undef, - $self->{_d}->get("Export to Post_Script..."), - "S", - $self->{_d}->get("Export to PostScript..."), - sub { - $self->export_to_ps(); - } - ], - [ - "ZoomIn", - 'gtk-zoom-in', - undef, - "plus", - undef, - sub { - $self->zoom_in_cb($self); - } - ], - [ - "ControlEqual", - 'gtk-zoom-in', - undef, - "equal", - undef, - sub { - $self->zoom_in_cb($self); - } - ], - [ - "ControlKpAdd", - 'gtk-zoom-in', - undef, - "KP_Add", - undef, - sub { - $self->zoom_in_cb($self); - } - ], - [ - "ZoomOut", - 'gtk-zoom-out', - undef, - "minus", - undef, - sub { - $self->zoom_out_cb($self); - } - ], - [ - "ControlKpSub", - 'gtk-zoom-out', - undef, - "KP_Subtract", - undef, - sub { - $self->zoom_out_cb($self); - } - ], - [ - "ZoomNormal", - 'gtk-zoom-100', - undef, - "0", - undef, - sub { - $self->zoom_normal_cb($self); - } - ], - ); - - my @toggle_actions = ([ - "Autoscroll", - undef, - $self->{_d}->get("Automatic scrolling"), - undef, undef, - sub { - my $widget = shift; - - if ($widget->get_active) { - $self->{_autoscroll} = TRUE; - } else { - $self->{_autoscroll} = FALSE; - } - - #'redraw-when-scrolled' to reduce the flicker of static items - # - #this property is not available in older versions - #it was added to goocanvas on Mon Nov 17 10:28:07 2008 UTC - #http://svn.gnome.org/viewvc/goocanvas?view=revision&revision=28 - if ($self->{_canvas} && $self->{_canvas}->find_property('redraw-when-scrolled')) { - $self->{_canvas}->set('redraw-when-scrolled' => !$self->{_autoscroll}); - } - } - ], - [ - "Fullscreen", - 'gtk-fullscreen', - undef, "F11", undef, - sub { - my $action = shift; - - if ($action->get_active) { - $self->{_drawing_window}->fullscreen; - } else { - $self->{_drawing_window}->unfullscreen; - } - } - ]); - - my @drawing_actions = ( - ["Select", 'shutter-pointer', $self->{_d}->get("Select"), "0", $self->{_d}->get("Select item to move or resize it"), 10], - ["Freehand", 'shutter-freehand', $self->{_d}->get("Freehand"), "1", $self->{_d}->get("Draw a freehand line"), 20], - ["Highlighter", 'shutter-highlighter', $self->{_d}->get("Highlighter"), "2", $self->{_d}->get("Highlighter"), 30], - ["Line", 'shutter-line', $self->{_d}->get("Line"), "3", $self->{_d}->get("Draw a straight line"), 40], - ["Arrow", 'shutter-arrow', $self->{_d}->get("Arrow"), "4", $self->{_d}->get("Draw an arrow"), 50], - ["Rect", 'shutter-rectangle', $self->{_d}->get("Rectangle"), "5", $self->{_d}->get("Draw a rectangle"), 60], - ["Ellipse", 'shutter-ellipse', $self->{_d}->get("Ellipse"), "6", $self->{_d}->get("Draw a ellipse"), 70], - ["Text", 'shutter-text', $self->{_d}->get("Text"), "7", $self->{_d}->get("Add some text to the screenshot"), 80], - ["Censor", 'shutter-censor', $self->{_d}->get("Censor"), "8", $self->{_d}->get("Censor portions of your screenshot to hide private data"), 90], - ["Pixelize", 'shutter-pixelize', $self->{_d}->get("Pixelize"), "8", $self->{_d}->get("Pixelize selected areas of your screenshot to hide private data"), 100], - ["Number", 'shutter-number', $self->{_d}->get("Number"), "9", $self->{_d}->get("Add an auto-increment shape to the screenshot"), 110], - ["Crop", 'shutter-crop', $self->{_d}->get("Crop"), "c", $self->{_d}->get("Crop your screenshot"), 120]); - - my $uimanager = Gtk3::UIManager->new(); - - #keyboard accel_group - my $accelgroup = $uimanager->get_accel_group; - $self->{_drawing_window}->add_accel_group($accelgroup); - - # Setup the main group. - my $main_group = Gtk3::ActionGroup->new("main"); - $main_group->add_actions(\@main_actions); - - #setup the menu toggle group - my $toggle_group = Gtk3::ActionGroup->new("toggle"); - $toggle_group->add_toggle_actions(\@toggle_actions); - - # Setup the drawing group. - my $drawing_group = Gtk3::ActionGroup->new("drawing"); - $drawing_group->add_radio_actions(\@drawing_actions, 10, sub { my $action = shift; $self->change_drawing_tool_cb($action); }); - - $uimanager->insert_action_group($main_group, 0); - $uimanager->insert_action_group($toggle_group, 0); - $uimanager->insert_action_group($drawing_group, 0); - - my $ui_info = " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "; - - eval { $uimanager->add_ui_from_string($ui_info) }; - - if ($@) { - die "Unable to create menus: $@\n"; - } - - return $uimanager; + return Shutter::Draw::UIManager->new( app => $self )->setup; } sub import_from_dnd { @@ -6917,7 +6513,7 @@ #need at least 2 points push @{$self->{_items}{$item}{'points'}}, @points; - $self->{_items}{$item}->set(points => points_to_canvas_points(@{$self->{_items}{$item}{'points'}})); + $self->{_items}{$item}->set(points => Shutter::Draw::Utils::points_to_canvas_points(@{$self->{_items}{$item}{'points'}})); $self->{_items}{$item}->set(transform => $transform) if $transform; if ($highlighter) { @@ -6978,7 +6574,7 @@ #need at least 2 points push @{$self->{_items}{$item}{'points'}}, @points; - $self->{_items}{$item}->set(points => points_to_canvas_points(@{$self->{_items}{$item}{'points'}})); + $self->{_items}{$item}->set(points => Shutter::Draw::Utils::points_to_canvas_points(@{$self->{_items}{$item}{'points'}})); $self->{_items}{$item}->set(transform => $transform) if $transform; $self->setup_item_signals($self->{_items}{$item}); @@ -7308,7 +6904,7 @@ $self->{_items}{$item}{line} = GooCanvas2::CanvasPolyline->new( parent=>$self->{_canvas}->get_root_item, close_path=>FALSE, - points=>points_to_canvas_points( + points => Shutter::Draw::Utils::points_to_canvas_points( $item->get('x'), $item->get('y'), $item->get('x') + $item->get('width'), @@ -7546,15 +7142,87 @@ return $item; } -sub points_to_canvas_points { - my @points = @_; - my $num_points = scalar(@points) / 2; - my $result = GooCanvas2::CanvasPoints::new(num_points=>$num_points); - for (my $i = 0; $i < @points; $i += 2) { - $result->set_point($i / 2, $points[$i], $points[$i+1]); - } - return $result; + +# getters and setters + +sub gettext { shift->{_d} } +sub dicons { shift->{_dicons} } +sub icons { shift->{_icons} } +sub clipboard { shift->{_clipboard} } +sub items { shift->{_items} } +sub drawing_window { shift->{_drawing_window} } +sub canvas { shift->{_canvas} } + +sub cut { + my $self = shift; + $self->{_cut} = shift if scalar @_; + return $self->{_cut}; +} +sub current_copy_item { + my $self = shift; + $self->{_current_copy_item} = shift if scalar @_; + return $self->{_current_copy_item}; +} + +sub current_item { + my $self = shift; + $self->{_current_item} = shift if scalar @_; + return $self->{_current_item}; } +sub current_new_item { + my $self = shift; + $self->{_current_new_item} = shift if scalar @_; + return $self->{_current_new_item}; +} + +sub canvas_bg { + my $self = shift; + $self->{_canvas_bg} = shift if scalar @_; + return $self->{_canvas_bg}; +} + +sub factory { + my $self = shift; + $self->{_factory} = shift if scalar @_; + return $self->{_factory}; +} + +sub autoscroll { + my $self = shift; + $self->{_autoscroll} = shift if scalar @_; + return $self->{_autoscroll}; +} + +sub stroke_color { + my $self = shift; + $self->{_stroke_color} = shift if scalar @_; + return $self->{_stroke_color}; +} + +sub fill_color { + my $self = shift; + $self->{_fill_color} = shift if scalar @_; + return $self->{_fill_color}; +} + +sub line_width { + my $self = shift; + $self->{_line_width} = shift if scalar @_; + return $self->{_line_width}; +} + +sub font { + my $self = shift; + $self->{_font} = shift if scalar @_; + return $self->{_font}; +} + +sub uid { shift->{_uid} } + +sub increase_uid { shift->{_uid}++ } + +sub uimanager { shift->{_uimanager} } + 1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/Ellipse.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/Ellipse.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/Ellipse.pm 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/Ellipse.pm 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,197 @@ +################################################### +# +# Copyright (C) 2021 Alexander Ruzhnikov +# +# This file is part of Shutter. +# +# Shutter is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Shutter is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shutter; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +################################################### + +package Shutter::Draw::Ellipse; + +use 5.010; +use Moo; + +use GooCanvas2; +use Glib qw/ TRUE FALSE /; + +use constant POSITION_INDENT => 20; + +has app => ( is => "ro", required => 1 ); +has event => ( is => "rw", lazy => 1 ); +has copy_item => ( is => "rw", lazy => 1 ); +has numbered => ( is => "rw", lazy => 1 ); +has X => ( is => "rw", default => sub {0} ); +has Y => ( is => "rw", default => sub {0} ); +has width => ( is => "rw", default => sub {0} ); +has height => ( is => "rw", default => sub {0} ); + +has stroke_color => ( is => "rw", lazy => 1, default => sub { shift->app->stroke_color } ); +has fill_color => ( is => "rw", lazy => 1, default => sub { shift->app->fill_color } ); +has line_width => ( is => "rw", lazy => 1, default => sub { shift->app->line_width } ); + +sub setup { + my ( $self, $event, $copy_item, $numbered ) = @_; + + $self->event($event); + $self->copy_item($copy_item); + $self->numbered($numbered); + + $self->_check_event_and_copy_item; + + my $item = $self->_create_item; + + $self->app->current_new_item($item) unless $self->copy_item; + $self->app->items->{$item} = $item; + + $self->_setup_item_ellipse($item); + + if ( $self->numbered ) { + $self->_setup_ellipse_numbered($item); + } else { + + # set type flag + $item->{type} = 'ellipse'; + $item->{uid} = $self->app->uid; + $self->app->increase_uid; + } + + # save color and opacity as well + $item->{fill_color} = $self->app->fill_color; + $item->{stroke_color} = $self->app->stroke_color; + + # create rectangles + $self->app->handle_rects( 'create', $item ); + if ( $self->copy_item ) { + $self->app->handle_embedded( 'update', $item ); + $self->app->handle_rects( 'hide', $item ); + } + + if ( $self->numbered ) { + $self->app->setup_item_signals( $item->{text} ); + $self->app->setup_item_signals_extra( $item->{text} ); + } + + $self->app->setup_item_signals( $item->{ellipse} ); + $self->app->setup_item_signals_extra( $item->{ellipse} ); + + $self->app->setup_item_signals($item); + $self->app->setup_item_signals_extra($item); + + return $item; +} + +sub _setup_item_ellipse { + my ( $self, $item ) = @_; + + $item->{ellipse} = GooCanvas2::CanvasEllipse->new( + 'parent' => $self->app->canvas->get_root_item, + 'x' => $self->X, + 'y' => $self->Y, + 'width' => $self->width, + 'height' => $self->height, + 'fill-color-gdk-rgba' => $self->fill_color, + 'stroke-color-gdk-rgba' => $self->stroke_color, + 'line-width' => $self->line_width, + ); +} + +sub _setup_ellipse_numbered { + my ( $self, $item ) = @_; + + my $number = $self->app->get_highest_auto_digit + 1; + + my $txt = GooCanvas2::CanvasText->new( + 'parent' => $self->app->canvas->get_root_item, + 'text' => "" . $number . "", + 'x' => $item->{ellipse}->get('center-x'), + 'y' => $item->{ellipse}->get('center-y'), + 'width' => -1, + 'anchor' => 'center', + 'use-markup' => TRUE, + 'fill-color-gdk-rgba' => $self->stroke_color, + 'line-width' => $self->line_width, + ); + + $txt->{digit} = $number; + $item->{text} = $txt; + + $item->{type} = 'number'; + $item->{uid} = $self->app->uid; + + $self->app->increase_uid; + + #adjust parent rectangle if numbered ellipse + my $tb = $txt->get_bounds; + + #keep ratio = 1 + my $qs = abs( $tb->x1 - $tb->x2 ); + $qs = abs( $tb->y1 - $tb->y2 ) if abs( $tb->y1 - $tb->y2 ) > abs( $tb->x1 - $tb->x2 ); + + #add line width of parent ellipse + $qs += $item->{ellipse}->get('line-width') + 5; + + $item->set( + 'x' => $self->copy_item ? ( $self->X + POSITION_INDENT ) : ( $self->X - $qs ), + 'y' => $self->copy_item ? ( $self->Y + POSITION_INDENT ) : ( $self->Y - $qs ), + 'width' => $qs, + 'height' => $qs, + 'visibility' => 'hidden', + ); + + $self->app->handle_embedded( 'hide', $item ); +} + +sub _check_event_and_copy_item { + my $self = shift; + + if ( $self->event ) { + $self->X( $self->event->x ); + $self->Y( $self->event->y ); + } elsif ( $self->copy_item ) { + $self->X( $self->copy_item->get('x') + POSITION_INDENT ); + $self->Y( $self->copy_item->get('y') + POSITION_INDENT ); + + $self->width( $self->copy_item->get('width') ); + $self->height( $self->copy_item->get('height') ); + + $self->stroke_color( $self->app->items->{ $self->copy_item }->{stroke_color} ); + $self->fill_color( $self->app->items->{ $self->copy_item }->{fill_color} ); + $self->line_width( $self->app->items->{ $self->copy_item }->{ellipse}->get('line-width') ); + + $self->numbered(TRUE) if exists $self->app->items->{ $self->copy_item }->{text}; + } +} + +sub _create_item { + my $self = shift; + + my $item = GooCanvas2::CanvasRect->new( + 'parent' => $self->app->canvas->get_root_item, + 'x' => $self->X, + 'y' => $self->Y, + 'width' => $self->width, + 'height' => $self->height, + 'fill-color-rgba' => 0, + 'line-dash' => GooCanvas2::CanvasLineDash->newv( [ 5, 5 ] ), + 'line-width' => 1, + 'stroke-color' => 'gray', + ); + + return $item; +} + +1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/UIManager.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/UIManager.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/UIManager.pm 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/UIManager.pm 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,508 @@ +################################################### +# +# Copyright (C) 2021 Alexander Ruzhnikov +# +# This file is part of Shutter. +# +# Shutter is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Shutter is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shutter; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +################################################### + +package Shutter::Draw::UIManager; + +use Moo; +use Gtk3; +use Glib qw/ TRUE FALSE /; + +has app => ( is => "ro", required => 1 ); + +has dicons => ( is => "ro", lazy => 1, default => sub { shift->app->dicons } ); +has gettext => ( is => "ro", lazy => 1, default => sub { shift->app->gettext } ); + +sub setup { + my $self = shift; + + $self->app->factory( $self->_create_factory ); + + my $uimanager = Gtk3::UIManager->new; + + # keyboard accel_group + my $accelgroup = $uimanager->get_accel_group; + $self->app->drawing_window->add_accel_group($accelgroup); + + $uimanager->insert_action_group( $self->_create_main_group, 0 ); + $uimanager->insert_action_group( $self->_create_toggle_group, 0 ); + $uimanager->insert_action_group( $self->_create_drawing_group, 0 ); + + eval { + $uimanager->add_ui_from_string( $self->_get_ui_info ); + 1; + } or do { + die "Unable to create menus: $@\n"; + }; + + return $uimanager; +} + +sub _create_factory { + my $self = shift; + + my $factory = Gtk3::IconFactory->new; + + $factory->add( 'shutter-ellipse', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-ellipse.png' ) ) ); + + $factory->add( 'shutter-eraser', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-eraser.png' ) ) ); + + $factory->add( 'shutter-freehand', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-freehand.png' ) ) ); + + $factory->add( 'shutter-highlighter', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-highlighter.png' ) ) ); + + $factory->add( 'shutter-pointer', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-pointer.png' ) ) ); + + $factory->add( 'shutter-rectangle', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-rectangle.png' ) ) ); + + $factory->add( 'shutter-line', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-line.png' ) ) ); + + $factory->add( 'shutter-arrow', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-arrow.png' ) ) ); + + $factory->add( 'shutter-text', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-text.png' ) ) ); + + $factory->add( 'shutter-censor', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-censor.png' ) ) ); + $factory->add( 'shutter-pixelize', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-pixelize.png' ) ) ); + $factory->add( 'shutter-number', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/draw-number.png' ) ) ); + $factory->add( 'shutter-crop', + Gtk3::IconSet->new_from_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file( $self->dicons . '/transform-crop.png' ) ) ); + + $factory->add_default(); + + return $factory; +} + +sub _create_main_group { + my $self = shift; + + # Setup the main group. + my $main_group = Gtk3::ActionGroup->new("main"); + $main_group->add_actions( $self->_create_main_actions ); + + return $main_group; +} + +sub _create_main_actions { + my $self = shift; + + my @main_actions = ( + [ "File", undef, $self->gettext->get("_File") ], + [ "Edit", undef, $self->gettext->get("_Edit") ], + [ "Tools", undef, $self->gettext->get("_Tools") ], + [ "View", undef, $self->gettext->get("_View") ], + [ "Undo", + 'gtk-undo', + undef, + "Z", + $self->gettext->get("Undo last action"), + sub { + $self->app->abort_current_mode; + $self->app->xdo( 'undo', 'ui' ); + } + ], + [ "Redo", + 'gtk-redo', + undef, + "Y", + $self->gettext->get("Do again the last undone action"), + sub { + $self->abort_current_mode; + $self->xdo( 'redo', 'ui' ); + } + ], + [ "Copy", + 'gtk-copy', + undef, + "C", + $self->gettext->get("Copy selection to clipboard"), + sub { + + #clear clipboard + $self->app->clipboard->set_text(""); + $self->app->cut(FALSE); + $self->app->current_copy_item( $self->app->current_item ); + } + ], + [ "Cut", + 'gtk-cut', + undef, + "X", + $self->gettext->get("Cut selection to clipboard"), + sub { + + #clear clipboard + $self->app->clipboard->set_text(""); + $self->app->cut(TRUE); + $self->app->current_copy_item( $self->app->current_item ); + $self->app->clear_item_from_canvas( $self->app->current_copy_item ); + } + ], + [ "Paste", + 'gtk-paste', + undef, + "V", + $self->gettext->get("Paste objects from clipboard"), + sub { + $self->app->paste_item( $self->app->current_copy_item, $self->app->cut ); + $self->app->cut(FALSE); + } + ], + [ "Delete", 'gtk-delete', undef, "Delete", + $self->gettext->get("Delete current object"), + sub { $self->app->clear_item_from_canvas( $self->app->current_item ) } + ], + [ "Clear", + 'gtk-clear', + undef, + "Delete", + $self->gettext->get("Clear canvas"), + sub { + + #store items to delete in temporary hash + #sort them uid + my %time_hash; + for my $item ( values %{ $self->app->items } ) { + next if exists $item->{image} && $item->{image} == $self->app->canvas_bg; + $time_hash{ $item->{uid} } = $item; + } + + #delete items + for my $key ( sort keys %time_hash ) { + $self->app->clear_item_from_canvas( $time_hash{$key} ); + } + } + ], + [ "Stop", 'gtk-stop', undef, "Escape", + $self->gettext->get("Abort current mode"), + sub { $self->app->abort_current_mode } + ], + [ "Close", 'gtk-close', undef, "Q", + $self->gettext->get("Close this window"), + sub { $self->app->quit(TRUE) } + ], + [ "Save", + 'gtk-save', + undef, + "S", + $self->gettext->get("Save image"), + sub { + $self->app->save(); + $self->app->quit(FALSE); + } + ], + [ "ExportTo", + 'gtk-save-as', + $self->gettext->get("Export to _File..."), + "E", + $self->gettext->get("Export to File..."), + sub { + $self->app->export_to_file(); + } + ], + [ "ExportToSvg", + undef, + $self->gettext->get("_Export to SVG..."), + "V", + $self->gettext->get("Export to SVG..."), + sub { + $self->app->export_to_svg(); + } + ], + [ "ExportToPdf", + undef, + $self->gettext->get("E_xport to PDF..."), + "P", + $self->gettext->get("Export to PDF..."), + sub { + $self->app->export_to_pdf(); + } + ], + [ "ExportToPS", + undef, + $self->gettext->get("Export to Post_Script..."), + "S", + $self->gettext->get("Export to PostScript..."), + sub { + $self->app->export_to_ps(); + } + ], + [ "ZoomIn", 'gtk-zoom-in', undef, "plus", undef, sub { $self->app->zoom_in_cb( $self->app ) } ], + [ "ControlEqual", 'gtk-zoom-in', undef, "equal", undef, sub { $self->app->zoom_in_cb( $self->app ) } ], + [ "ControlKpAdd", + 'gtk-zoom-in', + undef, + "KP_Add", + undef, + sub { + $self->app->zoom_in_cb( $self->app ); + } + ], + [ "ZoomOut", 'gtk-zoom-out', undef, "minus", undef, sub { $self->app->zoom_out_cb( $self->app ) } ], + [ "ControlKpSub", + 'gtk-zoom-out', + undef, + "KP_Subtract", + undef, + sub { + $self->app->zoom_out_cb( $self->app ); + } + ], + [ "ZoomNormal", + 'gtk-zoom-100', + undef, + "0", + undef, + sub { + $self->app->zoom_normal_cb( $self->app ); + } + ], + ); + + return \@main_actions; +} + +sub _create_toggle_group { + my $self = shift; + + #setup the menu toggle group + my $toggle_group = Gtk3::ActionGroup->new("toggle"); + $toggle_group->add_toggle_actions( $self->_create_toggle_actions ); + + return $toggle_group; +} + +sub _create_toggle_actions { + my $self = shift; + + my @toggle_actions = ( + [ "Autoscroll", + undef, + $self->gettext->get("Automatic scrolling"), + undef, undef, + sub { + my $widget = shift; + + if ( $widget->get_active ) { + $self->app->autoscroll(TRUE); + } else { + $self->app->autoscroll(FALSE); + } + + #'redraw-when-scrolled' to reduce the flicker of static items + # + #this property is not available in older versions + #it was added to goocanvas on Mon Nov 17 10:28:07 2008 UTC + #http://svn.gnome.org/viewvc/goocanvas?view=revision&revision=28 + if ( $self->app->canvas && $self->app->canvas->find_property('redraw-when-scrolled') ) { + $self->app->canvas->set( 'redraw-when-scrolled' => !$self->app->autoscroll ); + } + } + ], + [ "Fullscreen", + 'gtk-fullscreen', + undef, "F11", undef, + sub { + my $action = shift; + + if ( $action->get_active ) { + $self->app->drawing_window->fullscreen; + } else { + $self->app->drawing_window->unfullscreen; + } + } + ], + ); + + return \@toggle_actions; +} + +sub _create_drawing_group { + my $self = shift; + + # Setup the drawing group. + my $drawing_actions = $self->_create_drawing_actions; + my $drawing_group = Gtk3::ActionGroup->new("drawing"); + $drawing_group->add_radio_actions( + $drawing_actions, + 10, + sub { + my $action = shift; + $self->app->change_drawing_tool_cb($action); + } ); + + return $drawing_group; +} + +sub _create_drawing_actions { + my $self = shift; + + my @drawing_actions = ( + [ "Select", 'shutter-pointer', $self->gettext->get("Select"), + "0", $self->gettext->get("Select item to move or resize it"), 10 + ], + [ "Freehand", 'shutter-freehand', + $self->gettext->get("Freehand"), "1", + $self->gettext->get("Draw a freehand line"), 20 + ], + [ "Highlighter", 'shutter-highlighter', + $self->gettext->get("Highlighter"), "2", + $self->gettext->get("Highlighter"), 30 + ], + [ "Line", 'shutter-line', + $self->gettext->get("Line"), "3", + $self->gettext->get("Draw a straight line"), 40 + ], + [ "Arrow", 'shutter-arrow', $self->gettext->get("Arrow"), "4", $self->gettext->get("Draw an arrow"), 50 ], + [ "Rect", 'shutter-rectangle', + $self->gettext->get("Rectangle"), "5", + $self->gettext->get("Draw a rectangle"), 60 + ], + [ "Ellipse", 'shutter-ellipse', + $self->gettext->get("Ellipse"), "6", + $self->gettext->get("Draw a ellipse"), 70 + ], + [ "Text", 'shutter-text', $self->gettext->get("Text"), + "7", $self->gettext->get("Add some text to the screenshot"), 80 + ], + [ "Censor", 'shutter-censor', $self->gettext->get("Censor"), + "8", $self->gettext->get("Censor portions of your screenshot to hide private data"), 90 + ], + [ "Pixelize", 'shutter-pixelize', $self->gettext->get("Pixelize"), + "8", $self->gettext->get("Pixelize selected areas of your screenshot to hide private data"), 100 + ], + [ "Number", 'shutter-number', $self->gettext->get("Number"), + "9", $self->gettext->get("Add an auto-increment shape to the screenshot"), 110 + ], + [ "Crop", 'shutter-crop', + $self->gettext->get("Crop"), "c", + $self->gettext->get("Crop your screenshot"), 120 + ], + ); + + return \@drawing_actions; +} + +sub _get_ui_info { + my $self = shift; + + return " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; +} + +1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/Utils.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/Utils.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Draw/Utils.pm 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Draw/Utils.pm 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,45 @@ +################################################### +# +# Copyright (C) 2021 Alexander Ruzhnikov +# +# This file is part of Shutter. +# +# Shutter is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Shutter is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shutter; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +################################################### + +package Shutter::Draw::Utils; + +use 5.010; +use strict; +use warnings; + +use Gtk3; +use GooCanvas2; + +sub points_to_canvas_points { + my @points = @_; + + my $num_points = scalar(@points) / 2; + my $result = GooCanvas2::CanvasPoints::new( num_points => $num_points ); + + for ( my $i = 0; $i < @points; $i += 2 ) { + $result->set_point( $i / 2, $points[$i], $points[ $i + 1 ] ); + } + + return $result; +} + +1; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Pixbuf/Save.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Pixbuf/Save.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Pixbuf/Save.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Pixbuf/Save.pm 2024-03-31 16:18:07.000000000 +0000 @@ -132,6 +132,22 @@ eval { $pixbuf->save($filename, $filetype, "tEXt::Software" => "Shutter", compression => $quality); }; } elsif ($filetype eq 'bmp') { eval { $pixbuf->save($filename, $filetype); }; + } elsif ($filetype eq 'webp') { + + #get quality value from settings if not set + if (my $settings = $self->{_common}->get_globalsettings_object) { + if (defined $settings->get_webp_quality) { + $quality = $settings->get_webp_quality; + } else { + $quality = 98; + } + } else { + $quality = 98; + } + + print "Saving file $filename, $filetype, $quality\n" if $self->{_common}->get_debug; + + eval { $pixbuf->save($filename, $filetype, "tEXt::Software" => "Shutter", quality => $quality); }; } elsif ($filetype eq 'pdf') { print "Saving file $filename, $filetype\n" if $self->{_common}->get_debug; diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Screenshot/SelectorAdvanced.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Screenshot/SelectorAdvanced.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Screenshot/SelectorAdvanced.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Screenshot/SelectorAdvanced.pm 2024-03-31 16:18:07.000000000 +0000 @@ -59,6 +59,7 @@ $self->{_init_y} = shift; $self->{_init_w} = shift; $self->{_init_h} = shift; + $self->{_confirmation_necessary} = shift; $self->{_dpi_scale} = Gtk3::Window->new('toplevel')->get('scale-factor'); @@ -446,6 +447,25 @@ $self->{_prop_active} = TRUE; Gtk3::Gdk::keyboard_grab($self->{_prop_window}->get_window, 0, Gtk3::get_current_event_time()); } + } elsif ($event->button == 1) { + if (not $self->{_confirmation_necessary}) { + $self->{_select_window}->hide; + $self->{_zoom_window}->hide; + $self->{_prop_window}->hide; + + #A short timeout to give the server a chance to + #redraw the area + Glib::Timeout->add( + $self->{_hide_time}, + sub { + Gtk3->main_quit; + return FALSE; + }); + Gtk3->main(); + + $output = $self->take_screenshot($s, $clean_pixbuf); + $self->quit; + } } #handle motion-notify diff -Nru shutter-0.99.2/share/shutter/resources/modules/Shutter/Screenshot/SelectorAuto.pm shutter-0.99.5/share/shutter/resources/modules/Shutter/Screenshot/SelectorAuto.pm --- shutter-0.99.2/share/shutter/resources/modules/Shutter/Screenshot/SelectorAuto.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/Shutter/Screenshot/SelectorAuto.pm 2024-03-31 16:18:07.000000000 +0000 @@ -59,7 +59,7 @@ my $d = $self->{_sc}->get_gettext; my $output; - if ($x && $y && $width && $height) { + if ($x>=0 && $y>=0 && $width && $height) { ($output) = $self->get_pixbuf_from_drawable($self->{_root}, $x, $y, $width, $height); #section not valid diff -Nru shutter-0.99.2/share/shutter/resources/modules/WebService/Dropbox.pm shutter-0.99.5/share/shutter/resources/modules/WebService/Dropbox.pm --- shutter-0.99.2/share/shutter/resources/modules/WebService/Dropbox.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/modules/WebService/Dropbox.pm 1970-01-01 00:00:00.000000000 +0000 @@ -1,901 +0,0 @@ -package WebService::Dropbox; -use strict; -use warnings; -use Carp (); -use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK SEEK_SET SEEK_END); -use JSON::MaybeXS; -use Net::OAuth; -use URI; -use URI::Escape; - -our $VERSION = '1.22'; - -my $request_token_url = 'https://api.dropbox.com/1/oauth/request_token'; -my $access_token_url = 'https://api.dropbox.com/1/oauth/access_token'; -my $authorize_url = 'https://www.dropbox.com/1/oauth/authorize'; - -__PACKAGE__->mk_accessors( - qw/ - key - secret - request_token - request_secret - access_token - access_secret - root - - no_decode_json - error - code - request_url - request_method - timeout - / -); - -$WebService::Dropbox::USE_LWP = 0; - -sub import { - eval { - require Furl::HTTP; - require IO::Socket::SSL; - }; - if ($@) { - __PACKAGE__->use_lwp; - } -} - -sub use_lwp { - require LWP::UserAgent; - require HTTP::Request; - $WebService::Dropbox::USE_LWP++; -} - -sub new { - my ($class, $args) = @_; - - bless { - key => $args->{key} || '', - secret => $args->{secret} || '', - request_token => $args->{request_token} || '', - request_secret => $args->{request_secret} || '', - access_token => $args->{access_token} || '', - access_secret => $args->{access_secret} || '', - root => $args->{root} || 'dropbox', - timeout => $args->{timeout} || (60 * 60 * 24), - no_decode_json => $args->{no_decode_json} || 0, - no_uri_escape => $args->{no_uri_escape} || 0, - env_proxy => $args->{lwp_env_proxy} || $args->{env_proxy} || 0, - }, $class; -} - -sub login { - my ($self, $callback_url) = @_; - - my $body = $self->api({ - method => 'POST', - url => $request_token_url - }) or return; - - my $response = Net::OAuth->response('request token')->from_post_body($body); - $self->request_token($response->token); - $self->request_secret($response->token_secret); - - my $url = URI->new($authorize_url); - $url->query_form( - oauth_token => $response->token, - oauth_callback => $callback_url - ); - $url->as_string; -} - -sub auth { - my $self = shift; - - my $body = $self->api({ - method => 'POST', - url => $access_token_url - }) or return; - - my $response = Net::OAuth->response('access token')->from_post_body($body); - $self->access_token($response->token); - $self->access_secret($response->token_secret); -} - -sub account_info { - my $self = shift; - - $self->api_json({ - url => 'https://api.dropbox.com/1/account/info' - }); -} - -sub files { - my ($self, $path, $output, $params, $opts) = @_; - - $opts ||= {}; - if (ref $output eq 'CODE') { - $opts->{write_code} = $output; # code ref - } elsif (ref $output) { - $opts->{write_file} = $output; # file handle - binmode $opts->{write_file}; - } else { - open $opts->{write_file}, '>', $output; # file path - Carp::croak("invalid output, output must be code ref or filehandle or filepath.") - unless $opts->{write_file}; - binmode $opts->{write_file}; - } - $self->api({ - url => $self->url('https://api-content.dropbox.com/1/files/' . $self->root, $path), - extra_params => $params, - %$opts - }); - - return if $self->error; - return 1; -} - -sub files_put { - my ($self, $path, $content, $params, $opts) = @_; - - $opts ||= {}; - $self->api_json({ - method => 'PUT', - url => $self->url('https://api-content.dropbox.com/1/files_put/' . $self->root, $path), - content => $content, - extra_params => $params, - %$opts - }); -} - -sub files_post { - my ($self, $path, $content, $params, $opts) = @_; - - $opts ||= {}; - $self->api_json({ - method => 'POST', - url => $self->url('https://api-content.dropbox.com/1/files/' . $self->root, $path), - content => $content, - extra_params => $params, - %$opts - }); -} - -sub files_put_chunked { - my ($self, $path, $content, $params, $opts, $limit) = @_; - - $limit ||= 4 * 1024 * 1024; # A typical chunk is 4 MB - - my $upload; - $upload = sub { - my $data = shift; - my $buf; - my $total = $limit; - my $chunk = 1024; - my $tmp = File::Temp->new; - while (my $read = read($content, $buf, $chunk)) { - $tmp->print($buf); - $total -= $read; - if ($chunk > $total) { - $chunk = $total; - } - last unless $chunk; - } - - if ($total == $limit) { - $data->{upload_id} or die 'read error.'; - return $self->commit_chunked_upload( - $path, - { - upload_id => $data->{upload_id}, - ($params ? %$params : ()) - }, - $opts - ) || die $self->error; - } - - $tmp->flush; - $tmp->seek(0, 0); - $data = $self->chunked_upload($tmp, {($data ? %$data : ()), ($params ? %$params : ())}, $opts) or die $self->error; - $upload->({ - upload_id => $data->{upload_id}, - offset => $data->{offset}}); - }; - $upload->(); -} - -sub chunked_upload { - my ($self, $content, $params, $opts) = @_; - - $opts ||= {}; - $self->api_json({ - method => 'PUT', - url => $self->url('https://api-content.dropbox.com/1/chunked_upload', ''), - content => $content, - extra_params => $params, - %$opts - }); -} - -sub commit_chunked_upload { - my ($self, $path, $params, $opts) = @_; - - $opts ||= {}; - $self->api_json({ - method => 'POST', - url => $self->url('https://api-content.dropbox.com/1/commit_chunked_upload/' . $self->root, $path), - extra_params => $params, - %$opts - }); -} - -sub metadata { - my ($self, $path, $params) = @_; - - $self->api_json({ - url => $self->url('https://api.dropbox.com/1/metadata/' . $self->root, $path), - extra_params => $params - }); -} - -sub delta { - my ($self, $params) = @_; - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/delta', ''), - extra_params => $params - }); -} - -sub revisions { - my ($self, $path, $params) = @_; - - $self->api_json({ - url => $self->url('https://api.dropbox.com/1/revisions/' . $self->root, $path), - extra_params => $params - }); -} - -sub restore { - my ($self, $path, $params) = @_; - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/restore/' . $self->root, $path), - extra_params => $params - }); -} - -sub search { - my ($self, $path, $params) = @_; - - $self->api_json({ - url => $self->url('https://api.dropbox.com/1/search/' . $self->root, $path), - extra_params => $params - }); -} - -sub shares { - my ($self, $path, $params) = @_; - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/shares/' . $self->root, $path), - extra_params => $params - }); -} - -sub media { - my ($self, $path, $params) = @_; - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/media/' . $self->root, $path), - extra_params => $params - }); -} - -sub copy_ref { - my ($self, $path, $params) = @_; - - $self->api_json({ - method => 'GET', - url => $self->url('https://api.dropbox.com/1/copy_ref/' . $self->root, $path), - extra_params => $params - }); -} - -sub thumbnails { - my ($self, $path, $output, $params, $opts) = @_; - - $opts ||= {}; - if (ref $output eq 'CODE') { - $opts->{write_code} = $output; # code ref - } elsif (ref $output) { - $opts->{write_file} = $output; # file handle - binmode $opts->{write_file}; - } else { - open $opts->{write_file}, '>', $output; # file path - Carp::croak("invalid output, output must be code ref or filehandle or filepath.") - unless $opts->{write_file}; - binmode $opts->{write_file}; - } - $opts->{extra_params} = $params if $params; - $self->api({ - url => $self->url('https://api-content.dropbox.com/1/thumbnails/' . $self->root, $path), - extra_params => $params, - %$opts, - }); - return if $self->error; - return 1; -} - -sub create_folder { - my ($self, $path, $params) = @_; - - $params ||= {}; - $params->{root} ||= $self->root; - $params->{path} = $self->path($path); - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/fileops/create_folder', ''), - extra_params => $params - }); -} - -sub copy { - my ($self, $from, $to_path, $params) = @_; - - $params ||= {}; - $params->{root} ||= $self->root; - $params->{to_path} = $self->path($to_path); - if (ref $from) { - $params->{from_copy_ref} = $from->{copy_ref}; - } else { - $params->{from_path} = $self->path($from); - } - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/fileops/copy', ''), - extra_params => $params - }); -} - -sub move { - my ($self, $from_path, $to_path, $params) = @_; - - $params ||= {}; - $params->{root} ||= $self->root; - $params->{from_path} = $self->path($from_path); - $params->{to_path} = $self->path($to_path); - - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/fileops/move', ''), - extra_params => $params - }); -} - -sub delete { - my ($self, $path, $params) = @_; - - $params ||= {}; - $params->{root} ||= $self->root; - $params->{path} ||= $self->path($path); - $self->api_json({ - method => 'POST', - url => $self->url('https://api.dropbox.com/1/fileops/delete', ''), - extra_params => $params - }); -} - -# private - -sub parse_error ($) { - eval { decode_json($_[0])->{error} } || $_[0]; -} - -sub api { - my ($self, $args) = @_; - - $args->{method} ||= 'GET'; - $args->{url} = $self->oauth_request_url($args); - - $self->request_url($args->{url}); - $self->request_method($args->{method}); - - return $self->api_lwp($args) if $WebService::Dropbox::USE_LWP; - - if (my $write_file = delete $args->{write_file}) { - $args->{write_code} = sub { - $write_file->print($_[3]); - }; - } - if (my $write_code = delete $args->{write_code}) { - $args->{write_code} = sub { - if ($_[0] =~ qr{ \A 2 }xms) { - $write_code->(@_); - } else { - $self->error(parse_error($_[3])); - } - }; - } - - my ($minor_version, $code, $msg, $headers, $body) = $self->furl->request(%$args); - $self->code($code); - if ($code !~ qr{ \A 2 }xms) { - unless ($self->error) { - $self->error($body || $msg); - } - return; - } else { - $self->error(undef); - } - - return $body; -} - -sub api_lwp { - my ($self, $args) = @_; - - my $headers = []; - if ($args->{write_file}) { - $args->{write_code} = sub { - my $buf = shift; - $args->{write_file}->print($buf); - }; - } - if ($args->{content}) { - my $buf; - my $content = delete $args->{content}; - $args->{content} = sub { - read($content, $buf, 1024); - return $buf; - }; - my $assert = sub { - $_[0] or Carp::croak("Failed to $_[1] for Content-Length: $!",); - }; - $assert->(defined(my $cur_pos = tell($content)), 'tell'); - $assert->(seek($content, 0, SEEK_END), 'seek'); - $assert->(defined(my $end_pos = tell($content)), 'tell'); - $assert->(seek($content, $cur_pos, SEEK_SET), 'seek'); - my $content_length = $end_pos - $cur_pos; - push @$headers, 'Content-Length' => $content_length; - } - if ($args->{headers}) { - push @$headers, @{$args->{headers}}; - } - my $req = HTTP::Request->new($args->{method}, $args->{url}, $headers, $args->{content}); - my $ua = LWP::UserAgent->new; - $ua->timeout($self->timeout); - $ua->env_proxy if $self->{env_proxy}; - my $res = $ua->request($req, $args->{write_code}); - $self->code($res->code); - if ($res->is_success) { - $self->error(undef); - } else { - $self->error(parse_error($res->decoded_content)); - } - return $res->decoded_content; -} - -sub api_json { - my ($self, $args) = @_; - - my $body = $self->api($args) or return; - return if $self->error; - return $body if $self->no_decode_json; - return decode_json($body); -} - -sub oauth_request_url { - my ($self, $args) = @_; - - Carp::croak("missing url.") unless $args->{url}; - Carp::croak("missing method.") unless $args->{method}; - - my ($type, $token, $token_secret); - if ($args->{url} eq $request_token_url) { - $type = 'request token'; - } elsif ($args->{url} eq $access_token_url) { - Carp::croak("missing request_token.") unless $self->request_token; - Carp::croak("missing request_secret.") unless $self->request_secret; - $type = 'access token'; - $token = $self->request_token; - $token_secret = $self->request_secret; - } else { - Carp::croak("missing access_token, please `\$dropbox->auth;`.") unless $self->access_token; - Carp::croak("missing access_secret, please `\$dropbox->auth;`.") unless $self->access_secret; - $type = 'protected resource'; - $token = $self->access_token; - $token_secret = $self->access_secret; - } - - my $request = Net::OAuth->request($type)->new( - consumer_key => $self->key, - consumer_secret => $self->secret, - request_url => $args->{url}, - request_method => uc($args->{method}), - signature_method => 'PLAINTEXT', # HMAC-SHA1 can't delete %20.txt bug... - timestamp => time, - nonce => $self->nonce, - token => $token, - token_secret => $token_secret, - extra_params => $args->{extra_params}, - ); - $request->sign; - $request->to_url; -} - -sub furl { - my $self = shift; - unless ($self->{furl}) { - $self->{furl} = Furl::HTTP->new( - timeout => $self->timeout, - ssl_opts => { - SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER(), - }, - ); - $self->{furl}->env_proxy if $self->{env_proxy}; - } - $self->{furl}; -} - -sub url { - my ($self, $base, $path, $params) = @_; - my $url = URI->new($base . uri_escape_utf8($self->path($path), q{^a-zA-Z0-9_.~/-})); - $url->query_form($params) if $params; - $url->as_string; -} - -sub path { - my ($self, $path) = @_; - return '' unless defined $path; - return '' unless length $path; - $path =~ s|^/||; - return '/' . $path; -} - -sub nonce { - my $length = 16; - my @chars = ('A' .. 'Z', 'a' .. 'z', '0' .. '9'); - my $ret; - for (1 .. $length) { - $ret .= $chars[int rand @chars]; - } - return $ret; -} - -sub mk_accessors { - my $package = shift; - no strict 'refs'; - foreach my $field (@_) { - *{$package . '::' . $field} = sub { - return $_[0]->{$field} if scalar(@_) == 1; - return $_[0]->{$field} = scalar(@_) == 2 ? $_[1] : [@_[1 .. $#_]]; - }; - } -} - -sub env_proxy { $_[0]->{env_proxy} = defined $_[1] ? $_[1] : 1 } - -# Backward Compatibility -sub lwp_env_proxy { shift->env_proxy(@_) } - -1; -__END__ - -=head1 NAME - -WebService::Dropbox - Perl interface to Dropbox API - -=head1 SYNOPSIS - - use WebService::Dropbox; - - my $dropbox = WebService::Dropbox->new({ - key => '...', # App Key - secret => '...' # App Secret - }); - - # get access token - if (!$access_token or !$access_secret) { - my $url = $dropbox->login or die $dropbox->error; - warn "Please Access URL and press Enter: $url"; - ; - $dropbox->auth or die $dropbox->error; - warn "access_token: " . $dropbox->access_token; - warn "access_secret: " . $dropbox->access_secret; - } else { - $dropbox->access_token($access_token); - $dropbox->access_secret($access_secret); - } - - my $info = $dropbox->account_info or die $dropbox->error; - - # download - # https://www.dropbox.com/developers/reference/api#files - my $fh_get = IO::File->new('some file', '>'); - $dropbox->files('make_test_folder/test.txt', $fh_get) or die $dropbox->error; - $fh_get->close; - - # upload - # https://www.dropbox.com/developers/reference/api#files_put - my $fh_put = IO::File->new('some file'); - $dropbox->files_put('make_test_folder/test.txt', $fh_put) or die $dropbox->error; - $fh_put->close; - - # filelist(metadata) - # https://www.dropbox.com/developers/reference/api#metadata - my $data = $dropbox->metadata('folder_a'); - -=head1 DESCRIPTION - -WebService::Dropbox is Perl interface to Dropbox API - -- Support Dropbox v1 REST API - -- Support Furl (Fast!!!) - -- Streaming IO (Low Memory) - -- Default URI Escape (The specified path is utf8 decoded string) - -=head1 API - -=head2 login(callback_url) - get request token and request secret - - my $callback_url = '...'; # optional - my $url = $dropbox->login($callback_url) or die $dropbox->error; - warn "Please Access URL and press Enter: $url"; - -=head2 auth - get access token and access secret - - $dropbox->auth or die $dropbox->error; - warn "access_token: " . $dropbox->access_token; - warn "access_secret: " . $dropbox->access_secret; - -=head2 root - set access type - - # Access Type is App folder - # Your app only needs access to a single folder within the user's Dropbox - $dropbox->root('sandbox'); - - # Access Type is Full Dropbox (default) - # Your app needs access to the user's entire Dropbox - $dropbox->root('dropbox'); - -L - -=head2 account_info - - my $info = $dropbox->account_info or die $dropbox->error; - - # { - # "referral_link": "https://www.dropbox.com/referrals/r1a2n3d4m5s6t7", - # "display_name": "John P. User", - # "uid": 12345678, - # "country": "US", - # "quota_info": { - # "shared": 253738410565, - # "quota": 107374182400000, - # "normal": 680031877871 - # }, - # "email": "john@example.com" - # } - -L - -=head2 files(path, output, [params, opts]) - download (no file list, file list is metadata) - - # Current Rev - my $fh_get = IO::File->new('some file', '>'); - $dropbox->files('folder/file.txt', $fh_get) or die $dropbox->error; - $fh_get->close; - - # Specified Rev - $dropbox->files('folder/file.txt', $fh_get, { rev => ... }) or die $dropbox->error; - - # Code ref - $dropbox->files('folder/file.txt', sub { - # compatible with LWP::UserAgent and Furl::HTTP - my $chunk = @_ == 4 ? @_[3] : $_[0]; - print $chunk; - }) or die $dropbox->error; - - # Range - $dropbox->files('folder/file.txt', $fh_get) or die $dropbox->error; - > "0123456789" - $dropbox->files('folder/file.txt', $fh_get, undef, { headers => ['Range' => 'bytes=5-6'] }) or die $dropbox->error; - > "56" - -L - -=head2 files_put(path, input) - Uploads a files - - my $fh_put = IO::File->new('some file'); - $dropbox->files_put('folder/test.txt', $fh_put) or die $dropbox->error; - $fh_put->close; - - # no overwrite (default true) - $dropbox->files_put('folder/test.txt', $fh_put, { overwrite => 0 }) or die $dropbox->error; - # create 'folder/test (1).txt' - - # Specified Parent Rev - $dropbox->files_put('folder/test.txt', $fh_put, { parent_rev => ... }) or die $dropbox->error; - # conflict prevention - -L - -=head2 files_put_chunked(path, input) - Uploads large files by chunked_upload and commit_chunked_upload. - - my $fh_put = IO::File->new('some large file'); - $dropbox->files_put('folder/test.txt', $fh_put) or die $dropbox->error; - $fh_put->close; - -L - -=head2 chunked_upload(input, [params]) - Uploads large files - - # large file 1/3 - my $fh_put = IO::File->new('large file part 1'); - my $data = $dropbox->chunked_upload($fh_put) or die $dropbox->error; - $fh_put->close; - - # large file 2/3 - $fh_put = IO::File->new('large file part 2'); - $data = $dropbox->chunked_upload($fh_put, { - upload_id => $data->{upload_id}, - offset => $data->{offset} - }) or die $dropbox->error; - $fh_put->close; - - # large file 3/3 - $fh_put = IO::File->new('large file part 3'); - $data = $dropbox->chunked_upload($fh_put, { - upload_id => $data->{upload_id}, - offset => $data->{offset} - }) or die $dropbox->error; - $fh_put->close; - - # commit - $dropbox->commit_chunked_upload('folder/test.txt', { - upload_id => $data->{upload_id} - }) or die $dropbox->error; - -L - -=head2 commit_chunked_upload(path, params) - Completes an upload initiated by the chunked_upload method. - - $dropbox->commit_chunked_upload('folder/test.txt', { - upload_id => $data->{upload_id} - }) or die $dropbox->error; - -L - -=head2 copy(from_path or from_copy_ref, to_path) - - # from_path - $dropbox->copy('folder/test.txt', 'folder/test_copy.txt') or die $dropbox->error; - - # from_copy_ref - my $copy_ref = $dropbox->copy_ref('folder/test.txt') or die $dropbox->error; - - $dropbox->copy($copy_ref, 'folder/test_copy.txt') or die $dropbox->error; - -L - -=head2 move(from_path, to_path) - - $dropbox->move('folder/test.txt', 'folder/test_move.txt') or die $dropbox->error; - -L - -=head2 delete(path) - - # folder delete - $dropbox->delete('folder') or die $dropbox->error; - - # file delete - $dropbox->delete('folder/test.txt') or die $dropbox->error; - -L - -=head2 create_folder(path) - - $dropbox->create_folder('some_folder') or die $dropbox->error; - -L - -=head2 metadata(path, [params]) - get file list - - my $data = $dropbox->metadata('some_folder') or die $dropbox->error; - - my $data = $dropbox->metadata('some_file') or die $dropbox->error; - - # 304 - my $data = $dropbox->metadata('some_folder', { hash => ... }); - return if $dropbox->code == 304; # not modified - die $dropbox->error if $dropbox->error; - return $data; - -L - -=head2 delta([params]) - get file list - - my $data = $dropbox->delta() or die $dropbox->error; - -L - -=head2 revisions(path, [params]) - - my $data = $dropbox->revisions('some_file') or die $dropbox->error; - -L - -=head2 restore(path, [params]) - - # params rev is Required - my $data = $dropbox->restore('some_file', { rev => $rev }) or die $dropbox->error; - -L - -=head2 search(path, [params]) - - # query rev is Required - my $data = $dropbox->search('some_file', { query => $query }) or die $dropbox->error; - -L - -=head2 shares(path, [params]) - - my $data = $dropbox->shares('some_file') or die $dropbox->error; - -L - -=head2 media(path, [params]) - - my $data = $dropbox->media('some_file') or die $dropbox->error; - -L - -=head2 copy_ref(path) - - my $copy_ref = $dropbox->copy_ref('folder/test.txt') or die $dropbox->error; - - $dropbox->copy($copy_ref, 'folder/test_copy.txt') or die $dropbox->error; - -L - -=head2 thumbnails(path, output) - - my $fh_get = File::Temp->new; - $dropbox->thumbnails('folder/file.txt', $fh_get) or die $dropbox->error; - $fh_get->flush; - $fh_get->seek(0, 0); - -L - -=head2 env_proxy - -enable HTTP_PROXY, NO_PROXY - - $dropbox->env_proxy; - -=head1 AUTHOR - -Shinichiro Aska - -=head1 SEE ALSO - -- L - -=head1 LICENSE - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. - -=cut diff -Nru shutter-0.99.2/share/shutter/resources/po/shutter/fr.po shutter-0.99.5/share/shutter/resources/po/shutter/fr.po --- shutter-0.99.2/share/shutter/resources/po/shutter/fr.po 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/po/shutter/fr.po 2024-03-31 16:18:07.000000000 +0000 @@ -1271,11 +1271,11 @@ "Shutter will wait up to %d seconds for the screenshot to capture before " "aborting the process if it's taking too long" msgstr[0] "" -"Shutter attendra jusqu'à %d seconde pour la capture d'écran avant " -"d'abandonné la tâche" +"Shutter attendra jusqu'à %d seconde que la capture d'écran se fasse avant" +"d'abandonner la tâche" msgstr[1] "" -"Shutter attendra jusqu'à %d secondes pour la capture d'écran avant " -"d'abandonné la tâche" +"Shutter attendra jusqu'à %d secondes que la capture d'écran se fasse avant" +"d'abandonner la tâche" #: translate_tmp.pl:8911 #: translate_tmp.pl:8912 @@ -1287,11 +1287,11 @@ "Shutter will wait up to %d minutes for the screenshot to capture before " "aborting the process if it's taking too long" msgstr[0] "" -"Shutter attendra jusqu'à %d minute pour la capture d'écran avant d'abandonné " -"la tâche" +"Shutter attendra jusqu'à %d minute que la capture d'écran se fasse avant" +"d'abandonner la tâche" msgstr[1] "" -"Shutter attendra jusqu'à %d minutes pour la capture d'écran avant " -"d'abandonné la tâche" +"Shutter attendra jusqu'à %d minutes que la capture d'écran se fasse avant " +"d'abandonner la tâche" #: translate_tmp.pl:9046 msgid "Host" @@ -2001,8 +2001,8 @@ "Set how long Shutter will wait for the screenshot to complete before " "aborting the process if it's taking too long" msgstr "" -"Définir le temps que Shutter doit attendre pour que la capture d'écran soit " -"finie avant d'abandonné la tâche" +"Définir la durée d'attente de Shutter pour que la capture d'écran soit " +"faite avant d'abandonner la tâche si cela prend trop de temps" #: translate_tmp.pl:233 msgid "Edit" diff -Nru shutter-0.99.2/share/shutter/resources/system/upload_plugins/upload/Dropbox.pm shutter-0.99.5/share/shutter/resources/system/upload_plugins/upload/Dropbox.pm --- shutter-0.99.2/share/shutter/resources/system/upload_plugins/upload/Dropbox.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/system/upload_plugins/upload/Dropbox.pm 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -#! /usr/bin/env perl -################################################### -# -# Copyright (C) 2008-2013 Mario Kemper -# -# This file is part of Shutter. -# -# Shutter is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# Shutter is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Shutter; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -################################################### - -package Dropbox; - -use lib $ENV{'SHUTTER_ROOT'} . '/share/shutter/resources/modules'; - -use utf8; -use strict; -use POSIX qw/setlocale/; -use Locale::gettext; -use Glib qw/TRUE FALSE/; -use Data::Dumper; - -use Shutter::Upload::Shared; -our @ISA = qw(Shutter::Upload::Shared); - -my $d = Locale::gettext->domain("shutter-upload-plugins"); -$d->dir($ENV{'SHUTTER_INTL'}); - -my %upload_plugin_info = ( - 'module' => "Dropbox", - 'url' => "https://www.dropbox.com/", - 'registration' => "https://www.dropbox.com/register", - 'description' => $d->get("Upload screenshots into your Dropbox"), - 'supports_anonymous_upload' => FALSE, - 'supports_authorized_upload' => FALSE, - 'supports_oauth_upload' => TRUE, -); - -binmode(STDOUT, ":utf8"); -if (exists $upload_plugin_info{$ARGV[0]}) { - print $upload_plugin_info{$ARGV[0]}; - exit; -} - -################################################### - -sub new { - my $class = shift; - - #call constructor of super class (host, debug_cparam, shutter_root, gettext_object, main_gtk_window, ua) - my $self = $class->SUPER::new(shift, shift, shift, shift, shift, shift); - - bless $self, $class; - return $self; -} - -sub init { - my $self = shift; - - #do custom stuff here - use WebService::Dropbox; - use IO::File; - use JSON::MaybeXS; - use URI::Escape qw(uri_escape); - use File::Basename qw(dirname basename); - use Path::Class; - - $WebService::Dropbox::USE_LWP = TRUE; - - $self->{_box} = undef; - $self->{_config} = {}; - $self->{_config_file} = file($ENV{'HOME'}, '.dropbox-api-config'); - - return $self->connect; -} - -sub connect { - my $self = shift; - - if (-f $self->{_config_file}) { - eval { - $self->{_config} = decode_json($self->{_config_file}->slurp); - $self->{_config}->{upload_folder} = $self->{_config}->{upload_folder} || 'Apps/Shutter'; - $self->{_box} = WebService::Dropbox->new($self->{_config}); - }; - if ($@) { - return FALSE; - } - } else { - $self->{_config}->{key} = 'fwsv9z8slaw0c0q'; - $self->{_config}->{secret} = 'hsxflivocvav6ag'; - $self->{_config}->{upload_folder} = 'Apps/Shutter'; - $self->{_config}->{callback_url} = ''; - return $self->setup; - } - - return TRUE; -} - -sub setup { - my $self = shift; - - if ($self->{_debug_cparam}) { - print "Setting up Dropbox...\n"; - } - - #some helpers - my $sd = Shutter::App::SimpleDialogs->new; - - #Authentication - $self->{_box} = WebService::Dropbox->new($self->{_config}); - my $login_link = $self->{_box}->login($self->{_config}->{callback_url}); - if ($self->{_box}->error) { - $sd->dlg_error_message($self->{_box}->error, $d->get("There was an error receiving the authentication URL.")); - print "ERROR: There was an error while receiving the Dropbox-URL. ", $self->{_box}->error, "\n"; - return FALSE; - } else { - my $response = $sd->dlg_info_message( - $d->get("Please click on the button below to authorize with Dropbox. Press 'Apply' when you are done."), - $d->get("Authorize with Dropbox"), - 'gtk-cancel', 'gtk-apply', undef, undef, undef, undef, undef, undef, Gtk3::LinkButton->new($login_link, $d->get("Authorize")), - ); - if ($response == 20) { - - if ($self->{_debug_cparam}) { - print "Dropbox: Authorizing...\n"; - } - - $self->{_box}->auth; - if ($self->{_box}->error) { - $sd->dlg_error_message($self->{_box}->error, $d->get("There was an error authenticating with Dropbox.")); - print "ERROR: There was an error while authenticating with Dropbox. ", $self->{_box}->error, "\n"; - return FALSE; - } - - #get atoken and asecret - $self->{_config}->{access_token} = $self->{_box}->access_token; - $self->{_config}->{access_secret} = $self->{_box}->access_secret; - - if ($self->{_debug_cparam}) { - print $self->{_config}->{access_token}, "\n"; - print $self->{_config}->{access_secret}, "\n"; - } - - $self->{_config_file}->openw->print(encode_json($self->{_config})); - chmod 0600, $self->{_config_file}; - - #again - $self->{_box} = WebService::Dropbox->new($self->{_config}); - - return TRUE; - } else { - return FALSE; - } - - } -} - -sub get_uid { - my $self = shift; - my $info = $self->{_box}->account_info(); - return $info->{uid}; -} - -sub escape { - my $self = shift; - my $str = shift; - my $escape = uri_escape($str); - return $escape; -} - -sub upload { - my ($self, $upload_filename) = @_; - - #store as object vars - $self->{_filename} = $upload_filename; - utf8::encode $upload_filename; - - eval { - my $upload_file = IO::File->new($upload_filename); - my $res = $self->{_box}->files_put($self->{_config}->{upload_folder} . "/" . basename($upload_filename), $upload_file); - $upload_file->close(); - - if (!$self->{_box}->error) { - $res = $self->{_box}->shares($self->{_config}->{upload_folder} . "/" . basename($upload_filename), {short_url => 0}); - } - - if (!$self->{_box}->error) { - - #set status (success) - $self->{_links}{'status'} = 200; - $self->{_links}->{'direct_link'} = $res->{url}; - - #print all links (debug) - if ($self->{_debug_cparam}) { - foreach (keys %{$self->{_links}}) { - print $_. ": " . $self->{_links}->{$_}, "\n"; - } - } - } else { - $self->{_links}{'status'} = $self->{_box}->error; - if ($self->{_box}->error =~ m/401/) { - unlink $self->{_config_file}; - $self->{_links}{'status'} = - $self->{_box}->error . ": " - . $d->get( - "Maybe you or Dropbox revoked or expired an access token. Please close this dialog and try again. Your account will be re-authenticated the next time you upload a file."); - } - } - }; - if ($@) { - $self->{_links}{'status'} = $@; - } - - return %{$self->{_links}}; -} - -1; diff -Nru shutter-0.99.2/share/shutter/resources/system/upload_plugins/upload/ToileLibre.pm shutter-0.99.5/share/shutter/resources/system/upload_plugins/upload/ToileLibre.pm --- shutter-0.99.2/share/shutter/resources/system/upload_plugins/upload/ToileLibre.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/system/upload_plugins/upload/ToileLibre.pm 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -#! /usr/bin/env perl -################################################### -# -# Copyright (C) 2010-2011 Vadim Rutkovsky , Mario Kemper and Shutter Team -# -# This file is part of Shutter. -# -# Shutter is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# Shutter is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Shutter; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -################################################### - -package ToileLibre; - -use lib $ENV{'SHUTTER_ROOT'} . '/share/shutter/resources/modules'; - -use utf8; -use strict; -use POSIX qw/setlocale/; -use Locale::gettext; -use Glib qw/TRUE FALSE/; - -use Shutter::Upload::Shared; -our @ISA = qw(Shutter::Upload::Shared); - -my $d = Locale::gettext->domain("shutter-upload-plugins"); -$d->dir($ENV{'SHUTTER_INTL'}); - -my %upload_plugin_info = ( - 'module' => "ToileLibre", - 'url' => "http://pix.toile-libre.org/", - 'registration' => "-", - 'description' => $d->get("Upload screenshots to pix.toile-libre.org"), - 'supports_anonymous_upload' => TRUE, - 'supports_authorized_upload' => FALSE, -); - -binmode(STDOUT, ":utf8"); -if (exists $upload_plugin_info{$ARGV[0]}) { - print $upload_plugin_info{$ARGV[0]}; - exit; -} - -################################################### - -sub new { - my $class = shift; - - #call constructor of super class (host, debug_cparam, shutter_root, gettext_object, main_gtk_window, ua) - my $self = $class->SUPER::new(shift, shift, shift, shift, shift, shift); - - bless $self, $class; - return $self; -} - -sub init { - my $self = shift; - - #do custom stuff here - use WWW::Mechanize; - use HTTP::Status; - use HTTP::Request::Common 'POST'; - - $self->{_mech} = WWW::Mechanize->new(agent => "$self->{_ua}", timeout => 20); - $self->{_http_status} = undef; - - return TRUE; -} - -sub upload { - my ($self, $upload_filename, $username, $password) = @_; - - #store as object vars - $self->{_filename} = $upload_filename; - $self->{_username} = $username; - $self->{_password} = $password; - - my $filesize = -s $upload_filename; - my $max_filesize = 15360000; - if ($filesize > $max_filesize) { - $self->{_links}{'status'} = 998; - $self->{_links}{'max_filesize'} = sprintf("%.2f", $max_filesize / 1024) . " KB"; - return %{$self->{_links}}; - } - - utf8::encode $upload_filename; - utf8::encode $password; - utf8::encode $username; - - eval { - - $self->{_mech}->get("http://pix.toile-libre.org"); - $self->{_http_status} = $self->{_mech}->status(); - - if (is_success($self->{_http_status})) { - - $self->{_mech}->request( - POST "http://pix.toile-libre.org/?action=upload", - Content_Type => 'form-data', - Content => [ - img => [$upload_filename], - ], - ); - - $self->{_http_status} = $self->{_mech}->status(); - - if (is_success($self->{_http_status})) { - my $html_file = $self->{_mech}->content; - - my @links = $html_file =~ m{ }gx; - - $self->{_links}{'view_image'} = $links[0]; - $self->{_links}{'direct_link'} = $links[1]; - $self->{_links}{'thumbnail_forum'} = $links[2]; - $self->{_links}{'forum'} = $links[3]; - $self->{_links}{'thumbnail website'} = $links[4]; - $self->{_links}{'website'} = $links[5]; - - if ($self->{_debug}) { - print "The following links were returned by http://pix.toile-libre.org:\n"; - print $self->{_links}{'view_image'}, "\n"; - print $self->{_links}{'direct_link'}, "\n"; - print $self->{_links}{'thumbnail_forum'}, "\n"; - print $self->{_links}{'forum'}, "\n"; - print $self->{_links}{'thumbnail website'}, "\n"; - print $self->{_links}{'website'}, "\n"; - } - - $self->{_links}{'status'} = $self->{_http_status}; - } else { - $self->{_links}{'status'} = $self->{_http_status}; - } - - } else { - $self->{_links}{'status'} = $self->{_http_status}; - } - - }; - if ($@) { - $self->{_links}{'status'} = $@; - } - - return %{$self->{_links}}; - -} - -1; diff -Nru shutter-0.99.2/share/shutter/resources/system/upload_plugins/upload/vgyme.pm shutter-0.99.5/share/shutter/resources/system/upload_plugins/upload/vgyme.pm --- shutter-0.99.2/share/shutter/resources/system/upload_plugins/upload/vgyme.pm 2021-10-24 19:10:52.000000000 +0000 +++ shutter-0.99.5/share/shutter/resources/system/upload_plugins/upload/vgyme.pm 1970-01-01 00:00:00.000000000 +0000 @@ -1,234 +0,0 @@ -#! /usr/bin/env perl -################################################### -# -# Copyright (C) 2014 SwooshyCueb -# -# This file is a part of Shutter. -# -# Shutter is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# Shutter is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Shutter; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -################################################### - -package vgyme; - -use lib $ENV{'SHUTTER_ROOT'} . '/share/shutter/resources/modules'; - -use utf8; -use strict; -use POSIX qw/setlocale/; -use Locale::gettext; -use Glib qw/TRUE FALSE/; - -use Shutter::Upload::Shared; -our @ISA = qw(Shutter::Upload::Shared); - -my $d = Locale::gettext->domain("shutter-upload-plugins"); -$d->dir($ENV{'SHUTTER_INTL'}); - -my %upload_plugin_info = ( - 'module' => "vgyme", - 'url' => "http://vgy.me/", - 'registration' => "-", - 'description' => $d->get("Upload screenshots and such to vgy.me (API v2)"), - 'supports_anonymous_upload' => TRUE, - 'supports_authorized_upload' => FALSE, - 'supports_oauth_upload' => FALSE, -); - -our ($url); - -$url = "http://vgy.me"; - -binmode(STDOUT, ":utf8"); -if (exists $upload_plugin_info{$ARGV[0]}) { - print $upload_plugin_info{$ARGV[0]}; - exit; -} - -# Methods below! Or whatever they're called in Perl... - -sub new { - my $class = shift; - - my $self = $class->SUPER::new(shift, shift, shift, shift, shift, shift); - - bless $self, $class; - return $self; -} - -sub init { - - my $self = shift; - - use WWW::Mechanize; - use HTTP::Status; - use HTTP::Request::Common 'POST'; - - use File::Temp; - use File::Spec; - use File::Copy; - - use JSON::MaybeXS; - - $self->{_mech} = WWW::Mechanize->new(agent => "$self->{_ua}", timeout => 20); - $self->{_http_status} = undef; - - return TRUE; -} - -sub upload { - my ($self, $upload_filename) = @_; - - #no point in assingning variables for username and password yet. - - $self->{_filename} = $upload_filename; - - utf8::encode $upload_filename; - - my $original_filename = $upload_filename; - my $ext = undef; - my $firsterr = undef; - my $json_data = undef; - my $json_worker = undef; - my $returned_data = undef; - my $nothumbnail = 0; - - eval { - #make sure service is actually up - $self->{_mech}->get($url); - $self->{_http_status} = $self->{_mech}->status(); - - if (is_success($self->{_http_status})) { - $json_worker = JSON::MaybeXS->new; - - #vgy.me doesn't like upper case file extensions. - $original_filename =~ /\.([^.]+)$/; - $ext = '.' . $1; - if ($ext ne lc $ext) { - $ext = lc $ext; - $upload_filename = File::Spec->catfile(File::Spec->tmpdir(), mktemp("shuttervgy~XXXXXX") . $ext); - copy($original_filename, $upload_filename); - } - - #Pickiness seems to have been mostly resolved, so only one attempt will be made. - #There aren't any error codes or error id strings, so we have to rely on the error message. - #That means if these ever change the script will need to be updated agian. - $self->{_mech}->request( - POST "http://vgy.me/upload.php", - Content_Type => 'form-data', - Content => [image => [$upload_filename],], - ); - - $self->{_http_status} = $self->{_mech}->status(); - - if (is_success($self->{_http_status})) { - - $returned_data = $self->{_mech}->content; - - #print "Returned data:\n$returned_data\n"; - - # Sometimes an invalid image will upload and be stored just fine, - # but will fail thumbnail creation. Errors are returned in the - # fetched data, before the JSON string, which causes problems for us. - # Here, we clean that up - if ($returned_data =~ m/<.*>\n/gms) { - $returned_data =~ s/^<.*>\n\{/\{/ms; - $nothumbnail = 1; - - #print "Cleaned returned data:\n$returned_data\n"; - } - - $json_data = $json_worker->decode($returned_data); - - #my (undef, undef, $ext) = fileparse($upload_filename); - #that should work, but it doesn't... - $upload_filename =~ /\.([^.]+)$/; - $ext = '.' . $1; - - if ($json_data->{'error'}) { - - #replace HTML line break with real line break - $json_data->{'errorMsg'} =~ s/
/\n/ig; - if ($json_data->{'errorMsg'} =~ m/That file is invalid/i) { - $self->{_links}{'status'} = 'The image seems to be invalid or broken.'; - warn "vgy.me rejected $upload_filename, as the image seems to be broken or invalid. Upload aborted.\n"; - } elsif ($json_data->{'errorMsg'} =~ m/The file is too big/i) { - - #limit is 10MB now, which is acceptable. It also seems to be enforced well - $self->{_links}{'status'} = "The image is too large.\nMaximum file size is 10 MB."; - warn "vgy.me rejected $upload_filename because it exceeded the 10MB file size limit. Upload aborted.\n"; - } elsif ($json_data->{'errorMsg'} =~ m/It appears that filetype is invalid/i) { - - #no bmp, no tiff, no tga - #actually bmp will upload just fine, but fails thumbnailng - $self->{_links}{'status'} = "The image did not have an acceptable file extension.\njpg, jpeg, png, and gif are allowed."; - warn "vgy.me rejected $upload_filename because it does not have an acceptable file extension. Upload aborted.\n"; - } elsif ($json_data->{'errorMsg'} =~ m/No file selected\. Aborted\./i) { - - #no bmp, no tiff, no tga - #actually bmp will upload just fine, but fails thumbnailng - $self->{_links}{'status'} = "File not sent to server. Try again."; - warn "vgy.me could not receive the file. Upload aborted.\n"; - } else { - $self->{_links}{'status'} = - 'An unknown error ocurred during upload.\n\nErrorMsg:\n' . $json_data->{'errorMsg'}; - if ($self->{_debug}) { - warn "An unknown error ocurred during upload of $upload_filename to vgy.me. Upload aborted.\n"; - } - } - return %{$self->{_links}}; - } - - #linkies! - $self->{_links}{'info'} = 'http:' . $json_data->{'imageUrl'}; - - #for some reason the api starts returning the URL at "//" - - $self->{_links}{'direct'} = 'http:' . $json_data->{'hotlinkUrl'}; - - if (!$nothumbnail) { - $self->{_links}{'thumbnail'} = 'http:' . $json_data->{'thumbNail'} . '.' . $json_data->{'fileExt'}; - - #thumbnails are pretty broken right now, lol - } - - if ($self->{_debug}) { - print "The following links were returned by http://vgy.me:\n"; - print "Info: \n$self->{_links}{'info'}\n"; - print "Direct Link: \n$self->{_links}{'direct'}\n"; - if (!$nothumbnail) { - print "Thumbnail: \n$self->{_links}{'thumbnail'}\n"; - } - } - - $self->{_links}{'status'} = $self->{_http_status}; - } else { - $self->{_links}{'status'} = $self->{_http_status}; - last; - } - - } else { - $self->{_links}{'status'} = $self->{_http_status}; - } - - }; - - if ($@) { - $self->{_links}{'status'} = $@; - } - return %{$self->{_links}}; -} - -1; diff -Nru shutter-0.99.2/t/Shutter/App/01_common.t shutter-0.99.5/t/Shutter/App/01_common.t --- shutter-0.99.2/t/Shutter/App/01_common.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/App/01_common.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,221 @@ +use 5.010; +use strict; +use warnings; + +use Gtk3; # to escape warnings "Too late to run INIT block" + +use Test::More tests => 3; +use Glib qw/ TRUE FALSE /; + +use constant MOCKED_ICONTHEME_VALUE => "FOO BAR BAZ"; + +require Test::Window; + +require_ok("Shutter::App::Common"); + +subtest "Create common object" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + + my $root = $ENV{TEST_APP_SHUTTER_PATH}; + my $name = "shutter"; + my $version = 0.544; + my $revision = 1234; + my $pid = 100500; + + my $sc = _get_common_object( $root, undef, $name, $version, $revision, $pid ); + + ok( defined $sc, "Object defined" ); + isa_ok( $sc, "Shutter::App::Common" ); + ok( exists $ENV{SHUTTER_INTL}, "defined SHUTTER_INTL" ); + is( $ENV{SHUTTER_INTL}, $sc->get_root . "/share/locale", '$ENV{SHUTTER_INTL} has a right value' ); +}; + +subtest "Getters and setters" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + + my $root = $ENV{TEST_APP_SHUTTER_PATH}; + my $name = "shutter"; + my $version = 0.544; + my $revision = 1234; + my $pid = 100500; + + my $sc = _get_common_object( $root, undef, $name, $version, $revision, $pid ); + + isa_ok( $sc, "Shutter::App::Common" ); + + is( $sc->get_root, $root, "get_root" ); + is( $sc->get_appname, $name, "get_appname" ); + is( $sc->get_version, $version, "get_version" ); + is( $sc->get_rev, $revision, "get_rev" ); + + subtest "main_window" => sub { + is( $sc->get_mainwindow, undef, "main_window is null" ); + $sc->set_mainwindow("AAAA"); + is( $sc->get_mainwindow, "AAAA", "main_window is filed" ); + }; + + subtest "icontheme" => sub { + ok( defined $sc->icontheme, "icontheme defined" ); + isa_ok( $sc->icontheme, "Gtk3::IconTheme" ); + ok( $sc->icontheme->has_icon("shutter"), "has icon 'shutter'" ); + }; + + subtest "gettext_object" => sub { + ok( defined $sc->get_gettext ); + isa_ok( $sc->get_gettext, "Locale::gettext" ); + }; + + subtest "notification_object" => sub { + is( $sc->get_notification_object, undef, "notification_object is null" ); + $sc->set_notification_object(1); + is( $sc->get_notification_object, 1, "notification_object is filled" ); + }; + + subtest "globalsettings_object" => sub { + is( $sc->get_globalsettings_object, undef, "globalsettings_object is null" ); + $sc->set_globalsettings_object( { foo => 1, bar => 2 } ); + ok( defined $sc->get_globalsettings_object, "globalsettings_object is filled" ); + ok( ref $sc->get_globalsettings_object eq "HASH", "globalsettings_object is filled by proper structure" ); + ok( $sc->get_globalsettings_object->{foo} == 1, "check first parameter" ); + ok( $sc->get_globalsettings_object->{bar} == 2, "check second parameter" ); + }; + + subtest "rusf" => sub { + is( $sc->get_rusf, undef, "rusf is null" ); + $sc->set_rusf(1); + is( $sc->get_rusf, 1, "rusf is filled" ); + }; + + subtest "ruof" => sub { + is( $sc->get_ruof, undef, "ruof is null" ); + $sc->set_ruof(0); + is( $sc->get_ruof, 0, "ruof is filled" ); + }; + + subtest "ruu_tab" => sub { + is( $sc->get_ruu_tab, 0, "ruu_tab is zero" ); + $sc->set_ruu_tab(1); + is( $sc->get_ruu_tab, 1, "ruu_tab is changed" ); + }; + + subtest "ruu_hosting" => sub { + is( $sc->get_ruu_hosting, 0, "ruu_hosting is zero" ); + $sc->set_ruu_hosting(1); + is( $sc->get_ruu_hosting, 1, "ruu_hosting is changed" ); + }; + + subtest "ruu_places" => sub { + is( $sc->get_ruu_places, 0, "ruu_places is zero" ); + $sc->set_ruu_places(1); + is( $sc->get_ruu_places, 1, "ruu_places is changed" ); + }; + + subtest "debug" => sub { + is( $sc->get_debug, TRUE, "debug is enabled by default" ); + $sc->set_debug(FALSE); + is( $sc->get_debug, FALSE, "debug is disabled" ); + }; + + subtest "clear_cache" => sub { + is( $sc->get_clear_cache, FALSE, "clear_cache is disabled by default" ); + $sc->set_clear_cache(TRUE); + is( $sc->get_clear_cache, TRUE, "clear_cache is enabled" ); + }; + + subtest "min" => sub { + is( $sc->get_min, FALSE, "min is disabled by default" ); + $sc->set_min(TRUE); + is( $sc->get_min, TRUE, "min is enabled" ); + }; + + subtest "disable_systray" => sub { + is( $sc->get_disable_systray, FALSE, "disable_systray is disabled by default" ); + $sc->set_disable_systray(TRUE); + is( $sc->get_disable_systray, TRUE, "disable_systray is enabled" ); + }; + + subtest "exit_after_capture" => sub { + is( $sc->get_exit_after_capture, FALSE, "exit_after_capture is disabled by default" ); + $sc->set_exit_after_capture(TRUE); + is( $sc->get_exit_after_capture, TRUE, "exit_after_capture is enabled" ); + }; + + subtest "no_session" => sub { + is( $sc->get_no_session, FALSE, "no_session is disabled by default" ); + $sc->set_no_session(TRUE); + is( $sc->get_no_session, TRUE, "no_session is enabled" ); + }; + + subtest "start_with" => sub { + my ( $start_with, $start_with_extra ) = $sc->get_start_with; + + is( $start_with, undef, "start_with is null" ); + is( $start_with_extra, undef, "start_with_extra is null" ); + + $sc->set_start_with( "foo", "bar" ); + + ( $start_with, $start_with_extra ) = $sc->get_start_with; + + is( $start_with, "foo", "start_with is filled" ); + is( $start_with_extra, "bar", "start_with_extra is filled" ); + }; + + subtest "profile_to_start_with" => sub { + is( $sc->get_profile_to_start_with, undef, "profile_to_start_with is null" ); + $sc->set_profile_to_start_with("foo"); + is( $sc->get_profile_to_start_with, "foo", "profile_to_start_with is filled" ); + }; + + subtest "export_filename" => sub { + is( $sc->get_export_filename, undef, "export_filename is null" ); + $sc->set_export_filename("foo"); + is( $sc->get_export_filename, "foo", "export_filename is filled" ); + }; + + subtest "include_cursor" => sub { + is( $sc->get_include_cursor, undef, "include_cursor is null" ); + $sc->set_include_cursor(TRUE); + is( $sc->get_include_cursor, TRUE, "include_cursor is filled" ); + }; + + subtest "remove_cursor" => sub { + is( $sc->get_remove_cursor, undef, "remove_cursor is null" ); + $sc->set_remove_cursor(FALSE); + is( $sc->get_remove_cursor, FALSE, "remove_cursor is filled" ); + }; + + subtest "delay" => sub { + is( $sc->get_delay, undef, "delay is null" ); + $sc->set_delay(15); + is( $sc->get_delay, 15, "delay is filled" ); + }; + + subtest "get_current_monitor" => sub { + my $mon = $sc->get_current_monitor; + + ok( defined $mon, "found current monitor" ); + for my $attribute (qw/ x y width height /) { + ok( exists $mon->{$attribute}, "attribute '$attribute' exists" ); + } + }; +}; + +done_testing(); + +sub _get_common_object { + my ( $root, $main_window, $name, $version, $revision, $pid ) = @_; + + return Shutter::App::Common->new( + shutter_root => $root, + main_window => $main_window, + appname => $name, + version => $version, + rev => $revision, + pid => $pid + ); +} + diff -Nru shutter-0.99.2/t/Shutter/App/02_directories.t shutter-0.99.5/t/Shutter/App/02_directories.t --- shutter-0.99.2/t/Shutter/App/02_directories.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/App/02_directories.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,110 @@ +use 5.010; +use strict; +use warnings; + +use Test::More tests => 9; +use Test::MockModule; +use Glib qw/ TRUE FALSE /; +use File::Temp qw/ tempdir /; + +require_ok("Shutter::App::Directories"); + +{ + # do not pollute /home with these tests + my $local_home = tempdir( CLEANUP => 1 ); + local $ENV{HOME} = $local_home; + + subtest "create_if_not_exists" => sub { + my $dir = tempdir( CLEANUP => 1 ) . "/foo"; + ok( !-d $dir && !-r $dir, "dir doesn't exist" ); + is( Shutter::App::Directories::create_if_not_exists($dir), $dir, "name of directories are the same" ); + ok( -d $dir && -r $dir, "dir exists" ); + }; + + subtest "get_root_dir" => sub { + my $expected_dir = Glib::get_user_cache_dir . "/" . Shutter::App::Directories->SHUTTER_DIR; + + subtest "invoke create_if_not_exists" => + invoke_create_if_not_exists->( $expected_dir, \&Shutter::App::Directories::get_root_dir ); + + is( Shutter::App::Directories::get_root_dir(), $expected_dir, "root dir" ); + }; + + subtest "get_cache_dir" => sub { + my $expected_dir + = Glib::get_user_cache_dir . "/" + . Shutter::App::Directories->SHUTTER_DIR . "/" + . Shutter::App::Directories->UNSAVED_DIR; + + subtest "invoke create_if_not_exists" => + invoke_create_if_not_exists->( $expected_dir, \&Shutter::App::Directories::get_cache_dir ); + + is( Shutter::App::Directories::get_cache_dir(), $expected_dir, "cache dir" ); + }; + + subtest "get_temp_dir" => sub { + my $expected_dir + = Glib::get_user_cache_dir . "/" + . Shutter::App::Directories->SHUTTER_DIR . "/" + . Shutter::App::Directories->TEMP_DIR; + + subtest "invoke create_if_not_exists" => + invoke_create_if_not_exists->( $expected_dir, \&Shutter::App::Directories::get_temp_dir ); + + is( Shutter::App::Directories::get_temp_dir(), $expected_dir, "temp dir" ); + }; + + subtest "get_autostart_dir" => sub { + my $expected_dir = Glib::get_user_config_dir . "/" . Shutter::App::Directories->AUTOSTART_DIR; + + subtest "invoke create_if_not_exists" => + invoke_create_if_not_exists->( $expected_dir, \&Shutter::App::Directories::get_autostart_dir ); + + is( Shutter::App::Directories::get_autostart_dir(), $expected_dir, "autostart dir" ); + }; + + subtest "get_home_dir" => sub { + is( Shutter::App::Directories::get_home_dir(), Glib::get_home_dir, "home dir" ); + }; + + subtest "get_config_dir" => sub { + is( Shutter::App::Directories::get_config_dir(), Glib::get_user_config_dir, "config dir" ); + }; + + subtest "create_hidden_home_dir_if_not_exist" => sub { + my $hidden_dir = $ENV{HOME} . "/" . Shutter::App::Directories->HIDDEN_SHUTTER_DIR; + my $profiles_dir = $hidden_dir . "/" . Shutter::App::Directories->PROFILES_DIR; + + ok( !-d $hidden_dir, "hidden dir doesn't exist" ); + ok( !-d $profiles_dir, "profiles dir doesn't exist" ); + + Shutter::App::Directories::create_hidden_home_dir_if_not_exist(); + + ok( -d $hidden_dir, "now hidden dir exists" ); + ok( -d $profiles_dir, "now profiles dir exists" ); + }; +}; + +sub invoke_create_if_not_exists { + my ( $expected_dir, $running_function ) = @_; + + return sub { + my $invoked = FALSE; + my $obtained_dir = undef; + my $mock = Test::MockModule->new("Shutter::App::Directories"); + $mock->mock( + "create_if_not_exists", + sub { + $obtained_dir = shift; + $invoked = TRUE; + return $obtained_dir; + } ); + + $running_function->(); + + is( $invoked, TRUE, "create_if_not_exists has been invoked" ); + is( $obtained_dir, $expected_dir, "got expected dir '$expected_dir'" ); + } +} + +done_testing(); diff -Nru shutter-0.99.2/t/Shutter/Draw/001_update_warning_text.t shutter-0.99.5/t/Shutter/Draw/001_update_warning_text.t --- shutter-0.99.2/t/Shutter/Draw/001_update_warning_text.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/Draw/001_update_warning_text.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,62 @@ + +use 5.010; +use strict; +use warnings; + +use Gtk3; # to escape warnings "Too late to run INIT block" +use Locale::gettext; +use Test::More; +use Test::MockModule; + +require_ok('Shutter::Draw::DrawingTool'); + +my $mock = Test::MockModule->new("Shutter::Draw::DrawingTool"); +$mock->mock( + "new", + sub { + my $cls = shift; + + return bless { + _start_time => time(), + _d => Locale::gettext->domain("shutter") }, $cls; + } ); + +subtest "Singular minute" => sub { + my $draw = Shutter::Draw::DrawingTool->new(); + my $warn_dialog = CustomWarnDialog->new(); + + $draw->update_warning_text($warn_dialog); + + ok( $warn_dialog->{type} eq "secondary-text" ); + like( $warn_dialog->{txt}, qr/from the last minute/ ); +}; + +subtest "Plural minutes" => sub { + my $draw = Shutter::Draw::DrawingTool->new(); + my $warn_dialog = CustomWarnDialog->new(); + + $draw->{_start_time} = $draw->{_start_time} - 120; + + $draw->update_warning_text($warn_dialog); + + ok( $warn_dialog->{type} eq "secondary-text" ); + like( $warn_dialog->{txt}, qr/from the last 2 minutes/ ); +}; + +done_testing(); + +package CustomWarnDialog { + + sub new { + my $cls = shift; + + return bless { type => undef, txt => undef }, $cls; + } + + sub set { + my ( $self, $type, $txt ) = @_; + + $self->{type} = $type; + $self->{txt} = $txt; + } +}; diff -Nru shutter-0.99.2/t/Shutter/Draw/002_ui_manager.t shutter-0.99.5/t/Shutter/Draw/002_ui_manager.t --- shutter-0.99.2/t/Shutter/Draw/002_ui_manager.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/Draw/002_ui_manager.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,203 @@ +use 5.020; # List::Util >= 1.33 +use strict; +use warnings; + +use List::Util qw/ all /; + +use Gtk3; # to escape warnings "Too late to run INIT block" +use Gtk3::ImageView 10; +use Glib qw/ TRUE FALSE /; +use Test::More tests => 5; +use Test::MockModule; + +require Test::Window; +require Test::Common; +require Test::SimpleApp; + +require Shutter::App::SimpleDialogs; +require Shutter::App::HelperFunctions; +require Shutter::App::Common; +require Shutter::Draw::DrawingTool; + +require_ok("Shutter::Draw::UIManager"); + +subtest "simply create uimanager" => sub { + my $app = Test::SimpleApp->new; + my $uimanager = Shutter::Draw::UIManager->new( app => $app ); + + can_ok( $uimanager, "setup" ); + ok( defined $uimanager, "uimanager defined" ); + is( $uimanager->app, $app, "check uimanager's app" ); +}; + +subtest "internal methods" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $dt = Shutter::Draw::DrawingTool->new($sc); + + $dt->{_d} = $sc->gettext_object; + $dt->{_dicons} = $sc->get_root . "/share/shutter/resources/icons/drawing_tool"; + $dt->{_icons} = $sc->get_root . "/share/shutter/resources/icons"; + + my $uimanager = Shutter::Draw::UIManager->new( app => $dt ); + + subtest "attributes" => sub { + is( $uimanager->gettext, $sc->get_gettext, "check attribute 'gettext'" ); + is( $uimanager->dicons, $dt->dicons, "check attribute 'dicons'" ); + }; + + subtest "create_factory" => sub { + my $f = $uimanager->_create_factory; + + ok( defined $f, "factory is defined" ); + isa_ok( $f, "Gtk3::IconFactory" ); + + # check existence of several icons + for my $stock_id (qw/ shutter-ellipse shutter-pointer shutter-text shutter-number /) { + ok( defined $f->lookup($stock_id), "found '$stock_id'" ); + isa_ok( $f->lookup($stock_id), "Gtk3::IconSet" ); + } + }; + + subtest "create_main_actions" => sub { + my $main_actions = $uimanager->_create_main_actions; + + ok( ref $main_actions eq "ARRAY", "main actions ref" ); + ok( ( all { ref $_ eq "ARRAY" } @{$main_actions} ), "ref of every action" ); + ok( ( all { defined $_->[0] } @{$main_actions} ), "the first element of every action is defined" ); + }; + + subtest "create_main_group" => sub { + my $main_group = $uimanager->_create_main_group; + + ok( defined $main_group, "main_group's defined" ); + isa_ok( $main_group, "Gtk3::ActionGroup" ); + + is( $main_group->get_name, "main", "name of main_group" ); + ok( scalar( $main_group->list_actions ) > 0, "list of actions is not empty" ); + + # check some actions in the list + for my $action (qw/ File Tools Redo Paste Stop ExportTo /) { + my $action_object = $main_group->get_action($action); + + ok( defined $action_object, "has the action '$action'" ); + isa_ok( $action_object, "Gtk3::Action" ); + } + }; + + subtest "create_toggle_actions" => sub { + my $toggle_actions = $uimanager->_create_toggle_actions; + + ok( defined $toggle_actions, "defined toggle_actions" ); + ok( ref $toggle_actions eq "ARRAY", "ref of toggle_actions" ); + ok( ( all { ref $_ eq "ARRAY" } @{$toggle_actions} ), "ref of every action" ); + ok( ( all { defined $_->[0] } @{$toggle_actions} ), "the first element of every action is defined" ); + }; + + subtest "create_toggle_group" => sub { + my $toggle_group = $uimanager->_create_toggle_group; + + ok( defined $toggle_group, "toggle_group's defined" ); + isa_ok( $toggle_group, "Gtk3::ActionGroup" ); + + is( $toggle_group->get_name, "toggle", "name of toggle_group" ); + ok( scalar( $toggle_group->list_actions ) > 0, "list of actions is not empty" ); + + for my $action (qw/ Autoscroll Fullscreen /) { + my $action_object = $toggle_group->get_action($action); + + ok( defined $action_object, "has the action '$action'" ); + isa_ok( $action_object, "Gtk3::ToggleAction" ); + } + }; + + subtest "create_drawing_actions" => sub { + my $drawing_actions = $uimanager->_create_drawing_actions; + + ok( defined $drawing_actions, "defined drawing_actions" ); + ok( ref $drawing_actions eq "ARRAY", "ref of drawing_actions" ); + ok( ( all { ref $_ eq "ARRAY" } @{$drawing_actions} ), "ref of every action" ); + ok( ( all { defined $_->[0] } @{$drawing_actions} ), "the first element of every action is defined" ); + }; + + subtest "create_drawing_group" => sub { + my $drawing_group = $uimanager->_create_drawing_group; + + ok( defined $drawing_group, "drawing_group's defined" ); + isa_ok( $drawing_group, "Gtk3::ActionGroup" ); + + is( $drawing_group->get_name, "drawing", "name of drawing_group" ); + ok( scalar( $drawing_group->list_actions ) > 0, "list of actions is not empty" ); + + for my $action (qw/ Select Highlighter Arrow Censor Crop /) { + my $action_object = $drawing_group->get_action($action); + + ok( defined $action_object, "has the action '$action'" ); + isa_ok( $action_object, "Gtk3::RadioAction" ); + } + }; + + subtest "get_ui_info" => sub { + my $ui_info = $uimanager->_get_ui_info; + + ok( $ui_info, "ui_info is not empty" ); + ok( $ui_info =~ m/\s+.+<\/ui>$/ms, "structure of ui_info" ); + }; +}; + +subtest "get wrong ui_info" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $dt = Shutter::Draw::DrawingTool->new($sc); + + $dt->{_d} = $sc->gettext_object; + $dt->{_dicons} = $sc->get_root . "/share/shutter/resources/icons/drawing_tool"; + $dt->{_icons} = $sc->get_root . "/share/shutter/resources/icons"; + $dt->{_drawing_window} = Gtk3::Window->new('toplevel'); + + my $mock = Test::MockModule->new("Shutter::Draw::UIManager"); + $mock->mock( "_get_ui_info", sub { return "foo bar baz" } ); + + my $error; + eval { + $dt->setup_uimanager; + 1; + } or do { + $error = $@; + }; + + like( $error, qr/Unable to create menus/, "error to create UI menu" ); +}; + +subtest "setup" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $dt = Shutter::Draw::DrawingTool->new($sc); + + $dt->{_d} = $sc->gettext_object; + $dt->{_dicons} = $sc->get_root . "/share/shutter/resources/icons/drawing_tool"; + $dt->{_icons} = $sc->get_root . "/share/shutter/resources/icons"; + $dt->{_drawing_window} = Gtk3::Window->new('toplevel'); + + my $uimanager = $dt->setup_uimanager; + + ok( defined $uimanager, "defined uimanager" ); + isa_ok( $uimanager, "Gtk3::UIManager" ); + + my @action_groups = $uimanager->get_action_groups; + is( scalar @action_groups, 3, "number of action groups" ); +}; + +done_testing(); diff -Nru shutter-0.99.2/t/Shutter/Draw/003_getters_and_setters.t shutter-0.99.5/t/Shutter/Draw/003_getters_and_setters.t --- shutter-0.99.2/t/Shutter/Draw/003_getters_and_setters.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/Draw/003_getters_and_setters.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,194 @@ +use 5.010; +use strict; +use warnings; + +use Gtk3; # to escape warnings "Too late to run INIT block" +use Gtk3::ImageView 10; +use Glib qw/ TRUE FALSE /; + +use Test::More tests => 4; + +require Test::Window; +require Test::Common; + +require Shutter::App::SimpleDialogs; +require Shutter::App::HelperFunctions; +require Shutter::App::Common; + +require_ok("Shutter::Draw::DrawingTool"); + +subtest "create drawing tool" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $drawing_tool = Shutter::Draw::DrawingTool->new($sc); + + ok( defined $drawing_tool, "DrawingTool object" ); +}; + +subtest "only getters" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $dt = Shutter::Draw::DrawingTool->new($sc); + + # TODO: fix it later. This attribute("_d") is being set up during invocation of the method "show", + # and we should fairly call this method here when the code base will be improved enough. + $dt->{_d} = $sc->gettext_object; + + is( $dt->gettext, $sc->get_gettext, "gettext" ); + + subtest "icons and dicons" => sub { + $dt->{_dicons} = $sc->get_root . "/share/shutter/resources/icons/drawing_tool"; + $dt->{_icons} = $sc->get_root . "/share/shutter/resources/icons"; + + is( $dt->dicons, $dt->{_dicons}, "dicons" ); + is( $dt->icons, $dt->{_icons}, "icons" ); + }; + + subtest "clipboard" => sub { + is( $dt->clipboard, $dt->{_clipboard}, "clipboard" ); + ok( defined $dt->clipboard, "clipboard is defined" ); + isa_ok( $dt->clipboard, "Gtk3::Clipboard" ); + }; + + subtest "items" => sub { + ok( exists $dt->{_items} && !defined $dt->{_items}, "items are empty" ); + $dt->{_items} = { foo => [], bar => [], baz => [] }; + is( $dt->items, $dt->{_items}, "items" ); + }; + + subtest "drawing_window" => sub { + ok( !exists $dt->{_drawing_window}, "there is no an attribute _drawing_window" ); + $dt->{_drawing_window} = Gtk3::Window->new('toplevel'); + is( $dt->drawing_window, $dt->{_drawing_window}, "drawing_window" ); + }; + + subtest "canvas" => sub { + ok( exists $dt->{_canvas} && !defined $dt->{_canvas}, "there's an attribute _canvas" ); + $dt->{_canvas} = GooCanvas2::Canvas->new; + ok( defined $dt->{_canvas}, "_canvas is defined now" ); + is( $dt->canvas, $dt->{_canvas}, "canvas" ); + } +}; + +subtest "getters and setters" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $dt = Shutter::Draw::DrawingTool->new($sc); + + subtest "cut" => sub { + is( $dt->cut, $dt->{_cut}, "getter of 'cut'" ); + is( $dt->cut, FALSE, "initial value of cut" ); + + $dt->cut(TRUE); + is( $dt->cut, $dt->{_cut}, "getter of 'cut'" ); + is( $dt->cut, TRUE, "value of cut has been changed" ); + }; + + subtest "current_copy_item" => sub { + is( $dt->current_copy_item, $dt->{_current_copy_item}, "getter of 'current_copy_item'" ); + ok( !defined $dt->current_copy_item, "initial value of current_copy_item" ); + + $dt->current_copy_item( { foo => 1, bar => 2, baz => 3 } ); + is( $dt->current_copy_item, $dt->{_current_copy_item}, "getter of 'current_copy_item'" ); + ok( ref $dt->current_copy_item eq "HASH", "value of current_copy_item has been changed" ); + }; + + subtest "current_item" => sub { + is( $dt->current_item, $dt->{_current_item}, "getter of 'current_item'" ); + ok( !defined $dt->current_item, "initial value of current_item" ); + + $dt->current_item( { foo => 1, bar => 2, baz => 3 } ); + is( $dt->current_item, $dt->{_current_item}, "getter of 'current_item'" ); + ok( ref $dt->current_item eq "HASH", "value of current_item has been changed" ); + }; + + subtest "current_new_item" => sub { + is( $dt->current_new_item, $dt->{_current_new_item}, "getter of 'current_new_item'" ); + ok( !defined $dt->current_new_item, "initial value of current_new_item" ); + + $dt->current_new_item( { foo => 1, bar => 2, baz => 3 } ); + is( $dt->current_new_item, $dt->{_current_new_item}, "getter of 'current_new_item'" ); + ok( ref $dt->current_new_item eq "HASH", "value of current_new_item has been changed" ); + }; + + subtest "canvas_bg" => sub { + ok( ! exists $dt->{_canvas_bg}, "_canvas_bg doesn't exist" ); + ok( ! defined $dt->canvas_bg, "canvas_bg getter returns undef" ); + }; + + subtest "factory" => sub { + is( $dt->factory, $dt->{_factory}, "getter of 'factory'" ); + ok( !defined $dt->factory, "initial value of factory" ); + + $dt->factory( Gtk3::IconFactory->new ); + is( $dt->factory, $dt->{_factory}, "getter of 'factory'" ); + isa_ok( $dt->factory, "Gtk3::IconFactory" ); + }; + + subtest "autoscroll" => sub { + is( $dt->autoscroll, $dt->{_autoscroll}, "getter of 'autoscroll'" ); + is( $dt->autoscroll, FALSE, "initial value of autoscroll" ); + + $dt->autoscroll(TRUE); + is( $dt->autoscroll, $dt->{_autoscroll}, "getter of 'autoscroll'" ); + is( $dt->autoscroll, TRUE, "value of autoscroll has been changed" ); + }; + + subtest "stroke_color" => sub { + is( $dt->stroke_color, $dt->{_stroke_color}, "getter of 'stroke_color'" ); + isa_ok( $dt->stroke_color, "Gtk3::Gdk::RGBA" ); + + my $new_value = Gtk3::Gdk::RGBA::parse('#0000ff'); + $dt->stroke_color($new_value); + + is( $dt->stroke_color, $new_value, "new value of stroke_color" ); + is( $dt->stroke_color, $dt->{_stroke_color}, "getter of 'stroke_color'" ); + }; + + subtest "fill_color" => sub { + is( $dt->fill_color, $dt->{_fill_color}, "getter of 'fill_color'" ); + isa_ok( $dt->fill_color, "Gtk3::Gdk::RGBA" ); + + my $new_value = Gtk3::Gdk::RGBA::parse('#ff0000'); + $dt->fill_color($new_value); + + is( $dt->fill_color, $new_value, "new value of fill_color" ); + is( $dt->fill_color, $dt->{_fill_color}, "getter of 'fill_color'" ); + }; + + subtest "line_width" => sub { + is( $dt->line_width, $dt->{_line_width}, "getter of 'line_width'" ); + ok( defined $dt->line_width, "line_width is not empty" ); + }; + + subtest "font" => sub { + is( $dt->font, $dt->{_font}, "getter of 'font'" ); + ok( defined $dt->font, "font is not empty" ); + }; + + subtest "uid" => sub { + is( $dt->uid, $dt->{_uid}, "getter of 'uid'" ); + ok( defined $dt->uid, "uid is defined" ); + + my $last_value = $dt->uid; + is( $last_value, $dt->uid, "uid wasn't changed" ); + $dt->increase_uid; + + ok( $dt->uid - $last_value == 1, "uid has been increased" ); + }; +}; + +done_testing(); + diff -Nru shutter-0.99.2/t/Shutter/Draw/004_ellipse.t shutter-0.99.5/t/Shutter/Draw/004_ellipse.t --- shutter-0.99.2/t/Shutter/Draw/004_ellipse.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/Draw/004_ellipse.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,82 @@ +use 5.010; +use strict; +use warnings; + +use Gtk3; # to escape warnings "Too late to run INIT block" +use Gtk3::ImageView 10; +use Glib qw/ TRUE FALSE /; +use Test::More; +use Data::Dumper; + +require Test::Window; +require Test::Common; +require Test::SimpleApp; + +require Shutter::App::SimpleDialogs; +require Shutter::App::HelperFunctions; +require Shutter::App::Common; +require Shutter::Draw::DrawingTool; + +require_ok("Shutter::Draw::Ellipse"); + +subtest "simply create ellipse" => sub { + my $app = Test::SimpleApp->new; + my $ellipse = Shutter::Draw::Ellipse->new( app => $app ); + + ok( defined $ellipse, "ellipse defined" ); + is( $ellipse->app, $app, "check ellipse's app" ); +}; + +subtest "internal methods" => sub { + plan skip_all => "no env TEST_APP_SHUTTER_PATH found" unless $ENV{TEST_APP_SHUTTER_PATH}; + + my $w = Test::Window::simple_window(); + my $sc = Test::Common::get_common_object(); + $sc->set_mainwindow($w); + + my $dt = Shutter::Draw::DrawingTool->new($sc); + $dt->{_canvas} = GooCanvas2::Canvas->new; + + my $ellipse = Shutter::Draw::Ellipse->new( app => $dt ); + + subtest "attributes" => sub { + ok( !defined $ellipse->event, "event is null" ); + ok( !defined $ellipse->copy_item, "copy_item is null" ); + ok( !defined $ellipse->numbered, "numbered is null" ); + is( $ellipse->X, 0, "value of X" ); + is( $ellipse->Y, 0, "value of Y" ); + is( $ellipse->width, 0, "value of width" ); + is( $ellipse->height, 0, "value of height" ); + + ok( defined $ellipse->stroke_color, "stroke_color is defined" ); + is( $ellipse->stroke_color, $dt->stroke_color, "stroke_color value" ); + + ok( defined $ellipse->fill_color, "fill_color is defined" ); + is( $ellipse->fill_color, $dt->fill_color, "fill_color value" ); + + ok( defined $ellipse->line_width, "line_width is defined" ); + is( $ellipse->line_width, $dt->line_width, "line_width value" ); + }; + + subtest "create_item" => sub { + my $item = $ellipse->_create_item; + + ok( defined $item, "item is defined" ); + isa_ok( $item, "GooCanvas2::CanvasRect" ); + + is( $item->get("parent"), $dt->canvas->get_root_item, "item's parent" ); + is( $item->get("x"), $ellipse->X, "item's X" ); + is( $item->get("y"), $ellipse->Y, "item's Y" ); + is( $item->get("width"), $ellipse->width, "item's width" ); + is( $item->get("height"), $ellipse->height, "item's height" ); + is( $item->get("line-width"), 1, "item's line-width" ); + is( $item->get("fill-color-rgba"), 0, "item's fill-color-rgba" ); + + ok( $item->get("line-dash"), "there's line-dash" ); + isa_ok( $item->get("line-dash"), "GooCanvas2::CanvasLineDash" ); + } +}; + +done_testing(); + +package SimpleApp { use Moo }; diff -Nru shutter-0.99.2/t/Shutter/Draw/005_utils.t shutter-0.99.5/t/Shutter/Draw/005_utils.t --- shutter-0.99.2/t/Shutter/Draw/005_utils.t 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/Shutter/Draw/005_utils.t 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,21 @@ +use 5.010; +use strict; +use warnings; + +use Gtk3; # to escape warnings "Too late to run INIT block" + +use Test::More; + +use_ok("Shutter::Draw::Utils"); + +subtest "points_to_canvas_points" => sub { + can_ok( "Shutter::Draw::Utils", "points_to_canvas_points" ); + + my @points = qw/116.295135498047 146.150695800781 463.101501464844 458.543548583984/; + my $res = Shutter::Draw::Utils::points_to_canvas_points(@points); + + ok( defined $res, "There's a result of points_to_canvas_points" ); + isa_ok( $res, "GooCanvas2::CanvasPoints" ); +}; + +done_testing; diff -Nru shutter-0.99.2/t/lib/Test/Common.pm shutter-0.99.5/t/lib/Test/Common.pm --- shutter-0.99.2/t/lib/Test/Common.pm 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/lib/Test/Common.pm 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,26 @@ +package Test::Common; + +use 5.010; +use strict; +use warnings; +use English; + +use Shutter::App::Common; + +sub get_common_object { + my $root = $ENV{TEST_APP_SHUTTER_PATH} or die "env TEST_APP_SHUTTER_PATH has to be set"; + my $name = "shutter"; + my $version = 0.544; + my $revision = 1234; + + return Shutter::App::Common->new( + shutter_root => $root, + main_window => undef, + appname => $name, + version => $version, + rev => $revision, + pid => $PID + ); +} + +1; diff -Nru shutter-0.99.2/t/lib/Test/SimpleApp.pm shutter-0.99.5/t/lib/Test/SimpleApp.pm --- shutter-0.99.2/t/lib/Test/SimpleApp.pm 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/lib/Test/SimpleApp.pm 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,5 @@ +package Test::SimpleApp; + +use Moo; + +1; diff -Nru shutter-0.99.2/t/lib/Test/Window.pm shutter-0.99.5/t/lib/Test/Window.pm --- shutter-0.99.2/t/lib/Test/Window.pm 1970-01-01 00:00:00.000000000 +0000 +++ shutter-0.99.5/t/lib/Test/Window.pm 2024-03-31 16:18:07.000000000 +0000 @@ -0,0 +1,24 @@ +package Test::Window; + +use 5.010; +use strict; +use warnings; + +use Gtk3; + +sub simple_window { + Gtk3::init; + + my $w = Gtk3::Window->new('toplevel'); + $w->set_title("Foo"); + $w->set_position('center'); + $w->set_modal(1); + $w->signal_connect( 'delete_event', sub { Gtk3->main_quit } ); + $w->set_default_size( 640, 480 ); + $w->show_all; + $w->get_window->focus( Gtk3::get_current_event_time() ); + + return $w; +} + +1;