/** * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved. * * This file is part of the QCAD project. * * QCAD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QCAD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QCAD. */ include("scripts/library.js"); include("scripts/map.js"); include("scripts/WidgetFactory.js"); include("CharacterMapDialog.js"); /** * \class SymbolMenuData * \brief Helper class for symbol popup menu. * \ingroup ecma_draw_text */ function SymbolMenuData(textWidget, data) { this.textWidget = textWidget; this.data = data; } SymbolMenuData.prototype.triggered = function() { var cursor = this.textWidget.textCursor(); cursor.insertText(this.data); }; /** * \class TextDialog * \brief Text creation or editing dialog. * \ingroup ecma_draw_text */ function TextDialog(mode) { if (isNull(mode)) { mode = TextDialog.Mode.Text; } this.mode = mode; this.dialog = undefined; this.textEdit = undefined; this.sourceEdit = undefined; this.actionTextBold = undefined; this.actionTextItalic = undefined; this.actionTextUnderline = undefined; this.actionSubscript = undefined; this.actionSuperscript = undefined; this.actionTextColor = undefined; //this.actionZoomIn = undefined; //this.actionZoomOut = undefined; this.actionTextSymbol = undefined; this.actionUndo = undefined; this.actionRedo = undefined; this.actionCut = undefined; this.actionCopy = undefined; this.actionPaste = undefined; this.comboFont = undefined; this.comboSize = undefined; // display text in text edit with this factor, compared to drawing unit: this.fontHeightFactor = 10.0; this.mainFont = undefined; this.initialColor = new RColor(); } TextDialog.prototype = {}; TextDialog.Mode = { Text : 0, Attribute : 1, AttributeDefinition : 2 }; TextDialog.prototype.setMode = function(mode) { this.mode = mode; }; TextDialog.prototype.getTextDocument = function() { if (RSettings.getQtVersion() >= 0x050000) { return this.textEdit.document; } else { return this.textEdit.document(); } }; TextDialog.prototype.getSourceDocument = function() { return this.sourceEdit.document(); }; TextDialog.prototype.show = function(textDataIn) { if (!isNull(textDataIn)) { if (isOfType(textDataIn, RAttributeData)) { this.mode = TextDialog.Mode.Attribute; } else if (isOfType(textDataIn, RAttributeDefinitionData)) { this.mode = TextDialog.Mode.AttributeDefinition; } else { this.mode = TextDialog.Mode.Text; } } this.dialog = WidgetFactory.createDialog(TextDialog.basePath, "TextDialog.ui", EAction.getMainWindow()); // initialize dialog controls: this.textEdit = this.dialog.findChild("Text"); this.sourceEdit = this.dialog.findChild("Source"); this.tabWidget = this.dialog.findChild("TabWidget"); // main font combo box: var comboMainFont = this.dialog.findChild("MainFont"); var comboFont = this.dialog.findChild("Font"); initFontComboBox(comboMainFont); initFontComboBox(comboFont); comboMainFont.currentFontChanged.connect(this, this.mainFontChanged); // main font height: var editMainHeight = this.dialog.findChild("MainHeight"); editMainHeight.valueChanged.connect(this, this.mainFontChanged); // main font bold: var checkMainBold = this.dialog.findChild("MainBold"); checkMainBold.toggled.connect(this, this.mainFontChanged); // main font italic: var checkMainItalic = this.dialog.findChild("MainItalic"); checkMainItalic.toggled.connect(this, this.mainFontChanged); // default line spacing: //var checkBoxDefaultLineSpacing = this.dialog.findChild("DefaultLineSpacing"); //checkBoxDefaultLineSpacing.toggled.connect(this, this.useDefaultLineSpacing); // line spacing: var editLineSpacingFactor = this.dialog.findChild("LineSpacingFactor"); // angle: var editAngle = this.dialog.findChild("Angle"); /* var buttonNew = this.dialog.findChild("New"); buttonNew.clicked.connect(this.textEdit, "clear"); var buttonOpen = this.dialog.findChild("Open"); var buttonSaveAs = this.dialog.findChild("SaveAs"); */ this.setupEditActions(); this.setupTextActions(); // update status buttons bold / italic / underline: this.textEdit.currentCharFormatChanged.connect(this, this.currentCharFormatChanged); this.textEdit.cursorPositionChanged.connect(this, this.cursorPositionChanged); var td = this.getTextDocument(); td.undoAvailable.connect(this.actionUndo, this.actionUndo.setEnabled); td.redoAvailable.connect(this.actionRedo, this.actionRedo.setEnabled); this.actionUndo.setEnabled(td.isUndoAvailable()); this.actionRedo.setEnabled(td.isRedoAvailable()); this.actionUndo.triggered.connect(this.textEdit, this.textEdit.undo); this.actionRedo.triggered.connect(this.textEdit, this.textEdit.redo); this.actionCut.setEnabled(false); this.actionCopy.setEnabled(false); this.actionCut.triggered.connect(this.textEdit, this.textEdit.cut); this.actionCopy.triggered.connect(this.textEdit, this.textEdit.copy); this.actionPaste.triggered.connect(this.textEdit, this.textEdit.paste); this.textEdit.copyAvailable.connect(this.actionCut, this.actionCut.setEnabled); this.textEdit.copyAvailable.connect(this.actionCopy, this.actionCopy.setEnabled); var cbSimpleText = this.dialog.findChild("SimpleText"); cbSimpleText.toggled.connect(this, this.simpleTextToggled); var buttonTopLeft = this.dialog.findChild("AlignTopLeft"); var buttonTopCenter = this.dialog.findChild("AlignTopCenter"); var buttonTopRight = this.dialog.findChild("AlignTopRight"); var buttonMiddleLeft = this.dialog.findChild("AlignMiddleLeft"); var buttonMiddleCenter = this.dialog.findChild("AlignMiddleCenter"); var buttonMiddleRight = this.dialog.findChild("AlignMiddleRight"); var buttonBaseLeft = this.dialog.findChild("AlignBaseLeft"); var buttonBaseCenter = this.dialog.findChild("AlignBaseCenter"); var buttonBaseRight = this.dialog.findChild("AlignBaseRight"); var buttonBottomLeft = this.dialog.findChild("AlignBottomLeft"); var buttonBottomCenter = this.dialog.findChild("AlignBottomCenter"); var buttonBottomRight = this.dialog.findChild("AlignBottomRight"); getClipboard().dataChanged.connect(this, this.clipboardDataChanged); // change dialog into attribute / attribute definition dialog: if (this.mode === TextDialog.Mode.AttributeDefinition || this.mode === TextDialog.Mode.Attribute) { this.dialog.findChild("Separator").visible = false; this.dialog.findChild("LineSpacingFactorLabel").visible = false; editLineSpacingFactor.visible = false; cbSimpleText.visible = false; this.tabWidget.removeTab(1); this.tabWidget.removeTab(0); this.dialog.resize(0,0); if (this.mode === TextDialog.Mode.AttributeDefinition) { this.dialog.windowTitle = entityTypeToString(RS.EntityAttributeDefinition, false); } else if (this.mode === TextDialog.Mode.Attribute) { this.dialog.windowTitle = entityTypeToString(RS.EntityAttribute, false); } } else { this.tabWidget.removeTab(2); } // sync rich text / source: this.tabWidget["currentChanged(int)"].connect(this, this.tabChanged); if (this.mode === TextDialog.Mode.AttributeDefinition) { this.dialog.findChild("ValueLabel").text = qsTr("Default value:"); } // initialize dialog from given text data (textDataIn): if (!isNull(textDataIn)) { cbSimpleText.checked = textDataIn.isSimple(); this.initialColor = textDataIn.getColor(); activateFont(comboMainFont, textDataIn.getFontName()); checkMainBold.checked = textDataIn.isBold(); checkMainItalic.checked = textDataIn.isItalic(); editMainHeight.setValue(textDataIn.getTextHeight()); this.sourceEdit.setPlainText(textDataIn.getEscapedText()); this.updateRichText(true); //qDebug("this.sourceEdit:", this.sourceEdit.plainText); if (textDataIn.getVAlign()==RS.VAlignTop) { if (textDataIn.getHAlign()==RS.HAlignLeft || textDataIn.getHAlign()==RS.HAlignFit || textDataIn.getHAlign()==RS.HAlignAlign) { buttonTopLeft.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignCenter) { buttonTopCenter.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignRight) { buttonTopRight.checked = true; } } else if (textDataIn.getVAlign()==RS.VAlignMiddle) { if (textDataIn.getHAlign()==RS.HAlignLeft || textDataIn.getHAlign()==RS.HAlignFit || textDataIn.getHAlign()==RS.HAlignAlign) { buttonMiddleLeft.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignCenter || textDataIn.getHAlign()==RS.HAlignMid) { buttonMiddleCenter.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignRight) { buttonMiddleRight.checked = true; } } else if (textDataIn.getVAlign()==RS.VAlignBase) { if (textDataIn.getHAlign()==RS.HAlignLeft || textDataIn.getHAlign()==RS.HAlignFit || textDataIn.getHAlign()==RS.HAlignAlign) { buttonBaseLeft.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignCenter) { buttonBaseCenter.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignRight) { buttonBaseRight.checked = true; } } else if (textDataIn.getVAlign()==RS.VAlignBottom) { if (textDataIn.getHAlign()==RS.HAlignLeft || textDataIn.getHAlign()==RS.HAlignFit || textDataIn.getHAlign()==RS.HAlignAlign) { buttonBottomLeft.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignCenter) { buttonBottomCenter.checked = true; } else if (textDataIn.getHAlign()==RS.HAlignRight) { buttonBottomRight.checked = true; } } editLineSpacingFactor.setValue(textDataIn.getLineSpacingFactor()); editAngle.setValue(textDataIn.getAngle()); // init values specific to attributes / attribute definitions: if (this.mode === TextDialog.Mode.Attribute || this.mode === TextDialog.Mode.AttributeDefinition) { this.dialog.findChild("AttributeTag").text = textDataIn.getTag(); this.dialog.findChild("AttributeValue").text = textDataIn.getText(); if (this.mode === TextDialog.Mode.AttributeDefinition) { this.dialog.findChild("AttributePrompt").text = textDataIn.getPrompt(); } else { this.dialog.findChild("AttributePrompt").visible = false; this.dialog.findChild("PromptLabel").visible = false; this.dialog.findChild("AttributeTag").enabled = false; this.dialog.findChild("AttributeValue").selectAll(); } } } else { WidgetFactory.restoreState(this.dialog); cbSimpleText.checked = RSettings.getBoolValue("TextDialog/SimpleText", false); // restore widgets that are not supported by WidgetFactory: var mainFontName = RSettings.getStringValue("TextDialog/MainFont", "Arial"); var mainFontIndex = comboMainFont.findText(mainFontName); if (mainFontIndex>0) { comboMainFont.currentIndex = mainFontIndex; } this.sourceEdit.setPlainText(RSettings.getStringValue("TextDialog/Source", "")); this.updateRichText(true); var vAlignment = RSettings.getIntValue("TextDialog/VAlignment", RS.VAlignTop); var hAlignment = RSettings.getIntValue("TextDialog/HAlignment", RS.HAlignLeft); if (vAlignment==RS.VAlignTop) { if (hAlignment==RS.HAlignLeft || hAlignment==RS.HAlignFit || hAlignment==RS.HAlignAlign) { buttonTopLeft.checked = true; } else if (hAlignment==RS.HAlignCenter) { buttonTopCenter.checked = true; } else if (hAlignment==RS.HAlignRight) { buttonTopRight.checked = true; } } else if (vAlignment==RS.VAlignMiddle) { if (hAlignment==RS.HAlignLeft || hAlignment==RS.HAlignFit || hAlignment==RS.HAlignAlign) { buttonMiddleLeft.checked = true; } else if (hAlignment==RS.HAlignCenter) { buttonMiddleCenter.checked = true; } else if (hAlignment==RS.HAlignRight) { buttonMiddleRight.checked = true; } } else if (vAlignment==RS.VAlignBase) { if (hAlignment==RS.HAlignLeft || hAlignment==RS.HAlignFit || hAlignment==RS.HAlignAlign) { buttonBaseLeft.checked = true; } else if (hAlignment==RS.HAlignCenter) { buttonBaseCenter.checked = true; } else if (hAlignment==RS.HAlignRight) { buttonBaseRight.checked = true; } } else if (vAlignment==RS.VAlignBottom) { if (hAlignment==RS.HAlignLeft || hAlignment==RS.HAlignFit || hAlignment==RS.HAlignAlign) { buttonBottomLeft.checked = true; } else if (hAlignment==RS.HAlignCenter) { buttonBottomCenter.checked = true; } else if (hAlignment==RS.HAlignRight) { buttonBottomRight.checked = true; } } var di = EAction.getDocumentInterface(); if (!isNull(di)) { // initial text color is current color (e.g. ByLayer, red, ..): this.initialColor = di.getCurrentColor(); } else { // initial color is invalid (no document, never reached): this.initialColor = new RColor(); debugger; } } this.mainFontChanged(); this.colorChanged(this.textEdit.textColor()); this.colorChanged(this.initialColor); if (cbSimpleText.checked) { this.sourceEdit.setFocus(Qt.OtherFocusReason); this.sourceEdit.selectAll(); } else { this.textEdit.setFocus(Qt.OtherFocusReason); this.textEdit.selectAll(); } this.currentCharFormatChanged(this.textEdit.currentCharFormat()); //this.colorChanged(); // exec text dialog: var res = this.dialog.exec(); if (this.tabWidget.currentIndex===0) { this.updateSource(true); } if (RSettings.getQtVersion() >= 0x060000) { // disconnect clipboard from changed event handler: getClipboard().dataChanged.disconnect(this, this.clipboardDataChanged); } // analyze dialog input: var textDataOut; if (isNull(textDataIn)) { if (this.mode === TextDialog.Mode.Attribute) { textDataOut = new RAttributeData(); } else if (this.mode === TextDialog.Mode.AttributeDefinition) { textDataOut = new RAttributeDefinitionData(); } else { textDataOut = new RTextData(); } } else { textDataOut = textDataIn; } textDataOut.setFontName(comboMainFont.currentFont.family()); textDataOut.setBold(checkMainBold.checked); textDataOut.setItalic(checkMainItalic.checked); textDataOut.setTextHeight(editMainHeight.getValue()); if (buttonTopLeft.checked || buttonTopCenter.checked || buttonTopRight.checked) { textDataOut.setVAlign(RS.VAlignTop); } else if (buttonMiddleLeft.checked || buttonMiddleCenter.checked || buttonMiddleRight.checked) { textDataOut.setVAlign(RS.VAlignMiddle); } else if (buttonBaseLeft.checked || buttonBaseCenter.checked || buttonBaseRight.checked) { textDataOut.setVAlign(RS.VAlignBase); } else if (buttonBottomLeft.checked || buttonBottomCenter.checked || buttonBottomRight.checked) { textDataOut.setVAlign(RS.VAlignBottom); } if (buttonTopLeft.checked || buttonMiddleLeft.checked || buttonBaseLeft.checked || buttonBottomLeft.checked) { textDataOut.setHAlign(RS.HAlignLeft); } else if (buttonTopCenter.checked || buttonMiddleCenter.checked || buttonBaseCenter.checked || buttonBottomCenter.checked) { textDataOut.setHAlign(RS.HAlignCenter); } else if (buttonTopRight.checked || buttonMiddleRight.checked || buttonBaseRight.checked || buttonBottomRight.checked) { textDataOut.setHAlign(RS.HAlignRight); } //textDataOut.setDrawingDirection(); //textDataOut.setLineSpacingStyle(); textDataOut.setLineSpacingFactor(editLineSpacingFactor.getValue()); textDataOut.setAngle(editAngle.getValue()); // init values specific to attributes / attribute definitions: if (this.mode === TextDialog.Mode.Attribute || this.mode === TextDialog.Mode.AttributeDefinition) { textDataOut.setSimple(true); textDataOut.setTag(this.dialog.findChild("AttributeTag").text); if (this.mode === TextDialog.Mode.AttributeDefinition) { textDataOut.setPrompt(this.dialog.findChild("AttributePrompt").text); } textDataOut.setText(this.dialog.findChild("AttributeValue").text); } else { textDataOut.setSimple(cbSimpleText.checked); textDataOut.setText(this.sourceEdit.toPlainText()); } WidgetFactory.saveState(this.dialog); // save state of unsupported widgets: RSettings.setValue("TextDialog/VAlignment", textDataOut.getVAlign()); RSettings.setValue("TextDialog/HAlignment", textDataOut.getHAlign()); RSettings.setValue("TextDialog/MainFont", textDataOut.getFontName()); destr(this.dialog); EAction.activateMainWindow(); if (res) { return textDataOut; } else { return undefined; } }; TextDialog.basePath = includeBasePath; TextDialog.prototype.setupEditActions = function() { var a; a = this.actionUndo = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Undo.svg")), qsTr("&Undo"), this.dialog); a.shortcut = QKeySequence.Undo; this.dialog.findChild("Undo").setDefaultAction(a); a = this.actionRedo = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Redo.svg")), qsTr("&Redo"), this.dialog); a.shortcut = QKeySequence.Redo; this.dialog.findChild("Redo").setDefaultAction(a); a = this.actionCut = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Cut.svg")), qsTr("Cu&t"), this.dialog); a.shortcut = QKeySequence.Cut; this.dialog.findChild("Cut").setDefaultAction(a); a = this.actionCopy = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Copy.svg")), qsTr("&Copy"), this.dialog); a.shortcut = QKeySequence.Copy; this.dialog.findChild("Copy").setDefaultAction(a); a = this.actionPaste = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Paste.svg")), qsTr("&Paste"), this.dialog); a.shortcut = QKeySequence.Paste; this.dialog.findChild("Paste").setDefaultAction(a); this.actionPaste.setEnabled(!getClipboard().text().isEmpty()); }; TextDialog.prototype.setupTextActions = function() { this.actionTextBold = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Bold.svg")), qsTr("&Bold"), this.dialog); this.actionTextBold.shortcut = new QKeySequence("Ctrl+B"); var bold = new QFont(); bold.setBold(true); this.actionTextBold.font = bold; this.actionTextBold.triggered.connect(this, this.textBold); this.actionTextBold.checkable = true; this.dialog.findChild("Bold").setDefaultAction(this.actionTextBold); this.actionTextItalic = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Italic.svg")), qsTr("&Italic"), this.dialog); this.actionTextItalic.shortcut = new QKeySequence("Ctrl+I"); var italic = new QFont(); italic.setItalic(true); this.actionTextItalic.font = italic; this.actionTextItalic.triggered.connect(this, this.textItalic); this.actionTextItalic.checkable = true; this.dialog.findChild("Italic").setDefaultAction(this.actionTextItalic); this.actionTextUnderline = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Underline.svg")), qsTr("&Underline"), this.dialog); this.actionTextUnderline.shortcut = new QKeySequence("Ctrl+U"); var underline = new QFont(); underline.setUnderline(true); this.actionTextUnderline.font = underline; this.actionTextUnderline.triggered.connect(this, this.textUnderline); this.actionTextUnderline.checkable = true; this.dialog.findChild("Underline").setDefaultAction(this.actionTextUnderline); this.actionSubscript = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Subscript.svg")), qsTr("&Subscript"), this.dialog); this.actionSubscript.shortcut = new QKeySequence("Shift+Ctrl+B"); this.actionSubscript.triggered.connect(this, this.textSubscript); this.actionSubscript.checkable = true; this.dialog.findChild("Subscript").setDefaultAction(this.actionSubscript); this.actionSuperscript = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Superscript.svg")), qsTr("&Superscript"), this.dialog); this.actionSuperscript.shortcut = new QKeySequence("Shift+Ctrl+P"); this.actionSuperscript.triggered.connect(this, this.textSuperscript); this.actionSuperscript.checkable = true; this.dialog.findChild("Superscript").setDefaultAction(this.actionSuperscript); // this.actionZoomIn = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/ZoomIn.svg")), qsTr("Zoom &In"), this.dialog); // this.actionZoomIn.shortcut = new QKeySequence(Qt.ControlModifier | Qt.Key_Plus); // this.actionZoomIn.triggered.connect(this.textEdit, "zoomIn"); // this.dialog.findChild("ZoomIn").setDefaultAction(this.actionZoomIn); // this.actionZoomOut = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/ZoomOut.svg")), qsTr("Zoom &Out"), this.dialog); // this.actionZoomOut.shortcut = new QKeySequence(Qt.ControlModifier | Qt.Key_Minus); // this.actionZoomOut.triggered.connect(this.textEdit, "zoomOut"); // this.dialog.findChild("ZoomOut").setDefaultAction(this.actionZoomOut); var pix = new QPixmap(16, 16); pix.fill(new QColor(Qt.black)); this.actionTextColor = new QAction(new QIcon(pix), qsTr("&Color..."), this.dialog); this.actionTextColor.triggered.connect(this, this.textColor); this.dialog.findChild("Color").setDefaultAction(this.actionTextColor); this.actionTextSymbol = new QAction(new QIcon(autoIconPath(TextDialog.basePath + "/Symbol.svg")), qsTr("&Special Characters..."), this.dialog); this.actionTextSymbol.triggered.connect(this, this.textSymbol); this.dialog.findChild("Symbol").setDefaultAction(this.actionTextSymbol); this.comboFont = this.dialog.findChild("Font"); if (RSettings.getQtVersion() >= 0x060000) { this.comboFont.textActivated.connect(this, this.textFamily); } else { this.comboFont["activated(QString)"].connect(this, this.textFamily); } this.comboSize = this.dialog.findChild("Height"); this.comboSize.editable = true; if (RSettings.getQtVersion() >= 0x060000) { this.comboSize.textActivated.connect(this, this.textSize); } else { this.comboSize["activated(QString)"].connect(this, this.textSize); } this.comboSize.setEditText("1.0"); this.comboSize.editTextChanged.connect(this, this.textSize); }; /** * \return Main font to use with main height set in in drawing units * as points. E.g. a font with point size 15.0 means the font height * is really 15 drawing units. */ TextDialog.prototype.getMainFont = function() { // get main font with main height: var comboMainFont = this.dialog.findChild("MainFont"); var font = comboMainFont.currentFont; var editMainHeight = this.dialog.findChild("MainHeight"); var h = editMainHeight.getValue(); if (isNumber(h) && h>0.0) { font.setPointSizeF(h); } var checkMainBold = this.dialog.findChild("MainBold"); font.setBold(checkMainBold.checked); var checkMainItalic = this.dialog.findChild("MainItalic"); font.setItalic(checkMainItalic.checked); return font; }; /** * Called when user changes the main font (initial font for text). */ TextDialog.prototype.mainFontChanged = function() { var font = this.getMainFont(); if (font.pointSizeF()>RS.PointTolerance) { this.fontHeightFactor = RSettings.getDoubleValue("Text/PreviewFontSize", 14.0) / font.pointSizeF(); } else { this.fontHeightFactor = 10.0; } if (this.tabWidget.currentIndex===1) { this.updateRichText(true); } var document = this.getTextDocument(); var html = document.toHtml(); // qDebug("\n\nmainFontChanged: html before fix: ", html, "\n\n"); html = this.fixHtml(html); // qDebug("\n\nmainFontChanged: html after fix: ", html, "\n\n"); var previousMainFont = this.mainFont; this.mainFont = font; font.setPointSizeF(font.pointSizeF() * this.fontHeightFactor); this.textEdit.font = font; document.defaultFont = font; //qDebug("\n\nHTML after setting default font: \n", html, "\n\n"); // replace all fonts that matched the previous main font with new // main font. this is _most likely_ what the user expects: if (!isNull(previousMainFont)) { html.replace("font-family:'%1';".arg(previousMainFont.family()), "font-family:'%1';".arg(font.family())); } // qDebug("\n\nmainFontChanged: html after font replace: ", html, "\n\n"); this.textEdit.setHtml(html); // qDebug("\n\nnew html: ", this.getTextDocument().toHtml(), "\n\n"); this.updateSource(true); }; TextDialog.prototype.fixHtml = function(html) { var font = this.getMainFont(); //html = html.replace(/>[ ]*<"); var htmlNbsp = ""; var cdata = false; for (var i=0; i') { cdata = true; } // create nbsp from all spaces. QTextEdit will simplify white space // to one single space otherwise. else if (cdata && (html[i]===' ' || html[i]==='\u00A0')) { htmlNbsp += ' '; continue; } htmlNbsp += html[i]; } // change font in HTML directly. changing default font of document // keeps content formatting by adding font style with previous // default font to all content: var dom = new QDomDocument(); if (RSettings.getQtVersion()>=0x060500) { dom.setContent(htmlNbsp); } else { dom.setContent(htmlNbsp, false); } var docElem = dom.documentElement(); //qDebug("initial font color: ", this.initialColor); var n = docElem.firstChild(); while (!n.isNull()) { var e = n.toElement(); if(!e.isNull()) { if (e.tagName().toLowerCase()==="body") { var qcol = this.initialColor.toCompat(); var bodyStyle = ("font-family:'%1';" + "font-size:%2pt;" + "font-weight:%3;" + "font-style:%4;" + "color:%5;") .arg(font.family()) .arg(font.pointSizeF() * this.fontHeightFactor) .arg(font.weight()<=QFont.Normal ? "normal" : "bold") .arg(font.italic() ? "italic" : "normal") .arg(qcol.name()); // brighter background of text area in macOS dark mode: if (RSettings.hasDarkGuiBackground() && RS.getSystemId()==="osx") { bodyStyle += "background-color:#dddddd;"; } e.setAttribute("style", bodyStyle); } } n = n.nextSibling(); } return dom.toString(-1); }; TextDialog.prototype.simpleTextToggled = function(enable) { if (this.mode!==TextDialog.Mode.Text) { return; } var editLineSpacingFactor = this.dialog.findChild("LineSpacingFactor"); if (enable) { this.tabWidget.setTabEnabled(0, false); this.tabWidget.currentIndex = 1; editLineSpacingFactor.enabled = false; } else { this.tabWidget.setTabEnabled(0, true); this.tabWidget.currentIndex = 0; editLineSpacingFactor.enabled = true; } }; TextDialog.prototype.mergeFormatOnWordOrSelection = function(format) { var cursor = this.textEdit.textCursor(); if (!cursor.hasSelection()) { cursor.select(QTextCursor.WordUnderCursor); } cursor.mergeCharFormat(format); this.textEdit.mergeCurrentCharFormat(format); }; /** * Called when user clicks bold button. */ TextDialog.prototype.textBold = function() { var fmt = new QTextCharFormat(); fmt.setFontWeight(this.actionTextBold.checked ? QFont.Bold : QFont.Normal); this.mergeFormatOnWordOrSelection(fmt); }; /** * Called when user clicks italic button. */ TextDialog.prototype.textItalic = function() { var fmt = new QTextCharFormat(); fmt.setFontItalic(this.actionTextItalic.checked); this.mergeFormatOnWordOrSelection(fmt); }; /** * Called when user clicks underline button. */ TextDialog.prototype.textUnderline = function() { var fmt = new QTextCharFormat(); fmt.setFontUnderline(this.actionTextUnderline.checked); this.mergeFormatOnWordOrSelection(fmt); }; /** * Called when user clicks subscript button. */ TextDialog.prototype.textSubscript = function() { var fmt = new QTextCharFormat(); fmt.setVerticalAlignment(this.actionSubscript.checked ? QTextCharFormat.AlignSubScript : QTextCharFormat.AlignNormal); this.mergeFormatOnWordOrSelection(fmt); }; /** * Called when user clicks superscript button. */ TextDialog.prototype.textSuperscript = function() { var fmt = new QTextCharFormat(); fmt.setVerticalAlignment(this.actionSuperscript.checked ? QTextCharFormat.AlignSuperScript : QTextCharFormat.AlignNormal); this.mergeFormatOnWordOrSelection(fmt); }; /** * Called when user changes font. */ TextDialog.prototype.textFamily = function(f) { var fmt = new QTextCharFormat(); // Qt 5.13, 5.14, 5.15 bug: appends the font as fall-back instead of setting the font: //fmt.setFontFamily(f); fmt.setFontFamilies([f]); this.mergeFormatOnWordOrSelection(fmt); }; /** * Called when user changes font size. */ TextDialog.prototype.textSize = function(p) { var pointSize = parseFloat(p); if (pointSize > 0) { var fmt = new QTextCharFormat(); fmt.setFontPointSize(pointSize * this.fontHeightFactor); this.mergeFormatOnWordOrSelection(fmt); } }; /** * Called when user changes font color. */ TextDialog.prototype.textColor = function() { var colorNameList = RColor.getNameList(false); var colorList = RColor.getColorList(false); if (colorNameList.length!==colorList.length) { qWarning("Not same number of colors as color names. Aborting..."); return; } var menu = new QMenu(this.dialog); for (var i=0; i < colorNameList.length; ++i) { var label = colorNameList[i]; var color = colorList[i]; if (label==="---") { menu.addSeparator(); } else { var action = menu.addAction(RColor.getIcon(color, new QSize(16,16)), label); action.setProperty("color", color); action.iconVisibleInMenu = true; } } var chosenAction = menu.exec(QCursor.pos()); if (isNull(chosenAction)) { return; } var chosenColor = chosenAction.property("color"); if (chosenColor.isValid()) { if (chosenColor.isByLayer()) { chosenColor = RColor.CompatByLayer; } else if (chosenColor.isByBlock()) { chosenColor = RColor.CompatByBlock; } else { chosenColor = new QColor(chosenColor.red(), chosenColor.green(), chosenColor.blue(), chosenColor.alpha()); } } else { // custom color: show color dialog: chosenColor = QColorDialog.getColor(this.textEdit.textColor(), this.dialog); if (!chosenColor.isValid()) { // color dialog canceled: return; } } var fmt = new QTextCharFormat(); fmt.setForeground(new QBrush(chosenColor)); this.mergeFormatOnWordOrSelection(fmt); this.colorChanged(chosenColor); }; /** * Called when user is about to insert a symbol (special character). */ TextDialog.prototype.textSymbol = function() { var action; var menu = new QMenu(this.dialog); // common special characters var textWidget = this.dialog.findChild("Text"); var charsMap = this.getSpecialCharsMap(); var keys = charsMap.getKeys(); for (var i = 0; i < keys.length; ++i) { var str = charsMap.get(keys[i])[0]; var character = charsMap.get(keys[i])[1]; action = menu.addAction(str + "\t" + character); var menuData = new SymbolMenuData(textWidget, character); action.triggered.connect(menuData, menuData.triggered); } // other characters menu.addSeparator(); action = menu.addAction(qsTr("Others...")); action.triggered.connect(this, function(){ var dlg = new CharacterMapDialog(this); dlg.show(); }); menu.exec(QCursor.pos()); }; /** * Character format under cursor changed. Update relevant controls. */ TextDialog.prototype.currentCharFormatChanged = function(format) { this.fontChanged(format.font()); this.colorChanged(format.foreground().color()); this.actionSubscript.setChecked(format.verticalAlignment()==QTextCharFormat.AlignSubScript); this.actionSuperscript.setChecked(format.verticalAlignment()==QTextCharFormat.AlignSuperScript); }; /** * Called when user changes the current font or the font under the cursor changed. */ TextDialog.prototype.fontChanged = function(font) { activateFont(this.comboFont, font.family()); this.comboSize.blockSignals(true); this.comboSize.setEditText("%1".arg(font.pointSizeF() / this.fontHeightFactor)); this.comboSize.blockSignals(false); this.actionTextBold.setChecked(font.bold()); this.actionTextItalic.setChecked(font.italic()); this.actionTextUnderline.setChecked(font.underline()); }; /** * Called when user changes the current color or the color under the cursor changed. */ TextDialog.prototype.colorChanged = function(c) { if (!RSettings.hasDarkGuiBackground()) { // make sure that very bright text is readable: if (c.lightness()>240) { var p = this.textEdit.palette; p.setColor(QPalette.Active, QPalette.Base, new QColor(Qt.lightGray)); this.textEdit.palette = p; } } var pix = new QPixmap(16, 16); pix.fill(c); this.actionTextColor.icon = new QIcon(pix); }; TextDialog.prototype.cursorPositionChanged = function() { //alignmentChanged(textEdit->alignment()); }; /** * Called to switch to default line spacing (factor 1.0). */ /* TextDialog.prototype.useDefaultLineSpacing = function(checked) { var editLineSpacingFactor = this.dialog.findChild("LineSpacingFactor"); if (checked) { editLineSpacingFactor.text = "1.0"; } editLineSpacingFactor.enabled = !checked; }; */ /** * Called when clipboard text becomes available / unavailable. */ TextDialog.prototype.clipboardDataChanged = function() { if (isDeleted(this.actionPaste)) { return; } this.actionPaste.setEnabled(!getClipboard().text().isEmpty()); }; TextDialog.prototype.tabChanged = function(index) { switch (index) { case 0: this.updateRichText(false); break; case 1: this.updateSource(false); break; } }; /** * Updates the source based on the rich text edit. */ TextDialog.prototype.updateSource = function(force) { if (this.getTextDocument().modified || force===true) { var html = this.textEdit.html; html = this.fixHtml(html); var textDocument = new QTextDocument(); textDocument.setHtml(html); textDocument.defaultFont = this.getTextDocument().defaultFont; var cbSimpleText = this.dialog.findChild("SimpleText"); var source = RTextBasedData.toEscapedText(textDocument, this.initialColor, this.fontHeightFactor, cbSimpleText.checked); //qDebug("source: \n\n", source, "\n\n"); this.sourceEdit.setPlainText(source); this.getTextDocument().modified = false; } }; /** * Updates the rich text edit based on the source. */ TextDialog.prototype.updateRichText = function(force) { if (this.getSourceDocument().modified || force===true) { var source = this.getSourceDocument().toPlainText(); source = source.replace("\n", "\\P"); var richText = RTextBasedData.toRichText(source, this.getMainFont(), this.fontHeightFactor); //richText = richText.replace("", ""); //qDebug("richText: \n\n", richText, "\n\n"); // TODO: preserve tabs (img with arrow?) this.textEdit.setHtml(richText); //qDebug("HTML from text edit: \n\n", this.textEdit.html, "\n\n"); this.getSourceDocument().modified = false; } }; //TextDialog.prototype.activateFont = function(fontComboBox, fontName) { // var index = fontComboBox.findText(fontName, Qt.MatchFixedString); // fontComboBox.setCurrentIndex(index); //}; TextDialog.prototype.getSpecialCharsMap = function() { if (isNull(TextDialog.specialChars)) { TextDialog.specialChars = new Map(); TextDialog.specialChars.put("degree", [ qsTr("Degree"), "\u00B0" ]); TextDialog.specialChars.put("plus_minus", [ qsTr("Plus-Minus"), "\u00B1" ]); //TextDialog.specialChars.put("diameter", [ qsTr("Diameter"), "\u2300" ]); TextDialog.specialChars.put("diameter", [ qsTr("Diameter"), "\u00F8" ]); TextDialog.specialChars.put("allmost_equal", [ qsTr("Almost equal"), "\u2248" ]); TextDialog.specialChars.put("multiplication", [ qsTr("Multiplication"), "\u00D7" ]); TextDialog.specialChars.put("division", [ qsTr("Division"), "\u00F7" ]); TextDialog.specialChars.put("delta", [ qsTr("Delta"), "\u0394" ]); TextDialog.specialChars.put("pi", [ qsTr("Pi"), "\u03C0" ]); TextDialog.specialChars.put("identity", [ qsTr("Identity"), "\u2261" ]); TextDialog.specialChars.put("not_equal", [ qsTr("Not equal"), "\u2260" ]); TextDialog.specialChars.put("ohm", [ qsTr("Ohm"), "\u2126" ]); TextDialog.specialChars.put("omega", [ qsTr("Omega"), "\u03A9" ]); TextDialog.specialChars.put("squared", [ qsTr("Squared"), "\u00B2" ]); TextDialog.specialChars.put("cubed", [ qsTr("Cubed"), "\u00B3" ]); TextDialog.specialChars.put("nbsp", [ qsTr("No-break space"), " " ]); } return TextDialog.specialChars; };