1 /****************************************************************************
2 *   Copyright (C) 2015 by Jens Nissen jens-chessx@gmx.net                   *
3 ****************************************************************************/
4 /****************************************************************************
5 **
6 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
7 ** Contact: http://www.qt-project.org/legal
8 **
9 ** This file is part of the demonstration applications of the Qt Toolkit.
10 **
11 ** $QT_BEGIN_LICENSE:LGPL21$
12 ** Commercial License Usage
13 ** Licensees holding valid commercial Qt licenses may use this file in
14 ** accordance with the commercial license agreement provided with the
15 ** Software or, alternatively, in accordance with the terms contained in
16 ** a written agreement between you and Digia. For licensing terms and
17 ** conditions see http://qt.digia.com/licensing. For further information
18 ** use the contact form at http://qt.digia.com/contact-us.
19 **
20 ** GNU Lesser General Public License Usage
21 ** Alternatively, this file may be used under the terms of the GNU Lesser
22 ** General Public License version 2.1 or version 3 as published by the Free
23 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
24 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
25 ** following information to ensure the GNU Lesser General Public License
26 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
27 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
28 **
29 ** In addition, as a special exception, Digia gives you certain additional
30 ** rights. These rights are described in the Digia Qt LGPL Exception
31 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include <QAction>
38 #include <QApplication>
39 #include <QClipboard>
40 #include <QColorDialog>
41 #include <QComboBox>
42 #include <QDockWidget>
43 #include <QFontComboBox>
44 #include <QFile>
45 #include <QFileDialog>
46 #include <QFileInfo>
47 #include <QFontDatabase>
48 #include <QImageReader>
49 #include <QSpinBox>
50 #include <QMenu>
51 #include <QMenuBar>
52 #include <QTextCodec>
53 #include <QTextEdit>
54 #include <QToolBar>
55 #include <QTextCursor>
56 #include <QTextDocumentWriter>
57 #include <QTextList>
58 #include <QtDebug>
59 #include <QMessageBox>
60 #include <QMimeData>
61 #ifndef QT_NO_PRINTER
62 #include <QPrintDialog>
63 #include <QPrinter>
64 #include <QPrintPreviewDialog>
65 #endif
66 
67 #include "GameMimeData.h"
68 #include "settings.h"
69 #include "textedit.h"
70 #include "ooo/converter.h"
71 
72 #if defined(_MSC_VER) && defined(_DEBUG)
73 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
74 #define new DEBUG_NEW
75 #endif // _MSC_VER
76 
77 const QString rsrcPath = ":/textedit";
78 
TextEdit(QWidget * parent,QMenu * menu)79 TextEdit::TextEdit(QWidget *parent, QMenu *menu)
80     : ToolMainWindow(parent)
81 {
82     setObjectName("Scratchpad");
83     textEdit = new PasteTextEdit(this);
84 
85     setupFileActions(menu);
86     setupEditActions(menu);
87     setupTextActions(menu);
88     setupStyleActions();
89 
90     connect(textEdit, SIGNAL(currentCharFormatChanged(QTextCharFormat)),
91             this, SLOT(currentCharFormatChanged(QTextCharFormat)));
92     connect(textEdit, SIGNAL(cursorPositionChanged()),
93             this, SLOT(cursorPositionChanged()));
94     connect(textEdit, SIGNAL(linkActivated(QString)), this, SIGNAL(linkActivated(QString)));
95 
96     setCentralWidget(textEdit);
97     textEdit->setFocus();
98     setCurrentFileName(QString());
99 
100     QFont textFont("Helvetica");
101     textFont.setStyleHint(QFont::SansSerif);
102     textEdit->setFont(textFont);
103     fontChanged(textEdit->font());
104     colorChanged(textEdit->textColor());
105     alignmentChanged(textEdit->alignment());
106 
107     connect(actionUndo, SIGNAL(triggered()), textEdit, SLOT(undo()));
108     connect(actionRedo, SIGNAL(triggered()), textEdit, SLOT(redo()));
109 
110     actionCut->setEnabled(false);
111     actionCopy->setEnabled(false);
112 
113     connect(actionCut, SIGNAL(triggered()), textEdit, SLOT(cut()));
114     connect(actionCopy, SIGNAL(triggered()), textEdit, SLOT(copy()));
115     connect(actionPaste, SIGNAL(triggered()), textEdit, SLOT(paste()));
116 
117     connect(textEdit, SIGNAL(copyAvailable(bool)), actionCut, SLOT(setEnabled(bool)));
118     connect(textEdit, SIGNAL(copyAvailable(bool)), actionCopy, SLOT(setEnabled(bool)));
119 
120     connect(actionPickBoard, SIGNAL(triggered()), this, SLOT(pickBoard()));
121 
122 #ifndef QT_NO_CLIPBOARD
123     connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
124 #endif
125 
126     textEdit->setUndoRedoEnabled(true);
127     fileNew();
128 }
129 
setupDocumentActions()130 void TextEdit::setupDocumentActions()
131 {
132     connect(textEdit->document(), SIGNAL(modificationChanged(bool)),
133             actionSave, SLOT(setEnabled(bool)), Qt::UniqueConnection);
134     connect(textEdit->document(), SIGNAL(modificationChanged(bool)),
135             this, SLOT(setWindowModified(bool)), Qt::UniqueConnection);
136     connect(textEdit->document(), SIGNAL(undoAvailable(bool)),
137             actionUndo, SLOT(setEnabled(bool)), Qt::UniqueConnection);
138     connect(textEdit->document(), SIGNAL(redoAvailable(bool)),
139             actionRedo, SLOT(setEnabled(bool)), Qt::UniqueConnection);
140 
141     textEdit->document()->clearUndoRedoStacks();
142 
143     setWindowModified(textEdit->document()->isModified());
144     actionSave->setEnabled(textEdit->document()->isModified());
145     actionUndo->setEnabled(textEdit->document()->isUndoAvailable());
146     actionRedo->setEnabled(textEdit->document()->isRedoAvailable());
147 }
148 
setupFileActions(QMenu * xmenu)149 void TextEdit::setupFileActions(QMenu* xmenu)
150 {
151     QToolBar *tb = new QToolBar(this);
152     tb->setObjectName("ScrpFileActions");
153     tb->setWindowTitle(tr("File Actions"));
154     addToolBar(tb);
155 
156     QMenu *menu = xmenu->addMenu(tr("File"));
157 
158     QAction *a;
159 
160     QIcon newIcon = QIcon(rsrcPath + "/filenew.png");
161     a = new QAction( newIcon, tr("New"), this);
162     a->setPriority(QAction::LowPriority);
163     connect(a, SIGNAL(triggered()), this, SLOT(fileNew()));
164     tb->addAction(a);
165     menu->addAction(a);
166 
167     a = new QAction(QIcon(rsrcPath + "/fileopen.png"),
168                     tr("Open..."), this);
169     connect(a, SIGNAL(triggered()), this, SLOT(fileOpen()));
170     tb->addAction(a);
171     menu->addAction(a);
172 
173     menu->addSeparator();
174 
175     actionSave = a = new QAction(QIcon(rsrcPath + "/filesave.png"),
176                                  tr("Save"), this);
177     connect(a, SIGNAL(triggered()), this, SLOT(fileSave()));
178     a->setEnabled(false);
179     tb->addAction(a);
180     menu->addAction(a);
181 
182     a = new QAction(tr("Save As..."), this);
183     a->setPriority(QAction::LowPriority);
184     connect(a, SIGNAL(triggered()), this, SLOT(fileSaveAs()));
185     menu->addAction(a);
186     menu->addSeparator();
187 
188 #ifndef QT_NO_PRINTER
189     a = new QAction(QIcon(rsrcPath + "/fileprint.png"),
190                     tr("Print..."), this);
191     a->setPriority(QAction::LowPriority);
192     connect(a, SIGNAL(triggered()), this, SLOT(filePrint()));
193     tb->addAction(a);
194     menu->addAction(a);
195 
196     a = new QAction(QIcon(rsrcPath + "/fileprint.png"),
197                     tr("Print Preview..."), this);
198     connect(a, SIGNAL(triggered()), this, SLOT(filePrintPreview()));
199     menu->addAction(a);
200 
201     a = new QAction(QIcon(rsrcPath + "/exportpdf.png"),
202                     tr("Export PDF..."), this);
203     a->setPriority(QAction::LowPriority);
204     connect(a, SIGNAL(triggered()), this, SLOT(filePrintPdf()));
205     tb->addAction(a);
206     menu->addAction(a);
207 
208     menu->addSeparator();
209 #endif
210 
211 }
212 
setupEditActions(QMenu * xmenu)213 void TextEdit::setupEditActions(QMenu* xmenu)
214 {
215     QToolBar *tb = new QToolBar(this);
216     tb->setObjectName("ScrpEditActions");
217     tb->setWindowTitle(tr("Edit Actions"));
218     addToolBar(tb);
219 
220     QMenu *menu = xmenu->addMenu(tr("Edit"));
221 
222     QAction *a;
223     a = actionUndo = new QAction(QIcon(rsrcPath + "/editundo.png"),
224                                               tr("Undo"), this);
225     tb->addAction(a);
226     menu->addAction(a);
227     a = actionRedo = new QAction(QIcon(rsrcPath + "/editredo.png"),
228                                               tr("Redo"), this);
229     a->setPriority(QAction::LowPriority);
230     tb->addAction(a);
231     menu->addAction(a);
232     menu->addSeparator();
233     a = actionCut = new QAction(QIcon(rsrcPath + "/editcut.png"),
234                                              tr("Cut"), this);
235     a->setPriority(QAction::LowPriority);
236     tb->addAction(a);
237     menu->addAction(a);
238     a = actionCopy = new QAction(QIcon(rsrcPath + "/editcopy.png"),
239                                  tr("Copy"), this);
240     a->setPriority(QAction::LowPriority);
241     tb->addAction(a);
242     menu->addAction(a);
243     a = actionPaste = new QAction(QIcon(rsrcPath + "/editpaste.png"),
244                                   tr("Paste"), this);
245     a->setPriority(QAction::LowPriority);
246     tb->addAction(a);
247     menu->addAction(a);
248     menu->addSeparator();
249 
250 #ifndef QT_NO_CLIPBOARD
251     if (const QMimeData *md = QApplication::clipboard()->mimeData())
252         actionPaste->setEnabled(canInsertFromMimeData(md));
253 #endif
254 
255     a = actionPickBoard = new QAction(QIcon(":/images/new_board.png"), tr("Pick board"), this);
256     a->setPriority(QAction::LowPriority);
257     tb->addAction(a);
258     menu->addAction(a);
259 }
260 
setupTextActions(QMenu * xmenu)261 void TextEdit::setupTextActions(QMenu *xmenu)
262 {
263     QToolBar *tb = new QToolBar(this);
264     tb->setObjectName("ScrpTextActions");
265     tb->setWindowTitle(tr("Format Actions"));
266     addToolBar(tb);
267 
268     QMenu *menu = xmenu->addMenu(tr("Format"));
269 
270     actionTextBold = new QAction(QIcon(rsrcPath + "/textbold.png"),
271                                  tr("Bold"), this);
272     actionTextBold->setPriority(QAction::LowPriority);
273     QFont bold;
274     bold.setBold(true);
275     actionTextBold->setFont(bold);
276     connect(actionTextBold, SIGNAL(triggered()), this, SLOT(textBold()));
277     tb->addAction(actionTextBold);
278     menu->addAction(actionTextBold);
279     actionTextBold->setCheckable(true);
280 
281     actionTextItalic = new QAction(QIcon(rsrcPath + "/textitalic.png"),
282                                    tr("Italic"), this);
283     actionTextItalic->setPriority(QAction::LowPriority);
284     QFont italic;
285     italic.setItalic(true);
286     actionTextItalic->setFont(italic);
287     connect(actionTextItalic, SIGNAL(triggered()), this, SLOT(textItalic()));
288     tb->addAction(actionTextItalic);
289     menu->addAction(actionTextItalic);
290     actionTextItalic->setCheckable(true);
291 
292     actionTextUnderline = new QAction(QIcon(rsrcPath + "/textunder.png"),
293                                       tr("Underline"), this);
294     actionTextUnderline->setPriority(QAction::LowPriority);
295     QFont underline;
296     underline.setUnderline(true);
297     actionTextUnderline->setFont(underline);
298     connect(actionTextUnderline, SIGNAL(triggered()), this, SLOT(textUnderline()));
299     tb->addAction(actionTextUnderline);
300     menu->addAction(actionTextUnderline);
301     actionTextUnderline->setCheckable(true);
302 
303     menu->addSeparator();
304     tb->addSeparator();
305 
306     actionImageSize = new QAction(QIcon(rsrcPath + "/editresize.png"),
307                                   tr("Resize"), this);
308     connect(actionImageSize, SIGNAL(triggered()), this, SLOT(imageResize()));
309 
310     imageSize = new QSpinBox(this);
311     imageSize->setMinimum(10);
312     imageSize->setMaximum(200);
313     imageSize->setValue(100);
314 
315     tb->addAction(actionImageSize);
316     tb->addWidget(imageSize);
317     menu->addAction(actionImageSize);
318 
319     menu->addSeparator();
320     tb->addSeparator();
321 
322     QActionGroup *grp = new QActionGroup(this);
323     connect(grp, SIGNAL(triggered(QAction*)), this, SLOT(textAlign(QAction*)));
324 
325     // Make sure the alignLeft  is always left of the alignRight
326     if (QApplication::isLeftToRight())
327     {
328         actionAlignLeft = new QAction(QIcon(rsrcPath + "/textleft.png"),
329                                       tr("Left"), grp);
330         actionAlignCenter = new QAction(QIcon(rsrcPath + "/textcenter.png"),
331                                         tr("Center"), grp);
332         actionAlignRight = new QAction(QIcon(rsrcPath + "/textright.png"),
333                                        tr("Right"), grp);
334     }
335     else
336     {
337         actionAlignRight = new QAction(QIcon(rsrcPath + "/textright.png"),
338                                        tr("Right"), grp);
339         actionAlignCenter = new QAction(QIcon(rsrcPath + "/textcenter.png"),
340                                         tr("Center"), grp);
341         actionAlignLeft = new QAction(QIcon(rsrcPath + "/textleft.png"),
342                                       tr("Left"), grp);
343     }
344     actionAlignJustify = new QAction(QIcon(rsrcPath + "/textjustify.png"),
345                                      tr("Justify"), grp);
346 
347     actionAlignLeft->setCheckable(true);
348     actionAlignLeft->setPriority(QAction::LowPriority);
349     actionAlignCenter->setCheckable(true);
350     actionAlignCenter->setPriority(QAction::LowPriority);
351     actionAlignRight->setCheckable(true);
352     actionAlignRight->setPriority(QAction::LowPriority);
353     actionAlignJustify->setCheckable(true);
354     actionAlignJustify->setPriority(QAction::LowPriority);
355 
356     tb->addActions(grp->actions());
357     menu->addActions(grp->actions());
358 
359     menu->addSeparator();
360 
361     QPixmap pix(16, 16);
362     pix.fill(Qt::black);
363     actionTextColor = new QAction(pix, tr("Color..."), this);
364     connect(actionTextColor, SIGNAL(triggered()), this, SLOT(textColor()));
365     tb->addAction(actionTextColor);
366     menu->addAction(actionTextColor);
367 }
368 
setupStyleActions()369 void TextEdit::setupStyleActions()
370 {
371     QToolBar* tb = new QToolBar(this);
372     tb->setObjectName("ScrpStyleActions");
373     tb->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
374     tb->setWindowTitle(tr("Style Actions"));
375     addToolBarBreak(Qt::TopToolBarArea);
376     addToolBar(tb);
377 
378     QComboBox* comboStyle = new QComboBox(tb);
379     tb->addWidget(comboStyle);
380     comboStyle->addItem("Standard");
381     comboStyle->addItem("Bullet List (Disc)");
382     comboStyle->addItem("Bullet List (Circle)");
383     comboStyle->addItem("Bullet List (Square)");
384     comboStyle->addItem("Ordered List (Decimal)");
385     comboStyle->addItem("Ordered List (Alpha lower)");
386     comboStyle->addItem("Ordered List (Alpha upper)");
387     comboStyle->addItem("Ordered List (Roman lower)");
388     comboStyle->addItem("Ordered List (Roman upper)");
389     connect(comboStyle, SIGNAL(activated(int)), this, SLOT(textStyle(int)));
390 
391     comboFont = new QFontComboBox(tb);
392     tb->addWidget(comboFont);
393     connect(comboFont, SIGNAL(activated(QString)), this, SLOT(textFamily(QString)));
394 
395     comboSize = new QComboBox(tb);
396     comboSize->setObjectName("comboSize");
397     tb->addWidget(comboSize);
398     comboSize->setEditable(true);
399 
400     QFontDatabase db;
401     foreach(int size, db.standardSizes())
402         comboSize->addItem(QString::number(size));
403 
404     connect(comboSize, SIGNAL(activated(QString)), this, SLOT(textSize(QString)));
405     comboSize->setCurrentIndex(comboSize->findText(QString::number(QApplication::font().pointSize())));
406 }
407 
load(const QString & f)408 bool TextEdit::load(const QString &f)
409 {
410     if (!QFile::exists(f))
411         return false;
412     QFile file(f);
413     if (!file.open(QFile::ReadOnly))
414         return false;
415 
416     if (f.endsWith("odt"))
417     {
418         OOO::Converter *doc_odt = new OOO::Converter();
419         QTextDocument *doc = doc_odt->convert(f);
420 
421         // Set Document
422         textEdit->setDocument(doc);
423 		delete doc_odt;
424     }
425     else
426     {
427         QByteArray data = file.readAll();
428         QTextCodec *codec = Qt::codecForHtml(data);
429         QString str = codec->toUnicode(data);
430         if (Qt::mightBeRichText(str))
431         {
432             textEdit->setHtml(str);
433         }
434         else
435         {
436             str = QString::fromLocal8Bit(data);
437             textEdit->setPlainText(str);
438         }
439     }
440     setCurrentFileName(f);
441     return true;
442 }
443 
saveDocument()444 bool TextEdit::saveDocument()
445 {
446     if (!textEdit->document()->isModified())
447         return true;
448 
449     QMessageBox::StandardButton ret;
450     ret = QMessageBox::warning(this, tr("Application"),
451                                tr("The scratchpad has been modified.\n"
452                                   "Do you want to save your changes?"),
453                                QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
454     if (ret == QMessageBox::Save)
455         return fileSave();
456     else if (ret == QMessageBox::Cancel)
457         return false;
458     return true;
459 }
460 
setCurrentFileName(const QString & fileName)461 void TextEdit::setCurrentFileName(const QString &fileName)
462 {
463     this->fileName = fileName;
464     textEdit->document()->setModified(false);
465 
466     QString shownName;
467     if (fileName.isEmpty())
468         shownName = "untitled.txt";
469     else
470         shownName = QFileInfo(fileName).fileName();
471 
472     setWindowTitle(tr("%1[*]").arg(shownName));
473     setWindowModified(false);
474 }
475 
raise()476 void TextEdit::raise()
477 {
478     // This is a hack to show the widget in case it is located in a hidden dock and main menu is invoked
479     QDockWidget* d = qobject_cast<QDockWidget*>(parent());
480     if (d) { d->show(); d->raise(); }
481 }
482 
fileNew()483 void TextEdit::fileNew()
484 {
485     if (saveDocument())
486     {
487         raise();
488         textEdit->clear();
489         setCurrentFileName(QString());
490         setupDocumentActions();
491     }
492 }
493 
fileOpen()494 void TextEdit::fileOpen()
495 {
496     QString dir = AppSettings->commonDataPath();
497     QDir().mkpath(dir);
498 
499     QString fn = QFileDialog::getOpenFileName(this, tr("Open File..."),
500                                               dir, tr("ODF files (*.odt);;HTML-Files (*.htm *.html);;All Files (*)"));
501     if (!fn.isEmpty())
502     {
503         raise();
504         load(fn);
505         setupDocumentActions();
506     }
507 }
508 
fileSave()509 bool TextEdit::fileSave()
510 {
511     if (fileName.isEmpty())
512     {
513         return fileSaveAs();
514     }
515     if (fileName.startsWith(QStringLiteral(":/")))
516     {
517         return fileSaveAs();
518     }
519 
520     // Second nasty surprise: the writer cannot write images in HTML correctly
521     QTextDocumentWriter writer(fileName);
522     bool success = writer.write(textEdit->document());
523     if (success)
524     {
525         textEdit->document()->setModified(false);
526     }
527     return success;
528 }
529 
fileSaveAs()530 bool TextEdit::fileSaveAs()
531 {
532     QString fn = QFileDialog::getSaveFileName(this, tr("Save as..."), QString(),
533                                               tr("ODF files (*.odt);;HTML-Files "
534                                                  "(*.htm *.html);;All Files (*)"));
535     if (fn.isEmpty())
536     {
537         return false;
538     }
539     if (!(fn.endsWith(".odt", Qt::CaseInsensitive)
540           || fn.endsWith(".htm", Qt::CaseInsensitive)
541           || fn.endsWith(".html", Qt::CaseInsensitive)))
542     {
543         fn += ".odt"; // default
544     }
545     setCurrentFileName(fn);
546     return fileSave();
547 }
548 
filePrint()549 void TextEdit::filePrint()
550 {
551 #if !defined(QT_NO_PRINTER) && !defined(QT_NO_PRINTDIALOG)
552     QPrinter printer(QPrinter::HighResolution);
553     QPrintDialog *dlg = new QPrintDialog(&printer, this);
554     if (textEdit->textCursor().hasSelection())
555         dlg->addEnabledOption(QAbstractPrintDialog::PrintSelection);
556     dlg->setWindowTitle(tr("Print Document"));
557     if (dlg->exec() == QDialog::Accepted)
558         textEdit->print(&printer);
559     delete dlg;
560 #endif
561 }
562 
filePrintPreview()563 void TextEdit::filePrintPreview()
564 {
565 #if !defined(QT_NO_PRINTER) && !defined(QT_NO_PRINTDIALOG)
566     QPrinter printer(QPrinter::HighResolution);
567     QPrintPreviewDialog preview(&printer, this);
568     connect(&preview, SIGNAL(paintRequested(QPrinter*)), SLOT(printPreview(QPrinter*)));
569     preview.exec();
570 #endif
571 }
572 
printPreview(QPrinter * printer)573 void TextEdit::printPreview(QPrinter *printer)
574 {
575 #ifdef QT_NO_PRINTER
576     Q_UNUSED(printer);
577 #else
578     textEdit->print(printer);
579 #endif
580 }
581 
582 
filePrintPdf()583 void TextEdit::filePrintPdf()
584 {
585 #ifndef QT_NO_PRINTER
586     QString fileName = QFileDialog::getSaveFileName(this, "Export PDF",
587                                                     QString(), "*.pdf");
588     if (!fileName.isEmpty()) {
589         if (QFileInfo(fileName).suffix().isEmpty())
590             fileName.append(".pdf");
591         QPrinter printer(QPrinter::HighResolution);
592         printer.setOutputFormat(QPrinter::PdfFormat);
593         printer.setOutputFileName(fileName);
594         textEdit->document()->print(&printer);
595     }
596 #endif
597 }
598 
textBold()599 void TextEdit::textBold()
600 {
601     QTextCharFormat fmt;
602     fmt.setFontWeight(actionTextBold->isChecked() ? QFont::Bold : QFont::Normal);
603     fmt.setBackground(Qt::white);
604     mergeFormatOnWordOrSelection(fmt);
605 }
606 
textUnderline()607 void TextEdit::textUnderline()
608 {
609     QTextCharFormat fmt;
610     fmt.setFontUnderline(actionTextUnderline->isChecked());
611     fmt.setBackground(Qt::white);
612     mergeFormatOnWordOrSelection(fmt);
613 }
614 
textItalic()615 void TextEdit::textItalic()
616 {
617     QTextCharFormat fmt;
618     fmt.setFontItalic(actionTextItalic->isChecked());
619     fmt.setBackground(Qt::white);
620     mergeFormatOnWordOrSelection(fmt);
621 }
622 
textFamily(const QString & f)623 void TextEdit::textFamily(const QString &f)
624 {
625     QTextCharFormat fmt;
626     fmt.setFontFamily(f);
627     fmt.setBackground(Qt::white);
628     mergeFormatOnWordOrSelection(fmt);
629 }
630 
textSize(const QString & p)631 void TextEdit::textSize(const QString &p)
632 {
633     qreal pointSize = p.toFloat();
634     if (p.toFloat() > 0)
635     {
636         QTextCharFormat fmt;
637         fmt.setFontPointSize(pointSize);
638         fmt.setBackground(Qt::white);
639         mergeFormatOnWordOrSelection(fmt);
640     }
641 }
642 
textStyle(int styleIndex)643 void TextEdit::textStyle(int styleIndex)
644 {
645     QTextCursor cursor = textEdit->textCursor();
646 
647     if (styleIndex != 0)
648     {
649         QTextListFormat::Style style = QTextListFormat::ListStyleUndefined;
650 
651         switch (styleIndex) {
652             default:
653             case 1:
654                 style = QTextListFormat::ListDisc;
655                 break;
656             case 2:
657                 style = QTextListFormat::ListCircle;
658                 break;
659             case 3:
660                 style = QTextListFormat::ListSquare;
661                 break;
662             case 4:
663                 style = QTextListFormat::ListDecimal;
664                 break;
665             case 5:
666                 style = QTextListFormat::ListLowerAlpha;
667                 break;
668             case 6:
669                 style = QTextListFormat::ListUpperAlpha;
670                 break;
671             case 7:
672                 style = QTextListFormat::ListLowerRoman;
673                 break;
674             case 8:
675                 style = QTextListFormat::ListUpperRoman;
676                 break;
677         }
678 
679         cursor.beginEditBlock();
680 
681         QTextBlockFormat blockFmt = cursor.blockFormat();
682 
683         QTextListFormat listFmt;
684 
685         if (cursor.currentList())
686         {
687             listFmt = cursor.currentList()->format();
688         }
689         else
690         {
691             listFmt.setIndent(blockFmt.indent() + 1);
692             blockFmt.setIndent(0);
693             cursor.setBlockFormat(blockFmt);
694         }
695 
696         listFmt.setStyle(style);
697         listFmt.setBackground(Qt::white);
698         cursor.createList(listFmt);
699 
700         cursor.endEditBlock();
701     }
702     else
703     {
704         // TODO: The list style cannot be removed at the moment
705         QTextBlockFormat bfmt;
706         bfmt.setObjectIndex(-1);
707         bfmt.setBackground(Qt::white);
708         cursor.setBlockFormat(bfmt);
709     }
710 }
711 
textColor()712 void TextEdit::textColor()
713 {
714     QColor col = QColorDialog::getColor(textEdit->textColor(), this);
715     if (!col.isValid())
716         return;
717     QTextCharFormat fmt;
718     fmt.setForeground(col);
719     fmt.setBackground(Qt::white);
720     mergeFormatOnWordOrSelection(fmt);
721     colorChanged(col);
722 }
723 
textAlign(QAction * a)724 void TextEdit::textAlign(QAction *a)
725 {
726     if (a == actionAlignLeft)
727         textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
728     else if (a == actionAlignCenter)
729         textEdit->setAlignment(Qt::AlignHCenter);
730     else if (a == actionAlignRight)
731         textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
732     else if (a == actionAlignJustify)
733         textEdit->setAlignment(Qt::AlignJustify);
734 }
735 
currentCharFormatChanged(const QTextCharFormat & format)736 void TextEdit::currentCharFormatChanged(const QTextCharFormat &format)
737 {
738     fontChanged(format.font());
739     colorChanged(format.foreground().color());
740 }
741 
cursorPositionChanged()742 void TextEdit::cursorPositionChanged()
743 {
744     alignmentChanged(textEdit->alignment());
745 }
746 
pickBoard()747 void TextEdit::pickBoard()
748 {
749     QImage image;
750     double scaling = ((double) imageSize->value())/100.0;
751     emit requestBoardImage(image, scaling);
752     textEdit->insertImage(image);
753 }
754 
clipboardDataChanged()755 void TextEdit::clipboardDataChanged()
756 {
757 #ifndef QT_NO_CLIPBOARD
758     if (const QMimeData *md = QApplication::clipboard()->mimeData())
759         actionPaste->setEnabled(canInsertFromMimeData(md));
760 #endif
761 }
762 
mergeFormatOnWordOrSelection(const QTextCharFormat & format)763 void TextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
764 {
765     QTextCursor cursor = textEdit->textCursor();
766     if (!cursor.hasSelection())
767         cursor.select(QTextCursor::WordUnderCursor);
768 
769     cursor.mergeCharFormat(format);
770     textEdit->mergeCurrentCharFormat(format);
771 }
772 
fontChanged(const QFont & f)773 void TextEdit::fontChanged(const QFont &f)
774 {
775     comboFont->setCurrentIndex(comboFont->findText(QFontInfo(f).family()));
776     comboSize->setCurrentIndex(comboSize->findText(QString::number(f.pointSize())));
777     actionTextBold->setChecked(f.bold());
778     actionTextItalic->setChecked(f.italic());
779     actionTextUnderline->setChecked(f.underline());
780 }
781 
colorChanged(const QColor & c)782 void TextEdit::colorChanged(const QColor &c)
783 {
784     QPixmap pix(16, 16);
785     pix.fill(c);
786     actionTextColor->setIcon(pix);
787 }
788 
alignmentChanged(Qt::Alignment a)789 void TextEdit::alignmentChanged(Qt::Alignment a)
790 {
791     if (a & Qt::AlignLeft)
792         actionAlignLeft->setChecked(true);
793     else if (a & Qt::AlignHCenter)
794         actionAlignCenter->setChecked(true);
795     else if (a & Qt::AlignRight)
796         actionAlignRight->setChecked(true);
797     else if (a & Qt::AlignJustify)
798         actionAlignJustify->setChecked(true);
799 }
800 
canInsertFromMimeData(const QMimeData * source) const801 bool TextEdit::canInsertFromMimeData(const QMimeData* source) const
802 {
803     return textEdit->canInsertUsingMimeData(source);
804 }
805 
insertFromMimeData(const QMimeData * source)806 void TextEdit::insertFromMimeData(const QMimeData* source)
807 {
808     textEdit->insertUsingMimeData(source);
809 }
810 
imageResize()811 void TextEdit::imageResize()
812 {
813     QPair<double,double> factor;
814     factor.first = factor.second = imageSize->value();
815     textEdit->resizeImage(factor);
816 }
817 
818 ///////////////////////////////////////////////////////////////////////////////////////
819 
PasteTextEdit(QWidget * parent)820 PasteTextEdit::PasteTextEdit(QWidget *parent) : QTextEdit(parent), m_imageCounter(0)
821 {
822   installEventFilter(this);
823 }
824 
~PasteTextEdit()825 PasteTextEdit::~PasteTextEdit()
826 {
827   removeEventFilter(this);
828 }
829 
eventFilter(QObject * obj,QEvent * event)830 bool PasteTextEdit::eventFilter(QObject *obj, QEvent *event)
831 {
832     if(event->type() == QEvent::Shortcut)
833     {
834         if (obj != this)
835         {
836             return true;
837         }
838     }
839 
840     // standard event processing
841     return QObject::eventFilter(obj, event);
842 }
843 
canInsertUsingMimeData(const QMimeData * source) const844 bool PasteTextEdit::canInsertUsingMimeData(const QMimeData *source) const
845 {
846     return canInsertFromMimeData(source);
847 }
848 
insertUsingMimeData(const QMimeData * source)849 void PasteTextEdit::insertUsingMimeData(const QMimeData *source)
850 {
851     insertFromMimeData(source);
852 }
853 
canInsertFromMimeData(const QMimeData * source) const854 bool PasteTextEdit::canInsertFromMimeData(const QMimeData *source) const
855 {
856     const DbMimeData* m = qobject_cast<const DbMimeData*>(source);
857     return source && (source->hasImage() || (m && m->hasUrls()) || QTextEdit::canInsertFromMimeData(source));
858 }
859 
insertImage(const QImage & image)860 void PasteTextEdit::insertImage(const QImage& image)
861 {
862     QUrl url(QString("dropped_image_%1").arg(m_imageCounter++));
863     dropImage(url, image);
864 }
865 
insertFromMimeData(const QMimeData * source)866 void PasteTextEdit::insertFromMimeData(const QMimeData *source)
867 {
868     if (!source) return;
869 
870     const DbMimeData* dbMimeData = qobject_cast<const DbMimeData*>(source);
871 
872     if(dbMimeData && dbMimeData->hasUrls())
873     {
874         foreach (QUrl url, dbMimeData->urls())
875         {
876             QString link = QString("<a href='%1'>%2</a>").arg(url.toString(), url.fileName());
877             QTextEdit::insertHtml(link);
878             textCursor().insertText("\n");
879         }
880     }
881 
882     if (source->hasImage())
883     {
884         insertImage(qvariant_cast<QImage>(source->imageData()));
885     }
886     else if (source->hasUrls())
887     {
888         foreach (QUrl url, source->urls())
889         {
890             QFileInfo info(url.toLocalFile());
891             if (QImageReader::supportedImageFormats().contains(info.suffix().toLower().toLatin1()))
892                 dropImage(url, QImage(info.filePath()));
893             else
894                 dropTextFile(url);
895         }
896     }
897     else
898     {
899         if (source->hasText())
900         {
901             QString s = source->text();
902             if (s.startsWith("<"))
903             {
904                 QTextEdit::insertHtml(s);
905                 return;
906             }
907         }
908         QTextEdit::insertFromMimeData(source);
909     }
910 }
911 
dropImage(const QUrl & url,const QImage & image)912 void PasteTextEdit::dropImage(const QUrl& url, const QImage& image)
913 {
914     if (!image.isNull())
915     {
916         document()->addResource(QTextDocument::ImageResource, url, image);
917         QTextImageFormat imageFormat;
918         imageFormat.setWidth( image.width() );
919         imageFormat.setHeight( image.height() );
920         imageFormat.setName( url.toString() );
921         imageFormat.setBackground(Qt::white);
922         textCursor().insertImage(imageFormat);
923     }
924 }
925 
dropTextFile(const QUrl & url)926 void PasteTextEdit::dropTextFile(const QUrl& url)
927 {
928     QFile file(url.toLocalFile());
929     if (file.open(QIODevice::ReadOnly | QIODevice::Text))
930     {
931         textCursor().insertText(file.readAll());
932     }
933 }
934 
resizeImage(QPair<double,double> factor)935 void PasteTextEdit::resizeImage(QPair<double, double> factor)
936 {
937     QTextBlock currentBlock = textCursor().block();
938     QTextBlock::iterator it;
939 
940     for (it = currentBlock.begin(); !(it.atEnd()); ++it)
941     {
942         QTextFragment fragment = it.fragment();
943 
944         if (fragment.isValid())
945         {
946             if(fragment.charFormat().isImageFormat())
947             {
948                 QTextImageFormat newImageFormat = fragment.charFormat().toImageFormat();
949 
950                 if (newImageFormat.isValid())
951                 {
952                     double w = newImageFormat.width() * factor.first / 100.0;
953                     double h = newImageFormat.height() * factor.first / 100.0;
954 
955                     if (w && h)
956                     {
957                         newImageFormat.setWidth(w);
958                         newImageFormat.setHeight(h);
959 
960                         QTextCursor tc = textCursor();
961 
962                         tc.setPosition(fragment.position());
963                         tc.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
964                         tc.setCharFormat(newImageFormat);
965                     }
966                 }
967             }
968         }
969     }
970 }
971