Skip to content

Commit 1f295a8

Browse files
committed
Merge branch 'out-of-memory'
Conflicts: MainWindow.h
2 parents ba6972d + 4d19818 commit 1f295a8

18 files changed

+1031
-86
lines changed

BackgroundExecutor.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
Scan Tailor - Interactive post-processing tool for scanned pages.
3-
Copyright (C) 2007-2008 Joseph Artsimovich <joseph_a@mail.ru>
3+
Copyright (C) Joseph Artsimovich <joseph.artsimovich@gmail.com>
44
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License as published by
@@ -17,10 +17,12 @@
1717
*/
1818

1919
#include "BackgroundExecutor.h"
20+
#include "OutOfMemoryHandler.h"
2021
#include <QCoreApplication>
2122
#include <QObject>
2223
#include <QThread>
2324
#include <QEvent>
25+
#include <new>
2426
#include <assert.h>
2527

2628
template<typename T>
@@ -103,17 +105,21 @@ BackgroundExecutor::Dispatcher::Dispatcher(Impl& owner)
103105
void
104106
BackgroundExecutor::Dispatcher::customEvent(QEvent* event)
105107
{
106-
TaskEvent* evt = dynamic_cast<TaskEvent*>(event);
107-
assert(evt);
108-
109-
TaskPtr const& task = evt->payload();
110-
assert(task);
111-
112-
TaskResultPtr const result((*task)());
113-
if (result) {
114-
QCoreApplication::postEvent(
115-
&m_rOwner, new ResultEvent(result)
116-
);
108+
try {
109+
TaskEvent* evt = dynamic_cast<TaskEvent*>(event);
110+
assert(evt);
111+
112+
TaskPtr const& task = evt->payload();
113+
assert(task);
114+
115+
TaskResultPtr const result((*task)());
116+
if (result) {
117+
QCoreApplication::postEvent(
118+
&m_rOwner, new ResultEvent(result)
119+
);
120+
}
121+
} catch (std::bad_alloc const&) {
122+
OutOfMemoryHandler::instance().handleOutOfMemorySituation();
117123
}
118124
}
119125

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ SET(
442442
SkinnedButton.cpp SkinnedButton.h
443443
BubbleAnimation.cpp BubbleAnimation.h
444444
ProcessingIndicationWidget.cpp ProcessingIndicationWidget.h
445+
NonOwningWidget.cpp NonOwningWidget.h
445446
Dpi.cpp Dpi.h Dpm.cpp Dpm.h
446447
SmartFilenameOrdering.cpp SmartFilenameOrdering.h
447448
ImageInfo.cpp ImageInfo.h
@@ -453,6 +454,8 @@ SET(
453454
LoadFilesStatusDialog.cpp LoadFilesStatusDialog.h
454455
ProjectCreationContext.cpp ProjectCreationContext.h
455456
ProjectOpeningContext.cpp ProjectOpeningContext.h
457+
OutOfMemoryHandler.cpp OutOfMemoryHandler.h
458+
OutOfMemoryDialog.cpp OutOfMemoryDialog.h
456459
MainWindow.cpp MainWindow.h
457460
ConsoleBatch.cpp ConsoleBatch.h
458461
CommandLine.cpp CommandLine.h

MainWindow.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
#include "FixDpiDialog.h"
6060
#include "LoadFilesStatusDialog.h"
6161
#include "SettingsDialog.h"
62+
#include "OutOfMemoryHandler.h"
63+
#include "OutOfMemoryDialog.h"
6264
#include "filters/fix_orientation/Filter.h"
6365
#include "filters/fix_orientation/Task.h"
6466
#include "filters/fix_orientation/CacheDrivenTask.h"
@@ -130,6 +132,7 @@ MainWindow::MainWindow()
130132
m_ptrStages(new StageSequence(m_ptrPages, PageSelectionAccessor(this))),
131133
m_ptrWorkerThread(new WorkerThread),
132134
m_ptrInteractiveQueue(new ProcessingTaskQueue(ProcessingTaskQueue::RANDOM_ORDER)),
135+
m_ptrOutOfMemoryDialog(new OutOfMemoryDialog),
133136
m_curFilter(0),
134137
m_ignoreSelectionChanges(0),
135138
m_ignorePageOrderingChanges(0),
@@ -168,14 +171,21 @@ MainWindow::MainWindow()
168171
addAction(actionPrevPage);
169172
addAction(actionPrevPageQ);
170173
addAction(actionNextPageW);
171-
174+
175+
// Should be enough to save a project.
176+
OutOfMemoryHandler::instance().allocateEmergencyMemory(3*1024*1024);
177+
172178
connect(actionFirstPage, SIGNAL(triggered(bool)), SLOT(goFirstPage()));
173179
connect(actionLastPage, SIGNAL(triggered(bool)), SLOT(goLastPage()));
174180
connect(actionPrevPage, SIGNAL(triggered(bool)), SLOT(goPrevPage()));
175181
connect(actionNextPage, SIGNAL(triggered(bool)), SLOT(goNextPage()));
176182
connect(actionPrevPageQ, SIGNAL(triggered(bool)), this, SLOT(goPrevPage()));
177183
connect(actionNextPageW, SIGNAL(triggered(bool)), this, SLOT(goNextPage()));
178184
connect(actionAbout, SIGNAL(triggered(bool)), this, SLOT(showAboutDialog()));
185+
connect(
186+
&OutOfMemoryHandler::instance(),
187+
SIGNAL(outOfMemory()), SLOT(handleOutOfMemorySituation())
188+
);
179189

180190
connect(
181191
filterList->selectionModel(),
@@ -1253,6 +1263,8 @@ MainWindow::saveProjectTriggered()
12531263
void
12541264
MainWindow::saveProjectAsTriggered()
12551265
{
1266+
// XXX: this function is duplicated in OutOfMemoryDialog.
1267+
12561268
QString project_dir;
12571269
if (!m_projectFile.isEmpty()) {
12581270
project_dir = QFileInfo(m_projectFile).absolutePath();
@@ -1423,6 +1435,24 @@ MainWindow::showAboutDialog()
14231435
dialog->show();
14241436
}
14251437

1438+
/**
1439+
* This function is called asynchronously, always from the main thread.
1440+
*/
1441+
void
1442+
MainWindow::handleOutOfMemorySituation()
1443+
{
1444+
deleteLater();
1445+
1446+
m_ptrOutOfMemoryDialog->setParams(
1447+
m_projectFile, m_ptrStages, m_ptrPages, m_selectedPage, m_outFileNameGen
1448+
);
1449+
1450+
closeProjectWithoutSaving();
1451+
1452+
m_ptrOutOfMemoryDialog->setAttribute(Qt::WA_DeleteOnClose);
1453+
m_ptrOutOfMemoryDialog.release()->show();
1454+
}
1455+
14261456
/**
14271457
* Note: the removed widgets are not deleted.
14281458
*/

MainWindow.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class CompositeCacheDrivenTask;
6464
class TabbedDebugImages;
6565
class ProcessingTaskQueue;
6666
class FixDpiDialog;
67+
class OutOfMemoryDialog;
6768
class QLineF;
6869
class QRectF;
6970
class QLayout;
@@ -160,6 +161,8 @@ private slots:
160161
void openSettingsDialog();
161162

162163
void showAboutDialog();
164+
165+
void handleOutOfMemorySituation();
163166
private:
164167
enum SavePromptResult { SAVE, DONT_SAVE, CANCEL };
165168

@@ -278,6 +281,7 @@ private slots:
278281
SelectedPage m_selectedPage;
279282
QObjectCleanupHandler m_optionsWidgetCleanup;
280283
QObjectCleanupHandler m_imageWidgetCleanup;
284+
std::auto_ptr<OutOfMemoryDialog> m_ptrOutOfMemoryDialog;
281285
int m_curFilter;
282286
int m_ignoreSelectionChanges;
283287
int m_ignorePageOrderingChanges;

NonOwningWidget.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Scan Tailor - Interactive post-processing tool for scanned pages.
3+
Copyright (C) Joseph Artsimovich <joseph.artsimovich@gmail.com>
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include "NonOwningWidget.h"
20+
#include <boost/foreach.hpp>
21+
22+
NonOwningWidget::NonOwningWidget(QWidget* parent)
23+
: QWidget(parent)
24+
{
25+
}
26+
27+
NonOwningWidget::~NonOwningWidget()
28+
{
29+
BOOST_FOREACH(QObject* child, children()) {
30+
if (QWidget* widget = dynamic_cast<QWidget*>(child)) {
31+
widget->setParent(0);
32+
}
33+
}
34+
}

NonOwningWidget.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Scan Tailor - Interactive post-processing tool for scanned pages.
3+
Copyright (C) Joseph Artsimovich <joseph.artsimovich@gmail.com>
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#ifndef NON_OWNING_WIDGET_H_
20+
#define NON_OWNING_WIDGET_H_
21+
22+
#include <QWidget>
23+
24+
/**
25+
* \brief Your normal QWidget, except it doesn't delete its children with itself,
26+
* rather it calls setParent(0) on them.
27+
*/
28+
class NonOwningWidget : public QWidget
29+
{
30+
public:
31+
NonOwningWidget(QWidget* parent = 0);
32+
33+
virtual ~NonOwningWidget();
34+
};
35+
36+
#endif

OutOfMemoryDialog.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
Scan Tailor - Interactive post-processing tool for scanned pages.
3+
Copyright (C) Joseph Artsimovich <joseph.artsimovich@gmail.com>
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include "OutOfMemoryDialog.h"
20+
#include "OutOfMemoryDialog.h.moc"
21+
#include "ProjectWriter.h"
22+
#include "RecentProjects.h"
23+
#include <QFileDialog>
24+
#include <QMessageBox>
25+
#include <QFileInfo>
26+
#include <QSettings>
27+
#include <QVariant>
28+
29+
OutOfMemoryDialog::OutOfMemoryDialog(QWidget* parent)
30+
: QDialog(parent)
31+
{
32+
ui.setupUi(this);
33+
34+
ui.topLevelStack->setCurrentWidget(ui.mainPage);
35+
36+
connect(ui.saveProjectBtn, SIGNAL(clicked()), SLOT(saveProject()));
37+
connect(ui.saveProjectAsBtn, SIGNAL(clicked()), SLOT(saveProjectAs()));
38+
connect(ui.dontSaveBtn, SIGNAL(clicked()), SLOT(reject()));
39+
}
40+
41+
void
42+
OutOfMemoryDialog::setParams(
43+
QString const& project_file,
44+
IntrusivePtr<StageSequence> const& stages,
45+
IntrusivePtr<ProjectPages> const& pages,
46+
SelectedPage const& selected_page,
47+
OutputFileNameGenerator const& out_file_name_gen)
48+
{
49+
m_projectFile = project_file;
50+
m_ptrStages = stages;
51+
m_ptrPages = pages;
52+
m_selectedPage = selected_page;
53+
m_outFileNameGen = out_file_name_gen;
54+
55+
ui.saveProjectBtn->setVisible(!project_file.isEmpty());
56+
}
57+
58+
void
59+
OutOfMemoryDialog::saveProject()
60+
{
61+
if (m_projectFile.isEmpty()) {
62+
saveProjectAs();
63+
} else if (saveProjectWithFeedback(m_projectFile)) {
64+
showSaveSuccessScreen();
65+
}
66+
}
67+
68+
void
69+
OutOfMemoryDialog::saveProjectAs()
70+
{
71+
// XXX: this function is duplicated MainWindow
72+
73+
QString project_dir;
74+
if (!m_projectFile.isEmpty()) {
75+
project_dir = QFileInfo(m_projectFile).absolutePath();
76+
} else {
77+
QSettings settings;
78+
project_dir = settings.value("project/lastDir").toString();
79+
}
80+
81+
QString project_file(
82+
QFileDialog::getSaveFileName(
83+
this, QString(), project_dir,
84+
tr("Scan Tailor Projects")+" (*.ScanTailor)"
85+
)
86+
);
87+
if (project_file.isEmpty()) {
88+
return;
89+
}
90+
91+
if (!project_file.endsWith(".ScanTailor", Qt::CaseInsensitive)) {
92+
project_file += ".ScanTailor";
93+
}
94+
95+
if (saveProjectWithFeedback(project_file)) {
96+
m_projectFile = project_file;
97+
showSaveSuccessScreen();
98+
99+
QSettings settings;
100+
settings.setValue(
101+
"project/lastDir",
102+
QFileInfo(m_projectFile).absolutePath()
103+
);
104+
105+
RecentProjects rp;
106+
rp.read();
107+
rp.setMostRecent(m_projectFile);
108+
rp.write();
109+
}
110+
}
111+
112+
bool
113+
OutOfMemoryDialog::saveProjectWithFeedback(QString const& project_file)
114+
{
115+
ProjectWriter writer(m_ptrPages, m_selectedPage, m_outFileNameGen);
116+
117+
if (!writer.write(project_file, m_ptrStages->filters())) {
118+
QMessageBox::warning(
119+
this, tr("Error"),
120+
tr("Error saving the project file!")
121+
);
122+
return false;
123+
}
124+
125+
return true;
126+
}
127+
128+
void
129+
OutOfMemoryDialog::showSaveSuccessScreen()
130+
{
131+
ui.topLevelStack->setCurrentWidget(ui.saveSuccessPage);
132+
}

0 commit comments

Comments
 (0)