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