]> git.sven.stormbind.net Git - sven/vym.git/blob - src/texteditor.cpp
New upstream version 2.9.22
[sven/vym.git] / src / texteditor.cpp
1 #include "texteditor.h"
2
3 #include <QAction>
4 #include <QActionGroup>
5 #include <QApplication>
6 #include <QColorDialog>
7 #include <QComboBox>
8 #include <QFileDialog>
9 #include <QFontDialog>
10 #include <QMenuBar>
11 #include <QMessageBox>
12 #include <QPrintDialog>
13 #include <QPrinter>
14 #include <QStatusBar>
15 #include <QTextEdit>
16 #include <QToolBar>
17
18 #include <typeinfo>
19
20 #include "mainwindow.h"
21 #include "settings.h"
22 #include "shortcuts.h"
23
24 extern Main *mainWindow;
25 extern int statusbarTime;
26 extern Settings settings;
27
28 extern QAction *actionViewToggleNoteEditor;
29
30 extern QString vymName;
31
32 extern Switchboard switchboard;
33
34 extern QPrinter *printer;
35 extern bool debug;
36
37 ///////////////////////////////////////////////////////////////////////
38 ///////////////////////////////////////////////////////////////////////
39
40 TextEditor::TextEditor()
41 {
42     statusBar()->hide(); // Hide sizeGrip on default, which comes with statusBar
43
44     e = new QTextEdit(this);
45     e->setFocus();
46     e->setTabStopDistance(20); // unit is pixel, default would be 80
47     e->setAutoFillBackground(true);
48     e->installEventFilter(this);
49     connect(e, SIGNAL(textChanged()), this, SLOT(editorChanged()));
50     setCentralWidget(e);
51     statusBar()->showMessage(tr("Ready", "Statusbar message"), statusbarTime);
52
53     connect(e, SIGNAL(currentCharFormatChanged(const QTextCharFormat &)), this,
54             SLOT(formatChanged(const QTextCharFormat &)));
55
56     // Don't show menubar per default
57     menuBar()->hide();
58
59     // Toolbars
60     setupFileActions();
61     setupEditActions();
62     setupFormatActions();
63     setupSettingsActions();
64
65     // Various states
66     blockChangedSignal = false;
67     blockTextUpdate = false;
68     setInactive();
69
70     editorName = "Text editor";
71     setEditorTitle("");
72 }
73
74 TextEditor::~TextEditor()
75 {
76     // Save Settings
77     QString n = QString("/satellite/%1/").arg(shortcutScope);
78     settings.setValue(n + "geometry/size", size());
79     settings.setValue(n + "geometry/pos", pos());
80     settings.setValue(n + "state", saveState(0));
81
82     QString s;
83     if (actionSettingsFonthintDefault->isChecked())
84         s = "fixed";
85     else
86         s = "variable";
87     settings.setValue(n + "fonts/fonthintDefault", s);
88     settings.setValue(n + "fonts/varFont", varFont.toString());
89     settings.setValue(n + "fonts/fixedFont", fixedFont.toString());
90
91     settings.setValue(n + "colors/richTextDefaultBackground", colorRichTextDefaultBackground.name());
92     settings.setValue(n + "colors/richTextDefaultForeground", colorRichTextDefaultForeground.name());
93 }
94
95 void TextEditor::init(const QString &scope)
96 {
97     shortcutScope = scope;
98     QString n = QString("/satellite/%1/").arg(shortcutScope);
99     restoreState(settings.value(n + "state", 0).toByteArray());
100     filenameHint = "";
101     fixedFont.fromString(
102         settings.value(n + "fonts/fixedFont", "Courier,12,-1,5,48,0,0,0,1,0")
103             .toString());
104     varFont.fromString(
105         settings
106             .value(n + "fonts/varFont", "DejaVu Sans Mono,12,-1,0,50,0,0,0,0,0")
107             .toString());
108     QString s =
109         settings.value(n + "fonts/fonthintDefault", "variable").toString();
110     if (s == "fixed") {
111         actionSettingsFonthintDefault->setChecked(true);
112         e->setCurrentFont(fixedFont);
113     }
114     else {
115         actionSettingsFonthintDefault->setChecked(false);
116         e->setCurrentFont(varFont);
117     }
118
119     // Default colors for RichText  //FIXME-2 here? Though we use plainText as default?
120     QPixmap pix(16, 16);
121     colorRichTextDefaultBackground.setNamedColor(
122         settings.value(n + "colors/richTextDefaultBackground", "#ffffff").toString());
123     pix.fill(colorRichTextDefaultBackground);
124     actionFilledEditorColor->setIcon(pix);
125
126
127     colorRichTextDefaultForeground.setNamedColor(
128         settings.value(n + "colors/richTextDefaultForeground", "#000000").toString());
129     pix.fill(colorRichTextDefaultForeground);
130     actionFontColor->setIcon(pix);
131
132     // Default is PlainText
133     actionFormatRichText->setChecked(false);
134     clear();
135 }
136
137 bool TextEditor::isEmpty()
138 {
139     if (e->toPlainText().length() > 0)
140         return false;
141     else
142         return true;
143 }
144
145 void TextEditor::setEditorTitle(const QString &s)
146 {
147     editorTitle = (s.isEmpty()) ? editorName : editorName + ": " + s;
148
149     // Set title of parent dockWidget
150     if (parentWidget())
151         parentWidget()->setWindowTitle(editorTitle);
152
153     setWindowTitle(editorTitle);
154 }
155
156 QString TextEditor::getEditorTitle() { return editorTitle; }
157
158 void TextEditor::setEditorName(const QString &s) { editorName = s; }
159
160 void TextEditor::setFont(const QFont &font)
161 {
162     blockChangedSignal = true;
163
164     QTextCursor tc = e->textCursor();
165     QTextCharFormat format = tc.charFormat();
166
167     tc.select(QTextCursor::Document);
168     format.setFont(font);
169     tc.setCharFormat(format);
170     tc.clearSelection();
171     fontChanged(fixedFont);
172
173     blockChangedSignal = false;
174 }
175
176 void TextEditor::setFontHint(const QString &fh)
177 {
178     if (fh == "fixed") {
179         actionFormatUseFixedFont->setChecked(true);
180         e->setCurrentFont(fixedFont);
181         setFont(fixedFont);
182     }
183     else {
184         actionFormatUseFixedFont->setChecked(false);
185         e->setCurrentFont(varFont);
186         setFont(varFont);
187     }
188 }
189
190 QString TextEditor::getFontHint()
191 {
192     if (actionFormatUseFixedFont->isChecked())
193         return "fixed";
194     else
195         return "var";
196 }
197
198 QString TextEditor::getFontHintDefault()
199 {
200     if (actionSettingsFonthintDefault->isChecked())
201         return "fixed";
202     else
203         return "var";
204 }
205
206 void TextEditor::setFilename(const QString &fn)
207 {
208     if (state == filledEditor) {
209         if (fn.isEmpty()) {
210             filename = "";
211             statusBar()->showMessage(
212                 tr("No filename available for this note.", "Statusbar message"),
213                 statusbarTime);
214         }
215         else {
216             filename = fn;
217             statusBar()->showMessage(
218                 tr(QString("Current filename is %1").arg(filename).toUtf8(),
219                    "Statusbar message"),
220                 statusbarTime);
221         }
222     }
223 }
224
225 QString TextEditor::getFilename() { return filename; }
226
227 void TextEditor::setFilenameHint(const QString &fnh) { filenameHint = fnh; }
228
229 QString TextEditor::getFilenameHint() { return filenameHint; }
230
231 QString TextEditor::getText()
232 {
233     if (e->toPlainText().isEmpty())
234         return QString();
235
236     if (actionFormatRichText->isChecked())
237         return e->toHtml();
238     else
239         return e->toPlainText();
240 }
241
242 VymText TextEditor::getVymText()
243 {
244     VymText vt;
245
246     if (actionFormatRichText->isChecked())
247         vt.setRichText(e->toHtml());
248     else
249         vt.setPlainText(e->toPlainText());
250
251     if (actionFormatUseFixedFont->isChecked())
252         vt.setFontHint(getFontHint());
253
254     return vt;
255 }
256
257 bool TextEditor::findText(const QString &t,
258                           const QTextDocument::FindFlags &flags)
259 {
260     if (e->find(t, flags))
261         return true;
262     else
263         return false;
264 }
265
266 bool TextEditor::findText(const QString &t,
267                           const QTextDocument::FindFlags &flags, int i)
268 {
269     // Position at beginning
270     QTextCursor c = e->textCursor();
271     c.setPosition(0, QTextCursor::MoveAnchor);
272     e->setTextCursor(c);
273
274     // Search for t
275     int j = 0;
276     while (j <= i) {
277         if (!e->find(t, flags))
278             return false;
279         j++;
280     }
281     return true;
282 }
283
284 void TextEditor::setTextCursor(const QTextCursor &cursor)
285 {
286     e->setTextCursor(cursor);
287 }
288
289 QTextCursor TextEditor::getTextCursor() { return e->textCursor(); }
290
291 void TextEditor::setFocus() { e->setFocus(); }
292
293 void TextEditor::setupFileActions()
294 {
295     QToolBar *tb = addToolBar(tr("Note Actions"));
296     tb->setObjectName("noteEditorFileActions");
297     QMenu *fileMenu = menuBar()->addMenu(tr("&Note", "Menubar"));
298
299     QString tag = tr("Texteditor", "Shortcuts");
300     QAction *a;
301     a = new QAction(QPixmap(":/fileopen.png"), tr("&Import..."), this);
302     a->setShortcut(Qt::CTRL + Qt::Key_O);
303     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
304     switchboard.addSwitch("textLoad", shortcutScope, a, tag);
305     connect(a, SIGNAL(triggered()), this, SLOT(textLoad()));
306     tb->addAction(a);
307     fileMenu->addAction(a);
308     actionFileLoad = a;
309
310     fileMenu->addSeparator();
311     a = new QAction(QPixmap(":/filesave.png"), tr("&Export..."), this);
312     a->setShortcut(Qt::CTRL + Qt::Key_S);
313     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
314     switchboard.addSwitch("textSave", shortcutScope, a, tag);
315     connect(a, SIGNAL(triggered()), this, SLOT(textSave()));
316     tb->addAction(a);
317     fileMenu->addAction(a);
318     addAction(a);
319     actionFileSave = a;
320
321     a = new QAction(tr("Export &As... (HTML)"), this);
322     connect(a, SIGNAL(triggered()), this, SLOT(textSaveAs()));
323     fileMenu->addAction(a);
324     actionFileSaveAs = a;
325
326     a = new QAction(tr("Export &As...(ASCII)"), this);
327     a->setShortcut(Qt::ALT + Qt::Key_X);
328     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
329     switchboard.addSwitch("textExportAsASCII", shortcutScope, a, tag);
330     connect(a, SIGNAL(triggered()), this, SLOT(textExportAsASCII()));
331     fileMenu->addAction(a);
332     addAction(a);
333     actionFileSaveAs = a;
334
335     fileMenu->addSeparator();
336     a = new QAction(QPixmap(":/fileprint.png"), tr("&Print..."), this);
337     a->setShortcut(Qt::CTRL + Qt::Key_P);
338     switchboard.addSwitch("textPrint", shortcutScope, a, tag);
339     connect(a, SIGNAL(triggered()), this, SLOT(textPrint()));
340     tb->addAction(a);
341     fileMenu->addAction(a);
342     actionFilePrint = a;
343
344     a = new QAction(QPixmap(":/edittrash.png"), tr("&Delete All"), this);
345     connect(a, SIGNAL(triggered()), this, SLOT(deleteAll()));
346     fileMenu->addAction(a);
347     tb->addAction(a);
348     actionFileDeleteAll = a;
349 }
350
351 void TextEditor::setupEditActions()
352 {
353     QString tag = tr("Texteditor", "Shortcuts");
354     QToolBar *editToolBar = addToolBar(tr("Edit Actions"));
355     editToolBar->setObjectName("noteEditorEditActions");
356     editToolBar->hide();
357     QMenu *editMenu = menuBar()->addMenu(tr("Edi&t"));
358
359     QAction *a;
360     a = new QAction(QPixmap(":/undo.png"), tr("&Undo"), this);
361     a->setShortcut(Qt::CTRL + Qt::Key_Z);
362     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
363     switchboard.addSwitch("textUndo", shortcutScope, a, tag);
364     connect(a, SIGNAL(triggered()), e, SLOT(undo()));
365     editMenu->addAction(a);
366     editToolBar->addAction(a);
367     actionEditUndo = a;
368
369     a = new QAction(QPixmap(":/redo.png"), tr("&Redo"), this);
370     a->setShortcut(Qt::CTRL + Qt::Key_Y);
371     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
372     switchboard.addSwitch("textRedo", shortcutScope, a, tag);
373     connect(a, SIGNAL(triggered()), e, SLOT(redo()));
374     editMenu->addAction(a);
375     editToolBar->addAction(a);
376     actionEditRedo = a;
377
378     editMenu->addSeparator();
379     a = new QAction(QPixmap(), tr("Select and copy &all"), this);
380     a->setShortcutContext(Qt::WidgetShortcut);
381     a->setShortcut(Qt::CTRL + Qt::Key_A);
382     switchboard.addSwitch("textCopyAll", shortcutScope, a, tag);
383     connect(a, SIGNAL(triggered()), this, SLOT(editCopyAll()));
384     editMenu->addAction(a);
385
386     editMenu->addSeparator();
387     a = new QAction(QPixmap(":/editcopy.png"), tr("&Copy"), this);
388     a->setShortcut(Qt::CTRL + Qt::Key_C);
389     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
390     switchboard.addSwitch("textCopy", shortcutScope, a, tag);
391     connect(a, SIGNAL(triggered()), e, SLOT(copy()));
392     editMenu->addAction(a);
393     editToolBar->addAction(a);
394     actionEditCopy = a;
395
396     a = new QAction(QPixmap(":/editcut.png"), tr("Cu&t"), this);
397     a->setShortcut(Qt::CTRL + Qt::Key_X);
398     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
399     switchboard.addSwitch("textCut", shortcutScope, a, tag);
400     connect(a, SIGNAL(triggered()), e, SLOT(cut()));
401     editMenu->addAction(a);
402     editToolBar->addAction(a);
403     actionEditCut = a;
404
405     a = new QAction(QPixmap(":/editpaste.png"), tr("&Paste"), this);
406     a->setShortcut(Qt::CTRL + Qt::Key_V);
407     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
408     switchboard.addSwitch("textPaste", shortcutScope, a, tag);
409     connect(a, SIGNAL(triggered()), e, SLOT(paste()));
410     editMenu->addAction(a);
411     editToolBar->addAction(a);
412     actionEditPaste = a;
413 }
414
415 void TextEditor::setupFormatActions()
416 {
417     QString tag = tr("Texteditor", "Shortcuts");
418     fontHintsToolBar =
419         addToolBar(tr("Font hints", "toolbar in texteditor"));
420     fontHintsToolBar->setObjectName("noteEditorFontToolBar");
421     QMenu *formatMenu = menuBar()->addMenu(tr("F&ormat"));
422
423     QAction *a;
424
425     a = new QAction(QPixmap(":/formatfixedfont.png"), tr("&Font hint"), this);
426     a->setShortcut(Qt::CTRL + Qt::Key_H);
427     a->setCheckable(true);
428     a->setChecked(
429         settings.value("/noteeditor/fonts/useFixedByDefault", false).toBool());
430     switchboard.addSwitch("textToggleFonthint", shortcutScope, a, tag);
431     connect(a, SIGNAL(triggered()), this, SLOT(toggleFonthint()));
432     formatMenu->addAction(a);
433     fontHintsToolBar->addAction(a);
434     actionFormatUseFixedFont = a;
435
436     // Original icon: ./share/icons/oxygen/22x22/actions/format-text-color.png
437     a = new QAction(QPixmap(":/formatrichtext.png"), tr("&Richtext"), this);
438     a->setShortcut(Qt::CTRL + Qt::Key_R);
439     //    a->setShortcutContext (Qt::WidgetShortcut);
440     a->setCheckable(true);
441     switchboard.addSwitch("textToggleRichText", shortcutScope, a, tag);
442     connect(a, SIGNAL(triggered()), this, SLOT(toggleRichText()));
443     formatMenu->addAction(a);
444     fontHintsToolBar->addAction(a);
445     actionFormatRichText = a;
446
447     fontToolBar = addToolBar(tr("Fonts", "toolbar in texteditor"));
448     fontToolBar->setObjectName("noteEditorFontToolBar");
449
450     comboFont = new QComboBox;
451     fontToolBar->addWidget(comboFont);
452     QFontDatabase fontDB;
453     comboFont->insertItems(0, fontDB.families());
454     connect(comboFont, SIGNAL(activated(const QString &)), this,
455             SLOT(textFamily(const QString &)));
456
457     comboSize = new QComboBox;
458     fontToolBar->addWidget(comboSize);
459     QList<int> sizes = fontDB.standardSizes();
460     QList<int>::iterator it = sizes.begin();
461     int i = 0;
462     while (it != sizes.end()) {
463         i++;
464         ++it; // increment i before using it
465         comboSize->insertItem(i, QString::number(*it));
466     }
467     connect(comboSize, SIGNAL(activated(const QString &)), this,
468             SLOT(textSize(const QString &)));
469
470     formatMenu->addSeparator();
471
472     formatToolBar = addToolBar(tr("Format", "toolbar in texteditor"));
473     formatToolBar->setObjectName("noteEditorFormatToolBar");
474
475     QPixmap pix(16, 16);
476     pix.fill(e->textColor());
477     a = new QAction(pix, tr("&Color..."), this);
478     formatMenu->addAction(a);
479     formatToolBar->addAction(a);
480     connect(a, SIGNAL(triggered()), this, SLOT(textColor()));
481     actionTextColor = a;
482
483     a = new QAction(QPixmap(":/text_bold.png"), tr("&Bold"), this);
484     a->setShortcut(Qt::CTRL + Qt::Key_B);
485 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
486     switchboard.addSwitch("textToggleBold", shortcutScope, a, tag);
487     connect(a, SIGNAL(triggered()), this, SLOT(textBold()));
488     formatToolBar->addAction(a);
489     formatMenu->addAction(a);
490     a->setCheckable(true);
491     actionTextBold = a;
492
493     a = new QAction(QPixmap(":/text_italic.png"), tr("&Italic"), this);
494     a->setShortcut(Qt::CTRL + Qt::Key_I);
495 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
496     switchboard.addSwitch("textToggleItalic", shortcutScope, a, tag);
497     connect(a, SIGNAL(triggered()), this, SLOT(textItalic()));
498     formatToolBar->addAction(a);
499     formatMenu->addAction(a);
500     a->setCheckable(true);
501     actionTextItalic = a;
502
503     a = new QAction(QPixmap(":/text_under.png"), tr("&Underline"), this);
504     a->setShortcut(Qt::CTRL + Qt::Key_U);
505 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
506     switchboard.addSwitch("textToggleUnderline", shortcutScope, a, tag);
507     connect(a, SIGNAL(triggered()), this, SLOT(textUnderline()));
508     formatToolBar->addAction(a);
509     formatMenu->addAction(a);
510     a->setCheckable(true);
511     // richTextWidgets.append((QWidget*)a);
512     actionTextUnderline = a;
513     formatMenu->addSeparator();
514
515     QActionGroup *actGrp2 = new QActionGroup(this);
516     actGrp2->setExclusive(true);
517     a = new QAction(QPixmap(":/text_sub.png"), tr("Subs&cript"), actGrp2);
518     a->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B);
519 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
520     a->setCheckable(true);
521     formatToolBar->addAction(a);
522     formatMenu->addAction(a);
523     switchboard.addSwitch("textToggleSub", shortcutScope, a, tag);
524     connect(a, SIGNAL(triggered()), this, SLOT(textVAlign()));
525     actionAlignSubScript = a;
526
527     a = new QAction(QPixmap(":/text_super.png"), tr("Su&perscript"), actGrp2);
528     a->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_P);
529 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
530     a->setCheckable(true);
531     formatToolBar->addAction(a);
532     formatMenu->addAction(a);
533     switchboard.addSwitch("textToggleSuper", shortcutScope, a, tag);
534     connect(a, SIGNAL(triggered()), this, SLOT(textVAlign()));
535     actionAlignSuperScript = a;
536     QActionGroup *grp = new QActionGroup(this);
537     connect(grp, SIGNAL(triggered(QAction *)), this,
538             SLOT(textAlign(QAction *)));
539
540     formatMenu->addSeparator();
541
542     a = new QAction(QPixmap(":/text_left.png"), tr("&Left"), grp);
543     // a->setShortcut( Qt::CTRL+Qt::Key_L );
544     a->setCheckable(true);
545     formatToolBar->addAction(a);
546     formatMenu->addAction(a);
547     actionAlignLeft = a;
548     a = new QAction(QPixmap(":/text_center.png"), tr("C&enter"), grp);
549     // a->setShortcut(  Qt::CTRL + Qt::Key_E);
550     a->setCheckable(true);
551     formatToolBar->addAction(a);
552     formatMenu->addAction(a);
553     actionAlignCenter = a;
554     a = new QAction(QPixmap(":/text_right.png"), tr("&Right"), grp);
555     // a->setShortcut(Qt::CTRL + Qt::Key_R );
556     a->setCheckable(true);
557     formatToolBar->addAction(a);
558     formatMenu->addAction(a);
559     actionAlignRight = a;
560     a = new QAction(QPixmap(":/text_block.png"), tr("&Justify"), grp);
561     // a->setShortcut(Qt::CTRL + Qt::Key_J );
562     a->setCheckable(true);
563     formatToolBar->addAction(a);
564     formatMenu->addAction(a);
565     actionAlignJustify = a;
566 }
567
568 void TextEditor::setupSettingsActions()
569 {
570     QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
571
572     QAction *a;
573     a = new QAction(tr("Set &fixed font"), this);
574     connect(a, SIGNAL(triggered()), this, SLOT(setFixedFont()));
575     settingsMenu->addAction(a);
576     actionSettingsFixedFont = a;
577
578     a = new QAction(tr("Set &variable font"), this);
579     connect(a, SIGNAL(triggered()), this, SLOT(setVarFont()));
580     settingsMenu->addAction(a);
581     actionSettingsVarFont = a;
582
583     a = new QAction(tr("&fixed font is default"), this);
584     a->setCheckable(true);
585     // set state later in constructor...
586     settingsMenu->addAction(a);
587     actionSettingsFonthintDefault = a;
588
589     settingsMenu->addSeparator();
590
591     a = new QAction(
592         tr("Set RichText default background color", "TextEditor") + "...", this);
593     settingsMenu->addAction(a);
594     connect(a, SIGNAL(triggered()), this, SLOT(selectColorRichTextDefaultBackground()));
595     actionFilledEditorColor = a;
596
597     a = new QAction(tr("Set RichText default font color", "TextEditor") + "...", this);
598     settingsMenu->addAction(a);
599     connect(a, SIGNAL(triggered()), this, SLOT(selectColorRichTextDefaultForeground()));
600     actionFontColor = a;
601 }
602
603 void TextEditor::textLoad()
604 {
605     if (state != inactiveEditor) {
606         if (!isEmpty()) {
607             QMessageBox mb(vymName + " - " + tr("Note Editor"),
608                            "Loading will overwrite the existing note",
609                            QMessageBox::Warning,
610                            QMessageBox::Yes | QMessageBox::Default,
611                            QMessageBox::Cancel, 0);
612             mb.setButtonText(QMessageBox::Yes, "Load note");
613             switch (mb.exec()) {
614             case QMessageBox::Cancel:
615                 return;
616                 break;
617             }
618         }
619         // Load note
620         QFileDialog *fd = new QFileDialog(this);
621         QStringList types;
622         types << "Text (*.txt *.html)"
623               << "VYM notes and HTML (*.html)"
624               << "ASCII texts (*.txt)"
625               << "All files (*)";
626         fd->setNameFilters(types);
627         fd->setDirectory(QDir().current());
628         fd->show();
629         QString fn;
630         if (fd->exec() == QDialog::Accepted && !fd->selectedFiles().isEmpty())
631             fn = fd->selectedFiles().first();
632
633         if (!fn.isEmpty()) {
634             QFile f(fn);
635             if (!f.open(QIODevice::ReadOnly))
636                 return;
637
638             QTextStream ts(&f);
639             setTextAuto(ts.readAll());
640             editorChanged();
641         }
642     }
643 }
644
645 void TextEditor::closeEvent(QCloseEvent *ce)
646 {
647     ce->accept(); // TextEditor can be reopened with show()
648     hide();
649     emit(windowClosed());
650     return;
651 }
652
653 bool TextEditor::eventFilter(QObject *obj, QEvent *ev)
654 {
655     if (obj == e) {
656         if (ev->type() == QEvent::KeyPress) {
657             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
658             if (keyEvent == QKeySequence::Paste) {
659                 // switch editor mode to match clipboard content before pasting
660                 const QClipboard *clipboard = QApplication::clipboard();
661                 const QMimeData *mimeData = clipboard->mimeData();
662
663                 if (mimeData->hasHtml() && !actionFormatRichText->isChecked())
664                     setRichTextMode(true);
665             }
666         }
667     }
668     // pass the event on to the parent class
669     return QMainWindow::eventFilter(obj, ev);
670 }
671
672 void TextEditor::editorChanged()
673 {
674     //qDebug() << "TE::editorChanged" << editorName << "blockChanged: " << blockChangedSignal;
675     EditorState oldState = state;
676     if (isEmpty())
677         state = emptyEditor;
678     else
679         state = filledEditor;
680
681     if (!blockChangedSignal) {
682         blockTextUpdate = true;
683         emit(textHasChanged(getVymText()));
684         blockTextUpdate = false;
685     }
686
687     if (state == oldState)
688         return;
689
690     updateState();
691 }
692
693 void TextEditor::setRichText(const QString &t)
694 {
695     blockChangedSignal = true;
696     e->setReadOnly(false);
697     e->setHtml(t);
698     actionFormatRichText->setChecked(true);
699
700     // Update state including colors
701     updateState();
702
703     updateActions();
704     blockChangedSignal = false;
705 }
706
707 void TextEditor::setPlainText(const QString &t)
708 {
709     blockChangedSignal = true;
710     e->setReadOnly(false);
711
712     e->setPlainText(t);
713     actionFormatRichText->setChecked(false);
714
715     // Reset also text format
716     QTextCharFormat textformat;
717     textformat.setForeground(qApp->palette().color(QPalette::WindowText));
718     textformat.setFont(varFont);
719     e->setCurrentCharFormat(textformat);
720
721     // Update state including colors
722     updateState();
723
724     updateActions();
725     blockChangedSignal = false;
726 }
727
728 void TextEditor::setTextAuto(const QString &t)
729 {
730     if (Qt::mightBeRichText(t))
731         setRichText(t);
732     else
733         setPlainText(t);
734 }
735
736 void TextEditor::setVymText(const VymText &vt)
737 {
738     // While a note is being edited, we are sending textHasChanged
739     // Then we don't want to update the text additionally from outside,
740     // as this would position cursor at beginning of text
741     if (blockTextUpdate) return;
742
743     if (vt.getText() == getText()) return;
744
745     if (vt.isRichText())
746         setRichText(vt.getText());
747     else
748         setPlainText(vt.getText());
749 }
750
751 void TextEditor::setInactive()
752 {
753     setState(inactiveEditor);
754 }
755
756 void TextEditor::editCopyAll()
757 {
758     e->selectAll();
759     e->copy();
760 }
761
762 void TextEditor::clear()
763 {
764     //qDebug() << "TE::clear" << editorName;
765     bool blockChangedOrg = blockChangedSignal;
766
767     blockChangedSignal = true;
768     e->clear();
769     setState(emptyEditor);
770
771     blockChangedSignal = blockChangedOrg;
772 }
773
774 void TextEditor::deleteAll()
775 {
776     e->clear();
777 }
778
779 void TextEditor::textSaveAs()
780 {
781     QString caption = tr("Export Note to single file");
782     QString fn = QFileDialog::getSaveFileName(
783         this, caption, QString(), "VYM Note (HTML) (*.html);;All files (*)",
784         0, QFileDialog::DontConfirmOverwrite);
785
786     if (!fn.isEmpty()) {
787         QFile file(fn);
788         if (file.exists()) {
789             QMessageBox mb(
790                 vymName,
791                 tr("The file %1\nexists already.\nDo you want to overwrite it?",
792                    "dialog 'save note as'")
793                     .arg(fn),
794                 QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
795                 QMessageBox::Cancel | QMessageBox::Escape, Qt::NoButton);
796             mb.setButtonText(QMessageBox::Yes, tr("Overwrite"));
797             mb.setButtonText(QMessageBox::No, tr("Cancel"));
798             switch (mb.exec()) {
799             case QMessageBox::Yes:
800                 // save
801                 filename = fn;
802                 textSave();
803                 return;
804             case QMessageBox::Cancel:
805                 // do nothing
806                 break;
807             }
808         }
809         else {
810             filename = fn;
811             textSave();
812             return;
813         }
814     }
815     statusBar()->showMessage(
816         tr("Couldn't export note ", "dialog 'save note as'") + fn,
817         statusbarTime);
818 }
819
820 void TextEditor::textSave()
821 {
822     if (filename.isEmpty()) {
823         textSaveAs();
824         return;
825     }
826
827     QString text = e->toHtml(); // FIXME-4 or plaintext? check...
828     QFile f(filename);
829     if (!f.open(QIODevice::WriteOnly)) {
830         statusBar()->showMessage(QString("Could not write to %1").arg(filename),
831                                  statusbarTime);
832         return;
833     }
834
835     QTextStream t(&f);
836     t.setCodec("UTF-8");
837     t << text;
838     f.close();
839
840     e->document()->setModified(false);
841
842     statusBar()->showMessage(QString("Note exported as %1").arg(filename),
843                              statusbarTime);
844 }
845
846 void TextEditor::textExportAsASCII()
847 {
848     QString fn, s;
849     if (!filenameHint.isEmpty()) {
850         if (!filenameHint.contains(".txt"))
851             s = filenameHint + ".txt";
852         else
853             s = filenameHint;
854     }
855     else
856         s = QString();
857     QString caption = tr("Export Note to single file (ASCII)");
858     fn = QFileDialog::getSaveFileName(
859         this, caption, s, "VYM Note (ASCII) (*.txt);;All files (*)");
860     int ret = -1;
861
862     if (!fn.isEmpty()) {
863         QFile file(fn);
864         if (file.exists()) {
865             QMessageBox mb(
866                 vymName,
867                 tr("The file %1\nexists already.\nDo you want to overwrite it?",
868                    "dialog 'save note as'")
869                     .arg(fn),
870                 QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
871                 QMessageBox::Cancel | QMessageBox::Escape, Qt::NoButton);
872             mb.setButtonText(QMessageBox::Yes, tr("Overwrite"));
873             mb.setButtonText(QMessageBox::No, tr("Cancel"));
874             ret = mb.exec();
875         }
876         if (ret == QMessageBox::Cancel)
877             return;
878
879         // save
880         if (!file.open(QIODevice::WriteOnly))
881             statusBar()->showMessage(
882                 QString("Could not write to %1").arg(filename), statusbarTime);
883         else {
884             QTextStream t(&file);
885             t << getVymText().getTextASCII();
886             file.close();
887
888             statusBar()->showMessage(QString("Note exported as %1").arg(fn),
889                                      statusbarTime);
890         }
891     }
892 }
893
894 void TextEditor::textPrint()
895 {
896     QTextDocument *document = e->document();
897
898     if (!printer)
899         mainWindow->setupPrinter();
900
901     QPrintDialog dialog(printer, this);
902     dialog.setWindowTitle(tr("Print", "TextEditor"));
903     if (dialog.exec() != QDialog::Accepted)
904         return;
905
906     document->print(printer);
907 }
908
909 void TextEditor::textEditUndo() {}
910
911 void TextEditor::toggleFonthint()
912 {
913     if (!actionFormatUseFixedFont->isChecked()) {
914         e->setCurrentFont(varFont);
915         setFont(varFont);
916     }
917     else {
918         e->setCurrentFont(fixedFont);
919         setFont(fixedFont);
920     }
921     emit(textHasChanged(getVymText()));
922 }
923
924 void TextEditor::setRichTextMode(bool b)
925 {
926     //qDebug() << "TE::setRichTextMode b=" << b;
927     actionFormatUseFixedFont->setEnabled(false);
928     if (b) {
929         setRichText(e->toHtml());
930
931         // Use default foreground color for all text when switching to RichText
932         QTextCursor cursor = e->textCursor();
933         e->selectAll();
934         e->setTextColor(colorRichTextDefaultForeground);
935         e->setTextCursor(cursor);
936         
937     } else {
938         setPlainText(e->toPlainText());
939     }
940     emit(textHasChanged(getVymText()));
941 }
942
943 void TextEditor::toggleRichText()
944 {
945     if (actionFormatRichText->isChecked())
946         setRichTextMode(true);
947     else
948         setRichTextMode(false);
949 }
950
951 void TextEditor::setFixedFont()
952 {
953     bool ok;
954     QFont font = QFontDialog::getFont(&ok, fixedFont, this);
955     if (ok)
956         fixedFont = font;
957 }
958
959 void TextEditor::setVarFont()
960 {
961     bool ok;
962     QFont font = QFontDialog::getFont(&ok, varFont, this);
963     if (ok)
964         varFont = font;
965 }
966
967 void TextEditor::textBold()
968 {
969     if (actionTextBold->isChecked())
970         e->setFontWeight(QFont::Bold);
971     else
972         e->setFontWeight(QFont::Normal);
973 }
974
975 void TextEditor::textUnderline()
976 {
977     e->setFontUnderline(actionTextUnderline->isChecked());
978 }
979
980 void TextEditor::textItalic()
981 {
982     e->setFontItalic(actionTextItalic->isChecked());
983 }
984
985 void TextEditor::textFamily(const QString &f) { e->setFontFamily(f); }
986
987 void TextEditor::textSize(const QString &p) { e->setFontPointSize(p.toInt()); }
988
989 void TextEditor::textColor()
990 {
991     QColor col = QColorDialog::getColor(e->textColor(), this);
992     if (!col.isValid())
993         return;
994     e->setTextColor(col);
995     /*
996     QPixmap pix( 16, 16 );
997     pix.fill( col );
998     actionTextColor->setIcon( pix );
999     */
1000 }
1001
1002 void TextEditor::textAlign(QAction *a)
1003 {
1004     QTextCursor c = e->textCursor();
1005
1006     if (a == actionAlignLeft)
1007         e->setAlignment(Qt::AlignLeft);
1008     else if (a == actionAlignCenter)
1009         e->setAlignment(Qt::AlignHCenter);
1010     else if (a == actionAlignRight)
1011         e->setAlignment(Qt::AlignRight);
1012     else if (a == actionAlignJustify)
1013         e->setAlignment(Qt::AlignJustify);
1014 }
1015
1016 void TextEditor::textVAlign()
1017 {
1018     QTextCharFormat format;
1019
1020     if (sender() == actionAlignSuperScript &&
1021         actionAlignSuperScript->isChecked()) {
1022         format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
1023     }
1024     else if (sender() == actionAlignSubScript &&
1025              actionAlignSubScript->isChecked()) {
1026         format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
1027     }
1028     else {
1029         format.setVerticalAlignment(QTextCharFormat::AlignNormal);
1030     }
1031     e->mergeCurrentCharFormat(format);
1032 }
1033
1034 void TextEditor::fontChanged(const QFont &f)
1035 {
1036     int i = comboFont->findText(f.family());
1037     if (i >= 0)
1038         comboFont->setCurrentIndex(i);
1039     i = comboSize->findText(QString::number(f.pointSize()));
1040     if (i >= 0)
1041         comboSize->setCurrentIndex(i);
1042     actionTextBold->setChecked(f.bold());
1043     actionTextItalic->setChecked(f.italic());
1044     actionTextUnderline->setChecked(f.underline());
1045 }
1046
1047 void TextEditor::colorChanged(const QColor &c)
1048 {
1049     QPixmap pix(16, 16);
1050     pix.fill(c);
1051     actionTextColor->setIcon(pix);
1052 }
1053
1054 void TextEditor::formatChanged(const QTextCharFormat &f)
1055 {
1056     if (!actionFormatRichText->isChecked())
1057         return;
1058     fontChanged(f.font());
1059     colorChanged(f.foreground().color());
1060     alignmentChanged(e->alignment());
1061     verticalAlignmentChanged(f.verticalAlignment());
1062 }
1063
1064 void TextEditor::alignmentChanged(int a)
1065 {
1066     if ((a == Qt::AlignLeft) || (a & Qt::AlignLeft))
1067         actionAlignLeft->setChecked(true);
1068     else if ((a & Qt::AlignHCenter))
1069         actionAlignCenter->setChecked(true);
1070     else if ((a & Qt::AlignRight))
1071         actionAlignRight->setChecked(true);
1072     else if ((a & Qt::AlignJustify))
1073         actionAlignJustify->setChecked(true);
1074 }
1075
1076 void TextEditor::verticalAlignmentChanged(QTextCharFormat::VerticalAlignment a)
1077 {
1078     actionAlignSubScript->setChecked(false);
1079     actionAlignSuperScript->setChecked(false);
1080     switch (a) {
1081     case QTextCharFormat::AlignSuperScript:
1082         actionAlignSuperScript->setChecked(true);
1083         break;
1084     case QTextCharFormat::AlignSubScript:
1085         actionAlignSubScript->setChecked(true);
1086         break;
1087     default:;
1088     }
1089 }
1090
1091 void TextEditor::updateActions()
1092 {
1093     bool b;
1094     b = (state == inactiveEditor) ? false : true;
1095
1096     actionFileLoad->setEnabled(b);
1097     actionFileSave->setEnabled(b);
1098     actionFileSaveAs->setEnabled(b);
1099     actionFilePrint->setEnabled(b);
1100     actionFileDeleteAll->setEnabled(b);
1101     actionEditUndo->setEnabled(b);
1102     actionEditRedo->setEnabled(b);
1103     actionEditCopy->setEnabled(b);
1104     actionEditCut->setEnabled(b);
1105     actionEditPaste->setEnabled(b);
1106     actionFormatUseFixedFont->setEnabled(b);
1107     actionFormatRichText->setEnabled(b);
1108
1109     if (!actionFormatRichText->isChecked() || !b) {
1110         comboFont->setEnabled(false);
1111         comboSize->setEnabled(false);
1112         fontToolBar->hide();
1113         formatToolBar->hide();
1114         actionTextColor->setEnabled(false);
1115         actionTextBold->setEnabled(false);
1116         actionTextUnderline->setEnabled(false);
1117         actionTextItalic->setEnabled(false);
1118         actionTextColor->setEnabled(false);
1119         actionAlignSubScript->setEnabled(false);
1120         actionAlignSuperScript->setEnabled(false);
1121         actionAlignLeft->setEnabled(false);
1122         actionAlignCenter->setEnabled(false);
1123         actionAlignRight->setEnabled(false);
1124         actionAlignJustify->setEnabled(false);
1125     }
1126     else {
1127         comboFont->setEnabled(true);
1128         comboSize->setEnabled(true);
1129         fontToolBar->show();
1130         formatToolBar->show();
1131         actionTextColor->setEnabled(true);
1132         actionTextBold->setEnabled(true);
1133         actionTextUnderline->setEnabled(true);
1134         actionTextItalic->setEnabled(true);
1135         actionTextColor->setEnabled(true);
1136         actionAlignSubScript->setEnabled(true);
1137         actionAlignSuperScript->setEnabled(true);
1138         actionAlignLeft->setEnabled(true);
1139         actionAlignCenter->setEnabled(true);
1140         actionAlignRight->setEnabled(true);
1141         actionAlignJustify->setEnabled(true);
1142         actionFormatUseFixedFont->setEnabled(false);
1143     }
1144 }
1145
1146 void TextEditor::setState(EditorState s) // FIXME-2 called 12x when reselecting once in ME
1147                                          // 5 alone for HeadingEditor
1148 {
1149     //qDebug() << "TE::setState" << s << editorName;
1150     QPalette p = qApp->palette();
1151     QColor baseColor;
1152     state = s;
1153     switch (state) {
1154         case emptyEditor:
1155             if (actionFormatRichText->isChecked())
1156                 e->setTextColor(colorRichTextDefaultForeground);
1157             else
1158                 e->setTextColor(p.color(QPalette::Text));
1159
1160         case filledEditor:
1161             if (actionFormatRichText->isChecked()) {
1162                 if (useColorMapBackground)
1163                     baseColor = colorMapBackground;
1164                 else
1165                     baseColor = colorRichTextDefaultBackground;
1166             } else {
1167                 baseColor = p.color(QPalette::Base);
1168             }
1169             e->setReadOnly(false);
1170             break;
1171         case inactiveEditor:
1172             baseColor = Qt::black;
1173             e->setReadOnly(true);
1174     }
1175     p.setColor(QPalette::Base, baseColor);
1176     e->setPalette(p);
1177
1178     updateActions();
1179 }
1180
1181 void TextEditor::updateState()
1182 {
1183     //qDebug() << "TE::updateState" << editorName;
1184     if (isEmpty())
1185         setState(emptyEditor);
1186     else
1187         setState(filledEditor);
1188 }
1189
1190 void TextEditor::selectColorRichTextDefaultBackground()
1191 {
1192     QColor col = QColorDialog::getColor(colorRichTextDefaultBackground, nullptr);
1193     if (!col.isValid())
1194         return;
1195     colorRichTextDefaultBackground = col;
1196     QPixmap pix(16, 16);
1197     pix.fill(colorRichTextDefaultBackground);
1198     actionFilledEditorColor->setIcon(pix);
1199 }
1200
1201 void TextEditor::selectColorRichTextDefaultForeground()
1202 {
1203     QColor col = QColorDialog::getColor(colorRichTextDefaultForeground, nullptr);
1204     if (!col.isValid())
1205         return;
1206     setColorRichTextDefaultForeground(col);
1207 }
1208
1209 void TextEditor::setColorRichTextDefaultForeground(const QColor &col)
1210 {
1211     if (!col.isValid()) return;
1212
1213     colorRichTextDefaultForeground = col;
1214     QPixmap pix(16, 16);
1215     pix.fill(colorRichTextDefaultForeground);
1216     actionFontColor->setIcon(pix);
1217 }
1218
1219 void TextEditor::setColorMapBackground(const QColor &col)
1220 {
1221     colorMapBackground = col;
1222 }
1223
1224 void TextEditor::setUseColorMapBackground(bool b)
1225 {
1226     useColorMapBackground = b;
1227 }