Writing about integer sequences and more.
[fic.git] / gui.h
blob3d5c78da61f2737518442a5f7037b5037d467cca
1 #ifndef GUI_HEADER_
2 #define GUI_HEADER_
4 #include "headers.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 DEBUG_ONLY( bool result= )
108 QObject::connect(sender,signal,receiver,slot,type);
109 ASSERT(result);
112 /** Represents the encoding-settings dialog */
113 class SettingsDialog: public QDialog { Q_OBJECT
114 QGroupBox *setBox; ///< the settings group-box
115 QTreeWidget *treeWidget; ///< the settings tree
116 QDialogButtonBox *loadSaveButtons; ///< the button-box for loading and saving
117 IRoot *settings; ///< the settings we edit
119 /** Returns a reference to the parent instance of ImageViewer */
120 ImageViewer& parentViewer() {
121 ImageViewer *result= debugCast<ImageViewer*>(parent());
122 ASSERT(result);
123 return *result;
125 /** Initializes the settings tree and group-box contents from #settings */
126 void initialize();
127 private slots:
128 /** Changes the contents of the settings group-box when selecting in the settings tree */
129 void currentItemChanged(QTreeWidgetItem *current,QTreeWidgetItem *previous);
130 /** Adjusts the #settings when changed in the settings group-box */
131 void settingChanges(int which);
132 /** Handles loading and saving of the settings */
133 void loadSaveClick(QAbstractButton *button);
134 public:
135 /** Initializes all the widgets in the dialog */
136 SettingsDialog(ImageViewer *parent,IRoot *settingsHolder);
137 /** Returns the edited #settings */
138 IRoot* getSettings() { return settings; }
141 /** Converts signals from various types of widgets */
142 class SignalChanger: public QObject { Q_OBJECT
143 int signal;
144 public:
145 /** Configures to emit notify(\p whichSignal) when the state of widget \p parent changes
146 * (it represents settings of type \p type) */
147 SignalChanger( int whichSignal, QWidget *parent=0, Module::ChoiceType type=Module::Stop )
148 : QObject(parent), signal(whichSignal) {
149 if (!parent)
150 return;
151 // connect widget's changing signal to my notifyXxx slot
152 const char *signalString=0, *slotString=0;
153 switch (type) {
154 case Module::Int:
155 case Module::IntLog2:
156 signalString= SIGNAL(valueChanged(int));
157 slotString= SLOT(notifyInt(int));
158 break;
159 case Module::Float:
160 signalString= SIGNAL(valueChanged(double));
161 slotString= SLOT(notifyDouble(double));
162 break;
163 case Module::ModuleCombo:
164 case Module::Combo:
165 signalString= SIGNAL(currentIndexChanged(int));
166 slotString= SLOT(notifyInt(int));
167 break;
168 default:
169 ASSERT(false);
170 }// switch
171 aConnect( parent, signalString, this, slotString );
173 public slots:
174 void notifyInt(int) { emit notify(signal); }
175 void notifyDouble(double) { emit notify(signal); }
176 signals:
177 void notify(int);
178 }; // SignalChanger class
182 /** A dialog showing encoding progress */
183 class EncodingProgress: public QProgressDialog { Q_OBJECT
184 /** Encoding-thread type */
185 class EncThread: public QThread { //Q_OBJECT
186 IRoot *root;
187 QImage image;
188 bool success;
189 UpdateInfo updateInfo;
190 public:
191 EncThread(IRoot *root_,const QImage &image_,const UpdateInfo &updateInfo_)
192 : root(root_), image(image_), updateInfo(updateInfo_) {}
193 virtual void run()
194 { success= root->encode(image,updateInfo); }
195 bool getSuccess() const
196 { return success; }
197 }; // EncThread class
199 static EncodingProgress *instance; ///< Pointer to the single instance of the class
201 bool terminate /// Value indicating whether the encoding should be interrupted
202 , updateMaxProgress
203 , updateProgress;
204 int progress /// The progress of the encoding - "the number of pixels encoded"
205 , maxProgress; ///< The maximum value of progress
207 IRoot *modules_encoding;///< The encoding modules
208 UpdateInfo updateInfo; ///< UpdateInfo passed to encoding modules
210 QTimer updateTimer; ///< Updates the dialog regularly
211 QTime encTime; ///< Measures encoding time
212 EncThread encThread; ///< The encoding thread
214 private:
215 /** Sets the maximum progress (the value corresponding to 100%) */
216 static void incMaxProgress(int increment) {
217 instance->maxProgress+= increment;
218 instance->updateMaxProgress= true;
220 /** Increase the progress by a value */
221 static void incProgress(int increment) {
222 instance->progress+= increment;
223 instance->updateProgress= true;
225 /// \todo Should we provide a thread-safe version?
226 /// q_atomic_fetch_and_add_acquire_int(&instance->progress,increment);
227 /// instance->setValue((volatile int&)instance->progress);
229 private slots:
230 /** Slot for catching cancel-pressed signal */
231 void setTerminate()
232 { terminate= true; }
233 /** Updating slot - called regularly by a timer */
234 void update() {
235 if (updateMaxProgress) {
236 setMaximum(maxProgress);
237 updateMaxProgress= false;
239 if (updateProgress) {
240 setValue(progress);
241 updateProgress= false;
244 private:
245 /** Creates and initializes the dialog */
246 EncodingProgress(ImageViewer *parent)
247 : QProgressDialog( tr("Encoding..."), tr("Cancel"), 0, 100, parent, Qt::Dialog )
248 , terminate(false), updateMaxProgress(false), updateProgress(false)
249 , progress(0), maxProgress(0)
250 , modules_encoding(parent->modules_settings->clone())
251 , updateInfo( terminate, &incMaxProgress, &incProgress ), updateTimer(this)
252 , encThread( modules_encoding, parent->imageLabel->pixmap()->toImage(), updateInfo ) {
253 ASSERT(!instance);
254 instance= this;
255 // set some dialog features
256 setWindowModality(Qt::ApplicationModal);
257 setAutoClose(false);
258 setAutoReset(false);
259 // to switch to terminating status when cancel is clicked
260 aConnect( this, SIGNAL(canceled()), this, SLOT(setTerminate()) );
261 // start the updating timer
262 aConnect( &updateTimer, SIGNAL(timeout()), this, SLOT(update()) );
263 updateTimer.start(1000);
264 encTime.start(); // start measuring the time
265 // start the encoding thread, set it to call ImageViewer::encDone when finished
266 aConnect( &encThread, SIGNAL(finished()), parent, SLOT(encDone()) );
267 encThread.start(QThread::LowPriority);
269 /** Only zeroes #instance */
270 ~EncodingProgress() {
271 ASSERT(this==instance);
272 instance= 0;
274 public:
275 /** Creates the dialog and starts encoding */
276 static void create(ImageViewer *parent) {
277 new EncodingProgress(parent);
279 /** Collects results and destroys the dialog */
280 static IRoot* destroy(int &elapsed) {
281 ASSERT( instance && instance->encThread.isFinished() );
282 // get the encoding result if successful, delete it otherwise
283 IRoot *result= instance->modules_encoding;
284 if ( !instance->encThread.getSuccess() ) {
285 delete result;
286 result= 0;
288 elapsed= instance->encTime.elapsed();
289 // delete the dialog
290 delete instance;
291 return result;
293 }; // EncodingProgress class
295 #endif // GUI_HEADER_