]> git.sven.stormbind.net Git - sven/vym.git/blob - src/texteditor.cpp
Replace Pierre as the maintainer
[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     switchboard.addSwitch("textExportAsASCII", shortcutScope, a, tag);
328     connect(a, SIGNAL(triggered()), this, SLOT(textExportAsASCII()));
329     fileMenu->addAction(a);
330     addAction(a);
331     actionFileSaveAs = a;
332
333     fileMenu->addSeparator();
334     a = new QAction(QPixmap(":/fileprint.png"), tr("&Print..."), this);
335     a->setShortcut(Qt::CTRL + Qt::Key_P);
336     switchboard.addSwitch("textPrint", shortcutScope, a, tag);
337     connect(a, SIGNAL(triggered()), this, SLOT(textPrint()));
338     tb->addAction(a);
339     fileMenu->addAction(a);
340     actionFilePrint = a;
341
342     a = new QAction(QPixmap(":/edittrash.png"), tr("&Delete All"), this);
343     connect(a, SIGNAL(triggered()), this, SLOT(deleteAll()));
344     fileMenu->addAction(a);
345     tb->addAction(a);
346     actionFileDeleteAll = a;
347 }
348
349 void TextEditor::setupEditActions()
350 {
351     QString tag = tr("Texteditor", "Shortcuts");
352     QToolBar *editToolBar = addToolBar(tr("Edit Actions"));
353     editToolBar->setObjectName("noteEditorEditActions");
354     editToolBar->hide();
355     QMenu *editMenu = menuBar()->addMenu(tr("Edi&t"));
356
357     QAction *a;
358     a = new QAction(QPixmap(":/undo.png"), tr("&Undo"), this);
359     a->setShortcut(Qt::CTRL + Qt::Key_Z);
360     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
361     switchboard.addSwitch("textUndo", shortcutScope, a, tag);
362     connect(a, SIGNAL(triggered()), e, SLOT(undo()));
363     editMenu->addAction(a);
364     editToolBar->addAction(a);
365     actionEditUndo = a;
366
367     a = new QAction(QPixmap(":/redo.png"), tr("&Redo"), this);
368     a->setShortcut(Qt::CTRL + Qt::Key_Y);
369     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
370     switchboard.addSwitch("textRedo", shortcutScope, a, tag);
371     connect(a, SIGNAL(triggered()), e, SLOT(redo()));
372     editMenu->addAction(a);
373     editToolBar->addAction(a);
374     actionEditRedo = a;
375
376     editMenu->addSeparator();
377     a = new QAction(QPixmap(), tr("Select and copy &all"), this);
378     a->setShortcutContext(Qt::WidgetShortcut);
379     a->setShortcut(Qt::CTRL + Qt::Key_A);
380     switchboard.addSwitch("textCopyAll", shortcutScope, a, tag);
381     connect(a, SIGNAL(triggered()), this, SLOT(editCopyAll()));
382     editMenu->addAction(a);
383
384     editMenu->addSeparator();
385     a = new QAction(QPixmap(":/editcopy.png"), tr("&Copy"), this);
386     a->setShortcut(Qt::CTRL + Qt::Key_C);
387     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
388     switchboard.addSwitch("textCopy", shortcutScope, a, tag);
389     connect(a, SIGNAL(triggered()), e, SLOT(copy()));
390     editMenu->addAction(a);
391     editToolBar->addAction(a);
392     actionEditCopy = a;
393
394     a = new QAction(QPixmap(":/editcut.png"), tr("Cu&t"), this);
395     a->setShortcut(Qt::CTRL + Qt::Key_X);
396     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
397     switchboard.addSwitch("textCut", shortcutScope, a, tag);
398     connect(a, SIGNAL(triggered()), e, SLOT(cut()));
399     editMenu->addAction(a);
400     editToolBar->addAction(a);
401     actionEditCut = a;
402
403     a = new QAction(QPixmap(":/editpaste.png"), tr("&Paste"), this);
404     a->setShortcut(Qt::CTRL + Qt::Key_V);
405     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
406     switchboard.addSwitch("textPaste", shortcutScope, a, tag);
407     connect(a, SIGNAL(triggered()), e, SLOT(paste()));
408     editMenu->addAction(a);
409     editToolBar->addAction(a);
410     actionEditPaste = a;
411 }
412
413 void TextEditor::setupFormatActions()
414 {
415     QString tag = tr("Texteditor", "Shortcuts");
416     fontHintsToolBar =
417         addToolBar(tr("Font hints", "toolbar in texteditor"));
418     fontHintsToolBar->setObjectName("noteEditorFontToolBar");
419     QMenu *formatMenu = menuBar()->addMenu(tr("F&ormat"));
420
421     QAction *a;
422
423     a = new QAction(QPixmap(":/formatfixedfont.png"), tr("&Font hint"), this);
424     a->setShortcut(Qt::CTRL + Qt::Key_H);
425     a->setCheckable(true);
426     a->setChecked(
427         settings.value("/noteeditor/fonts/useFixedByDefault", false).toBool());
428     switchboard.addSwitch("textToggleFonthint", shortcutScope, a, tag);
429     connect(a, SIGNAL(triggered()), this, SLOT(toggleFonthint()));
430     formatMenu->addAction(a);
431     fontHintsToolBar->addAction(a);
432     actionFormatUseFixedFont = a;
433
434     // Original icon: ./share/icons/oxygen/22x22/actions/format-text-color.png
435     a = new QAction(QPixmap(":/formatrichtext.png"), tr("&Richtext"), this);
436     a->setShortcut(Qt::CTRL + Qt::Key_R);
437     //    a->setShortcutContext (Qt::WidgetShortcut);
438     a->setCheckable(true);
439     switchboard.addSwitch("textToggleRichText", shortcutScope, a, tag);
440     connect(a, SIGNAL(triggered()), this, SLOT(toggleRichText()));
441     formatMenu->addAction(a);
442     fontHintsToolBar->addAction(a);
443     actionFormatRichText = a;
444
445     fontToolBar = addToolBar(tr("Fonts", "toolbar in texteditor"));
446     fontToolBar->setObjectName("noteEditorFontToolBar");
447
448     comboFont = new QComboBox;
449     fontToolBar->addWidget(comboFont);
450     QFontDatabase fontDB;
451     comboFont->insertItems(0, fontDB.families());
452     connect(comboFont, SIGNAL(activated(const QString &)), this,
453             SLOT(textFamily(const QString &)));
454
455     comboSize = new QComboBox;
456     fontToolBar->addWidget(comboSize);
457     QList<int> sizes = fontDB.standardSizes();
458     QList<int>::iterator it = sizes.begin();
459     int i = 0;
460     while (it != sizes.end()) {
461         i++;
462         ++it; // increment i before using it
463         comboSize->insertItem(i, QString::number(*it));
464     }
465     connect(comboSize, SIGNAL(activated(const QString &)), this,
466             SLOT(textSize(const QString &)));
467
468     formatMenu->addSeparator();
469
470     formatToolBar = addToolBar(tr("Format", "toolbar in texteditor"));
471     formatToolBar->setObjectName("noteEditorFormatToolBar");
472
473     QPixmap pix(16, 16);
474     pix.fill(e->textColor());
475     a = new QAction(pix, tr("&Color..."), this);
476     formatMenu->addAction(a);
477     formatToolBar->addAction(a);
478     connect(a, SIGNAL(triggered()), this, SLOT(textColor()));
479     actionTextColor = a;
480
481     a = new QAction(QPixmap(":/text_bold.png"), tr("&Bold"), this);
482     a->setShortcut(Qt::CTRL + Qt::Key_B);
483 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
484     switchboard.addSwitch("textToggleBold", shortcutScope, a, tag);
485     connect(a, SIGNAL(triggered()), this, SLOT(textBold()));
486     formatToolBar->addAction(a);
487     formatMenu->addAction(a);
488     a->setCheckable(true);
489     actionTextBold = a;
490
491     a = new QAction(QPixmap(":/text_italic.png"), tr("&Italic"), this);
492     a->setShortcut(Qt::CTRL + Qt::Key_I);
493 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
494     switchboard.addSwitch("textToggleItalic", shortcutScope, a, tag);
495     connect(a, SIGNAL(triggered()), this, SLOT(textItalic()));
496     formatToolBar->addAction(a);
497     formatMenu->addAction(a);
498     a->setCheckable(true);
499     actionTextItalic = a;
500
501     a = new QAction(QPixmap(":/text_under.png"), tr("&Underline"), this);
502     a->setShortcut(Qt::CTRL + Qt::Key_U);
503 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
504     switchboard.addSwitch("textToggleUnderline", shortcutScope, a, tag);
505     connect(a, SIGNAL(triggered()), this, SLOT(textUnderline()));
506     formatToolBar->addAction(a);
507     formatMenu->addAction(a);
508     a->setCheckable(true);
509     // richTextWidgets.append((QWidget*)a);
510     actionTextUnderline = a;
511     formatMenu->addSeparator();
512
513     QActionGroup *actGrp2 = new QActionGroup(this);
514     actGrp2->setExclusive(true);
515     a = new QAction(QPixmap(":/text_sub.png"), tr("Subs&cript"), actGrp2);
516     a->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B);
517 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
518     a->setCheckable(true);
519     formatToolBar->addAction(a);
520     formatMenu->addAction(a);
521     switchboard.addSwitch("textToggleSub", shortcutScope, a, tag);
522     connect(a, SIGNAL(triggered()), this, SLOT(textVAlign()));
523     actionAlignSubScript = a;
524
525     a = new QAction(QPixmap(":/text_super.png"), tr("Su&perscript"), actGrp2);
526     a->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_P);
527 //    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
528     a->setCheckable(true);
529     formatToolBar->addAction(a);
530     formatMenu->addAction(a);
531     switchboard.addSwitch("textToggleSuper", shortcutScope, a, tag);
532     connect(a, SIGNAL(triggered()), this, SLOT(textVAlign()));
533     actionAlignSuperScript = a;
534     QActionGroup *grp = new QActionGroup(this);
535     connect(grp, SIGNAL(triggered(QAction *)), this,
536             SLOT(textAlign(QAction *)));
537
538     formatMenu->addSeparator();
539
540     a = new QAction(QPixmap(":/text_left.png"), tr("&Left"), grp);
541     // a->setShortcut( Qt::CTRL+Qt::Key_L );
542     a->setCheckable(true);
543     formatToolBar->addAction(a);
544     formatMenu->addAction(a);
545     actionAlignLeft = a;
546     a = new QAction(QPixmap(":/text_center.png"), tr("C&enter"), grp);
547     // a->setShortcut(  Qt::CTRL + Qt::Key_E);
548     a->setCheckable(true);
549     formatToolBar->addAction(a);
550     formatMenu->addAction(a);
551     actionAlignCenter = a;
552     a = new QAction(QPixmap(":/text_right.png"), tr("&Right"), grp);
553     // a->setShortcut(Qt::CTRL + Qt::Key_R );
554     a->setCheckable(true);
555     formatToolBar->addAction(a);
556     formatMenu->addAction(a);
557     actionAlignRight = a;
558     a = new QAction(QPixmap(":/text_block.png"), tr("&Justify"), grp);
559     // a->setShortcut(Qt::CTRL + Qt::Key_J );
560     a->setCheckable(true);
561     formatToolBar->addAction(a);
562     formatMenu->addAction(a);
563     actionAlignJustify = a;
564 }
565
566 void TextEditor::setupSettingsActions()
567 {
568     QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
569
570     QAction *a;
571     a = new QAction(tr("Set &fixed font"), this);
572     connect(a, SIGNAL(triggered()), this, SLOT(setFixedFont()));
573     settingsMenu->addAction(a);
574     actionSettingsFixedFont = a;
575
576     a = new QAction(tr("Set &variable font"), this);
577     connect(a, SIGNAL(triggered()), this, SLOT(setVarFont()));
578     settingsMenu->addAction(a);
579     actionSettingsVarFont = a;
580
581     a = new QAction(tr("&fixed font is default"), this);
582     a->setCheckable(true);
583     // set state later in constructor...
584     settingsMenu->addAction(a);
585     actionSettingsFonthintDefault = a;
586
587     settingsMenu->addSeparator();
588
589     a = new QAction(
590         tr("Set RichText default background color", "TextEditor") + "...", this);
591     settingsMenu->addAction(a);
592     connect(a, SIGNAL(triggered()), this, SLOT(selectColorRichTextDefaultBackground()));
593     actionFilledEditorColor = a;
594
595     a = new QAction(tr("Set RichText default font color", "TextEditor") + "...", this);
596     settingsMenu->addAction(a);
597     connect(a, SIGNAL(triggered()), this, SLOT(selectColorRichTextDefaultForeground()));
598     actionFontColor = a;
599 }
600
601 void TextEditor::textLoad()
602 {
603     if (state != inactiveEditor) {
604         if (!isEmpty()) {
605             QMessageBox mb(vymName + " - " + tr("Note Editor"),
606                            "Loading will overwrite the existing note",
607                            QMessageBox::Warning,
608                            QMessageBox::Yes | QMessageBox::Default,
609                            QMessageBox::Cancel, 0);
610             mb.setButtonText(QMessageBox::Yes, "Load note");
611             switch (mb.exec()) {
612             case QMessageBox::Cancel:
613                 return;
614                 break;
615             }
616         }
617         // Load note
618         QFileDialog *fd = new QFileDialog(this);
619         QStringList types;
620         types << "Text (*.txt *.html)"
621               << "VYM notes and HTML (*.html)"
622               << "ASCII texts (*.txt)"
623               << "All files (*)";
624         fd->setNameFilters(types);
625         fd->setDirectory(QDir().current());
626         fd->show();
627         QString fn;
628         if (fd->exec() == QDialog::Accepted && !fd->selectedFiles().isEmpty())
629             fn = fd->selectedFiles().first();
630
631         if (!fn.isEmpty()) {
632             QFile f(fn);
633             if (!f.open(QIODevice::ReadOnly))
634                 return;
635
636             QTextStream ts(&f);
637             setTextAuto(ts.readAll());
638             editorChanged();
639         }
640     }
641 }
642
643 void TextEditor::closeEvent(QCloseEvent *ce)
644 {
645     ce->accept(); // TextEditor can be reopened with show()
646     hide();
647     emit(windowClosed());
648     return;
649 }
650
651 bool TextEditor::eventFilter(QObject *obj, QEvent *ev)
652 {
653     if (obj == e) {
654         if (ev->type() == QEvent::KeyPress) {
655             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
656             if (keyEvent == QKeySequence::Paste) {
657                 // switch editor mode to match clipboard content before pasting
658                 const QClipboard *clipboard = QApplication::clipboard();
659                 const QMimeData *mimeData = clipboard->mimeData();
660
661                 if (mimeData->hasHtml() && !actionFormatRichText->isChecked())
662                     setRichTextMode(true);
663             }
664         }
665     }
666     // pass the event on to the parent class
667     return QMainWindow::eventFilter(obj, ev);
668 }
669
670 void TextEditor::editorChanged()
671 {
672     //qDebug() << "TE::editorChanged" << editorName << "blockChanged: " << blockChangedSignal;
673     EditorState oldState = state;
674     if (isEmpty())
675         state = emptyEditor;
676     else
677         state = filledEditor;
678
679     if (!blockChangedSignal) {
680         blockTextUpdate = true;
681         emit(textHasChanged(getVymText()));
682         blockTextUpdate = false;
683     }
684
685     if (state == oldState)
686         return;
687
688     updateState();
689 }
690
691 void TextEditor::setRichText(const QString &t)
692 {
693     blockChangedSignal = true;
694     e->setReadOnly(false);
695     e->setHtml(t);
696     actionFormatRichText->setChecked(true);
697
698     // Update state including colors
699     updateState();
700
701     updateActions();
702     blockChangedSignal = false;
703 }
704
705 void TextEditor::setPlainText(const QString &t)
706 {
707     blockChangedSignal = true;
708     e->setReadOnly(false);
709
710     e->setPlainText(t);
711     actionFormatRichText->setChecked(false);
712
713     // Reset also text format
714     QTextCharFormat textformat;
715     textformat.setForeground(qApp->palette().color(QPalette::WindowText));
716     textformat.setFont(varFont);
717     e->setCurrentCharFormat(textformat);
718
719     // Update state including colors
720     updateState();
721
722     updateActions();
723     blockChangedSignal = false;
724 }
725
726 void TextEditor::setTextAuto(const QString &t)
727 {
728     if (Qt::mightBeRichText(t))
729         setRichText(t);
730     else
731         setPlainText(t);
732 }
733
734 void TextEditor::setVymText(const VymText &vt)
735 {
736     // While a note is being edited, we are sending textHasChanged
737     // Then we don't want to update the text additionally from outside,
738     // as this would position cursor at beginning of text
739     if (blockTextUpdate) return;
740
741     if (vt.getText() == getText()) return;
742
743     if (vt.isRichText())
744         setRichText(vt.getText());
745     else
746         setPlainText(vt.getText());
747 }
748
749 void TextEditor::setInactive()
750 {
751     setState(inactiveEditor);
752 }
753
754 void TextEditor::editCopyAll()
755 {
756     e->selectAll();
757     e->copy();
758 }
759
760 void TextEditor::clear()
761 {
762     //qDebug() << "TE::clear" << editorName;
763     bool blockChangedOrg = blockChangedSignal;
764
765     blockChangedSignal = true;
766     e->clear();
767     setState(emptyEditor);
768
769     blockChangedSignal = blockChangedOrg;
770 }
771
772 void TextEditor::deleteAll()
773 {
774     e->clear();
775 }
776
777 void TextEditor::textSaveAs()
778 {
779     QString caption = tr("Export Note to single file");
780     QString fn = QFileDialog::getSaveFileName(
781         this, caption, QString(), "VYM Note (HTML) (*.html);;All files (*)",
782         0, QFileDialog::DontConfirmOverwrite);
783
784     if (!fn.isEmpty()) {
785         QFile file(fn);
786         if (file.exists()) {
787             QMessageBox mb(
788                 vymName,
789                 tr("The file %1\nexists already.\nDo you want to overwrite it?",
790                    "dialog 'save note as'")
791                     .arg(fn),
792                 QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
793                 QMessageBox::Cancel | QMessageBox::Escape, Qt::NoButton);
794             mb.setButtonText(QMessageBox::Yes, tr("Overwrite"));
795             mb.setButtonText(QMessageBox::No, tr("Cancel"));
796             switch (mb.exec()) {
797             case QMessageBox::Yes:
798                 // save
799                 filename = fn;
800                 textSave();
801                 return;
802             case QMessageBox::Cancel:
803                 // do nothing
804                 break;
805             }
806         }
807         else {
808             filename = fn;
809             textSave();
810             return;
811         }
812     }
813     statusBar()->showMessage(
814         tr("Couldn't export note ", "dialog 'save note as'") + fn,
815         statusbarTime);
816 }
817
818 void TextEditor::textSave()
819 {
820     if (filename.isEmpty()) {
821         textSaveAs();
822         return;
823     }
824
825     QString text = e->toHtml(); // FIXME-4 or plaintext? check...
826     QFile f(filename);
827     if (!f.open(QIODevice::WriteOnly)) {
828         statusBar()->showMessage(QString("Could not write to %1").arg(filename),
829                                  statusbarTime);
830         return;
831     }
832
833     QTextStream t(&f);
834     t.setCodec("UTF-8");
835     t << text;
836     f.close();
837
838     e->document()->setModified(false);
839
840     statusBar()->showMessage(QString("Note exported as %1").arg(filename),
841                              statusbarTime);
842 }
843
844 void TextEditor::textExportAsASCII()
845 {
846     QString fn, s;
847     if (!filenameHint.isEmpty()) {
848         if (!filenameHint.contains(".txt"))
849             s = filenameHint + ".txt";
850         else
851             s = filenameHint;
852     }
853     else
854         s = QString();
855     QString caption = tr("Export Note to single file (ASCII)");
856     fn = QFileDialog::getSaveFileName(
857         this, caption, s, "VYM Note (ASCII) (*.txt);;All files (*)");
858     int ret = -1;
859
860     if (!fn.isEmpty()) {
861         QFile file(fn);
862         if (file.exists()) {
863             QMessageBox mb(
864                 vymName,
865                 tr("The file %1\nexists already.\nDo you want to overwrite it?",
866                    "dialog 'save note as'")
867                     .arg(fn),
868                 QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
869                 QMessageBox::Cancel | QMessageBox::Escape, Qt::NoButton);
870             mb.setButtonText(QMessageBox::Yes, tr("Overwrite"));
871             mb.setButtonText(QMessageBox::No, tr("Cancel"));
872             ret = mb.exec();
873         }
874         if (ret == QMessageBox::Cancel)
875             return;
876
877         // save
878         if (!file.open(QIODevice::WriteOnly))
879             statusBar()->showMessage(
880                 QString("Could not write to %1").arg(filename), statusbarTime);
881         else {
882             QTextStream t(&file);
883             t << getVymText().getTextASCII();
884             file.close();
885
886             statusBar()->showMessage(QString("Note exported as %1").arg(fn),
887                                      statusbarTime);
888         }
889     }
890 }
891
892 void TextEditor::textPrint()
893 {
894     QTextDocument *document = e->document();
895
896     if (!printer)
897         mainWindow->setupPrinter();
898
899     QPrintDialog dialog(printer, this);
900     dialog.setWindowTitle(tr("Print", "TextEditor"));
901     if (dialog.exec() != QDialog::Accepted)
902         return;
903
904     document->print(printer);
905 }
906
907 void TextEditor::textEditUndo() {}
908
909 void TextEditor::toggleFonthint()
910 {
911     if (!actionFormatUseFixedFont->isChecked()) {
912         e->setCurrentFont(varFont);
913         setFont(varFont);
914     }
915     else {
916         e->setCurrentFont(fixedFont);
917         setFont(fixedFont);
918     }
919     emit(textHasChanged(getVymText()));
920 }
921
922 void TextEditor::setRichTextMode(bool b)
923 {
924     //qDebug() << "TE::setRichTextMode b=" << b;
925     actionFormatUseFixedFont->setEnabled(false);
926     if (b) {
927         setRichText(e->toHtml());
928
929         // Use default foreground color for all text when switching to RichText
930         QTextCursor cursor = e->textCursor();
931         e->selectAll();
932         e->setTextColor(colorRichTextDefaultForeground);
933         e->setTextCursor(cursor);
934         
935     } else {
936         setPlainText(e->toPlainText());
937     }
938     emit(textHasChanged(getVymText()));
939 }
940
941 void TextEditor::toggleRichText()
942 {
943     if (actionFormatRichText->isChecked())
944         setRichTextMode(true);
945     else
946         setRichTextMode(false);
947 }
948
949 void TextEditor::setFixedFont()
950 {
951     bool ok;
952     QFont font = QFontDialog::getFont(&ok, fixedFont, this);
953     if (ok)
954         fixedFont = font;
955 }
956
957 void TextEditor::setVarFont()
958 {
959     bool ok;
960     QFont font = QFontDialog::getFont(&ok, varFont, this);
961     if (ok)
962         varFont = font;
963 }
964
965 void TextEditor::textBold()
966 {
967     if (actionTextBold->isChecked())
968         e->setFontWeight(QFont::Bold);
969     else
970         e->setFontWeight(QFont::Normal);
971 }
972
973 void TextEditor::textUnderline()
974 {
975     e->setFontUnderline(actionTextUnderline->isChecked());
976 }
977
978 void TextEditor::textItalic()
979 {
980     e->setFontItalic(actionTextItalic->isChecked());
981 }
982
983 void TextEditor::textFamily(const QString &f) { e->setFontFamily(f); }
984
985 void TextEditor::textSize(const QString &p) { e->setFontPointSize(p.toInt()); }
986
987 void TextEditor::textColor()
988 {
989     QColor col = QColorDialog::getColor(e->textColor(), this);
990     if (!col.isValid())
991         return;
992     e->setTextColor(col);
993     /*
994     QPixmap pix( 16, 16 );
995     pix.fill( col );
996     actionTextColor->setIcon( pix );
997     */
998 }
999
1000 void TextEditor::textAlign(QAction *a)
1001 {
1002     QTextCursor c = e->textCursor();
1003
1004     if (a == actionAlignLeft)
1005         e->setAlignment(Qt::AlignLeft);
1006     else if (a == actionAlignCenter)
1007         e->setAlignment(Qt::AlignHCenter);
1008     else if (a == actionAlignRight)
1009         e->setAlignment(Qt::AlignRight);
1010     else if (a == actionAlignJustify)
1011         e->setAlignment(Qt::AlignJustify);
1012 }
1013
1014 void TextEditor::textVAlign()
1015 {
1016     QTextCharFormat format;
1017
1018     if (sender() == actionAlignSuperScript &&
1019         actionAlignSuperScript->isChecked()) {
1020         format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
1021     }
1022     else if (sender() == actionAlignSubScript &&
1023              actionAlignSubScript->isChecked()) {
1024         format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
1025     }
1026     else {
1027         format.setVerticalAlignment(QTextCharFormat::AlignNormal);
1028     }
1029     e->mergeCurrentCharFormat(format);
1030 }
1031
1032 void TextEditor::fontChanged(const QFont &f)
1033 {
1034     int i = comboFont->findText(f.family());
1035     if (i >= 0)
1036         comboFont->setCurrentIndex(i);
1037     i = comboSize->findText(QString::number(f.pointSize()));
1038     if (i >= 0)
1039         comboSize->setCurrentIndex(i);
1040     actionTextBold->setChecked(f.bold());
1041     actionTextItalic->setChecked(f.italic());
1042     actionTextUnderline->setChecked(f.underline());
1043 }
1044
1045 void TextEditor::colorChanged(const QColor &c)
1046 {
1047     QPixmap pix(16, 16);
1048     pix.fill(c);
1049     actionTextColor->setIcon(pix);
1050 }
1051
1052 void TextEditor::formatChanged(const QTextCharFormat &f)
1053 {
1054     if (!actionFormatRichText->isChecked())
1055         return;
1056     fontChanged(f.font());
1057     colorChanged(f.foreground().color());
1058     alignmentChanged(e->alignment());
1059     verticalAlignmentChanged(f.verticalAlignment());
1060 }
1061
1062 void TextEditor::alignmentChanged(int a)
1063 {
1064     if ((a == Qt::AlignLeft) || (a & Qt::AlignLeft))
1065         actionAlignLeft->setChecked(true);
1066     else if ((a & Qt::AlignHCenter))
1067         actionAlignCenter->setChecked(true);
1068     else if ((a & Qt::AlignRight))
1069         actionAlignRight->setChecked(true);
1070     else if ((a & Qt::AlignJustify))
1071         actionAlignJustify->setChecked(true);
1072 }
1073
1074 void TextEditor::verticalAlignmentChanged(QTextCharFormat::VerticalAlignment a)
1075 {
1076     actionAlignSubScript->setChecked(false);
1077     actionAlignSuperScript->setChecked(false);
1078     switch (a) {
1079     case QTextCharFormat::AlignSuperScript:
1080         actionAlignSuperScript->setChecked(true);
1081         break;
1082     case QTextCharFormat::AlignSubScript:
1083         actionAlignSubScript->setChecked(true);
1084         break;
1085     default:;
1086     }
1087 }
1088
1089 void TextEditor::updateActions()
1090 {
1091     bool b;
1092     b = (state == inactiveEditor) ? false : true;
1093
1094     actionFileLoad->setEnabled(b);
1095     actionFileSave->setEnabled(b);
1096     actionFileSaveAs->setEnabled(b);
1097     actionFilePrint->setEnabled(b);
1098     actionFileDeleteAll->setEnabled(b);
1099     actionEditUndo->setEnabled(b);
1100     actionEditRedo->setEnabled(b);
1101     actionEditCopy->setEnabled(b);
1102     actionEditCut->setEnabled(b);
1103     actionEditPaste->setEnabled(b);
1104     actionFormatUseFixedFont->setEnabled(b);
1105     actionFormatRichText->setEnabled(b);
1106
1107     if (!actionFormatRichText->isChecked() || !b) {
1108         comboFont->setEnabled(false);
1109         comboSize->setEnabled(false);
1110         fontToolBar->hide();
1111         formatToolBar->hide();
1112         actionTextColor->setEnabled(false);
1113         actionTextBold->setEnabled(false);
1114         actionTextUnderline->setEnabled(false);
1115         actionTextItalic->setEnabled(false);
1116         actionTextColor->setEnabled(false);
1117         actionAlignSubScript->setEnabled(false);
1118         actionAlignSuperScript->setEnabled(false);
1119         actionAlignLeft->setEnabled(false);
1120         actionAlignCenter->setEnabled(false);
1121         actionAlignRight->setEnabled(false);
1122         actionAlignJustify->setEnabled(false);
1123     }
1124     else {
1125         comboFont->setEnabled(true);
1126         comboSize->setEnabled(true);
1127         fontToolBar->show();
1128         formatToolBar->show();
1129         actionTextColor->setEnabled(true);
1130         actionTextBold->setEnabled(true);
1131         actionTextUnderline->setEnabled(true);
1132         actionTextItalic->setEnabled(true);
1133         actionTextColor->setEnabled(true);
1134         actionAlignSubScript->setEnabled(true);
1135         actionAlignSuperScript->setEnabled(true);
1136         actionAlignLeft->setEnabled(true);
1137         actionAlignCenter->setEnabled(true);
1138         actionAlignRight->setEnabled(true);
1139         actionAlignJustify->setEnabled(true);
1140         actionFormatUseFixedFont->setEnabled(false);
1141     }
1142 }
1143
1144 void TextEditor::setState(EditorState s) // FIXME-2 called 12x when reselecting once in ME
1145                                          // 5 alone for HeadingEditor
1146 {
1147     //qDebug() << "TE::setState" << s << editorName;
1148     QPalette p = qApp->palette();
1149     QColor baseColor;
1150     state = s;
1151     switch (state) {
1152         case emptyEditor:
1153             if (actionFormatRichText->isChecked())
1154                 e->setTextColor(colorRichTextDefaultForeground);
1155             else
1156                 e->setTextColor(p.color(QPalette::Text));
1157
1158         case filledEditor:
1159             if (actionFormatRichText->isChecked()) {
1160                 if (useColorMapBackground)
1161                     baseColor = colorMapBackground;
1162                 else
1163                     baseColor = colorRichTextDefaultBackground;
1164             } else {
1165                 baseColor = p.color(QPalette::Base);
1166             }
1167             e->setReadOnly(false);
1168             break;
1169         case inactiveEditor:
1170             baseColor = Qt::black;
1171             e->setReadOnly(true);
1172     }
1173     p.setColor(QPalette::Base, baseColor);
1174     e->setPalette(p);
1175
1176     updateActions();
1177 }
1178
1179 void TextEditor::updateState()
1180 {
1181     //qDebug() << "TE::updateState" << editorName;
1182     if (isEmpty())
1183         setState(emptyEditor);
1184     else
1185         setState(filledEditor);
1186 }
1187
1188 void TextEditor::selectColorRichTextDefaultBackground()
1189 {
1190     QColor col = QColorDialog::getColor(colorRichTextDefaultBackground, nullptr);
1191     if (!col.isValid())
1192         return;
1193     colorRichTextDefaultBackground = col;
1194     QPixmap pix(16, 16);
1195     pix.fill(colorRichTextDefaultBackground);
1196     actionFilledEditorColor->setIcon(pix);
1197 }
1198
1199 void TextEditor::selectColorRichTextDefaultForeground()
1200 {
1201     QColor col = QColorDialog::getColor(colorRichTextDefaultForeground, nullptr);
1202     if (!col.isValid())
1203         return;
1204     setColorRichTextDefaultForeground(col);
1205 }
1206
1207 void TextEditor::setColorRichTextDefaultForeground(const QColor &col)
1208 {
1209     if (!col.isValid()) return;
1210
1211     colorRichTextDefaultForeground = col;
1212     QPixmap pix(16, 16);
1213     pix.fill(colorRichTextDefaultForeground);
1214     actionFontColor->setIcon(pix);
1215 }
1216
1217 void TextEditor::setColorMapBackground(const QColor &col)
1218 {
1219     colorMapBackground = col;
1220 }
1221
1222 void TextEditor::setUseColorMapBackground(bool b)
1223 {
1224     useColorMapBackground = b;
1225 }