Writing: start about other papers concerning the topic.
[fic.git] / gui.h
blobfa31741163fa6696dfcede14e0598af7c0473bab
1 #ifndef GUI_HEADER_
2 #define GUI_HEADER_
4 #include "interfaces.h"
6 #include <QAction>
7 #include <QApplication>
8 #include <QComboBox>
9 #include <QDialogButtonBox>
10 #include <QDoubleSpinBox>
11 #include <QFileDialog>
12 #include <QFileInfo>
13 #include <QGridLayout>
14 #include <QGroupBox>
15 #include <QLabel>
16 #include <QMainWindow>
17 #include <QMenu>
18 #include <QMenuBar>
19 #include <QMessageBox>
20 #include <QMouseEvent>
21 #include <QProgressDialog>
22 #include <QScrollArea>
23 #include <QScrollBar>
24 #include <QSpinBox>
25 #include <QStatusBar>
26 #include <QThread>
27 #include <QTime>
28 #include <QTimer>
29 #include <QTranslator>
30 #include <QTreeWidgetItem>
32 class ImageViewer;
33 class SettingsDialog;
34 class EncodingProgress;
36 /** Represents the main window of the program, providing a GUI */
37 class ImageViewer: public QMainWindow { Q_OBJECT
38 friend class SettingsDialog;
39 friend class EncodingProgress;
41 static const int AutoIterationCount= 10;
43 IRoot *modules_settings /// Module tree holding current settings
44 , *modules_encoding; ///< Module tree that's currently encoding or the last one
45 int zoom; ///< The current zoom, see IRoot::fromStream
46 std::string encData; ///< String containing (if nonempty) the last encoded/decoded data
48 QTranslator translator; ///< The application's only translator
49 QLabel *imageLabel; ///< A pointer to the label showing images
51 QDir lastPath;
52 /** \name Actions
53 * @{ */
54 QAction
55 readAct, writeAct, compareAct, exitAct,
56 settingsAct, encodeAct, saveAct,
57 loadAct, clearAct, iterateAct, zoomIncAct, zoomDecAct;
58 /// @}
59 /** \name Menus
60 * @{ */
61 QMenu imageMenu, compMenu, decompMenu;//, langMenu, helpMenu;
62 /// @}
63 void createActions(); ///< Creates all actions and connects them to correct slots
64 void createMenus(); ///< Creates the menu bar and fills it with actions
65 void translateUi(); ///< Sets all texts in the application (uses current language)
66 void updateActions(); ///< Updates the availability of all actions
67 void changePixmap(const QPixmap &pixmap) {
68 imageLabel->setPixmap(pixmap);
69 imageLabel->resize( pixmap.size() );
70 } ///< Shows a new image
71 private slots:
72 /** \name Methods performing the actions
73 @{ */
74 void read();
75 void write();
76 void compare();
77 void settings();
78 void encode();
79 void save();
80 void load();
81 void clear();
82 void iterate();
83 void zoomInc();
84 void zoomDec();
85 /// @}
86 void encDone();
87 public:
88 /** Initializes the object to default settings */
89 ImageViewer(QApplication &app);
90 /** Only releases the modules */
91 virtual ~ImageViewer()
92 { delete modules_settings; delete modules_encoding; }
93 private:
94 /** Reloads the image, iterates and shows it (returns true on success) */
95 bool rezoom();
96 /** Gets the path of the last used directory (from #lastPath) */
97 QString lastDir()
98 { QDir dir(lastPath); return dir.cdUp() ? dir.path() : QDir::currentPath(); }
99 #ifndef NDEBUG
100 void mousePressEvent(QMouseEvent *event);
101 #endif
104 /** A simple wrapper around QObject::connect that asserts successfulness of the connection */
105 inline void aConnect( const QObject *sender, const char *signal, const QObject *receiver
106 , const char *slot, Qt::ConnectionType type=Qt::AutoCompatConnection ) {
107 #ifndef NDEBUG
108 bool result=
109 #endif
110 QObject::connect(sender,signal,receiver,slot,type);
111 ASSERT(result);
114 /** Represents the encoding-settings dialog */
115 class SettingsDialog: public QDialog { Q_OBJECT
116 QGroupBox *setBox; ///< the settings group-box
117 QTreeWidget *treeWidget; ///< the settings tree
118 QDialogButtonBox *loadSaveButtons; ///< the button-box for loading and saving
119 IRoot *settings; ///< the settings we edit
121 /** Returns a reference to the parent instance of ImageViewer */
122 ImageViewer& parentViewer() {
123 ImageViewer *result= debugCast<ImageViewer*>(parent());
124 ASSERT(result);
125 return *result;
127 /** Initializes the settings tree and group-box contents from #settings */
128 void initialize();
129 private slots:
130 /** Changes the contents of the settings group-box when selecting in the settings tree */
131 void currentItemChanged(QTreeWidgetItem *current,QTreeWidgetItem *previous);
132 /** Adjusts the #settings when changed in the settings group-box */
133 void settingChanges(int which);
134 /** Handles loading and saving of the settings */
135 void loadSaveClick(QAbstractButton *button);
136 public:
137 /** Initializes all the widgets in the dialog */
138 SettingsDialog(ImageViewer *parent,IRoot *settingsHolder);
139 /** Returns the edited #settings */
140 IRoot* getSettings() { return settings; }
143 /** Converts signals from various types of widgets */
144 class SignalChanger: public QObject { Q_OBJECT
145 int signal;
146 public:
147 /** Configures to emit notify(\p whichSignal) when the state of widget \p parent changes
148 * (it represents settings of type \p type) */
149 SignalChanger( int whichSignal, QWidget *parent=0, Module::ChoiceType type=Module::Stop )
150 : QObject(parent), signal(whichSignal) {
151 if (!parent)
152 return;
153 // connect widget's changing signal to my notifyXxx slot
154 const char *signalString=0, *slotString=0;
155 switch (type) {
156 case Module::Int:
157 case Module::IntLog2:
158 signalString= SIGNAL(valueChanged(int));
159 slotString= SLOT(notifyInt(int));
160 break;
161 case Module::Float:
162 signalString= SIGNAL(valueChanged(double));
163 slotString= SLOT(notifyDouble(double));
164 break;
165 case Module::ModuleCombo:
166 case Module::Combo:
167 signalString= SIGNAL(currentIndexChanged(int));
168 slotString= SLOT(notifyInt(int));
169 break;
170 default:
171 ASSERT(false);
172 }// switch
173 aConnect( parent, signalString, this, slotString );
175 public slots:
176 void notifyInt(int) { emit notify(signal); }
177 void notifyDouble(double) { emit notify(signal); }
178 signals:
179 void notify(int);
180 }; // SignalChanger class
184 /** A dialog showing encoding progress */
185 class EncodingProgress: public QProgressDialog { Q_OBJECT
186 /** Encoding-thread type */
187 class EncThread: public QThread { //Q_OBJECT
188 IRoot *root;
189 QImage image;
190 bool success;
191 UpdateInfo updateInfo;
192 public:
193 EncThread(IRoot *root_,const QImage &image_,const UpdateInfo &updateInfo_)
194 : root(root_), image(image_), updateInfo(updateInfo_) {}
195 virtual void run()
196 { success= root->encode(image,updateInfo); }
197 bool getSuccess() const
198 { return success; }
199 }; // EncThread class
201 static EncodingProgress *instance; ///< Pointer to the single instance of the class
203 bool terminate /// Value indicating whether the encoding should be interrupted
204 , needUpdate;
205 int progress /// The progress of the encoding - "the number of pixels encoded"
206 , maxProgress; ///< The maximum value of progress
208 IRoot *modules_encoding;///< The encoding modules
209 UpdateInfo updateInfo; ///< UpdateInfo passed to encoding modules
211 QTimer updateTimer; ///< Updates the dialog regularly
212 QTime encTime; ///< Measures encoding time
213 EncThread encThread; ///< The encoding thread
215 private:
216 /** Sets the maximum progress (the value corresponding to 100%) */
217 static void incMaxProgress(int increment) {
218 instance->maxProgress+= increment;
219 instance->needUpdate= true;
221 /** Increase the progress by a value */
222 static void incProgress(int increment) {
223 instance->progress+= increment;
224 instance->needUpdate= true;
226 // TODO: Should we provide a thread-safe version?
227 // q_atomic_fetch_and_add_acquire_int(&instance->progress,increment);
228 // instance->setValue((volatile int&)instance->progress);
230 private slots:
231 /** Slot for catching cancel-pressed signal */
232 void setTerminate()
233 { terminate= true; }
234 /** Updating slot - called regularly by a timer */
235 void update() {
236 if (!needUpdate)
237 return;
238 needUpdate= false;
239 setMaximum(maxProgress);
240 setValue(progress);
242 private:
243 /** Creates and initializes the dialog */
244 EncodingProgress(ImageViewer *parent)
245 : QProgressDialog( tr("Encoding..."), tr("Cancel"), 0, 100, parent, Qt::Dialog )
246 , terminate(false), needUpdate(false)
247 , progress(0), maxProgress(0)
248 , modules_encoding(clone(parent->modules_settings))
249 , updateInfo( terminate, &incMaxProgress, &incProgress ), updateTimer(this)
250 , encThread( modules_encoding, parent->imageLabel->pixmap()->toImage(), updateInfo ) {
251 ASSERT(!instance);
252 instance= this;
253 // set some dialog features
254 setWindowModality(Qt::ApplicationModal);
255 setAutoClose(false);
256 setAutoReset(false);
257 // to switch to terminating status when cancel is clicked
258 aConnect( this, SIGNAL(canceled()), this, SLOT(setTerminate()) );
259 // start the updating timer
260 aConnect( &updateTimer, SIGNAL(timeout()), this, SLOT(update()) );
261 updateTimer.start(500);
262 encTime.start(); // start measuring the time
263 // start the encoding thread, set it to call ImageViewer::encDone when finished
264 aConnect( &encThread, SIGNAL(finished()), parent, SLOT(encDone()) );
265 encThread.start(QThread::LowPriority);
267 /** Only zeroes #instance */
268 ~EncodingProgress() {
269 ASSERT(this==instance);
270 instance= 0;
272 public:
273 /** Creates the dialog and starts encoding */
274 static void create(ImageViewer *parent) {
275 new EncodingProgress(parent);
277 /** Collects results and destroys the dialog */
278 static IRoot* destroy(int &elapsed) {
279 ASSERT( instance && instance->encThread.isFinished() );
280 // get the encoding result if successful, delete it otherwise
281 IRoot *result= instance->modules_encoding;
282 if ( !instance->encThread.getSuccess() ) {
283 delete result;
284 result= 0;
286 elapsed= instance->encTime.elapsed();
287 // delete the dialog
288 delete instance;
289 return result;
291 }; // EncodingProgress class
293 #endif // GUI_HEADER_