1 /*
2 
3     Minimum Profit - A Text Editor
4     Qt4 (and Qt5) driver.
5 
6     ttcdt <dev@triptico.com> et al.
7 
8     This software is released into the public domain.
9     NO WARRANTY. See file LICENSE for details.
10 
11 */
12 
13 /* override auto-generated definition in config.h */
14 extern "C" int qt4_drv_detect(int *argc, char ***argv);
15 extern "C" int qt5_drv_detect(int *argc, char ***argv);
16 
17 #include "config.h"
18 
19 #include <stdio.h>
20 #include <wchar.h>
21 #include <unistd.h>
22 
23 //#define MPDM_OLD_COMPAT
24 
25 #include "mpdm.h"
26 #include "mpsl.h"
27 #include "mp.h"
28 #include "mp.xpm"
29 
30 #ifdef CONFOPT_QT5
31 #include <QtWidgets>
32 #else
33 #include <QtGui>
34 #endif
35 
36 /** data **/
37 
38 #define MENU_CLASS QMenu
39 #define MENUBAR_CLASS QMenuBar
40 
41 #include "mpv_qk_common.cpp"
42 
43 class MPWindow : public QMainWindow
44 {
45 public:
46     MPWindow(QWidget * parent = 0);
47     ~MPWindow(void);
48     bool queryExit(void);
49     void closeEvent(QCloseEvent *event);
50     bool event(QEvent * event);
51     void save_settings(void);
52     MPArea *area;
53 };
54 
55 QApplication *app;
56 MPWindow *window;
57 
58 
59 /** MPWindow methods **/
60 
MPWindow(QWidget * parent)61 MPWindow::MPWindow(QWidget * parent) : QMainWindow(parent)
62 {
63     QVBoxLayout *vb;
64     QHBoxLayout *hb;
65 
66     setWindowTitle("mp " VERSION);
67 
68 //    menubar = this->menuBar();
69     qk_build_menu(this->menuBar());
70 
71     /* pick an optimal height for the menu & tabs */
72 //    int height = menubar->sizeHint().height();
73 
74     /* main area */
75     hb = new QHBoxLayout();
76     hb->setContentsMargins(0, 0, 0, 0);
77 
78     area = new MPArea();
79 
80     hb->addWidget(area);
81     hb->addWidget(area->scrollbar);
82     QWidget *cc = new QWidget();
83     cc->setLayout(hb);
84 
85     /* the full container */
86     vb = new QVBoxLayout();
87     vb->setContentsMargins(0, 0, 0, 0);
88 
89     vb->addWidget(area->file_tabs);
90     vb->addWidget(cc);
91 
92     QWidget *mc = new QWidget();
93     mc->setLayout(vb);
94 
95     this->statusBar()->addWidget(area->statusbar);
96 
97     setCentralWidget(mc);
98 
99     connect(area->scrollbar, SIGNAL(valueChanged(int)),
100             area, SLOT(from_scrollbar(int)));
101 
102     connect(area->file_tabs, SIGNAL(currentChanged(int)),
103             area, SLOT(from_filetabs(int)));
104 
105     connect(this->menuBar(), SIGNAL(triggered(QAction *)),
106             area, SLOT(from_menu(QAction *)));
107 
108     this->setWindowIcon(QIcon(QPixmap(mp_xpm)));
109 
110     mpdm_t v = mpdm_get_wcs(MP, L"state");
111     if ((v = mpdm_get_wcs(v, L"window")) == NULL) {
112         v = mpdm_set_wcs(mpdm_get_wcs(MP, L"state"), MPDM_O(), L"window");
113         mpdm_set_wcs(v, MPDM_I(20),  L"x");
114         mpdm_set_wcs(v, MPDM_I(20),  L"y");
115         mpdm_set_wcs(v, MPDM_I(600), L"w");
116         mpdm_set_wcs(v, MPDM_I(400), L"h");
117     }
118 
119     move(QPoint(mpdm_ival(mpdm_get_wcs(v, L"x")), mpdm_ival(mpdm_get_wcs(v, L"y"))));
120     resize(QSize(mpdm_ival(mpdm_get_wcs(v, L"w")), mpdm_ival(mpdm_get_wcs(v, L"h"))));
121 }
122 
123 
~MPWindow(void)124 MPWindow::~MPWindow(void)
125 {
126 }
127 
128 
save_settings(void)129 void MPWindow::save_settings(void)
130 {
131     mpdm_t v;
132 
133     v = mpdm_get_wcs(MP, L"state");
134     v = mpdm_set_wcs(v, MPDM_O(), L"window");
135     mpdm_set_wcs(v, MPDM_I(pos().x()),       L"x");
136     mpdm_set_wcs(v, MPDM_I(pos().y()),       L"y");
137     mpdm_set_wcs(v, MPDM_I(size().width()),  L"w");
138     mpdm_set_wcs(v, MPDM_I(size().height()), L"h");
139 }
140 
141 
queryExit(void)142 bool MPWindow::queryExit(void)
143 {
144     mp_process_event(MPDM_S(L"close-window"));
145 
146     return mp_exit_requested ? true : false;
147 }
148 
149 
closeEvent(QCloseEvent * event)150 void MPWindow::closeEvent(QCloseEvent *event)
151 {
152     mp_process_event(MPDM_S(L"close-window"));
153 
154     if (!mp_exit_requested)
155         event->ignore();
156 }
157 
158 
159 static mpdm_t qt4_drv_shutdown(mpdm_t a, mpdm_t ctxt);
160 
event(QEvent * event)161 bool MPWindow::event(QEvent *event)
162 {
163     /* do the processing */
164     bool r = QWidget::event(event);
165 
166     if (mp_exit_requested) {
167         save_settings();
168         QApplication::exit(0);
169     }
170 
171     return r;
172 }
173 
174 
175 /** driver functions **/
176 
qt4_drv_alert(mpdm_t a,mpdm_t ctxt)177 static mpdm_t qt4_drv_alert(mpdm_t a, mpdm_t ctxt)
178 {
179     /* 1# arg: prompt */
180     QMessageBox::information(window, "mp " VERSION,
181                              v_to_qstring(mpdm_get_i(a, 0)));
182 
183     return NULL;
184 }
185 
186 
qt4_drv_confirm(mpdm_t a,mpdm_t ctxt)187 static mpdm_t qt4_drv_confirm(mpdm_t a, mpdm_t ctxt)
188 {
189     int r;
190 
191     /* 1# arg: prompt */
192     r = QMessageBox::question(window, "mp" VERSION,
193                               v_to_qstring(mpdm_get_i(a, 0)),
194                               QMessageBox::Yes | QMessageBox::
195                               No | QMessageBox::Cancel);
196 
197     switch (r) {
198     case QMessageBox::Yes:
199         r = 1;
200         break;
201 
202     case QMessageBox::No:
203         r = 2;
204         break;
205 
206     case QMessageBox::Cancel:
207         r = 0;
208         break;
209     }
210 
211     return MPDM_I(r);
212 }
213 
214 
qt4_drv_openfile(mpdm_t a,mpdm_t ctxt)215 static mpdm_t qt4_drv_openfile(mpdm_t a, mpdm_t ctxt)
216 {
217     QString r;
218     char tmp[128];
219 
220     getcwd(tmp, sizeof(tmp));
221 
222     /* 1# arg: prompt */
223     r = QFileDialog::getOpenFileName(window,
224                                      v_to_qstring(mpdm_get_i(a, 0)), tmp);
225 
226     return qstring_to_v(r);
227 }
228 
229 
qt4_drv_savefile(mpdm_t a,mpdm_t ctxt)230 static mpdm_t qt4_drv_savefile(mpdm_t a, mpdm_t ctxt)
231 {
232     QString r;
233     char tmp[128];
234 
235     getcwd(tmp, sizeof(tmp));
236 
237     /* 1# arg: prompt */
238     r = QFileDialog::getSaveFileName(window,
239                                      v_to_qstring(mpdm_get_i(a, 0)), tmp);
240 
241     return qstring_to_v(r);
242 }
243 
244 
qt4_drv_openfolder(mpdm_t a,mpdm_t ctxt)245 static mpdm_t qt4_drv_openfolder(mpdm_t a, mpdm_t ctxt)
246 {
247     QString r;
248     char tmp[128];
249 
250     getcwd(tmp, sizeof(tmp));
251 
252     /* 1# arg: prompt */
253     r = QFileDialog::getExistingDirectory(window,
254                                     v_to_qstring(mpdm_get_i(a, 0)),
255                                     tmp,
256                                     QFileDialog::ShowDirsOnly);
257 
258     return qstring_to_v(r);
259 }
260 
261 
262 class MPForm : public QDialog
263 {
264 public:
265     QDialogButtonBox *button_box;
266 
MPForm(QWidget * parent=0)267     MPForm(QWidget * parent = 0) : QDialog(parent)
268     {
269         button_box = new QDialogButtonBox(QDialogButtonBox::Ok |
270                                           QDialogButtonBox::Cancel);
271 
272         connect(button_box, SIGNAL(accepted()), this, SLOT(accept()));
273         connect(button_box, SIGNAL(rejected()), this, SLOT(reject()));
274     }
275 };
276 
277 
qt4_drv_form(mpdm_t a,mpdm_t ctxt)278 static mpdm_t qt4_drv_form(mpdm_t a, mpdm_t ctxt)
279 {
280     int n;
281     mpdm_t widget_list;
282     QWidget *qlist[100];
283     mpdm_t r = NULL;
284 
285     MPForm *dialog = new MPForm(window);
286     dialog->setWindowTitle("mp " VERSION);
287 
288     dialog->setModal(true);
289 
290     widget_list = mpdm_get_i(a, 0);
291 
292     QWidget *form = new QWidget();
293     QFormLayout *fl = new QFormLayout();
294 
295     for (n = 0; n < (int) mpdm_size(widget_list); n++) {
296         mpdm_t w = mpdm_get_i(widget_list, n);
297         wchar_t *type;
298         mpdm_t t;
299         QLabel *ql = new QLabel("");
300         QWidget *qw = NULL;
301 
302         type = mpdm_string(mpdm_get_wcs(w, L"type"));
303 
304         if ((t = mpdm_get_wcs(w, L"label")) != NULL) {
305             ql->setText(v_to_qstring(mpdm_gettext(t)));
306         }
307 
308         t = mpdm_get_wcs(w, L"value");
309 
310         if (wcscmp(type, L"text") == 0) {
311             mpdm_t h;
312             QComboBox *qc = new QComboBox();
313 
314             qc->setEditable(true);
315             qc->setMinimumContentsLength(30);
316             qc->setMaxVisibleItems(8);
317 
318             if (t != NULL)
319                 qc->setEditText(v_to_qstring(t));
320 
321             if ((h = mpdm_get_wcs(w, L"history")) != NULL) {
322                 int i;
323 
324                 /* has history; fill it */
325                 h = mp_get_history(h);
326 
327                 for (i = 0; i < (int) mpdm_size(h); i++)
328                     qc->addItem(v_to_qstring(mpdm_get_i(h, i)));
329 
330                 qc->setCurrentIndex(mpdm_size(h) - 1);
331             }
332 
333             /* select all the editable field */
334             qc->lineEdit()->selectAll();
335 
336             qw = qc;
337         }
338         else
339         if (wcscmp(type, L"password") == 0) {
340             QLineEdit *qe = new QLineEdit();
341 
342             qe->setEchoMode(QLineEdit::Password);
343 
344             qw = qe;
345         }
346         else
347         if (wcscmp(type, L"checkbox") == 0) {
348             QCheckBox *qc = new QCheckBox();
349 
350             if (mpdm_ival(t))
351                 qc->setCheckState(Qt::Checked);
352 
353             qw = qc;
354         }
355         else
356         if (wcscmp(type, L"list") == 0) {
357             int i;
358             QTableWidget *qtw = new QTableWidget();
359             qtw->setSelectionBehavior(QAbstractItemView::SelectRows);
360             qtw->setShowGrid(false);
361 
362             qtw->setMinimumWidth(600);
363 
364             /* if it's the only widget, make it tall */
365             if (mpdm_size(widget_list) == 1)
366                 qtw->setMinimumHeight(400);
367 
368             qtw->horizontalHeader()->hide();
369             qtw->horizontalHeader()->setStretchLastSection(true);
370             qtw->verticalHeader()->hide();
371 
372             mpdm_t l = mpdm_get_wcs(w, L"list");
373 
374             qtw->setColumnCount(2);
375             qtw->setRowCount(mpdm_size(l));
376 
377             for (i = 0; i < (int) mpdm_size(l); i++) {
378                 QTableWidgetItem *qtwi;
379                 wchar_t *ptr1, *ptr2;
380 
381                 ptr1 = wcsdup(mpdm_string(mpdm_get_i(l, i)));
382                 if ((ptr2 = wcschr(ptr1, L'\t'))) {
383                     *ptr2 = L'\0';
384                     ptr2++;
385                 }
386                 else
387                     ptr2 = (wchar_t *)L"";
388 
389                 qtwi = new QTableWidgetItem(QString::fromWCharArray(ptr1));
390                 qtw->setItem(i, 0, qtwi);
391 
392                 qtwi = new QTableWidgetItem(QString::fromWCharArray(ptr2));
393                 qtwi->setTextAlignment(Qt::AlignRight);
394                 qtw->setItem(i, 1, qtwi);
395 
396                 qtw->setRowHeight(i, 24);
397 
398                 free(ptr1);
399             }
400 
401             qtw->selectRow(mpdm_ival(t));
402 
403             qtw->resizeColumnsToContents();
404 
405             qw = qtw;
406         }
407 
408         qlist[n] = qw;
409 
410         if (mpdm_size(widget_list) == 1) {
411             fl->addRow(ql);
412             fl->addRow(qw);
413         }
414         else
415             fl->addRow(ql, qw);
416 
417         if (n == 0)
418             qlist[n]->setFocus(Qt::OtherFocusReason);
419     }
420 
421     form->setLayout(fl);
422 
423     QVBoxLayout *ml = new QVBoxLayout();
424     ml->addWidget(form);
425     ml->addWidget(dialog->button_box);
426 
427     dialog->setLayout(ml);
428 
429     if (dialog->exec()) {
430         r = MPDM_A(mpdm_size(widget_list));
431 
432         /* fill the return values */
433         for (n = 0; n < (int) mpdm_size(widget_list); n++) {
434             mpdm_t w = mpdm_get_i(widget_list, n);
435             mpdm_t v = NULL;
436             wchar_t *type;
437 
438             type = mpdm_string(mpdm_get_wcs(w, L"type"));
439 
440             if (wcscmp(type, L"text") == 0) {
441                 mpdm_t h;
442                 QComboBox *ql = (QComboBox *) qlist[n];
443 
444                 v = mpdm_ref(qstring_to_v(ql->currentText()));
445 
446                 /* if it has history, add to it */
447                 if (v && (h = mpdm_get_wcs(w, L"history")) && mpdm_cmp_wcs(v, L"")) {
448                     h = mp_get_history(h);
449 
450                     if (mpdm_cmp(v, mpdm_get_i(h, -1)) != 0)
451                         mpdm_push(h, v);
452                 }
453 
454                 mpdm_unrefnd(v);
455             }
456             else
457             if (wcscmp(type, L"password") == 0) {
458                 QLineEdit *ql = (QLineEdit *) qlist[n];
459 
460                 v = qstring_to_v(ql->text());
461             }
462             else
463             if (wcscmp(type, L"checkbox") == 0) {
464                 QCheckBox *qb = (QCheckBox *) qlist[n];
465 
466                 v = MPDM_I(qb->checkState() == Qt::Checked);
467             }
468             else
469             if (wcscmp(type, L"list") == 0) {
470                 QListWidget *ql = (QListWidget *) qlist[n];
471 
472                 v = MPDM_I(ql->currentRow());
473             }
474 
475             mpdm_set_i(r, v, n);
476         }
477     }
478 
479     return r;
480 }
481 
482 
qt4_drv_update_ui(mpdm_t a,mpdm_t ctxt)483 static mpdm_t qt4_drv_update_ui(mpdm_t a, mpdm_t ctxt)
484 {
485     window->area->font = qk_build_font();
486     qk_build_colors();
487     qk_build_menu(window->menuBar());
488 
489     window->area->ls_width = -1;
490     window->area->ls_height = -1;
491     window->area->update();
492 
493     return NULL;
494 }
495 
496 
qt4_drv_busy(mpdm_t a,mpdm_t ctxt)497 static mpdm_t qt4_drv_busy(mpdm_t a, mpdm_t ctxt)
498 {
499     int onoff = mpdm_ival(mpdm_get_i(a, 0));
500 
501     window->setCursor(onoff ? Qt::WaitCursor : Qt::ArrowCursor);
502 
503     return NULL;
504 }
505 
506 
qt4_drv_main_loop(mpdm_t a,mpdm_t ctxt)507 static mpdm_t qt4_drv_main_loop(mpdm_t a, mpdm_t ctxt)
508 {
509     app->exec();
510 
511     return NULL;
512 }
513 
514 
qt4_drv_idle(mpdm_t a,mpdm_t ctxt)515 static mpdm_t qt4_drv_idle(mpdm_t a, mpdm_t ctxt)
516 {
517     int idle_msecs = (int) (mpdm_rval(mpdm_get_i(a, 0)) * 1000);
518 
519     window->area->timer->stop();
520 
521     if (idle_msecs)
522         window->area->timer->start(idle_msecs);
523 
524     return NULL;
525 }
526 
527 
qt4_register_functions(void)528 static void qt4_register_functions(void)
529 {
530     mpdm_t drv;
531 
532     drv = mpdm_get_wcs(mpdm_root(), L"mp_drv");
533     mpdm_set_wcs(drv, MPDM_X(qt4_drv_main_loop),   L"main_loop");
534     mpdm_set_wcs(drv, MPDM_X(qt4_drv_shutdown),    L"shutdown");
535     mpdm_set_wcs(drv, MPDM_X(qt4_drv_clip_to_sys), L"clip_to_sys");
536     mpdm_set_wcs(drv, MPDM_X(qt4_drv_sys_to_clip), L"sys_to_clip");
537     mpdm_set_wcs(drv, MPDM_X(qt4_drv_update_ui),   L"update_ui");
538     mpdm_set_wcs(drv, MPDM_X(qt4_drv_idle),        L"idle");
539     mpdm_set_wcs(drv, MPDM_X(qt4_drv_busy),        L"busy");
540     mpdm_set_wcs(drv, MPDM_X(qt4_drv_alert),       L"alert");
541     mpdm_set_wcs(drv, MPDM_X(qt4_drv_confirm),     L"confirm");
542     mpdm_set_wcs(drv, MPDM_X(qt4_drv_openfile),    L"openfile");
543     mpdm_set_wcs(drv, MPDM_X(qt4_drv_savefile),    L"savefile");
544     mpdm_set_wcs(drv, MPDM_X(qt4_drv_form),        L"form");
545     mpdm_set_wcs(drv, MPDM_X(qt4_drv_openfolder),  L"openfolder");
546 }
547 
548 
qt4_drv_startup(mpdm_t a,mpdm_t ctxt)549 static mpdm_t qt4_drv_startup(mpdm_t a, mpdm_t ctxt)
550 /* driver initialization */
551 {
552     qt4_register_functions();
553 
554     qk_build_colors();
555 
556     window = new MPWindow();
557     window->show();
558 
559     return NULL;
560 }
561 
562 
563 #ifdef CONFOPT_QT4
564 extern "C" Display *XOpenDisplay(char *);
565 
qt4_drv_detect(int * argc,char *** argv)566 extern "C" int qt4_drv_detect(int *argc, char ***argv)
567 {
568     int n, ret = 1;
569 
570     for (n = 0; n < *argc; n++) {
571         if (strcmp(argv[0][n], "-txt") == 0)
572             ret = 0;
573     }
574 
575     if (ret) {
576         Display *x11_display;
577 
578         /* try connecting directly to the Xserver */
579         if ((x11_display = XOpenDisplay((char *) NULL))) {
580             mpdm_t drv;
581 
582             /* this is where it crashes if no X server */
583             app = new QApplication(x11_display);
584 
585             drv = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"mp_drv");
586             mpdm_set_wcs(drv, MPDM_S(L"qt4"), L"id");
587             mpdm_set_wcs(drv, MPDM_X(qt4_drv_startup), L"startup");
588         }
589         else
590             ret = 0;
591     }
592 
593     return ret;
594 }
595 
596 #endif
597 
598 
qt5_drv_detect(int * argc,char *** argv)599 extern "C" int qt5_drv_detect(int *argc, char ***argv)
600 {
601     int n, ret = 1;
602 
603     for (n = 0; n < *argc; n++) {
604         if (strcmp(argv[0][n], "-txt") == 0)
605             ret = 0;
606     }
607 
608     if (ret) {
609         char *display;
610 
611         if ((display = getenv("DISPLAY")) && *display) {
612             mpdm_t drv;
613 
614             /* _argc cannot disappear */
615             static int _argc = 0;
616             app = new QApplication(_argc, NULL);
617 
618             drv = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"mp_drv");
619             mpdm_set_wcs(drv, MPDM_S(L"qt5"), L"id");
620             mpdm_set_wcs(drv, MPDM_X(qt4_drv_startup), L"startup");
621         }
622         else
623             ret = 0;
624     }
625 
626     return ret;
627 }
628 
629