Bug Summary

File:librecad/src/ui/dialogs/entity/qg_dlg_mtext.cpp
Warning:line 182, column 10
Value stored to 'leftToRight' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name qg_dlg_mtext.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/librecad/src -fcoverage-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/librecad/src -resource-dir /usr/lib/llvm-18/lib/clang/18 -D _REENTRANT -D DWGSUPPORT -D MUPARSER_STATIC -D QC_APPDIR="librecad" -D LC_VERSION=2.2.2_alpha1-607-g330d41cec -D LC_PRERELEASE=true; -D QT_NO_DEBUG -D QT_SVG_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D QT_GUI_LIB -D QT_NETWORK_LIB -D QT_CORE_LIB -I . -I /usr/include -I ../../libraries/lciconengine -I ../../libraries/libdxfrw/src -I ../../libraries/jwwlib/src -I ../../libraries/shapelib/src -I cmd -I lib/actions -I lib/actions/visual_snap -I lib/actions/options -I lib/creation -I lib/debug -I lib/engine -I lib/engine/document -I lib/engine/document/blocks -I lib/engine/document/container -I lib/engine/document/dimstyles -I lib/engine/document/entities -I lib/engine/document/entities/support -I lib/engine/document/fonts -I lib/engine/document/io -I lib/engine/document/layers -I lib/engine/document/patterns -I lib/engine/document/selection -I lib/engine/document/textstyles -I lib/engine/document/ucs -I lib/engine/document/variables -I lib/engine/document/views -I lib/engine/clipboard -I lib/engine/overlays -I lib/engine/overlays/angles_base -I lib/engine/overlays/highlight -I lib/engine/overlays/preview -I lib/engine/overlays/references -I lib/engine/overlays/crosshair -I lib/engine/overlays/info_cursor -I lib/engine/overlays/overlay_box -I lib/engine/overlays/ucs_mark -I lib/engine/undo -I lib/engine/utils -I lib/engine/settings -I lib/fileio -I lib/filters -I lib/generators -I lib/generators/makercamsvg -I lib/generators/layers -I lib/generators/image -I lib/gui -I lib/gui/grid -I lib/gui/render -I lib/gui/render/headless -I lib/gui/render/widget -I lib/information -I lib/math -I lib/modification -I lib/selection -I lib/selection/metaentity -I lib/selection/metaentity/entities -I lib/printing -I lib/properties -I actions -I actions/dock_widgets -I actions/dock_widgets/block -I actions/dock_widgets/entity_info -I actions/dock_widgets/layer -I actions/dock_widgets/library -I actions/dock_widgets/ucs_list -I actions/drawing -I actions/drawing/draw -I actions/drawing/draw/arc -I actions/drawing/draw/circle -I actions/drawing/draw/curve -I actions/drawing/draw/spline -I actions/drawing/draw/dimensions -I actions/drawing/draw/ellipse -I actions/drawing/draw/hatch -I actions/drawing/draw/image -I actions/drawing/draw/line -I actions/drawing/draw/point -I actions/drawing/draw/rect -I actions/drawing/draw/polygon -I actions/drawing/draw/misc -I actions/drawing/draw/line/shapes -I actions/drawing/draw/line/misc -I actions/drawing/draw/line/shapes/rect -I actions/drawing/draw/line/shapes/polygon -I actions/drawing/draw/polyline -I actions/drawing/draw/text -I actions/drawing/edit -I actions/drawing/info -I actions/drawing/pick -I actions/drawing/modify -I actions/drawing/pen -I actions/drawing/rel_zero -I actions/drawing/selection -I actions/drawing/snap -I actions/drawing/zoom -I actions/file -I actions/options -I actions/print_preview -I ui -I ui/action_options -I ui/action_options/circle -I ui/action_options/curve -I ui/action_options/spline -I ui/action_options/dimensions -I ui/action_options/edit -I ui/action_options/image -I ui/action_options/info -I ui/action_options/insert -I ui/action_options/line -I ui/action_options/rect -I ui/action_options/polygon -I ui/action_options/misc -I ui/action_options/modify -I ui/action_options/ellipse -I ui/action_options/other -I ui/action_options/polyline -I ui/action_options/point -I ui/action_options/print_preview -I ui/action_options/selection -I ui/action_options/snap -I ui/action_options/text -I ui/actions -I ui/components -I ui/components/relative_position_assistant -I ui/components/comboboxes -I ui/components/containers -I ui/components/creators -I ui/components/layouts -I ui/components/pen -I ui/components/status_bar -I ui/components/toolbars -I ui/components/utils -I ui/dialogs -I ui/dialogs/actions -I ui/dialogs/actions/modify -I ui/dialogs/actions/quick_selection -I ui/dialogs/modify -I ui/dialogs/entity -I ui/dialogs/creators -I ui/dialogs/file -I ui/dialogs/file/export -I ui/dialogs/file/export/layers -I ui/dialogs/file/export/image -I ui/dialogs/file/export/makercam -I ui/dialogs/main -I ui/dialogs/settings -I ui/dialogs/settings/dimstyles -I ui/dialogs/settings/dimstyles/dimstyle_manager -I ui/dialogs/settings/dimstyles/dimstyle_manager/support -I ui/dialogs/settings/options_device -I ui/dialogs/settings/options_drawing -I ui/dialogs/settings/options_general -I ui/dialogs/settings/options_widget -I ui/dialogs/settings/shortcuts -I ui/dock_widgets -I ui/dock_widgets/block_widget -I ui/dock_widgets/command_line -I ui/dock_widgets/entity_info -I ui/dock_widgets/layer_widget -I ui/dock_widgets/layers_tree -I ui/dock_widgets/library_widget -I ui/dock_widgets/pen_palette -I ui/dock_widgets/pen_wizard -I ui/dock_widgets/property_sheet -I ui/dock_widgets/property_sheet/lib -I ui/dock_widgets/property_sheet/lib/properties -I ui/dock_widgets/property_sheet/lib/view -I ui/dock_widgets/property_sheet/lib/view/edit -I ui/dock_widgets/property_sheet/lib/widgets -I ui/dock_widgets/property_sheet/lib/widgets/sheet -I ui/dock_widgets/property_sheet/metaentity -I ui/dock_widgets/property_sheet/metaentity/entities -I ui/dock_widgets/property_sheet/metaentity/entities/document -I ui/dock_widgets/property_sheet/properties -I ui/dock_widgets/property_sheet/properties/action -I ui/dock_widgets/property_sheet/properties/bool -I ui/dock_widgets/property_sheet/properties/color -I ui/dock_widgets/property_sheet/properties/double -I ui/dock_widgets/property_sheet/properties/enum -I ui/dock_widgets/property_sheet/properties/int -I ui/dock_widgets/property_sheet/properties/layer -I ui/dock_widgets/property_sheet/properties/linetype -I ui/dock_widgets/property_sheet/properties/linewidth -I ui/dock_widgets/property_sheet/properties/rect -I ui/dock_widgets/property_sheet/properties/rsvector -I ui/dock_widgets/property_sheet/properties/string -I ui/dock_widgets/views_list -I ui/dock_widgets/ucs_list -I ui/dock_widgets/workspaces -I ui/dock_widgets/cad -I ui/main -I ui/main/init -I ui/main/persistence -I ui/main/release_check -I ui/main/support -I ui/main/fontviewer -I ui/main/workspaces -I ui/view -I main -I main/console_dxf2pdf -I test -I plugins -I ../res -I ../res/arrows -I ../res/controls -I ../res/dxf -I ../res/gdt -I ../res/icons -I ../res/images -I ../../../Qt/6.9.0/gcc_64/include -I ../../../Qt/6.9.0/gcc_64/include/QtSvg -I ../../../Qt/6.9.0/gcc_64/include/QtPrintSupport -I ../../../Qt/6.9.0/gcc_64/include/QtWidgets -I ../../../Qt/6.9.0/gcc_64/include/QtGui -I ../../../Qt/6.9.0/gcc_64/include/QtNetwork -I ../../../Qt/6.9.0/gcc_64/include/QtCore -I ../../generated/librecad/moc -I ../../generated/librecad/ui -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-08-04-154929-5069-1 -x c++ ui/dialogs/entity/qg_dlg_mtext.cpp
1/****************************************************************************
2**
3** This file is part of the LibreCAD project, a 2D CAD program
4**
5** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7**
8**
9** This file may be distributed and/or modified under the terms of the
10** GNU General Public License version 2 as published by the Free Software
11** Foundation and appearing in the file gpl-2.0.txt included in the
12** packaging of this file.
13**
14** This program is distributed in the hope that it will be useful,
15** but WITHOUT ANY WARRANTY; without even the implied warranty of
16** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17** GNU General Public License for more details.
18**
19** You should have received a copy of the GNU General Public License
20** along with this program; if not, write to the Free Software
21** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22**
23** This copyright notice MUST APPEAR in all copies of the script!
24**
25**********************************************************************/
26
27
28#include "qg_dlg_mtext.h"
29
30#include <QFileDialog>
31#include <QTextBlock>
32#include <QTextBlockFormat>
33#include <QTextCursor>
34
35#include "lc_textbidi.h"
36#include "lc_linemath.h"
37#include "rs_font.h"
38#include "rs_fontlist.h"
39#include "rs_graphic.h"
40#include "rs_settings.h"
41
42using lc::textbidi::mirrorByLine;
43
44/*
45 * Constructs a QG_DlgMText as a child of 'parent', with the
46 * name 'name' and widget flags set to 'f'.
47 *
48 * The dialog will by default be modeless, unless you set 'modal' to
49 * true to construct a modal dialog.
50 */
51QG_DlgMText::QG_DlgMText(QWidget *parent, LC_GraphicViewport *viewport, RS_MText* mtext, bool forNew)
52 :LC_EntityPropertiesDlg(parent, "MTextProperties", viewport){
53 setupUi(this);
54 m_alignmentButtons = {{bTL, bTC, bTR, bML, bMC, bMR, bBL, bBC, bBR}};
55 init();
56 setEntity(mtext, forNew);
57}
58
59QG_DlgMText::~QG_DlgMText()
60{
61 try {
62 destroy();
63 } catch(...)
64 {}
65}
66
67/*
68 * Sets the strings of the subwidgets using the current
69 * language.
70 */
71void QG_DlgMText::languageChange(){
72 retranslateUi(this);
73}
74
75void QG_DlgMText::init() {
76 cbFont->init();
77 m_font = nullptr;
78 m_entity = nullptr;
79 m_isNew = false;
80 updateUniCharComboBox(0);
81 updateUniCharButton(0);
82
83 /*
84 * Ensure tabbing order of the widgets is as we would like. Using the Edit
85 * Tab Order tool in Qt Designer omits the "pen" compound widget from the
86 * tabbing list (as the tool is not aware that this user-written widget is a
87 * tabbable thing). The .ui file can be manually edited, but then if Qt
88 * Designer is used again to alter the layout, the pen widget gets dropped
89 * out of the order once more. Seems that the only reliable way of ensuring
90 * the order is correct is to set it programmatically.
91 */
92 std::array<QWidget*,31> buttons{cbLayer, wPen, cbFont,
93 leHeight, cbDefault, leLineSpacingFactor,
94 teText, bTL, bTC,
95 bTR, bML, bMC,
96 bMR, bBL, bBC,
97 bBR, leAngle, rbLeftToRight,
98 rbRightToLeft, cbSymbol, cbUniPage,
99 cbUniChar, bUnicode, buttonBox,
100 bClear, bLoad, bSave,
101 bCut, bCopy, bPaste};
102 // the order is cyclic
103 buttons[30] = buttons.front();
104 for (auto it = std::next(buttons.cbegin()); it != buttons.cend(); ++it) {
105 setTabOrder(*std::prev(it), *it);
106 }
107
108 /*
109 * We are using the frame colour of the teText QTextEdit widget to indicate when
110 * this is the selected widget - there is no automatic highlight of this widget
111 * class on selection. Ensure that known state of frame on start, and install Qt
112 * Event filter in order to get callback on focus in/out of the widget.
113 */
114 teText->setLineWidth(2);
115 teText->setMidLineWidth(0);
116 teText->setFrameStyle(QFrame::Box|QFrame::Plain);
117 teText->installEventFilter(this);
118
119 // events
120 connect(rbLeftToRight, &QRadioButton::toggled, this, &QG_DlgMText::layoutDirectionChanged);
121}
122
123
124void QG_DlgMText::updateUniCharComboBox(int) const {
125 const QString t = cbUniPage->currentText();
126 const int i1 = t.indexOf('-');
127 const int i2 = t.indexOf(']');
128 const int min = t.mid(1, i1-1).toInt(nullptr, 16);
129 const int max = t.mid(i1+1, i2-i1-1).toInt(nullptr, 16);
130
131 cbUniChar->clear();
132 for (int c=min; c<=max; c++) {
133 cbUniChar->addItem(QString{"[%1] %2"}.arg(c, 4, 16, QChar{'0'}).arg(QChar{c}));
134 }
135}
136
137//set saveText to false, so, settings won't be saved during destroy, feature request#3445306
138void QG_DlgMText::reject() {
139 m_saveSettings=false;
140 QDialog::reject();
141}
142
143void QG_DlgMText::destroy() const {
144 if (m_saveSettings) {
145 LC_GROUP_GUARDauto _guard_settings = RS_Settings::instance()->beginGroupGuard("Draw");
146 {
147 LC_SETRS_Settings::instance()->write("TextHeight", leHeight->text());
148 LC_SETRS_Settings::instance()->write("TextFont", cbFont->currentText());
149 LC_SETRS_Settings::instance()->write("TextDefault", cbDefault->isChecked());
150 LC_SETRS_Settings::instance()->write("TextAlignment", getAlignment());
151 //RS_SETTINGS->writeEntry("/TextLetterSpacing", leLetterSpacing->text());
152 //RS_SETTINGS->writeEntry("/TextWordSpacing", leWordSpacing->text());
153 LC_SETRS_Settings::instance()->write("TextLineSpacingFactor", leLineSpacingFactor->text());
154 const bool ltr = rbLeftToRight->isChecked();
155 const QString visible = teText->toPlainText();
156 LC_SETRS_Settings::instance()->write("TextString", ltr ? visible : mirrorByLine(visible));
157 //RS_SETTINGS->writeEntry("/TextShape", getShape());
158 LC_SETRS_Settings::instance()->write("TextAngle", leAngle->text());
159 //RS_SETTINGS->writeEntry("/TextRadius", leRadius->text());
160 LC_SETRS_Settings::instance()->write("TextLeftToRight", ltr);
161 }
162 }
163}
164
165/**
166 * Sets the text m_entity represented by this dialog.
167 */
168void QG_DlgMText::setEntity(RS_MText* t, const bool isNew) {
169 m_entity = t;
170 m_isNew = isNew;
171
172 QString font;
173 QString height;
174 QString def;
175 QString alignment;
176 //QString letterSpacing;
177 //QString wordSpacing;
178 QString lineSpacingFactor;
179 QString str;
180 //QString shape;
181 QString angle;
182 bool leftToRight = locale().textDirection() == Qt::LeftToRight;
Value stored to 'leftToRight' during its initialization is never read
183
184 if (isNew) {
185 wPen->hide();
186 lLayer->hide();
187 cbLayer->hide();
188 LC_GROUPRS_Settings::instance()->beginGroup("Draw");
189 {
190 //default font depending on locale
191 //default font depending on locale (RLZ-> check this: QLocale::system().name() returns "fr_FR")
192 // QByteArray iso = RS_System::localeToISO(QLocale::system().name().toLocal8Bit());
193 font = LC_GET_STRRS_Settings::instance()->readStr("TextFont", RS_FontList::getDefaultFont());
194 height = LC_GET_STRRS_Settings::instance()->readStr("TextHeight", "1.0");
195 def = LC_GET_STRRS_Settings::instance()->readStr("TextDefault", "1");
196 alignment = LC_GET_STRRS_Settings::instance()->readStr("TextAlignment", "1");
197 //letterSpacing = RS_SETTINGS->readEntry("/TextLetterSpacing", "0");
198 //wordSpacing = RS_SETTINGS->readEntry("/TextWordSpacing", "0");
199 lineSpacingFactor = LC_GET_STRRS_Settings::instance()->readStr("TextLineSpacingFactor", "1");
200 str = LC_GET_STRRS_Settings::instance()->readStr("TextString", "");
201 //shape = RS_SETTINGS->readEntry("/TextShape", "0");
202 angle = LC_GET_STRRS_Settings::instance()->readStr("TextAngle", "0");
203 //radius = RS_SETTINGS->readEntry("/TextRadius", "10");
204 leftToRight = LC_GET_BOOLRS_Settings::instance()->readBool("TextLeftToRight", true);
205 }
206 } else {
207 font = m_entity->getStyle();
208 setFont(font);
209 height = QString::number(m_entity->getHeight());
210 if (m_font != nullptr) {
211 if (LC_LineMath::isSameLength(m_font->getLineSpacingFactor(),m_entity->getLineSpacingFactor())) {
212 def = "1";
213 } else {
214 def = "0";
215 }
216 }
217 alignment = QString::number(m_entity->getAlignment());
218 //QString letterSpacing = RS_SETTINGS->readEntry("/TextLetterSpacing", "0");
219 //QString wordSpacing = RS_SETTINGS->readEntry("/TextWordSpacing", "0");
220 lineSpacingFactor = QString::number(m_entity->getLineSpacingFactor());
221
222/* // Doesn't make sense. We don't want to show native DXF strings in the Dialog.
223#if defined(OOPL_VERSION) && defined(Q_WS_WIN)
224 QCString iso = RS_System::localeToISO( QTextCodec::locale() );
225 QTextCodec *codec = QTextCodec::codecForName(iso);
226 if (codec) {
227 str = codec->toUnicode(RS_FilterDXF::toNativeString(text->getText().local8Bit()));
228 } else {
229 str = RS_FilterDXF::toNativeString(text->getText().local8Bit());
230 }
231#else*/
232 str = m_entity->getText();
233//#endif
234 //QString shape = RS_SETTINGS->readEntry("/TextShape", "0");
235
236 const double wcsAngle = m_entity->getAngle();
237 angle = toUIAngleDeg(wcsAngle);
238
239 RS_Graphic* graphic = m_entity->getGraphic();
240 if (graphic != nullptr) {
241 const auto layerList = graphic->getLayerList();
242 cbLayer->init(*layerList, false, false);
243 }
244
245 RS_Layer* layer = m_entity->getLayer(false);
246 if (layer != nullptr) {
247 cbLayer->setLayer(*layer);
248 }
249
250 wPen->setPen(m_entity, layer, tr("Pen"));
251 leftToRight = m_entity->getDrawingDirection() == RS_MTextData::LeftToRight;
252 }
253
254 cbDefault->setChecked(def=="1");
255 setFont(font);
256 leHeight->setText(height);
257 const unsigned index = alignment.toUInt() - 1;
258 setAlignment(*m_alignmentButtons[index%m_alignmentButtons.size()]);
259 if (def!="1" || m_font==nullptr) {
260 //leLetterSpacing->setText(letterSpacing);
261 //leWordSpacing->setText(wordSpacing);
262 leLineSpacingFactor->setText(lineSpacingFactor);
263 } else {
264 //leLetterSpacing->setText(font->getLetterSpacing());
265 //leWordSpacing->setText(font->getWordSpacing());
266 leLineSpacingFactor->setText(
267 QString::number(m_font->getLineSpacingFactor()));
268 }
269 teText->setText(leftToRight ? str : mirrorByLine(str));
270 //setShape(shape.toInt());
271 leAngle->setText(angle);
272 //leRadius->setText(radius);
273 teText->setFocus();
274 teText->selectAll();
275 // Block the radio buttons' toggled signal: layoutDirectionChanged is
276 // wired to rbLeftToRight and would re-mirror the buffer we just set.
277 {
278 QSignalBlocker bL(rbLeftToRight);
279 QSignalBlocker bR(rbRightToLeft);
280 rbLeftToRight->setChecked(leftToRight);
281 rbRightToLeft->setChecked(!leftToRight);
282 }
283 applyDirectionVisuals();
284}
285
286void QG_DlgMText::layoutDirectionChanged() const
287{
288 const bool leftToRight = rbLeftToRight->isChecked();
289 rbRightToLeft->setChecked(!leftToRight);
290
291 // Capture the cursor's (block, column, blockLength) BEFORE the mirror so
292 // we can map back to the mirrored column afterwards. Block structure is
293 // preserved by per-line mirroring (newlines unchanged), so block number
294 // and length stay the same — only the column flips within its block.
295 auto capture = [](const QTextDocument *doc, int pos) {
296 const QTextBlock block = doc->findBlock(pos);
297 const int blockNum = block.blockNumber();
298 int blockLen = block.length();
299 if (block.next().isValid())
300 --blockLen; // strip trailing newline
301 const int col = pos - block.position();
302 return std::tuple<int, int, int>{blockNum, col, blockLen};
303 };
304 const QTextCursor oldCursor = teText->textCursor();
305 const auto anchorInfo = capture(teText->document(), oldCursor.anchor());
306 const auto posInfo = capture(teText->document(), oldCursor.position());
307
308 // Flip the visible buffer so the same logical text now reads in the
309 // newly-selected direction. The entity's text field is unchanged here;
310 // it gets rewritten in updateEntity() from the (mirrored-back) buffer.
311 {
312 QSignalBlocker textBlocker(teText);
313 const QString visible = teText->toPlainText();
314 teText->setPlainText(mirrorByLine(visible));
315 }
316
317 // Restore cursor: the column index flips within its block (length is
318 // identical pre- and post-mirror, so col → blockLen - col).
319 auto restore = [](const QTextDocument *doc,
320 const std::tuple<int, int, int> &info) -> int {
321 const auto [blockNum, col, blockLen] = info;
322 const QTextBlock block = doc->findBlockByNumber(blockNum);
323 if (!block.isValid())
324 return 0;
325 return block.position() + (blockLen - col);
326 };
327 {
328 QTextCursor c = teText->textCursor();
329 c.setPosition(restore(teText->document(), anchorInfo));
330 c.setPosition(restore(teText->document(), posInfo),
331 QTextCursor::KeepAnchor);
332 teText->setTextCursor(c);
333 }
334
335 if (m_entity != nullptr) {
336 m_entity->setDrawingDirection(leftToRight ? RS_MTextData::LeftToRight
337 : RS_MTextData::RightToLeft);
338 }
339 applyDirectionVisuals();
340}
341
342void QG_DlgMText::applyDirectionVisuals() const {
343 const bool leftToRight = rbLeftToRight->isChecked();
344 const Qt::LayoutDirection direction =
345 leftToRight ? Qt::LeftToRight : Qt::RightToLeft;
346 teText->setLayoutDirection(direction);
347
348 QTextDocument *doc = teText->document();
349 if (doc == nullptr)
350 return;
351
352 QTextOption option = doc->defaultTextOption();
353 option.setTextDirection(direction);
354 doc->setDefaultTextOption(option);
355
356 // Stamp per-block layout direction so already-typed blocks reflow now —
357 // setDefaultTextOption alone only governs future content.
358 QSignalBlocker textBlocker(teText);
359 QTextCursor cursor(doc);
360 cursor.beginEditBlock();
361 cursor.movePosition(QTextCursor::Start);
362 do {
363 QTextBlockFormat fmt = cursor.blockFormat();
364 fmt.setLayoutDirection(direction);
365 cursor.setBlockFormat(fmt);
366 } while (cursor.movePosition(QTextCursor::NextBlock));
367 cursor.endEditBlock();
368
369 teText->update();
370}
371
372/**
373 * Updates the text m_entity represented by the dialog to fit the choices of the user.
374 */
375void QG_DlgMText::updateEntity() {
376 if (m_entity == nullptr) {
377 return;
378 }
379
380 m_entity->setStyle(cbFont->currentText());
381 m_entity->setHeight(leHeight->text().toDouble());
382
383 //fix for windows (causes troubles if locale returns en_us):
384 /*#if defined(OOPL_VERSION) && defined(Q_WS_WIN)
385 QCString iso = RS_System::localeToISO( QTextCodec::locale() );
386 text->setText(
387 RS_FilterDXF::toNativeString(
388 QString::fromLocal8Bit( QTextCodec::codecForName( iso )->fromUnicode( teText->text() ) )
389 )
390 );
391#else*/
392 {
393 const bool ltr = rbLeftToRight->isChecked();
394 const QString visible = teText->toPlainText();
395 m_entity->setText(ltr ? visible : mirrorByLine(visible));
396 }
397 //#endif
398 //text->setLetterSpacing(leLetterSpacing.toDouble());
399 m_entity->setLineSpacingFactor(leLineSpacingFactor->text().toDouble());
400 m_entity->setAlignment(getAlignment());
401
402 const double wcsAngle = toWCSAngle(leAngle, m_entity->getAngle());
403 m_entity->setAngle(wcsAngle);
404
405 m_entity->setDrawingDirection(rbLeftToRight->isChecked() ? RS_MTextData::LeftToRight : RS_MTextData::RightToLeft);
406 if (!m_isNew) {
407 m_entity->setPen(wPen->getPen());
408 m_entity->setLayer(cbLayer->getLayer());
409 }
410 m_entity->update();
411}
412
413size_t QG_DlgMText::alignmentButtonIdex(const QToolButton* button) const
414{
415 const auto it = std::find(m_alignmentButtons.cbegin(), m_alignmentButtons.cend(), button);
416 return it == m_alignmentButtons.cend() ? 0 : std::distance(m_alignmentButtons.cbegin(), it);
417}
418
419void QG_DlgMText::setAlignmentTL() {
420 setAlignment(*bTL);
421}
422
423void QG_DlgMText::setAlignmentTC() {
424 setAlignment(*bTC);
425}
426
427void QG_DlgMText::setAlignmentTR() {
428 setAlignment(*bTR);
429}
430
431void QG_DlgMText::setAlignmentML() {
432 setAlignment(*bML);
433}
434
435void QG_DlgMText::setAlignmentMC() {
436 setAlignment(*bMC);
437}
438
439void QG_DlgMText::setAlignmentMR() {
440 setAlignment(*bMR);
441}
442
443void QG_DlgMText::setAlignmentBL() {
444 setAlignment(*bBL);
445}
446
447void QG_DlgMText::setAlignmentBC() {
448 setAlignment(*bBC);
449}
450
451void QG_DlgMText::setAlignmentBR() {
452 setAlignment(*bBR);
453}
454
455void QG_DlgMText::setAlignment(QToolButton& button) {
456 button.setChecked(true);
457}
458
459int QG_DlgMText::getAlignment() const {
460
461 const auto it = std::find_if(m_alignmentButtons.cbegin(), m_alignmentButtons.cend(), [](const QToolButton* button){
462 return button->isChecked();});
463 const int index = (it == m_alignmentButtons.cend()) ? 0 : std::distance(m_alignmentButtons.cbegin(), it);
464 return index + 1;
465}
466
467void QG_DlgMText::setFont(const QString& f) {
468 int index = cbFont->findText(f);
469
470 // Issue #2069: default to the last font, likely unicode fonts
471 if (index == -1) {
472 index = cbFont->findText("unicode");
473 }
474 if (index >= 0) {
475 cbFont->setCurrentIndex(index);
476 m_font = cbFont->getFont();
477 defaultChanged(false);
478 }
479}
480
481void QG_DlgMText::defaultChanged(bool) const {
482 if (cbDefault->isChecked() && m_font != nullptr) {
483 leLineSpacingFactor->setText(
484 QString::number(m_font->getLineSpacingFactor()));
485 }
486}
487
488void QG_DlgMText::loadText() {
489 const QString fn = QFileDialog::getOpenFileName(this);
490 if (!fn.isEmpty()) {
491 load(fn);
492 }
493}
494
495void QG_DlgMText::load(const QString& fn) const {
496 QFile f(fn);
497 if (!f.open(QIODevice::ReadOnly)) {
498 return;
499 }
500
501 QTextStream ts(&f);
502 const QString contents = ts.readAll();
503 const bool ltr = rbLeftToRight->isChecked();
504 teText->setText(ltr ? contents : mirrorByLine(contents));
505}
506
507void QG_DlgMText::saveText() {
508 const QString fn = QFileDialog::getSaveFileName(this);
509 if (!fn.isEmpty()) {
510 save(fn);
511 }
512}
513
514void QG_DlgMText::save(const QString& fn) const {
515 const bool ltr = rbLeftToRight->isChecked();
516 const QString visible = teText->toPlainText();
517 const QString text = ltr ? visible : mirrorByLine(visible);
518 QFile f(fn);
519 if (f.open(QIODevice::WriteOnly)) {
520 QTextStream t(&f);
521 t << text;
522 f.close();
523 }
524}
525
526void QG_DlgMText::insertSymbol(int) const {
527 const QString str = cbSymbol->currentText();
528 const int i=str.indexOf('(');
529 if (i!=-1) {
530 teText->textCursor().insertText(str.mid(i+1, 1));
531 }
532}
533
534void QG_DlgMText::updateUniCharButton(int) const {
535 const QString t = cbUniChar->currentText();
536 const int i1 = t.indexOf(']');
537 const int c = t.mid(1, i1-1).toInt(nullptr, 16);
538 bUnicode->setText(QString{QChar{c}});
539}
540
541void QG_DlgMText::insertChar() const {
542 const QString t = cbUniChar->currentText();
543 const int i1 = t.indexOf(']');
544 const int c = t.mid(1, i1-1).toInt(nullptr, 16);
545 teText->textCursor().insertText( QString{QChar{c}} );
546}
547
548/*
549 * Event filter is used to detect focus in/out of the teText QTextEdit widget.
550 * On focus in set the widget frame highlighted, on focus out revert to normal.
551 */
552bool QG_DlgMText::eventFilter(QObject *obj, QEvent *event) {
553 const int type = event->type();
554 if (type == QEvent::FocusIn) {
555 if (obj == teText) {
556 QPalette palette = teText->palette();
557 const QColor color = palette.color(QPalette::Highlight);
558 palette.setColor(QPalette::WindowText, color);
559 teText->setPalette(palette);
560 }
561 } else if (type == QEvent::FocusOut) {
562 if (obj == teText) {
563 QPalette palette = teText->palette();
564 const QColor color = palette.color(QPalette::Dark);
565 palette.setColor(QPalette::WindowText, color);
566 teText->setPalette(palette);
567 }
568 }
569 return false;
570}