VLP-28 Ported logedit to QT4.
[vlp.git] / src / edit / editor.cpp
1 #include <unistd.h>
2 #include <QtCore/QTextStream>
3 #include <QtGui/QApplication>
4 #include <QtGui/QMenuBar>
5 #include <QtGui/QFileDialog>
6 #include <QtGui/QColor>
7 #include <QtGui/QPalette>
8 #include <QtGui/QCloseEvent>
9 #include <QtGui/QTextDocument>
10 #include <QtGui/QMainWindow>
11 #include <QtGui/QStatusBar>
12 #include <QtGui/QCheckBox>
13 #include <QtGui/QVBoxLayout>
14
15 #include "editor.h"
16
17 #define TYPENUM 5
18
19 Editor *editor;
20
21 /**
22  * @attention Currently not in use
23  */
24 char *UnitTypes[TYPENUM] = {
25         "CLASS",
26         "PROCEDURE",
27         "FUNCTION",
28         "PROCESS",
29         "COROUTINE"
30 };
31
32 /**
33  * My_Edit constructor. Initializes and sets variables of Multiline editor
34  * widget.
35  * @param parent Parent widget of this widget. If set, this widget becomes a 
36  *               child window inside parent. If not set this widget becomes a
37  *               top level window. Default: NULL
38  * @param name @deprecated If set, name is sent to the Qobject constructor, so widget is
39  *             identifiable (e.g. in Qt Designer)
40  */
41 My_Edit::My_Edit(QWidget *parent, const char *name)
42         : QTextEdit(parent)
43 {
44         parent = NULL;
45         name = NULL;
46 }
47
48 /**
49  * @copydoc QWidget::keyPressEvent(QKeyEvent*)
50  */
51 void My_Edit::keyPressEvent(QKeyEvent *ev)
52 {
53         QTextEdit::keyPressEvent(ev);
54         emit cursorMove();
55 }
56
57 /**
58  * Event invoked on program close.
59  * @copydoc QWidget::closeEvent(QCloseEvent*)
60  */
61 void Editor::closeEvent(QCloseEvent * e) {
62         e->accept();
63 }
64
65 /**
66  * Editor constructor. Initializes and sets variables of Loglan Editor.
67  * @param hdir Home directory of program (specified in configuration file)
68  * @param parent Parent widget of this widget. If set, this widget becomes a 
69  *               child window inside parent. If not set this widget becomes a
70  *               top level window. Default: NULL
71  * @param name If set, name is sent to the Qobject constructor, so widget is
72  *             identifiable (e.g. in Qt Designer)
73  */
74 Editor::Editor(char *hdir, QWidget * parent)
75         : QMainWindow(parent)
76 {
77         strcpy(HomeDir, hdir);
78         find_text = "";
79         sensitive = FALSE;
80         QMenu * file = NULL;
81 /*      QMenu * comp = new QMenu();*/
82 /*      QMenu * loglan = new QMenu();*/
83         QMenu * medit = NULL;
84         QAction* action = NULL;
85
86         file = menuBar()->addMenu("&File");
87         medit = menuBar()->addMenu("&Edit");
88
89         action = menuBar()->addAction("&Compile", this, SLOT(cmp()));
90
91         /*    m->insertItem( "&LOGLAN ", loglan );*/
92
93         action = menuBar()->addAction("&Properties", this, SLOT(props()));
94
95         file->addAction("New", this, SLOT(create()), QKeySequence(Qt::CTRL + Qt::Key_N));
96         file->addAction("Open", this, SLOT(load()), QKeySequence(Qt::CTRL + Qt::Key_O));
97         file->addAction("Save", this, SLOT(save()), QKeySequence(Qt::CTRL + Qt::Key_S));
98         file->addAction("Save as", this, SLOT(save_as()), QKeySequence(Qt::CTRL + Qt::Key_A));
99         file->addSeparator();
100         file->addAction("Quit ", this, SLOT(close()));
101
102         /* comp->insertItem("Compile ", this, SLOT(cmp()), CTRL + Key_C);*/
103         /* comp->insertItem("Gen ", this, SLOT(gen()), CTRL + Key_G);*/
104         /* comp->insertItem("Compile & Gen ", this, SLOT(comp_all()));*/
105
106         /* loglan->insertItem( "Program structure", this, SLOT(log_prog()));*/
107         /* loglan->insertItem( "Unit structure", this, SLOT(log_unit()));*/
108
109         e = new My_Edit(this, "editor");
110         connect(e, SIGNAL(cursorMove()), this, SLOT(updateline()));
111         medit->addAction("Copy", e, SLOT(copy()), QKeySequence(Qt::CTRL + Qt::Key_Insert));
112         medit->addAction("Paste", e, SLOT(paste()), QKeySequence(Qt::SHIFT + Qt::Key_Insert));
113         medit->addAction("Cut", e, SLOT(cut()), QKeySequence(Qt::CTRL + Qt::Key_Delete));
114         medit->addAction("Clear All", e, SLOT(clear()));
115         medit->addSeparator();
116         medit->addAction("Find", this, SLOT(findText()), QKeySequence(Qt::CTRL + Qt::Key_F));
117         medit->addAction("Find Next", this, SLOT(find_next()), QKeySequence(Qt::CTRL + Qt::Key_L));
118
119         msg = new QTextEdit(this);
120         msg->setReadOnly(TRUE);
121
122         QVBoxLayout * layout = new QVBoxLayout();
123         layout->setContentsMargins (3, 0, 3, 0);
124         layout->addWidget(e);
125         layout->addWidget(msg);
126         QWidget *window = new QWidget();
127         window->setLayout(layout);
128         setCentralWidget(window);
129
130         compiler_path.sprintf("%s/%s", HomeDir, "compile/logcomp");
131         gen_path.sprintf("%s/%s", HomeDir, "compile/gen");
132         file_path.sprintf("%s", HomeDir);
133
134         QColor col(200, 200, 200);
135         QPalette grp(Qt::black, col, col.lighter(), col.darker(), col.darker(), Qt::black, col);
136
137         msg->setPalette(grp);
138
139         position = new QLabel();
140         statusBar()->addPermanentWidget(position);
141 }
142
143 /**
144  * Editor destructor
145  */
146 Editor::~Editor()
147 {
148 }
149
150 /**
151  * Displays line:column position of cursor
152  */
153 void Editor::updateline()
154 {
155         char pom[255];
156         int cx;
157         int cy;
158
159         cx = e->textCursor().blockNumber();
160         cy = e->textCursor().columnNumber();
161         sprintf(pom,"%d:%d ", cx, cy);
162         position->setText(pom);
163 }
164
165 /**
166  * Event invoked on resizing editor application window.
167  * @copydoc QWidget::resizeEvent(QResizeEvent*)
168  * @param event Currently does nothing
169  */
170 void Editor::resizeEvent(QResizeEvent *event)
171 {
172 /*
173 TODO: Remove entire method?
174         if (e && m) {
175                 e->setGeometry(0, m->height(), width(),
176                                 3 * (int)((height() - m->height()) / 4));
177
178                 msg->setGeometry(0, m->height() + e->height(), width(),
179                                 (int)((height() - m->height()) / 4));
180
181                 position->setGeometry(width() - 80,
182                                                 m->height() + e->height() - 10,
183                                                 position->width(),
184                                                 position->height());
185         }
186 */
187 }
188
189 /**
190  * Displays additional window 
191  */
192 void Editor::load()
193 {
194         QString fn = QFileDialog::getOpenFileName(this, "Load file", file_path, "*.log");
195         if (!fn.isEmpty())
196                 load(fn.toAscii().data());
197 }
198
199 /**
200  * Loads given file content to the editor.
201  * @param fileName Filename of file which will be read.
202  */
203 void Editor::load(const char *fileName)
204 {
205         fname.sprintf("%s", fileName);
206
207         QFile f(fileName);
208         if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
209                 return;
210
211 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
212         e->setAutoUpdate(FALSE);
213 */
214         e->clear();
215
216         QTextStream t(&f);
217         while (!t.atEnd()) {
218                 QString s = t.readLine();
219                 e->append(s);
220         }
221         f.close();
222
223 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
224         e->setAutoUpdate(TRUE);
225 */
226         e->repaint();
227         setWindowTitle(fileName);
228 }
229
230 /**
231  * Saves editor content to the file.
232  * If content has been read from file, it is written to this file. Otherwise
233  * dialog is shown to save content to the specified by user, file.
234  */
235 void Editor::save()
236 {
237         if (fname.isEmpty()) {
238                 QString fn = QFileDialog::getSaveFileName(this, "Save file", 
239                                                         file_path, "*.log");
240                 if (!fn.isEmpty()) {
241                         fname.sprintf("%s", fn.toAscii().data());
242                         save(fn.toAscii().data());
243                 }
244         } else {
245                 save(fname.toAscii().data());
246         }
247         setWindowTitle(fname);
248 }
249
250 /**
251  * Saves editor content to the file.
252  * Forces saving editor content to the new file. Special dialog is shown for
253  * that purpose.
254  */
255 void Editor::save_as()
256 {
257         QString fn = QFileDialog::getSaveFileName(this, "Save file as",
258                                                         file_path, "*.log");
259         if (!fn.isEmpty()) {
260                 fname.sprintf("%s", fn.toAscii().data());
261                 save(fn.toAscii().data());
262         }
263         setWindowTitle(fname);
264 }
265
266 /**
267  * Saves editor content to the given filename.
268  * @param fileName File name of the file where content should be saved.
269  */
270 void Editor::save(const char *fileName)
271 {
272         QFile f(fileName);
273         if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
274                 return;
275         f.reset();
276         f.write(e->toPlainText().toAscii().data(), e->toPlainText().length());
277         f.close();
278 }
279
280 /**
281  * Empties editor content.
282  */
283 void Editor::create()
284 {
285         e->clear();
286         fname.sprintf("%s", "");
287 }
288
289 /**
290  * @attention Currently not in use
291  */
292 void Editor::print()
293 {
294 }
295
296 /**
297  * @attention Currently not in use.
298  * 
299  * Saves and compiles code.
300  */
301 void Editor::cmp()
302 {
303         save();
304         compile(COMP_MODE);
305 }
306
307 /**
308  * @attention Currently not in use.
309  * 
310  * Generates program.
311  */
312 void Editor::gen()
313 {
314         compile(GEN_MODE);
315 }
316
317 /**
318  * @attention Currently not in use.
319  * 
320  * Saves, compiles and generates code.
321  */
322 void Editor::comp_all()
323 {
324         save();
325         compile(ALL_MODE);
326 }
327
328 /**
329  * Invokes compiler in the giving mode.
330  * @param mode Mode of compilation. Possible values:
331  *             - COMP_MODE - compilation mode
332  *             - GEN_MODE - program generation mode
333  *             - ALL_MODE - compilation & generation mode.
334  */
335 void Editor::compile(int mode)
336 {
337         char cmd[255];
338 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
339         msg->setAutoUpdate(FALSE);
340 */
341         msg->setReadOnly(FALSE);
342         msg->clear();
343         msg->repaint();
344
345         /*i = fname.find('.');*/
346         /* if (i>=0) {*/
347
348         /*  fn.truncate(i);*/
349         switch(mode) {
350         case COMP_MODE:
351                 sprintf(cmd, "%s %s > comp_data!", compiler_path.toAscii().data(),
352                                                         fname.toAscii().data());
353                 break;
354         case GEN_MODE:
355                 sprintf(cmd, "%s %s > comp_data!", gen_path.toAscii().data(),
356                                                         fname.toAscii().data());
357                 break;
358         case ALL_MODE:
359                 sprintf(cmd, "%s %s > comp_data!", compiler_path.toAscii().data(),
360                                                         fname.toAscii().data());
361                 system(cmd);
362                 sprintf(cmd, "%s %s >> comp_data!", gen_path.toAscii().data(),
363                                                         fname.toAscii().data());
364         break;
365         }
366
367         system(cmd);
368         QFile f("comp_data!");
369         if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
370                 return;
371
372         QTextStream t(&f);
373         while (!t.atEnd()) {
374                 QString s = t.readLine();
375                 msg->append(s);
376         }
377         f.close();
378
379         msg->setReadOnly(TRUE);
380
381 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
382         msg->setAutoUpdate(TRUE);
383 */
384         msg->repaint();
385         unlink("comp_data!");
386         /*}*/
387 }
388
389 /**
390  * Displays window with editor properties
391  */
392 void Editor::props()
393 {
394         QDialog dlg(this, Qt::Dialog);
395
396         QLineEdit *files;
397         files = new QLineEdit(&dlg);
398         files->setGeometry(130, 20, 250, 30);
399         files->setText(file_path);
400         files->setMaxLength(32767);
401         files->setEchoMode(QLineEdit::Normal);
402         files->setFrame(TRUE);
403
404         QLabel *tmpQLabel;
405         tmpQLabel = new QLabel("Path to files:", &dlg);
406         tmpQLabel->setGeometry(10, 20, 100, 30);
407         tmpQLabel->setAlignment(Qt::AlignLeft);
408         tmpQLabel->setMargin(-1);
409
410         tmpQLabel = new QLabel("Path to compiler:", &dlg);
411         tmpQLabel->setGeometry(10, 60, 100, 30);
412         tmpQLabel->setAlignment(Qt::AlignLeft);
413         tmpQLabel->setMargin(-1);
414
415         /*
416         tmpQLabel = new QLabel(&dlg, "Label_3");
417         tmpQLabel->setGeometry(10, 100, 100, 30);
418         tmpQLabel->setText("Path to gen:");
419         tmpQLabel->setAlignment(289);
420         tmpQLabel->setMargin(-1);
421         */
422
423         QLineEdit *compp;
424         compp = new QLineEdit(compiler_path, &dlg);
425         compp->setGeometry(130, 60, 250, 30);
426         compp->setMaxLength(32767);
427         compp->setEchoMode(QLineEdit::Normal);
428         compp->setFrame(TRUE);
429
430         /*
431         QLineEdit* genp;
432         genp = new QLineEdit(&dlg, "g_path");
433         genp->setGeometry(130, 100, 250, 30);
434         genp->setText(gen_path.data());
435         genp->setMaxLength(32767);
436         genp->setEchoMode(QLineEdit::Normal);
437         genp->setFrame(TRUE);
438         */
439
440         QPushButton* tmpQPushButton;
441         tmpQPushButton = new QPushButton("Ok", &dlg);
442         tmpQPushButton->setGeometry(90, 100, 70, 30);
443         tmpQPushButton->setAutoRepeat(FALSE);
444 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
445         tmpQPushButton->setAutoResize(FALSE);
446 */
447         connect(tmpQPushButton,SIGNAL(clicked()), &dlg, SLOT(accept()));
448
449         tmpQPushButton = new QPushButton("Cancel", &dlg);
450         tmpQPushButton->setGeometry(180, 100, 70, 30);
451         tmpQPushButton->setAutoRepeat(FALSE);
452 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
453         tmpQPushButton->setAutoResize(FALSE);
454 */
455         connect(tmpQPushButton,SIGNAL(clicked()), &dlg, SLOT(reject()));
456         dlg.resize(400, 140);
457
458         if (dlg.exec()) {
459                 compiler_path.sprintf("%s", compp->text().toAscii().data());
460                 /* gen_path.sprintf("%s",genp->text()); */
461                 file_path.sprintf("%s", files->text().toAscii().data());
462         };
463 }
464
465 /**
466  * @attention Currently not in use
467  */
468 void Editor::log_unit()
469 {
470         /*
471         Code commented during Qt4 migration.
472         Code is not used, so there is no rush with this...
473         */
474         /*
475         QString txt;
476         QDialog dlg(this, Qt::Dialog);
477         int cx, cy, i;
478         char uname[255];
479
480         QLineEdit* files;
481         files = new QLineEdit(&dlg, "f_path");
482         files->setGeometry(130, 20, 250, 30);
483         files->setText("");
484         files->setMaxLength(32767);
485         files->setEchoMode(QLineEdit::Normal);
486         files->setFrame(TRUE);
487
488         QLabel* tmpQLabel;
489         tmpQLabel = new QLabel(&dlg, "Label_1");
490         tmpQLabel->setGeometry(10, 20, 100, 30);
491         tmpQLabel->setText("Unit name:");
492         tmpQLabel->setAlignment(289);
493         tmpQLabel->setMargin(-1);
494
495         QPushButton* tmpQPushButton;
496         tmpQPushButton = new QPushButton(&dlg, "OkBtn");
497         tmpQPushButton->setGeometry(40, 170, 70, 30);
498         tmpQPushButton->setText("Ok");
499         tmpQPushButton->setAutoRepeat(FALSE);
500         tmpQPushButton->setAutoResize(FALSE);
501         connect(tmpQPushButton,SIGNAL(clicked()), &dlg, SLOT(accept()));
502
503         tmpQPushButton = new QPushButton(&dlg, "CancelBtn");
504         tmpQPushButton->setGeometry(130, 170, 100, 30);
505         tmpQPushButton->setText("Cancel");
506         tmpQPushButton->setAutoRepeat(FALSE);
507         tmpQPushButton->setAutoResize(FALSE);
508         connect(tmpQPushButton,SIGNAL(clicked()), &dlg, SLOT(reject()));
509
510         tmpQLabel = new QLabel(&dlg, "Label_1");
511         tmpQLabel->setGeometry(10, 50, 100, 60);
512         tmpQLabel->setText("Unit type:");
513
514         QListBox lst(&dlg, "type");
515         for(i = 0; i < TYPENUM; i++) {
516                 lst.insertItem(UnitTypes[i]);
517         }
518         lst.setGeometry(130, 60, 180, 80);
519         lst.setCurrentItem(0);
520
521         if (dlg.exec()) {
522                 strcpy(uname, files->text());
523                 e->getCursorPosition(&cx, &cy);
524
525                 txt.sprintf("UNIT %s : %s( <params> );\nBEGIN\n\nEND %s;",
526                         uname, lst.text(lst.currentItem()).ascii(), uname);
527                 e->insertAt(txt, cx, cy);
528         }
529         */
530 }
531
532 /**
533  * @attention Currently not in use
534  */
535 void Editor::log_prog()
536 {
537         /*
538         Code commented during Qt4 migration.
539         Code is not used, so there is no rush with this...
540         */
541         /*
542         QString txt;
543         QDialog dlg(this, "unit", TRUE);
544         int cx, cy;
545         char uname[255];
546
547         QLineEdit *files;
548         files = new QLineEdit(&dlg, "f_path");
549         files->setGeometry(130, 20, 250, 30);
550         files->setText("");
551         files->setMaxLength(32767);
552         files->setEchoMode(QLineEdit::Normal);
553         files->setFrame(TRUE);
554
555         QLabel *tmpQLabel;
556         tmpQLabel = new QLabel(&dlg, "Label_1");
557         tmpQLabel->setGeometry(10, 20, 100, 30);
558         tmpQLabel->setText("Program name:");
559
560         QPushButton* tmpQPushButton;
561         tmpQPushButton = new QPushButton(&dlg, "OkBtn");
562         tmpQPushButton->setGeometry(40, 70, 70, 30);
563         tmpQPushButton->setText("Ok");
564         tmpQPushButton->setAutoRepeat(FALSE);
565         tmpQPushButton->setAutoResize(FALSE);
566         connect(tmpQPushButton,SIGNAL(clicked()), &dlg, SLOT(accept()));
567
568         tmpQPushButton = new QPushButton(&dlg, "CancelBtn");
569         tmpQPushButton->setGeometry(130, 70, 100, 30);
570         tmpQPushButton->setText("Cancel");
571         tmpQPushButton->setAutoRepeat(FALSE);
572         tmpQPushButton->setAutoResize(FALSE);
573         connect(tmpQPushButton,SIGNAL(clicked()), &dlg, SLOT(reject()));
574
575         if (dlg.exec()) {
576                 strcpy(uname, files->text());
577                 e->getCursorPosition(&cx, &cy);
578
579                 txt.sprintf("PROGRAM %s\n\nBEGIN\n\nEND ", uname);
580                 e->insertAt(txt, cx, cy);
581         }
582         */
583 }
584
585 /**
586  * Searches for given text in editor content.
587  * Displays window to set search parameters. If text is found sets cursor
588  * position on it.
589  */
590 void Editor::findText()
591 {
592         QDialog dlg(this, Qt::Dialog);
593         QString *txt;
594         int res, line, pom;
595
596         QLineEdit *tmpQLineEdit;
597         tmpQLineEdit = new QLineEdit("", &dlg);
598         tmpQLineEdit->setGeometry(60, 10, 180, 30);
599
600         QLabel *tmpQLabel;
601         tmpQLabel = new QLabel(&dlg);
602         tmpQLabel->setGeometry(10, 10, 50, 30);
603
604         tmpQLabel->setText("Text:");
605
606         QCheckBox *tmpQRadioButton;
607         tmpQRadioButton = new QCheckBox("Case sensitive", &dlg);
608         tmpQRadioButton->setGeometry(70, 50, 150, 30);
609         tmpQRadioButton->setAutoRepeat(FALSE);
610
611 /*TODO: Does not exists in Qt4. Use layout and set the size policy (https://qt-project.org/forums/viewthread/2112)
612         tmpQRadioButton->setAutoResize(FALSE);
613 */
614
615         QPushButton *okbtn, *cbtn;
616         okbtn = new QPushButton("Find", &dlg);
617         okbtn->setGeometry(260, 10, 100, 30);
618         okbtn->setDefault(TRUE);
619         connect(okbtn,SIGNAL(clicked()), &dlg, SLOT(accept()));
620
621         cbtn = new QPushButton("Close", &dlg);
622         cbtn->setGeometry(260, 50, 100, 30);
623         connect(cbtn, SIGNAL(clicked()), &dlg, SLOT(reject()));
624         dlg.resize(380, 90);
625
626         if (dlg.exec()) {
627                 sensitive = tmpQRadioButton->isChecked();
628                 find_text = tmpQLineEdit->text();
629                 
630                 QTextDocument::FindFlags flags = 0;
631                 
632                 if (sensitive) {
633                         flags |= QTextDocument::FindCaseSensitively;
634                 }
635                 e->find(find_text, flags);
636         }
637 }
638
639 /**
640  * Searches for next occurence of given text in editor content.
641  * Displays window to set search parameters. If text is found sets cursor
642  * position on it.
643  */
644 void Editor::find_next()
645 {
646         if (!find_text.isEmpty()) {
647                 QTextDocument::FindFlags flags = 0;
648                 
649                 if (sensitive) {
650                         flags |= QTextDocument::FindCaseSensitively;
651                 }
652                 e->find(find_text, flags);
653         }
654 }
655
656 /**
657  * Program main function.
658  * argv[1] is mandatory and should be a path to the home directory of
659  * application (same as in configuration file).
660  * @param argc Number of program arguments
661  * @param argv Program arguments
662  */
663 int main(int argc, char **argv)
664 {
665         QApplication app(argc, argv);
666         editor = new Editor(argv[1], NULL);
667         editor->resize(600, 400);
668         editor->show();
669         return app.exec();
670 }
671