From 4b23b1f3de16c743b394f12aa6cc9db63373594f Mon Sep 17 00:00:00 2001 From: Saleem Edah-Tally Date: Fri, 11 Jul 2025 21:10:59 +0200 Subject: [PATCH] Allow cancelling a scan session. - perform scanning in a thread - change the label of the scan button - trigger cancel with the same button and with the ESC key - process all GUI updates in asynchronous mode. Do not use a top window as parent of popups: - If a top window goes away in an application with multiple instances of XInsaneWidget, any call to a scanner widget leads to a crash. Minor changes. --- README.md | 4 + Resources/InsaneWidget/CMakeLists.txt | 1 - Resources/InsaneWidget/InsaneWorker.cpp | 12 +- Resources/InsaneWidget/InsaneWorker.h | 1 + Resources/InsaneWidget/XInsaneWidget.cpp | 149 ++++++++++++-- Resources/InsaneWidget/XInsaneWidget.h | 2 +- Resources/Lokalize/0_getstrings.sh | 2 + Resources/Lokalize/fr/S7.po | 236 +++++------------------ Resources/Utilities/MiscTools.cpp | 9 + Resources/Utilities/MiscTools.h | 1 + Resources/Utilities/TimeredStatusBar.cpp | 2 +- XS7.cpp | 2 +- 12 files changed, 206 insertions(+), 215 deletions(-) diff --git a/README.md b/README.md index cff1034..93a8a6e 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ Right click on the 'New' label to specify the number of faces and whether double Right click on the 'Scan' button to set the device properties. +'Ctrl - right' click on the 'Scan' button to define stamps. + +Press 'Esc' to cancel and reset a scan project. + Outputs: - a single PDF file with the number of requested pages diff --git a/Resources/InsaneWidget/CMakeLists.txt b/Resources/InsaneWidget/CMakeLists.txt index 8bb6658..5f39212 100644 --- a/Resources/InsaneWidget/CMakeLists.txt +++ b/Resources/InsaneWidget/CMakeLists.txt @@ -30,5 +30,4 @@ target_link_libraries(insanewidget minutils stampwidget ${wxWidgets_LIBRARIES} ${LIBINSANE_LIBRARIES} ${PODOFO_LIBRARIES} - ${NETPBM_LIBRARIES} ${PAPER_LIBRARIES}) diff --git a/Resources/InsaneWidget/InsaneWorker.cpp b/Resources/InsaneWidget/InsaneWorker.cpp index ef4674e..4271b5b 100644 --- a/Resources/InsaneWidget/InsaneWorker.cpp +++ b/Resources/InsaneWidget/InsaneWorker.cpp @@ -22,7 +22,6 @@ using namespace std; #define IERR(e) ("[" + to_string(e) + "] " + lis_strerror(e)) -static bool gs_cancelRequested = false; InsaneWorker::InsaneWorker ( InsaneWorkerEvent * evh ) { @@ -306,6 +305,8 @@ bool InsaneWorker::Scan(const std::string& dir, const std::string& basename, return false; } } + if (m_cancelRequested) + return false; auto makeFileName = [&] (int index) { // std:format is not friendly with a variable padwidth; requires a literal format. @@ -375,12 +376,13 @@ bool InsaneWorker::Scan(const std::string& dir, const std::string& basename, m_evh->OnPageStartScan(filePath, pageIndex, imageAttributes); do { - if (gs_cancelRequested) + if (m_cancelRequested) { session->cancel(session); + m_rootSourceItem->close(m_rootSourceItem); if (m_evh) m_evh->OnSessionCancelled(filePath); - gs_cancelRequested = false; + m_cancelRequested = false; return false; } try @@ -410,7 +412,7 @@ bool InsaneWorker::Scan(const std::string& dir, const std::string& basename, catch (std::bad_alloc& e) { m_rootSourceItem->close(m_rootSourceItem); - cout << "ABORT: " << e.what() << " - could not allocate " << bytesPerRow << " bytes." << endl; + cerr << "ABORT: " << e.what() << " - could not allocate " << bytesPerRow << " bytes." << endl; if (m_evh) m_evh->OnError("Insufficient system RAM."); return false; @@ -516,7 +518,7 @@ std::string InsaneWorker::ToLower(const std::string& input) void InsaneWorker::Cancel() { - gs_cancelRequested = true; + m_cancelRequested = true; } std::pair InsaneWorker::UpdateStartAndIncrement(const int startPageIndex, const int increment, diff --git a/Resources/InsaneWidget/InsaneWorker.h b/Resources/InsaneWidget/InsaneWorker.h index cc7ed71..f6dc767 100644 --- a/Resources/InsaneWidget/InsaneWorker.h +++ b/Resources/InsaneWidget/InsaneWorker.h @@ -88,6 +88,7 @@ private: lis_item * m_sourceItem = nullptr; InsaneWorkerEvent * m_evh = nullptr; + bool m_cancelRequested = false; std::vector m_devices; std::string m_deviceId; std::string m_source = "FlatBed"; diff --git a/Resources/InsaneWidget/XInsaneWidget.cpp b/Resources/InsaneWidget/XInsaneWidget.cpp index 0498b24..1d0b32b 100644 --- a/Resources/InsaneWidget/XInsaneWidget.cpp +++ b/Resources/InsaneWidget/XInsaneWidget.cpp @@ -40,6 +40,45 @@ private: wxWeakRef m_owner = nullptr; }; +// ---------------------------------------------------------------------------- +class BackgroundScan : public wxThread +{ +public: + BackgroundScan(XInsaneWidget * insaneWidget, InsaneWorker * insaneWorker, + const wxString& dir, const wxString& basename, + int start = 0, int padWidth = 2, int increment = 1) + { + m_insaneWidget = insaneWidget; + m_insaneWorker = insaneWorker; + m_dir = dir; + m_basename = basename; + m_start = start; + m_padWidth = padWidth; + m_increment = increment; + } + ExitCode Entry() override + { + /* + * We refrain from using a mutex/critical section. + * If 2 instances of XInsaneWidget in a single application scan on the same + * device simultaneously, the first one that starts scanning prevails and + * the second one triggers a "Device busy" error. + * If 2 instances of XInsaneWidget in a single application scan on different + * devices simultaneously, both jobs succeed. + * Since there cannot be concurrency leading to a crash ('Device busy'), we + * do not block the next line. It may limit an application that needs to + * scan from multiple devices. + */ + m_insaneWorker->Scan(m_dir.ToStdString(), m_basename.ToStdString(), m_start, m_padWidth, m_increment); + return (ExitCode) 0; + } +private: + wxWeakRef m_insaneWidget = nullptr; + InsaneWorker * m_insaneWorker = nullptr; + wxString m_dir, m_basename; + int m_start = 0, m_padWidth = 2, m_increment = 1; +}; + // ---------------------------------------------------------------------------- class ScanProjectHandler : public InsaneWorkerEvent { @@ -117,6 +156,41 @@ public: { m_stampDescriptors = descriptors; } + // WIP means InsaneWorker is busy, not the position in a scan project. + void SetWip(bool wip) + { + m_wip = wip; + } + bool GetWip() const + { + return m_wip; + } + /* Enable/disable the label and destination file controls per scanning and + * idle mode. + * Do not include in Reset() because the status of the scan button is + * independent of the rest. + */ + void UpdateControlsStatus(bool enable) + { + wxTheApp->CallAfter([this, enable] () + { + m_owner->lblNewDoc->Enable(enable); + m_owner->txtNewDoc->Enable(enable); + if (enable) + { + m_owner->btnScan->Enable(enable); + m_owner->btnScan->SetLabelText(_("Scan")); + } + else + { + /*Only XInsaneWidget::Setup() and XInsaneWidget::CancelScanning() + * disable the scan button. + */ + m_owner->btnScan->SetLabelText(_("Cancel")); + } + } + ); + } std::pair GetStartAndIncrement(InsaneWorker * insaneWorker) { wxASSERT_MSG(insaneWorker != nullptr, "insaneWorker is NULL."); @@ -130,26 +204,35 @@ public: { cerr << message << endl; Reset(); - MiscTools::MessageBox(_("A scan library error occurred."), true); + UpdateControlsStatus(true); + const wxString msg = _("A scan library error occurred."); + MiscTools::AsyncMessageBox(msg, true); } void OnError ( const std::string& message ) override { cerr << message << endl; Reset(); - MiscTools::MessageBox(_("A general error occurred."), true); + UpdateControlsStatus(true); + const wxString msg = _("A general error occurred."); + MiscTools::AsyncMessageBox(msg, true); } void OnSessionReadError(const std::string & filePath) override { const wxString msg = _("A session read error occurred."); cerr << msg << endl; Reset(); - MiscTools::MessageBox(msg, true); + UpdateControlsStatus(true); + MiscTools::AsyncMessageBox(msg, true); } void OnSessionCancelled(const std::string & filePath) override { - const wxString msg = _("Session cancelled."); + if (wxFileExists(filePath)) + wxRemoveFile(filePath); Reset(); - MiscTools::MessageBox(msg, true); + UpdateControlsStatus(true); + const wxString msg = _("Session cancelled."); + MiscTools::AsyncMessageBox(msg, true); + m_sb->CallAfter(&TimeredStatusBar::SetTransientText, msg, 3000); } // Every time a page is fully scanned. void OnPageEndScan(const std::string & filePath, uint pageIndex, @@ -158,7 +241,7 @@ public: m_startPageIndex = pageIndex; m_pixelFiles[pageIndex] = {filePath, imageAttributes}; - auto informProgress = [&] () + auto informProgress = [this] () { if (!m_sb || (m_total == 1)) return; @@ -175,20 +258,19 @@ public: wxString upperBoundMessage = _(". Turn the whole stack of pages."); msg += upperBoundMessage; } - - m_sb->SetStatusText(msg); + m_sb->CallAfter(&wxStatusBar::SetStatusText, msg, 0); }; if (m_outputType != PDF) { - // Convert pixel file to PNG using netpbm. + // Convert pixel file to PNG using wxImage. if (!PixelToImageWriter::Convert(filePath, imageAttributes.width, imageAttributes.height, m_stampDescriptors, m_outputType)) { const wxString msg = _("Failed to create output image."); cerr << msg << endl; if (m_sb) - m_sb->SetTransientText(msg); + m_sb->CallAfter(&TimeredStatusBar::SetTransientText, msg, 3000); } wxRemoveFile(filePath); informProgress(); @@ -214,7 +296,7 @@ public: const wxString msg = _("Failed to add page to PDF document."); cerr << msg << endl; if (m_sb) - m_sb->SetTransientText(msg); + m_sb->CallAfter(&TimeredStatusBar::SetTransientText, msg, 3000); } wxRemoveFile(filePath); informProgress(); @@ -251,16 +333,19 @@ public: m_pixelToPdfWriter.reset((new PixelToPdfWriter())); // For next scan project. } Reset(); - m_owner->txtNewDoc->Clear(); + m_owner->txtNewDoc->CallAfter(&wxTextCtrl::SetValue, wxString()); if (m_sb) { const wxString msg = _("Finished."); - m_sb->SetTransientText(msg); + m_sb->CallAfter(&TimeredStatusBar::SetTransientText, msg, 3000); } } + UpdateControlsStatus(true); + m_wip = false; } void OnStartScanSession(uint pageIndex, const ImageAttributes& imageAttributes) override { + // Dealing with stamps only. if (!m_stampDescriptors) return; for (StampDescriptor * descriptor : *m_stampDescriptors) @@ -277,6 +362,7 @@ public: m_increment = 1; m_totalEven = 0; m_pixelFiles.clear(); + m_wip = false; } private: @@ -295,6 +381,7 @@ private: int m_totalEven = 0; int m_startPageIndex = 0; int m_increment = 1; + bool m_wip = false; // Is InsaneWorker busy? }; @@ -316,7 +403,12 @@ void XInsaneWidget::Setup() lblNewDoc->Bind ( wxEVT_RIGHT_UP, &XInsaneWidget::OnLblNewDocRightClick, this ); txtNewDoc->Bind ( wxEVT_KEY_UP, &XInsaneWidget::OnTxtNewDocKeyPressed, this ); - m_ptwScannerWidget = std::unique_ptr (new wxPopupTransientWindow ( wxApp::GetGUIInstance()->GetTopWindow() )); + /* + * Don't use GetTopWindow() as parent. If it goes away in an application with + * multiples instances of XInsaneWidget, any call to the scanner widget leads + * to a crash. + */ + m_ptwScannerWidget = std::make_unique (GetParent()); m_ptwScannerWidget->Show ( false ); m_scanProject = std::make_unique(this, m_sb); m_insaneWorker = std::make_unique(m_scanProject.get()); @@ -336,12 +428,17 @@ void XInsaneWidget::Setup() // Show a popup to specifiy the number of pages to scan and back-sided scanning. void XInsaneWidget::OnLblNewDocRightClick ( wxMouseEvent& evt ) { + if (!lblNewDoc->IsEnabled()) + { + evt.Skip(); + return; + } /* * The previous ConfigEditorPopup is deleted here, which commits the * parameters to the config file. At any time, the current parameter values * can be lost if a crash occurs. */ - m_pageStack.reset(new ConfigEditorPopup(wxApp::GetGUIInstance()->GetTopWindow(), m_config)); + m_pageStack.reset(new ConfigEditorPopup(GetParent(), m_config)); PopupTransientWindow * ptw = m_pageStack->CreatePopup(); if ( !ptw ) { @@ -403,6 +500,12 @@ void XInsaneWidget::OnBtnScanRightClick ( wxMouseEvent& evt ) // Start scanning. void XInsaneWidget::OnBtnScanClick ( wxMouseEvent& evt ) { + if (m_scanProject->GetWip()) + { + CancelScanning(); + evt.Skip(); + return; + } const wxString dest = txtNewDoc->GetValue(); if (dest.IsEmpty()) { @@ -453,9 +556,14 @@ void XInsaneWidget::OnBtnScanClick ( wxMouseEvent& evt ) std::pair startAndIncrement = m_scanProject->GetStartAndIncrement(m_insaneWorker.get()); const int padWidth = ( ushort ) m_config->Read (_T("/Scanner/Counter/Length"), 2 ); - bool res = m_insaneWorker->Scan(destFile.GetPath().ToStdString(), - destFile.GetName().ToStdString(), - startAndIncrement.first, padWidth, startAndIncrement.second); + m_scanProject->UpdateControlsStatus(false); + BackgroundScan * bgScan = new BackgroundScan(this, m_insaneWorker.get(), + destFile.GetPath().ToStdString(), + destFile.GetName().ToStdString(), + startAndIncrement.first, padWidth, + startAndIncrement.second); + m_scanProject->SetWip(true); + bgScan->Run(); } evt.Skip(); } @@ -468,8 +576,11 @@ void XInsaneWidget::ResetScanProject() void XInsaneWidget::CancelScanning() { - if (m_insaneWorker) + if (m_insaneWorker && m_scanProject->GetWip()) + { m_insaneWorker->Cancel(); + btnScan->Enable(false); + } } void XInsaneWidget::EnableScanButton(bool enable) diff --git a/Resources/InsaneWidget/XInsaneWidget.h b/Resources/InsaneWidget/XInsaneWidget.h index 3d02fb2..e8c9bf3 100644 --- a/Resources/InsaneWidget/XInsaneWidget.h +++ b/Resources/InsaneWidget/XInsaneWidget.h @@ -63,7 +63,7 @@ public: XInsaneWidget( wxWindow* parent, TimeredStatusBar * sb, wxConfig * config, wxWindowID id = SYMBOL_INSANEWIDGET_IDNAME, const wxPoint& pos = SYMBOL_INSANEWIDGET_POSITION, const wxSize& size = SYMBOL_INSANEWIDGET_SIZE, long style = SYMBOL_INSANEWIDGET_STYLE ); void ResetScanProject(); - void CancelScanning(); // Not tested, probably doesn't work as intended. + void CancelScanning(); void EnableScanButton(bool enable); // For CallAfter. void Setup(); private: diff --git a/Resources/Lokalize/0_getstrings.sh b/Resources/Lokalize/0_getstrings.sh index 6b20ab7..2cfb511 100755 --- a/Resources/Lokalize/0_getstrings.sh +++ b/Resources/Lokalize/0_getstrings.sh @@ -15,6 +15,8 @@ DEST=$DOMAIN/${COMPONENT_NAME}.po [ -f $DEST ] && cp $DEST $DEST.bak-$(date +%F-%T) [ ! -f $DEST ] && touch $DEST +# Memo: use '--no-location' transiently to get rid of obsolete file locations. + xgettext --keyword=_ -d $DOMAIN $JOIN -o $DEST --c++ --from-code=UTF-8 $(find $SRC -type f -name "*.cpp") xgettext --keyword=_ -d $DOMAIN -j -o $DEST --c++ --from-code=UTF-8 $(find $SRC -type f -name "*.h") diff --git a/Resources/Lokalize/fr/S7.po b/Resources/Lokalize/fr/S7.po index a1366b3..8738b24 100644 --- a/Resources/Lokalize/fr/S7.po +++ b/Resources/Lokalize/fr/S7.po @@ -1,23 +1,23 @@ +# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. # -# SPDX-FileCopyrightText: 2025 Saleem EDAH-TALLY +#, fuzzy msgid "" msgstr "" -"Project-Id-Version: \n" +"Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-09 16:00+0200\n" -"PO-Revision-Date: 2025-07-09 16:03+0200\n" -"Last-Translator: Saleem EDAH-TALLY \n" -"Language-Team: French \n" -"Language: fr_FR\n" +"POT-Creation-Date: 2025-07-11 21:35+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Lokalize 25.04.3\n" -#: ../../Resources/UI/S7/s7.cpp:126 ../..//Resources/UI/S7/s7.cpp:126 +#: ../../Resources/UI/S7/s7.cpp:126 msgid "" "Select a destination directory.\n" "Double-click to go to the selected directory." @@ -25,11 +25,11 @@ msgstr "" "Choisissez un dossier destination\n" "Double-clic pour aller au dossier." -#: ../../Resources/UI/S7/s7.cpp:130 ../..//Resources/UI/S7/s7.cpp:130 +#: ../../Resources/UI/S7/s7.cpp:130 msgid "Basename" msgstr "Nom de base" -#: ../../Resources/UI/S7/s7.cpp:132 ../..//Resources/UI/S7/s7.cpp:132 +#: ../../Resources/UI/S7/s7.cpp:132 msgid "" "Specify a destination file basename (without extension).\n" "\n" @@ -39,24 +39,15 @@ msgstr "" "\n" "'CTRL + clic' : à propos" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:128 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:129 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:132 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:125 msgid "New" msgstr "Nouveau" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:130 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:131 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:134 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:127 msgid "'Right' click to define a scan project." msgstr "Clic droit pour définir un projet de numérisation" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:135 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:136 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:139 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:132 msgid "" "Full path to destination file without the extension; it is determined by the " "output type." @@ -64,16 +55,12 @@ msgstr "" "Chemin complet du fichier de destination sans l'extension; elle est " "déterminée par le format de sortie." -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:138 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:139 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:142 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:135 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:188 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:302 msgid "Scan" msgstr "Numériser" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:140 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:141 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:144 msgid "" "'Left click' to start the scan project.\n" "'Right click' to show the scanner widget." @@ -105,179 +92,113 @@ msgstr "Mode de numérisation" msgid "Scan resolution" msgstr "Résolution de la numérisation" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:128 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:107 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:132 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:133 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:171 msgid "A scan library error occurred." msgstr "Une erreur de bibliothèque est survenue." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:134 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:113 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:138 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:139 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:177 msgid "A general error occurred." msgstr "Une erreur générale est survenue." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:138 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:117 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:142 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:143 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:181 msgid "A session read error occurred." msgstr "Une erreur de lecture est survenue." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:145 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:124 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:149 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:150 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:190 msgid "Session cancelled." msgstr "Session annulée." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:162 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:141 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:166 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:167 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:208 msgid "Scanning: " msgstr "Numérisation :" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:164 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:143 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:168 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:169 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:210 msgid "Front face: " msgstr "Recto :" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:166 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:145 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:170 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:171 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:212 msgid "Back face: " msgstr "Verso :" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:170 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:149 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:174 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:175 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:216 msgid ". Turn the whole stack of pages." msgstr ". Retournez toute la pile de pages." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:182 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:161 msgid "Failed to create PNG image." msgstr "Échec de création d'image PNG." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:201 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:180 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:206 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:248 msgid "Wrong paper size: " msgstr "Mauvaise taille de papier :" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:201 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:180 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:206 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:248 msgid "; using A4." msgstr "; utilisation du format A4." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:186 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:212 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:213 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:214 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:255 msgid "Failed to add page to PDF document." msgstr "Échec d'ajout de page au document PDF." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:217 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:255 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:196 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:234 msgid "Unhandled output file format." msgstr "Format de fichier de sortie non pris en charge." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:264 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:243 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:255 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:256 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:257 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:298 msgid "Finished." msgstr "Terminé." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:333 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:317 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:356 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:355 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:351 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:398 msgid "Scan all front faces first, then all back faces in reverse order." msgstr "" "Numériser tous les faces recto, puis toutes les faces verso en ordre inverse." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:336 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:320 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:359 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:358 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:354 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:355 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:401 msgid "Total number of sides to scan (not total number of sheets)." msgstr "" "Nombre total de faces à numériser (et non pas le nombre total de feuilles)." -#: ../../Resources/InsaneWidget/lib/TimeredStatusBar.cpp:44 #: ../../Resources/Utilities/TimeredStatusBar.cpp:44 -#: ../..//Resources/Utilities/TimeredStatusBar.cpp:44 msgid "NUM" msgstr "NUM" -#: ../../Resources/InsaneWidget/lib/TimeredStatusBar.cpp:52 #: ../../Resources/Utilities/TimeredStatusBar.cpp:52 -#: ../..//Resources/Utilities/TimeredStatusBar.cpp:52 msgid "CAPS" msgstr "CAPS" -#: ../../Resources/InsaneWidget/XScannerWidget.cpp:50 -#: ../../Resources/InsaneWidget/XScannerWidget.cpp:62 #: ../../Resources/InsaneWidget/XScannerWidget.cpp:64 msgid "Searching for devices..." msgstr "Recherche de périphériques..." -#: ../../Resources/InsaneWidget/XScannerWidget.cpp:53 -#: ../../Resources/InsaneWidget/XScannerWidget.cpp:65 #: ../../Resources/InsaneWidget/XScannerWidget.cpp:67 msgid "Could not initialise insane api." msgstr "Échec d'initialisation de la bibliothèque 'insane'." -#: ../../XS7.cpp:46 msgid "'Shift + left' click'' to generate a new destination file name." msgstr "'Maj + clic gauche' pour générer un nouveau fichier de sortie." -#: ../../XS7.cpp:69 ../../XS7.cpp:104 ../../XS7.cpp:105 ../../XS7.cpp:107 +#: ../../XS7.cpp:107 msgid "Could not launch default file manager" msgstr "Échec de lancement du gestionnaire de fichier par défaut." -#: ../../XS7.cpp:93 ../../XS7.cpp:128 ../../XS7.cpp:129 ../../XS7.cpp:131 +#: ../../XS7.cpp:136 msgid "Invalid folder name." msgstr "Nom de dossier invalide." -#: ../../XS7.cpp:101 ../../XS7.cpp:136 ../../XS7.cpp:137 ../../XS7.cpp:139 +#: ../../XS7.cpp:144 msgid "Invalid file basename." msgstr "Nom de base de fichier invalide." -#: ../../XS7.cpp:123 ../../XS7.cpp:158 ../../XS7.cpp:159 ../../XS7.cpp:161 +#: ../../XS7.cpp:166 msgid "Copyright: Saleem Edah-Tally [Surgeon] [Hobbyist developer]\n" msgstr "Copyright: Saleem Edah-Tally [Chirurgien] [Développeur par hobby]\n" -#: ../../XS7.cpp:124 ../../XS7.cpp:159 ../../XS7.cpp:160 ../../XS7.cpp:162 +#: ../../XS7.cpp:167 msgid "License: CeCILL/CeCILL-C per file header." msgstr "Licence : CeCILL/CeCILL-C selon les entêtes de fichier." -#: ../../Resources/UI/S7/s7.h:44 ../..//Resources/UI/S7/s7.h:44 +#: ../../Resources/UI/S7/s7.h:44 msgid "S7" msgstr "S7" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:40 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:45 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:49 #: ../../Resources/InsaneWidget/UI/InsaneWidget.h:41 msgid "InsaneWidget" msgstr "InsaneWidget" @@ -310,34 +231,23 @@ msgstr "Résolution :" msgid "Paper size:" msgstr "Format de page :" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:332 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:316 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:355 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:354 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:350 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:351 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:397 msgid "Double sided:" msgstr "Recto-verso :" -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:334 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:318 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:357 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:356 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:353 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:399 msgid "Total:" msgstr "Total :" -#: ../../XS7.cpp:46 ../../XS7.cpp:81 ../../XS7.cpp:82 ../../XS7.cpp:84 +#: ../../XS7.cpp:84 msgid "'Shift + left' click to generate a new destination file name." msgstr "'Maj + clic gauche' pour générer un nouveau fichier de sortie." -#: ../../XS7.cpp:121 ../../XS7.cpp:45 ../../XS7.cpp:156 ../../XS7.cpp:157 -#: ../../XS7.cpp:47 ../../XS7.cpp:159 +#: ../../XS7.cpp:47 ../../XS7.cpp:164 msgid " - version " msgstr " - version " -#: ../../XS7.cpp:122 ../../XS7.cpp:157 ../../XS7.cpp:158 ../../XS7.cpp:160 +#: ../../XS7.cpp:165 msgid "" ", using InsaneWidget.\n" "\n" @@ -345,34 +255,26 @@ msgstr "" ", utilisant InsaneWidget.\n" "\n" -#: ../../Resources/InsaneWidget/XScannerWidget.cpp:73 -#: ../../Resources/InsaneWidget/XScannerWidget.cpp:85 #: ../../Resources/InsaneWidget/XScannerWidget.cpp:87 msgid " device(s) found." msgstr " périphériques trouvé(s)." -#: ../../XS7.cpp:29 ../../XS7.cpp:31 +#: ../../XS7.cpp:31 msgid "Config file tag." msgstr "Suffixe du fichier de configuration." -#: ../../XS7.cpp:30 ../../XS7.cpp:32 +#: ../../XS7.cpp:32 msgid "Show version and quit." msgstr "Afficher la version et quitter." -#: ../../XS7.cpp:31 ../../XS7.cpp:33 +#: ../../XS7.cpp:33 msgid "Show help and quit." msgstr "Afficher l'aide et quitter." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:391 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:390 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:408 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:409 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:461 msgid "Destination file missing." msgstr "Fichier de destination manquant." -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:130 -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:131 #: ../../Resources/StampWidget/UI/StampWidget.cpp:140 msgid "" "Create a stamp with this text, which can be multiline.\n" @@ -385,134 +287,92 @@ msgstr "" "CTRL + S : sauvegarder le texte actuel.\n" "CTRL + R : restaurer le texte sauvegardé." -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:138 -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:139 #: ../../Resources/StampWidget/UI/StampWidget.cpp:188 msgid "Select the font of the stamp text." msgstr "Sélectionnez la police de caractère du texte." -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:143 -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:144 msgid "Select the colour of the stamp text." msgstr "Sélectionnez la couleur du texte." -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:148 -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:155 #: ../../Resources/StampWidget/UI/StampWidget.cpp:199 msgid "Select the rotation angle of the stamp text." msgstr "Sélectionnez l'angle de rotation du texte." -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:144 -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:147 msgid "Stamp" msgstr "Tampon" -#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:34 #: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:35 msgid "Failed to read raw file." msgstr "Échec de lecture du fichier brut." -#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:60 -#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:68 #: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:69 msgid "Unhandled output image format." msgstr "Format de fichier d'image en sortie non pris en charge." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:187 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:188 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:229 msgid "Failed to create output image." msgstr "Échec de création de l'image de sortie." -#: ../../Resources/InsaneWidget/UI/StampWidget.h:46 -#: ../../Resources/InsaneWidget/UI/StampWidget.h:47 #: ../../Resources/StampWidget/UI/StampWidget.h:57 msgid "StampWidget" msgstr "" -#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:150 #: ../../Resources/StampWidget/UI/StampWidget.cpp:171 msgid "Location of the stamp on the output." msgstr "Place du tampon dans la sortie." -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:156 -#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:127 #: ../../Resources/StampWidget/UI/StampWidgets.cpp:127 msgid "+" msgstr "" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:158 -#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:129 #: ../../Resources/StampWidget/UI/StampWidgets.cpp:129 msgid "Add a stamp widget." msgstr "Ajouter un widget 'Tampon'." -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:161 -#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:132 #: ../../Resources/StampWidget/UI/StampWidgets.cpp:132 msgid "-" msgstr "" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:163 -#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:134 #: ../../Resources/StampWidget/UI/StampWidgets.cpp:134 msgid "Remove the selected stamp widget." msgstr "Supprimer le widget 'Tampon' sélectionné." -#: ../../Resources/InsaneWidget/XStampWidget.cpp:56 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:57 #: ../../Resources/StampWidget/XStampWidget.cpp:76 msgid "Centre" msgstr "Centre" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:57 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:58 #: ../../Resources/StampWidget/XStampWidget.cpp:77 msgid "North" msgstr "Nord" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:57 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:58 #: ../../Resources/StampWidget/XStampWidget.cpp:77 msgid "South" msgstr "Sud" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:57 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:58 #: ../../Resources/StampWidget/XStampWidget.cpp:77 msgid "West" msgstr "Ouest" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:58 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:59 #: ../../Resources/StampWidget/XStampWidget.cpp:78 msgid "North-east" msgstr "Nord-est" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:58 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:59 #: ../../Resources/StampWidget/XStampWidget.cpp:78 msgid "North-west" msgstr "Nord-ouest" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:59 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:60 #: ../../Resources/StampWidget/XStampWidget.cpp:79 msgid "South-east" msgstr "Sud-est" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:59 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:60 #: ../../Resources/StampWidget/XStampWidget.cpp:79 msgid "South-west" msgstr "Sud-ouest" -#: ../../Resources/InsaneWidget/XStampWidget.cpp:57 -#: ../../Resources/InsaneWidget/XStampWidget.cpp:58 #: ../../Resources/StampWidget/XStampWidget.cpp:77 msgid "East" msgstr "Est" -#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:137 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:141 msgid "" "'Left click' to start the scan project.\n" @@ -523,12 +383,10 @@ msgstr "" "'Clic droit' pour afficher les options du numériseur.\n" "'Ctrl + Clic droit' pour définir un tampon." -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:376 -#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:377 +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:423 msgid "Stamps" msgstr "Tampons" -#: ../../Resources/InsaneWidget/UI/StampWidgets.h:44 #: ../../Resources/StampWidget/UI/StampWidgets.h:44 msgid "StampWidgets" msgstr "" @@ -565,3 +423,7 @@ msgstr "Transparent" msgid "" "Check for a completely transparent background and for a transparent text." msgstr "Activez la transparence du texte et de l'arrière-plan. " + +#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:511 +msgid "Cancel" +msgstr "Annuler" diff --git a/Resources/Utilities/MiscTools.cpp b/Resources/Utilities/MiscTools.cpp index 60f91fc..1080954 100644 --- a/Resources/Utilities/MiscTools.cpp +++ b/Resources/Utilities/MiscTools.cpp @@ -116,6 +116,15 @@ void MiscTools::MessageBox ( const wxString& msg, const bool notify ) } } +void MiscTools::AsyncMessageBox(const wxString& msg, const bool notify) +{ + wxTheApp->CallAfter([msg, notify] () + { + MiscTools::MessageBox(msg, notify); + } + ); +} + wxTextValidator* MiscTools::MakeFileNameValidator ( bool excludeSpace ) { wxTextValidator * tval = new wxTextValidator ( wxFILTER_EXCLUDE_CHAR_LIST ); diff --git a/Resources/Utilities/MiscTools.h b/Resources/Utilities/MiscTools.h index b5a7bb6..363eb67 100644 --- a/Resources/Utilities/MiscTools.h +++ b/Resources/Utilities/MiscTools.h @@ -45,6 +45,7 @@ public: * Shows a message as a modal dialog or as a notification. */ static void MessageBox ( const wxString& msg, const bool notify = false ); + static void AsyncMessageBox ( const wxString& msg, const bool notify = false ); /** * Creates a validator excluding file name forbidden characters, path diff --git a/Resources/Utilities/TimeredStatusBar.cpp b/Resources/Utilities/TimeredStatusBar.cpp index eb1b324..d5ab2ca 100644 --- a/Resources/Utilities/TimeredStatusBar.cpp +++ b/Resources/Utilities/TimeredStatusBar.cpp @@ -14,7 +14,7 @@ TimeredStatusBar::TimeredStatusBar ( wxWindow * parent) { m_numFields = 3; /* We use default reasonable widths for CAPS and NUM. */ - const int widths[3] = {-1, 70, 70}; + const int widths[3] = {-1, 70, 90}; SetFieldsCount ( m_numFields, widths ); m_timer.Stop(); m_timer.SetOwner ( this ); diff --git a/XS7.cpp b/XS7.cpp index 367da14..8e18ed9 100644 --- a/XS7.cpp +++ b/XS7.cpp @@ -126,7 +126,7 @@ void XS7::OnAppKeyPressed(wxKeyEvent& evt) void XS7::OnNewDocLeftClick ( wxMouseEvent& evt ) { - if ( !evt.ShiftDown() ) + if ( !evt.ShiftDown() || !m_insaneWidget->lblNewDoc->IsEnabled() ) { evt.Skip(); return;