Bug Summary

File:librecad/src/ui/view/qg_graphicview.cpp
Warning:line 1353, column 14
Value stored to 'showSnapIndicatorShape' 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_graphicview.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/view/qg_graphicview.cpp
1/****************************************************************************
2**
3** This file is part of the LibreCAD project, a 2D CAD program
4**
5** Copyright (C) 2010-2011 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#include "qg_graphicview.h"
28
29#include <QGridLayout>
30#include <QMenu>
31#include <QNativeGestureEvent>
32#include <QPoint>
33#include <QPointingDevice>
34#include <QTimer>
35#include <cstdlib>
36#include <iostream>
37
38#include "lc_action_modify_move_adjust.h"
39#include "lc_action_select_single.h"
40#include "lc_actioncontext.h"
41#include "lc_eventhandler.h"
42#include "lc_graphicviewport.h"
43#include "lc_graphicviewrenderer.h"
44#include "lc_overlayentitiescontainer.h"
45#include "lc_quickinfowidget.h"
46#include "lc_rect.h"
47#include "lc_relative_point_input_widget.h"
48#include "lc_relative_position_editing_widget.h"
49#include "lc_ucs_mark.h"
50#include "lc_undosection.h"
51#include "qc_applicationwindow.h"
52#include "qg_blockwidget.h"
53#include "qg_scrollbar.h"
54#include "rs.h"
55#include "rs_actiondefault.h"
56#include "rs_blocklist.h"
57#include "rs_debug.h"
58#include "rs_dialogfactoryinterface.h"
59#include "rs_entity.h"
60#include "rs_entitycontainer.h"
61#include "rs_graphic.h"
62#include "rs_insert.h"
63#include "rs_selection.h"
64#include "rs_settings.h"
65
66#ifdef EMU_C99
67#include "emu_c99.h"
68#endif
69
70namespace {
71 // Issue #1765: set default cursor size: 32x32
72 constexpr int g_cursorSize = 32; // fixme - sand - move to common public place
73 // Issue #1787: cursor hot spot at center by using hotX=hotY=-1
74 constexpr int HOTSPOT_XY = -1;
75
76 // maximum length for displayed block name in context menu
77 constexpr int g_MaxBlockNameLength = 40; // fixme - sand - move to common public place
78
79 /*
80 * The zoomFactor effects how quickly the scroll wheel will zoom in & out.
81 *
82 * Benchmarks:
83 * 1.250 - the original; fast & usable, but seems a choppy & a bit 'jarring'
84 * 1.175 - still a bit choppy
85 * 1.150 - smoother than the original, but still 'quick' enough for good navigation.
86 * 1.137 - seems to work well for me
87 * 1.125 - about the lowest that would be acceptable and useful, a tad on the slow side for me
88 * 1.100 - a very slow & deliberate zooming, but feels very "cautious", "controlled", "safe", and "precise".
89 * 1.000 - goes nowhere. :)
90 */
91 constexpr double zoomFactor = 1.137; // fixme - to settings
92 // zooming factor is wheel angle delta divided by this factor
93 constexpr double ZOOM_WHEEL_DIVISOR = 200.; // fixme - to settings
94
95 // Helper function to test validity of a rect
96 bool withinValidRange(const double x) {
97 return x >= RS_MINDOUBLE-1.0E+10 && x <= RS_MAXDOUBLE1.0E+10;
98 }
99
100 bool withinValidRange(const RS_Vector& vp) {
101 return vp.valid && withinValidRange(vp.x) && withinValidRange(vp.y);
102 }
103
104 bool isRectValid(const RS_Vector& vpMin, const RS_Vector& vpMax) {
105 return withinValidRange(vpMin) && withinValidRange(vpMax) && vpMin.x < vpMax.x && vpMin.y < vpMax.y && vpMin.x + 1e6 >= vpMax.x &&
106 vpMin.y + 1e6 >= vpMax.y;
107 }
108}
109
110/**
111 * @brief snapEntity find the closest entity
112 * @param view
113 * @param event
114 * @param view - the graphic view
115 * @param event - the mouse event
116 * @return RS_Entity* - the closest entity within the range of g_cursorSize
117 * returns nullptr, if no entity is found in range
118 */
119RS_Entity* snapEntity(const QG_GraphicView& view, const QMouseEvent* event) {
120 if (event == nullptr) {
121 return nullptr;
122 }
123 const auto doc = view.getDocument();
124 if (doc == nullptr) {
125 // how it might be???
126 return nullptr;
127 }
128 const QPointF mapped = event->pos();
129 double distance = RS_MAXDOUBLE1.0E+10;
130 const auto viewPort = view.getViewPort();
131
132 const auto pos = viewPort->toWorldFromUi(mapped.x(), mapped.y());
133 const auto entity = doc->getNearestEntity(pos, &distance, RS2::ResolveNone);
134
135 return (viewPort->toGuiDX(distance) <= g_cursorSize) ? entity : nullptr;
136}
137
138// fixme - sand - remove, not needed?
139// Find an ancestor of the RS_Insert type.
140// Return nullptr, if none is found
141RS_Insert* getAncestorInsert(RS_Entity* entity) {
142 while (entity != nullptr) {
143 if (entity->rtti() == RS2::EntityInsert) {
144 RS_Insert* parent = getAncestorInsert(entity->getParent());
145 return parent != nullptr ? parent : static_cast<RS_Insert*>(entity);
146 }
147 entity = entity->getParent();
148 }
149 return nullptr;
150}
151
152// fixme - sand - remove, not needed?
153// whether the current insert is part of Text
154RS_Entity* getParentText(const RS_Insert* insert) {
155 if (insert == nullptr || insert->getBlock() != nullptr || insert->getParent() == nullptr) {
156 return nullptr;
157 }
158 switch (insert->getParent()->rtti()) {
159 case RS2::EntityText:
160 case RS2::EntityMText:
161 return insert->getParent();
162 default:
163 return nullptr;
164 }
165}
166
167// Show the entity property dialog on the closest entity in range
168void QG_GraphicView::showEntityPropertiesDialog(RS_Entity* entity) const {
169 if (entity == nullptr) {
170 return;
171 }
172
173 // snap to the top selected parent
174 while (entity != nullptr && entity->getParent() != nullptr && entity->getParent()->isSelected()) {
175 entity = entity->getParent();
176 }
177
178 launchEditProperty(entity);
179}
180
181void QG_GraphicView::launchEditProperty(RS_Entity* entity) const {
182 const auto* doc = getDocument();
183 if (entity == nullptr || doc == nullptr) {
184 return;
185 }
186 editAction(*entity);
187
188 // delete any temporary highlighting duplicates of the original
189 auto* defaultAction = dynamic_cast<RS_ActionDefault*>(getEventHandler()->getDefaultAction());
190 if (defaultAction != nullptr) {
191 defaultAction->clearHighLighting();
192 }
193}
194
195// Start the edit action:
196// Edit Block for an insert
197// Edit entity, otherwise
198void QG_GraphicView::editAction(RS_Entity& entity) const {
199 const auto doc = getDocument();
200 if (doc == nullptr) {
201 // fixme - DOC - review what for this check is used
202 return;
203 }
204 switch (entity.rtti()) {
205 case RS2::EntityInsert: {
206 // fixme - sand - rework block editing. Currently insert may not be changed after insertion! Window is needed!!
207 const auto& appWindow = QC_ApplicationWindow::getAppWindow(); // fixme - sand - remove static, it just one of parents?
208 RS_BlockList* blockList = appWindow->getBlockWidget()->getBlockList();
209 RS_Block* active = (blockList != nullptr) ? blockList->getActive() : nullptr;
210 const auto* insert = static_cast<RS_Insert*>(&entity);
211 RS_Block* current = insert->getBlockForInsert();
212 if (current == active) {
213 active = nullptr;
214 }
215 else if (blockList != nullptr) {
216 blockList->activate(current);
217 }
218 /*// fixme - sand - simplify
219 std::shared_ptr<RS_Block*> scoped{
220 &active,
221 [blockList](RS_Block** pointer) {
222 if (pointer != nullptr && *pointer != nullptr && blockList != nullptr) {
223 blockList->activate(*pointer);
224 }
225 }
226 };*/
227 switchToAction(RS2::ActionBlocksEdit);
228 if (active != nullptr && blockList != nullptr) {
229 blockList->activate(active);
230 }
231 break;
232 }
233 default: {
234 m_actionContext->saveContextMenuActionContext(&entity, RS_Vector(false), entity.isSelected());
235 switchToAction(RS2::ActionModifyEntity);
236 break;
237 }
238 }
239}
240
241// Support auto-panning when the cursor is close to the view border
242struct QG_GraphicView::AutoPanData {
243 void start(const double interval, QG_GraphicView& view) {
244 delayCounter = 0;
245 panTimer = std::make_unique<QTimer>(&view);
246 panTimer->start(interval);
247 connect(panTimer.get(), &QTimer::timeout, &view, &QG_GraphicView::autoPanStep);
248 }
249
250 std::unique_ptr<QTimer> panTimer;
251
252 QPoint panOffset;
253
254 unsigned delayCounter = 0U;
255 // skip the first events, to avoid unintensional panning
256 const unsigned delayCounterMax = 10U;
257 const double panOffsetMagnitude = 20.0;
258
259 const double panTimerIntervalMinimum = 20.0;
260 const double panTimerIntervalMaximum = 100.0;
261
262 // the sensitive border of the view
263 const RS_Vector probedAreaOffset = {50 /* pixels */, 50 /* pixels */};
264};
265
266struct QG_GraphicView::UCSHighlightData {
267 std::unique_ptr<QTimer> timer;
268
269 double timerInterval = 200.0;
270 int blinkNumber = 0;
271 int maxBlinkNumber = 15;
272 bool inVisiblePhase = false;
273 RS_Vector origin;
274 double angle = 0.0;
275 bool forWCS = false;
276
277 RS_Vector savedViewOffset = RS_Vector(0, 0, 0);
278 double savedViewFactor = 0.0;
279
280 void start(const double interval, QG_GraphicView& view) {
281 if (timer == nullptr) {
282 timer = std::make_unique<QTimer>(&view);
283 connect(timer.get(), &QTimer::timeout, &view, &QG_GraphicView::ucsHighlightStep);
284 }
285 timer->start(interval);
286 }
287
288 bool mayTick() {
289 blinkNumber++;
290 inVisiblePhase = !inVisiblePhase;
291 return blinkNumber <= maxBlinkNumber;
292 }
293
294 void stop() {
295 blinkNumber = 0;
296 inVisiblePhase = false;
297 timer->stop();
298 }
299};
300
301void createViewRenderer();
302
303void QG_GraphicView::initRelativePointInputWidget() {
304 m_relativePointWidgetHolder = new LC_RelativePointInputWidget(this, m_actionContext);
305 m_relativePointWidgetHolder->setVisible(false);
306}
307
308/**
309 * Constructor.
310 */
311// fixme - sand - files - init by action context???
312QG_GraphicView::QG_GraphicView(QWidget* parent, RS_Document* doc, LC_ActionContext* actionContext)
313 : RS_GraphicView(parent, {}), m_device("Mouse"),
314 m_cursorCad(new QCursor(QPixmap(":cursors/cur_cad_bmp.png"), HOTSPOT_XY, HOTSPOT_XY)),
315 m_cursorDel(new QCursor(QPixmap(":cursors/cur_del_bmp.png"), HOTSPOT_XY, HOTSPOT_XY)),
316 m_cursorSelect(new QCursor(QPixmap(":cursors/cur_select_bmp.png"), HOTSPOT_XY, HOTSPOT_XY)),
317 m_cursorMagnifier(new QCursor(QPixmap(":cursors/cur_glass_bmp.png"), HOTSPOT_XY, HOTSPOT_XY)),
318 m_cursorHand(new QCursor(QPixmap(":cursors/cur_hand_bmp.png"), HOTSPOT_XY, HOTSPOT_XY)), m_isSmoothScrolling(false),
319 m_ucsMarkOptions{std::make_unique<LC_UCSMarkOptions>()}, m_panData{std::make_unique<AutoPanData>()},
320 m_ucsHighlightData{std::make_unique<UCSHighlightData>()} {
321 RS_DEBUGRS_Debug::instance()->print("QG_GraphicView::QG_GraphicView()..");
322
323 if (doc != nullptr) {
324 setDocument(doc);
325 doc->setGraphicView(this);
326 actionContext->setDocumentAndView(doc, this);
327 setDefaultAction(new RS_ActionDefault(actionContext));
328 }
329
330 m_actionContext = actionContext;
331
332 getViewPort()->justSetOffsetAndFactor(0, 0, 4.0);
333 getViewPort()->setBorders(10, 10, 10, 10);
334
335 setMouseTracking(true);
336 setFocusPolicy(Qt::NoFocus);
337
338 // SourceForge issue 45 (Left-mouse drag shrinks window)
339 setAttribute(Qt::WA_NoMousePropagation);
340
341 // Issue #2264: prevents macOS from applying text-related features like the Caps Lock indicator to the non-text canvas
342#ifdef Q_OS_MAC
343 setAttribute(Qt::WA_InputMethodEnabled, false); setInputMethodHints(Qt::ImhNone);
344#endif
345}
346
347void QG_GraphicView::initView() {
348 createViewRenderer();
349 initRelativePointInputWidget();
350}
351
352void QG_GraphicView::createViewRenderer() {
353 if (getViewPort() != nullptr) {
354 getViewPort()->setSize(width(), height()); // fixme - sand - merge - CHECK THIS
355 setRenderer(std::make_unique<LC_GraphicViewRenderer>(getViewPort(), this));
356 }
357}
358
359void QG_GraphicView::layerToggled(RS_Layer*) {
360 redraw(RS2::RedrawDrawing);
361}
362
363/**
364 * Destructor
365 */
366QG_GraphicView::~QG_GraphicView() {
367 try {
368 // LC_ERR << "QG_GraphicView destructor";
369 cleanUp();
370 // LC_ERR << "QG_GraphicView destructor 1";
371 }
372 catch (...) {
373 LC_ERRRS_Debug::Log(RS_Debug::D_ERROR) << __func__ << "(): received exception";
374 }
375}
376
377/**
378 * @return width of widget.
379 */
380int QG_GraphicView::getWidth() const {
381 if (m_scrollbars) {
382 return width() - m_vScrollBar->sizeHint().width();
383 }
384 return width();
385}
386
387/**
388 * @return height of widget.
389 */
390int QG_GraphicView::getHeight() const {
391 if (m_scrollbars) {
392 return height() - m_hScrollBar->sizeHint().height();
393 }
394 return height();
395}
396
397/**
398 * Sets the mouse cursor to the given type.
399 */
400void QG_GraphicView::setMouseCursor(const RS2::CursorType cursorType) {
401 switch (cursorType) {
402 default: case RS2::ArrowCursor:
403 setCursor(Qt::ArrowCursor);
404 break;
405 case RS2::UpArrowCursor:
406 setCursor(Qt::UpArrowCursor);
407 break;
408 case RS2::CrossCursor:
409 setCursor(Qt::CrossCursor);
410 break;
411 case RS2::WaitCursor:
412 setCursor(Qt::WaitCursor);
413 break;
414 case RS2::IbeamCursor:
415 setCursor(Qt::IBeamCursor);
416 break;
417 case RS2::SizeVerCursor:
418 setCursor(Qt::SizeVerCursor);
419 break;
420 case RS2::SizeHorCursor:
421 setCursor(Qt::SizeHorCursor);
422 break;
423 case RS2::SizeBDiagCursor:
424 setCursor(Qt::SizeBDiagCursor);
425 break;
426 case RS2::SizeFDiagCursor:
427 setCursor(Qt::SizeFDiagCursor);
428 break;
429 case RS2::SizeAllCursor:
430 setCursor(Qt::SizeAllCursor);
431 break;
432 case RS2::BlankCursor:
433 setCursor(Qt::BlankCursor);
434 break;
435 case RS2::SplitVCursor:
436 setCursor(Qt::SplitVCursor);
437 break;
438 case RS2::SplitHCursor:
439 setCursor(Qt::SplitHCursor);
440 break;
441 case RS2::PointingHandCursor:
442 setCursor(Qt::PointingHandCursor);
443 break;
444 case RS2::ForbiddenCursor:
445 setCursor(Qt::ForbiddenCursor);
446 break;
447 case RS2::WhatsThisCursor:
448 setCursor(Qt::WhatsThisCursor);
449 break;
450 case RS2::OpenHandCursor:
451 setCursor(Qt::OpenHandCursor);
452 break;
453 case RS2::ClosedHandCursor:
454 setCursor(Qt::ClosedHandCursor);
455 break;
456 case RS2::CadCursor:
457 m_cursorHiding ? setCursor(Qt::BlankCursor) : setCursor(*m_cursorCad);
458 break;
459 case RS2::DelCursor:
460 setCursor(*m_cursorDel);
461 break;
462 case RS2::SelectCursor:
463 m_selectCursorHiding ? setCursor(Qt::BlankCursor) : setCursor(*m_cursorSelect);
464 break;
465 case RS2::MagnifierCursor:
466 setCursor(*m_cursorMagnifier);
467 break;
468 case RS2::MovingHandCursor:
469 setCursor(*m_cursorHand);
470 break;
471 }
472}
473
474/**
475 * Sets the text for the grid status widget in the left bottom corner.
476 */
477void QG_GraphicView::updateGridStatusWidget(QString text) {
478 emit gridStatusChanged(std::move(text));
479}
480
481void QG_GraphicView::dragEnterEvent(QDragEnterEvent* event) {
482 RS_GraphicView::dragEnterEvent(event);
483
484 /*
485 * fixme - sand - remove later, experiments with d&d
486 */
487 /* if (event->mimeData()->formats().contains("application/x-qabstractitemmodeldatalist")) {
488 // QStandardItemModel model;
489 // model.dropMimeData(event->mimeData(), Qt::CopyAction, 0,0, QModelIndex());
490 // auto item = model.item(0.0);
491 // LC_ERR << item->text();
492
493 QDrag::cancel();
494 QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
495 QC_ApplicationWindow::getAppWindow()->getLibraryWidget()->insert();
496 }*/
497}
498
499/**
500 * Redraws the widget.
501 */
502void QG_GraphicView::redraw(const RS2::RedrawMethod method, bool immediately) {
503 getRenderer()->invalidate(method);
504 update(); // Paint when ready to paint
505 if (immediately) {
506 repaint();
507 }
508}
509
510void QG_GraphicView::resizeEvent(QResizeEvent* e) {
511 RS_GraphicView::resizeEvent(e);
512 RS_DEBUGRS_Debug::instance()->print("QG_GraphicView::resizeEvent begin");
513 adjustOffsetControls();
514 adjustZoomControls();
515 // updateGrid();
516 // Small hack, delete the snapper during resizes
517 getViewPort()->clearOverlayDrawablesContainer(RS2::Snapper);
518 redraw();
519 RS_DEBUGRS_Debug::instance()->print("QG_GraphicView::resizeEvent end");
520}
521
522void QG_GraphicView::switchToAction(const RS2::ActionType actionType, void* data) const {
523 m_actionContext->setCurrentAction(actionType, data);
524}
525
526RS_Entity* QG_GraphicView::catchContextEntity(const QMouseEvent* event, RS_Vector& clickPos) const {
527 const auto doc = getDocument();
528 if (doc == nullptr || event == nullptr) {
529 return nullptr;
530 }
531
532 const QPointF mapped = event->pos();
533 double distance = RS_MAXDOUBLE1.0E+10;
534 const LC_GraphicViewport* viewPort = getViewPort();
535
536 clickPos = viewPort->toWorldFromUi(mapped.x(), mapped.y());
537 RS_Entity* entity = doc->getNearestEntity(clickPos, &distance, RS2::ResolveNone);
538
539 if (viewPort->toGuiDX(distance) <= g_cursorSize) {
540 return entity;
541 }
542 return nullptr;
543}
544
545bool QG_GraphicView::invokeContextMenuForMouseEvent(QMouseEvent* e) {
546 bool result = false;
547 RS_Vector clickPos;
548 RS_Entity* entity = catchContextEntity(e, clickPos);
549 const auto contextMenu = QC_ApplicationWindow::getAppWindow()->createGraphicViewContentMenu(e, this, entity, clickPos);
550 if (contextMenu != nullptr) {
551 if (!contextMenu->isEmpty()) {
552 auto actions = contextMenu->actions();
553 if (actions.size() == 1) {
554 const auto action = actions.front();
555 action->trigger();
556 result = true;
557 }
558 else {
559 contextMenu->exec(mapToGlobal(e->pos()));
560 result = true;
561 }
562 }
563 delete contextMenu;
564 }
565 return result;
566}
567
568void QG_GraphicView::mousePressEvent(QMouseEvent* event) {
569 // LC_ERR << "MOUSE PRESS";
570 // pan zoom with middle mouse button
571 if (event->button() == Qt::MiddleButton && event->modifiers() == Qt::NoModifier) {
572 switchToAction(RS2::ActionZoomPan);
573 getCurrentAction()->mousePressEvent(event);
574 }
575 else {
576 getEventHandler()->mousePressEvent(event);
577 }
578}
579
580void QG_GraphicView::mouseDoubleClickEvent(QMouseEvent* e) {
581 // LC_ERR << "MOUSE DOUBLE CLICK";
582 if (getEventHandler()->hasAction()) {
583 }
584 else {
585 const auto defaultAction = getEventHandler()->getDefaultAction();
586 RS_Vector clickPos;
587 RS_Entity* entity = catchContextEntity(e, clickPos);
588 if (entity == nullptr) {
589 if (defaultAction == nullptr) {
590 invokeContextMenuForMouseEvent(e);
591 }
592 else if (defaultAction->getStatus() == RS_ActionInterface::InitialActionStatus) {
593 invokeContextMenuForMouseEvent(e);
594 }
595 }
596 else {
597 if (e->button() == Qt::LeftButton && e->modifiers() == Qt::NoModifier) {
598 if (defaultAction == nullptr) {
599 showEntityPropertiesDialog(entity);
600 }
601 else if (defaultAction->getStatus() == RS_ActionInterface::InitialActionStatus) {
602 showEntityPropertiesDialog(entity);
603 }
604 }
605 else {
606 invokeContextMenuForMouseEvent(e);
607 }
608 }
609 }
610 /*else {
611 switch(e->button()){
612 case Qt::MiddleButton:
613 switchToAction(RS2::ActionZoomAuto);
614 break;
615 case Qt::LeftButton:
616 // double click on an entity to edit entity properties
617
618 showEntityPropertiesDialog(entity);
619 break;
620 default:
621 break;
622 }
623 }*/
624 e->accept();
625}
626
627void QG_GraphicView::mouseReleaseEvent(QMouseEvent* event) {
628 RS_DEBUGRS_Debug::instance()->print("QG_GraphicView::mouseReleaseEvent");
629
630 event->accept();
631 if (getEventHandler()->hasAction()) {
632 switch (event->button()) {
633 case Qt::RightButton: {
634 if (getEventHandler()->hasAction()) {
635 back(event->modifiers());
636 }
637 break;
638 }
639 case Qt::XButton1:
640 processEnterKey();
641 emit xbutton1_released();
642 break;
643 default:
644 getEventHandler()->mouseReleaseEvent(event);
645 break;
646 }
647 }
648 else {
649 const auto defaultAction = getEventHandler()->getDefaultAction();
650 if (defaultAction != nullptr) {
651 const int defaultActionStatus = defaultAction->getStatus();
652 if (defaultActionStatus == RS_ActionInterface::InitialActionStatus) {
653 if (isMouseReleaseEventForDefaultAction(event)) {
654 defaultAction->mouseReleaseEvent(event);
655 }
656 else {
657 invokeContextMenuForMouseEvent(event);
658 }
659 }
660 else {
661 defaultAction->mouseReleaseEvent(event);
662 }
663 }
664 else {
665 invokeContextMenuForMouseEvent(event);
666 }
667 }
668 RS_DEBUGRS_Debug::instance()->print("QG_GraphicView::mouseReleaseEvent: OK");
669}
670
671bool QG_GraphicView::isMouseReleaseEventForDefaultAction(const QMouseEvent* event) {
672 // should correspond to LC_DlgMenuAssigner::validateShortcut()
673 if (event->button() == Qt::LeftButton) {
674 const auto modifiers = event->modifiers();
675 if (modifiers == Qt::NoModifier) {
676 // select
677 return true;
678 }
679 const bool control = modifiers & Qt::ControlModifier;
680 const bool alt = modifiers & Qt::AltModifier;
681 const bool shift = modifiers & Qt::ShiftModifier;
682 if (control && !alt && !shift) {
683 // pan
684 return true;
685 }
686 if (shift && !alt && !control) {
687 // select contour
688 return true;
689 }
690 }
691 return false;
692}
693
694void QG_GraphicView::mouseMoveEvent(QMouseEvent* event) {
695 if (isClosing()) {
696 event->accept();
697 return;
698 }
699 // LC_ERR << "OWN MOUSE MOVE";
700 if (isAutoPan(event)) {
701 startAutoPanTimer(event);
702 event->accept();
703 return;
704 }
705 m_panData->panTimer.reset();
706 // handle auto-panning
707 event->accept();
708 getEventHandler()->mouseMoveEvent(event);
709}
710
711bool QG_GraphicView::proceedEvent(QEvent* event) {
712 // skip events without a default action
713 // Hatch preview in qg_dlghatch doesn't have its default action
714 if (dynamic_cast<QInputEvent*>(event) == nullptr || getDefaultAction() != nullptr) {
715 return QWidget::event(event);
716 } // LC_ERR<< "Event Skipped";
717 return true;
718}
719
720bool QG_GraphicView::event(QEvent* event) {
721 if (event->type() == QEvent::NativeGesture) {
722 const auto* nge = static_cast<QNativeGestureEvent*>(event);
723
724 if (nge->gestureType() == Qt::ZoomNativeGesture) {
725 const double v = nge->value();
726 const RS2::ZoomDirection direction = std::signbit(v) ? RS2::Out : RS2::In;
727 const double factor = 1. + std::abs(v);
728
729 // It seems the NativeGestureEvent::pos() incorrectly reports global coordinates
730 const QPointF g = mapFromGlobal(nge->globalPosition().toPoint());
731 const RS_Vector mouse = getViewPort()->toWorldFromUi(g.x(), g.y());
732 doZoom(direction, mouse, factor);
733 }
734 return true;
735 }
736 return proceedEvent(event);
737}
738
739void QG_GraphicView::doZoom(const RS2::ZoomDirection direction, const RS_Vector& center, const double zoomFactor) const {
740 if (direction == RS2::In) {
741 getViewPort()->zoomIn(zoomFactor, center);
742 }
743 else {
744 getViewPort()->zoomOut(zoomFactor, center);
745 }
746}
747
748/**
749 * support for the wacom graphic tablet.
750 */
751void QG_GraphicView::tabletEvent(QTabletEvent* e) {
752 if (testAttribute(Qt::WA_UnderMouse)) {
753#if QT_VERSION((6<<16)|(9<<8)|(0)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0))
754 switch (e->pointerType()) {
755 case QPointingDevice::PointerType::Eraser:
756 if (e->type() == QEvent::TabletRelease) {
757 if (getDocument() != nullptr) {
758 auto a = std::make_shared<LC_ActionSelectSingle>(m_actionContext);
759 setCurrentAction(a);
760 QMouseEvent ev(QEvent::MouseButtonRelease, e->position(), e->globalPosition(), Qt::LeftButton, Qt::LeftButton,
761 Qt::NoModifier); //RLZ
762 mouseReleaseEvent(&ev);
763 a->finish();
764
765 if (getDocument()->hasSelection()) {
766 switchToAction(RS2::ActionModifyDelete);
767 }
768 }
769 }
770 break;
771
772 case QPointingDevice::PointerType::Generic:
773 case QPointingDevice::PointerType::Pen:
774 case QPointingDevice::PointerType::Cursor:
775 if (e->type() == QEvent::TabletPress) {
776 QMouseEvent ev(QEvent::MouseButtonPress, e->position(), e->globalPosition(), Qt::LeftButton, Qt::LeftButton,
777 Qt::NoModifier); //RLZ
778 mousePressEvent(&ev);
779 }
780 else if (e->type() == QEvent::TabletRelease) {
781 QMouseEvent ev(QEvent::MouseButtonRelease, e->position(), e->globalPosition(), Qt::LeftButton, Qt::LeftButton,
782 Qt::NoModifier); //RLZ
783 mouseReleaseEvent(&ev);
784 }
785 else if (e->type() == QEvent::TabletMove) {
786 QMouseEvent ev(QEvent::MouseMove, e->position(), e->globalPosition(), Qt::NoButton, {}, Qt::NoModifier); //RLZ
787 mouseMoveEvent(&ev);
788 }
789 break;
790 default:
791 break;
792 }
793#else
794#if QT_VERSION((6<<16)|(9<<8)|(0)) >= QT_VERSION_CHECK(5, 15, 0)((5<<16)|(15<<8)|(0))
795 switch (e->deviceType()) {
796
797#else
798 switch (e->device()) {
799
800#endif
801 case QTabletEvent::Eraser:
802 if (e->type() == QEvent::TabletRelease) {
803 if (getDocument() != nullptr) {
804 RS_ActionSelectSingle* a = new RS_ActionSelectSingle(*getDocument(), *this);
805 setCurrentAction(a);
806 QMouseEvent ev(QEvent::MouseButtonRelease, e->position(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); //RLZ
807 mouseReleaseEvent(&ev);
808 a->finish();
809
810 if (getDocument()->hasSelection()) {
811 setCurrentAction(new RS_ActionModifyDelete(*getDocument(), *this));
812 }
813 }
814 } break; case QTabletEvent::Stylus: case QTabletEvent::Puck:
815 if (e->type() == QEvent::TabletPress) {
816 QMouseEvent ev(QEvent::MouseButtonPress, e->position(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); //RLZ
817 mousePressEvent(&ev);
818 }
819 else if (e->type() == QEvent::TabletRelease) {
820 QMouseEvent ev(QEvent::MouseButtonRelease, e->position(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); //RLZ
821 mouseReleaseEvent(&ev);
822 }
823 else if (e->type() == QEvent::TabletMove) {
824 QMouseEvent ev(QEvent::MouseMove, e->position(), Qt::NoButton, {}, Qt::NoModifier); //RLZ
825 mouseMoveEvent(&ev);
826 } break; default:
827 break;
828 }
829#endif
830 }
831
832 // a 'mouse' click:
833 /*if (e->pressure()>10 && lastPressure<10) {
834 QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
835 Qt::LeftButton, Qt::LeftButton);
836 mousePressEvent(&e);
837}
838 else if (e->pressure()<10 && lastPressure>10) {
839 QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
840 Qt::LeftButton, Qt::LeftButton);
841 mouseReleaseEvent(&e);
842} else if (lastPos!=e->pos()) {
843 QMouseEvent e(QEvent::MouseMove, e->pos(),
844 Qt::NoButton, 0);
845 mouseMoveEvent(&e);
846}
847
848 lastPressure = e->pressure();
849 lastPos = e->pos();
850 */
851}
852
853void QG_GraphicView::leaveEvent(QEvent* e) {
854 // stop auto-panning
855 m_panData->panTimer.reset();
856 getEventHandler()->mouseLeaveEvent();
857 QWidget::leaveEvent(e);
858}
859
860#if QT_VERSION((6<<16)|(9<<8)|(0)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0))
861void QG_GraphicView::enterEvent(QEnterEvent* e) {
862#else
863void QG_GraphicView::enterEvent(QEvent* e) {
864#endif
865 getEventHandler()->mouseEnterEvent();
866 QWidget::enterEvent(e);
867}
868
869void QG_GraphicView::focusOutEvent(QFocusEvent* e) {
870 QWidget::focusOutEvent(e);
871}
872
873void QG_GraphicView::focusInEvent(QFocusEvent* e) {
874 getEventHandler()->mouseEnterEvent();
875 QWidget::focusInEvent(e);
876}
877
878/**
879 * mouse wheel event. zooms in/out or scrolls when
880 * shift or ctrl is pressed.
881 */
882void QG_GraphicView::wheelEvent(QWheelEvent* e) {
883 // LC_ERR << "OWN WHEEL";
884 //RS_DEBUG->print("wheel: %d", e->delta());
885
886 //printf("state: %d\n", e->state());
887 //printf("ctrl: %d\n", Qt::ControlButton);
888
889 if (getDocument() == nullptr) {
890 return;
891 }
892
893#if QT_VERSION((6<<16)|(9<<8)|(0)) >= QT_VERSION_CHECK(5, 15, 0)((5<<16)|(15<<8)|(0))
894 // RS_Vector mouse = toGraph(e->position());
895 const QPointF& uiEventPosition = e->position();
896 const RS_Vector mouse = getViewPort()->toUCSFromGui(uiEventPosition.x(), uiEventPosition.y());
897#else
898 RS_Vector mouse = toGraph(e->position());
899#endif
900
901 if (m_device == "Trackpad") {
902 QPoint numPixels = e->pixelDelta();
903
904 // high-resolution scrolling triggers Pan instead of Zoom logic
905 m_isSmoothScrolling |= !numPixels.isNull();
906
907 if (m_isSmoothScrolling) {
908 if (e->phase() == Qt::ScrollEnd) {
909 m_isSmoothScrolling = false;
910 }
911 }
912 else // Trackpads that without high-resolution scrolling
913 // e.g. libinput-XWayland trackpads
914 {
915 numPixels = e->angleDelta() / 4;
916 }
917
918 if (!numPixels.isNull()) {
919 if (e->modifiers() == Qt::ControlModifier) {
920 // Hold ctrl to zoom. 1 % per pixel
921 const double v = (m_invertZoomDirection) ? (numPixels.y() / ZOOM_WHEEL_DIVISOR) : (-numPixels.y() / ZOOM_WHEEL_DIVISOR);
922 RS2::ZoomDirection direction;
923 if (v < 0) {
924 direction = RS2::Out;
925 }
926 else {
927 direction = RS2::In;
928 }
929
930 const double zoomFact = 1. + std::abs(v);
931 doZoom(direction, mouse, zoomFact);
932 }
933 else {
934 const int hDelta = (m_invertHorizontalScroll) ? -numPixels.x() : numPixels.x();
935 const int vDelta = (m_invertVerticalScroll) ? -numPixels.y() : numPixels.y();
936
937 // scroll by scrollbars: issue #479 (it has its own issues)
938 if (m_scrollbars) {
939 m_hScrollBar->setValue(m_hScrollBar->value() - hDelta);
940 m_vScrollBar->setValue(m_vScrollBar->value() - vDelta);
941 }
942 else {
943 getViewPort()->zoomPan(hDelta, vDelta);
944 }
945 }
946 redraw();
947 }
948 e->accept();
949 return;
950 }
951
952 if (e->angleDelta().isNull()) {
953 // A zero delta event occurs when smooth scrolling is ended. Ignore this
954 e->accept();
955 return;
956 }
957
958 bool scroll = false;
959 RS2::Direction direction = RS2::Up;
960
961 // scroll up / down:
962 const int angleDeltaY = e->angleDelta().y(); // delta for VERTICAL mouse wheel
963 int angleDeltaX = e->angleDelta().x(); // delta for HORIZONTAL mouse wheel
964
965 // for zoom, let's use just vertical scrolling, so below we'll rely on AngleDeltaY only.
966 // otherwise, horizontal scroll will not work :( Basically, that's a side-effect for porting to QT6
967 // so here let's use simpler logic
968 angleDeltaX = angleDeltaY;
969 if (e->modifiers() == Qt::ControlModifier) {
970 scroll = true;
971 direction = (angleDeltaY > 0) ? RS2::Up : RS2::Down;
972 }
973 else if (e->modifiers() == Qt::ShiftModifier) {
974 scroll = true;
975 direction = (angleDeltaX > 0) ? RS2::Left : RS2::Right;
976 }
977
978 // fixme - potentially, we can support mouses with two mouse wheels later if this will be reasonable.
979 // fixme - however, it looks as a kind of overkill - using on single vertical mouse wheel for scroll seems to be fine//
980 /*
981 if (e->modifiers() == Qt::ControlModifier) {
982 scroll = true;
983 if (angleDeltaY == 0){
984 //case Qt::Horizontal:
985 direction= (angleDeltaX > 0) ? RS2::Left : RS2::Right;
986 } else {
987 //case Qt::Vertical:
988 direction= (angleDeltaY > 0) ? RS2::Up : RS2::Down;
989 }
990 }
991 // scroll left / right:
992 else if (e->modifiers()==Qt::ShiftModifier) {
993 scroll = true;
994 if (angleDeltaY == 0){
995 //case Qt::Horizontal:
996 direction= (angleDeltaX > 0) ? RS2::Up : RS2::Down;
997 } else {
998 //case Qt::Vertical:
999 direction= (angleDeltaX > 0) ? RS2::Left : RS2::Right;
1000 }
1001 }*/
1002
1003 if (scroll && m_scrollbars) {
1004 //scroll by scrollbars: issue #479
1005
1006 int delta = 0;
1007
1008 switch (direction) {
1009 case RS2::Left:
1010 case RS2::Right:
1011 delta = (m_invertHorizontalScroll) ? -angleDeltaX : angleDeltaX;
1012 m_hScrollBar->setValue(m_hScrollBar->value() + delta);
1013 break;
1014 default:
1015 delta = (m_invertVerticalScroll) ? -angleDeltaY : angleDeltaY;
1016 m_vScrollBar->setValue(m_vScrollBar->value() + delta);
1017 break;
1018 }
1019 }
1020 // zoom in / out:
1021 else if (e->modifiers() == 0) {
1022 // LC_ERR << " AngleDelta Y " << angleDeltaY;
1023
1024 const RS2::ZoomDirection zoomDirection = ((angleDeltaY > 0) != m_invertZoomDirection) ? RS2::In : RS2::Out;
1025
1026 const QPoint viewCenter{getWidth() / 2, getHeight() / 2};
1027 const QPoint delta = viewCenter - uiEventPosition.toPoint();
1028
1029 if (getPanOnZoom()) {
1030 QCursor::setPos(mapToGlobal(viewCenter));
1031 getViewPort()->zoomPan(delta.x(), delta.y());
1032 }
1033 if (!getPanOnZoom() || !getSkipFirstZoom() || (abs(delta.x()) < 32 && abs(delta.y()) < 32)) {
1034 const RS_Vector& zoomCenter = mouse;
1035 // LC_ERR << " Mouse " << mouse << " Direction: " << (zoomDirection == RS2::In ? "In" : "Out");
1036
1037 /*// todo - well, actually this is one-shot action... and it will lead to full action processing chain in action handler
1038 // todo - are we REALLY need it there? alternatively, zoom may be part of this class)
1039 auto zoomAction = std::make_unique<RS_ActionZoomIn>(m_actionContext, zoomDirection, RS2::Both, &zoomCenter,m_scrollZoomFactor);
1040 zoomAction->trigger();*/
1041 doZoom(zoomDirection, zoomCenter, m_scrollZoomFactor);
1042 }
1043 }
1044 redraw();
1045
1046 /* QMouseEvent event
1047 {
1048 QEvent::MouseMove,
1049 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
1050 e->position(),
1051 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
1052 e->globalPosition(),
1053 #endif
1054 #else
1055 QPointF{static_cast<qreal>(e->x()), static_cast<qreal>(e->y())},
1056 #endif
1057 Qt::NoButton, Qt::NoButton, Qt::NoModifier
1058 };
1059 eventHandler->mouseMoveEvent(&event);
1060
1061 e->accept();*/
1062}
1063
1064// fixme - sand - move by keyboard support!!!
1065void QG_GraphicView::keyPressEvent(QKeyEvent* e) {
1066 if (getDocument() == nullptr) {
1067 return;
1068 }
1069 if (m_relativePointWidgetHolder->isVisible()) {
1070 return;
1071 }
1072 bool eventProcessed = false; // due to some weird reasons, even incoming event is already accepted (Win10)... so using own flag
1073 if (m_allowScrollAndMoveAdjustByKeys) {
1074 RS2::Direction direction = RS2::Up;
1075 bool scroll = e->modifiers() == Qt::NoModifier;
1076 const bool shift = e->modifiers() & Qt::ShiftModifier;
1077 const bool control = e->modifiers() & Qt::ControlModifier;
1078
1079 bool move = shift || control;
1080
1081 switch (e->key()) {
1082 case Qt::Key_Left:
1083 direction = RS2::Right;
1084 break;
1085 case Qt::Key_Right:
1086 direction = RS2::Left;
1087 break;
1088 case Qt::Key_Up:
1089 direction = RS2::Up;
1090 break;
1091 case Qt::Key_Down:
1092 direction = RS2::Down;
1093 break;
1094 default:
1095 scroll = false;
1096 move = false;
1097 break;
1098 }
1099
1100 if (scroll) {
1101 getViewPort()->zoomScroll(direction);
1102 eventProcessed = true;
1103 e->accept();
1104 }
1105 else if (move) {
1106 LC_ActionModifyMoveAdjust::MovementInfo::Step step = LC_ActionModifyMoveAdjust::MovementInfo::GRID;
1107 if (control) {
1108 if (shift) {
1109 step = LC_ActionModifyMoveAdjust::MovementInfo::META_GRID;
1110 }
1111 else {
1112 step = LC_ActionModifyMoveAdjust::MovementInfo::SUB_GRID;
1113 }
1114 }
1115 else if (shift) {
1116 step = LC_ActionModifyMoveAdjust::MovementInfo::GRID;
1117 }
1118
1119 LC_ActionModifyMoveAdjust::MovementInfo info(direction, step);
1120 switchToAction(RS2::ActionModifyMoveAdjust, &info);
1121 eventProcessed = true;
1122 e->accept();
1123 }
1124 }
1125 if (!eventProcessed) {
1126 getEventHandler()->keyPressEvent(e);
1127 }
1128}
1129
1130void QG_GraphicView::keyReleaseEvent(QKeyEvent* e) {
1131 getEventHandler()->keyReleaseEvent(e);
1132}
1133
1134/**
1135* Called whenever the graphic view has changed.
1136* Adjusts the scrollbar ranges / steps.
1137*/
1138void QG_GraphicView::adjustOffsetControls() {
1139 if (!m_scrollbars) {
1140 return;
1141 }
1142
1143 std::unique_lock<std::mutex> lock(m_scrollbarMutex, std::defer_lock);
1144 if (!lock.try_lock()) {
1145 return;
1146 }
1147
1148 if (getDocument() == nullptr || m_hScrollBar == nullptr || m_vScrollBar == nullptr) {
1149 return;
1150 }
1151 LC_LOGRS_Debug::Log() << __func__ << "(): begin";
1152
1153 // Same border source as LC_GraphicViewport::zoomAuto / MDI tile zoom —
1154 // not forcedCalculateBorders, which used to pin empty INSERT/text to (0,0)
1155 // and inflate the scroll range after graphic-view resize.
1156 auto *viewport = getViewPort();
1157 RS_Vector vpMin;
1158 RS_Vector vpMax;
1159 if (viewport != nullptr
1160 && viewport->getViewBorders(vpMin, vpMax)) {
1161 // view framing envelope (dense core for sheet-scale drawings)
1162 } else if (getDocument() != nullptr) {
1163 getDocument()->calculateBorders();
1164 vpMin = getDocument()->getMin();
1165 vpMax = getDocument()->getMax();
1166 }
1167
1168 // no drawing yet - still allow to scroll
1169 if (!isRectValid(vpMin, vpMax)) {
1170 vpMin = RS_Vector(-10, -10);
1171 vpMax = RS_Vector(100, 100);
1172 }
1173
1174 const int ox = getViewPort()->getOffsetX();
1175 const int oy = getViewPort()->getOffsetY();
1176
1177 int minVal = static_cast<int>(-1.25 * getWidth() - ox);
1178 int maxVal = static_cast<int>(0.25 * getWidth() - ox);
1179
1180 LC_LOGRS_Debug::Log() << __func__ << "(): x scrollbar range[" << minVal << ", " << maxVal << "]: " << getViewPort()->getOffsetX();
1181 if (minVal <= maxVal) {
1182 m_hScrollBar->setRange(minVal, maxVal);
1183 }
1184
1185 minVal = static_cast<int>(0.75 * getHeight() - oy);
1186 maxVal = static_cast<int>(0.25 * getHeight() - oy);
1187
1188 if (minVal <= maxVal) {
1189 m_vScrollBar->setRange(minVal, maxVal);
1190 }
1191
1192 m_hScrollBar->setPageStep(getWidth());
1193 m_vScrollBar->setPageStep(getHeight());
1194
1195 m_hScrollBar->setValue(-ox);
1196 m_vScrollBar->setValue(oy);
1197 LC_LOGRS_Debug::Log() << __func__ << "(): y scrollbar range[" << minVal << ", " << maxVal << "]: " << oy;
1198
1199 slotHScrolled(-ox);
1200 slotVScrolled(oy);
1201
1202 // RS_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
1203 // hScrollBar->minimum(), hScrollBar->maximum(),
1204 // hScrollBar->pageStep(), ox);
1205
1206 // RS_DEBUG->print(/*RS_Debug::D_WARNING, */"V min: %d / max: %d / step: %d / value: %d\n",
1207 // vScrollBar->minimum(), vScrollBar->maximum(),
1208 // vScrollBar->pageStep(), oy);
1209 LC_LOGRS_Debug::Log() << __func__ << "(): end";
1210}
1211
1212/**
1213 * override this to adjust controls and widgets that
1214 * control the zoom factor of the graphic.
1215 */
1216void QG_GraphicView::adjustZoomControls() {
1217}
1218
1219
1220/**
1221 * Slot for horizontal scroll events.
1222 */
1223void QG_GraphicView::slotHScrolled(const int value) {
1224 const auto viewport = getViewPort();
1225 if (m_hScrollBar->maximum() == m_hScrollBar->minimum()) {
1226 getDocument()->calculateBorders();
1227 const RS_Vector min = getDocument()->getMin();
1228 const RS_Vector max = getDocument()->getMax();
1229 RS_Vector ucsMin;
1230 RS_Vector ucsMax;
1231 viewport->ucsBoundingBox(min, max, ucsMin, ucsMax);
1232 const RS_Vector containerSize = ucsMax - ucsMin;
1233 viewport->centerOffsetX(ucsMin, containerSize);
1234 }
1235 else {
1236 viewport->setOffsetX(-value);
1237 }
1238 redraw();
1239}
1240
1241/**
1242 * Slot for vertical scroll events.
1243 */
1244void QG_GraphicView::slotVScrolled(const int value) {
1245 // Scrollbar behaviour tends to change with every Qt version..
1246 // so let's keep old code in here for now
1247
1248 if (m_vScrollBar->maximum() == m_vScrollBar->minimum()) {
1249 getDocument()->calculateBorders();
1250 const RS_Vector min = getDocument()->getMin();
1251 const RS_Vector max = getDocument()->getMax();
1252 RS_Vector ucsMin;
1253 RS_Vector ucsMax;
1254 getViewPort()->ucsBoundingBox(min, max, ucsMin, ucsMax);
1255 const RS_Vector containerSize = ucsMax - ucsMin;
1256 getViewPort()->centerOffsetY(ucsMin, containerSize);
1257 }
1258 else {
1259 getViewPort()->setOffsetY(value);
1260 }
1261 redraw();
1262}
1263
1264/**
1265 * @brief setOffset
1266 * @param ox offset X
1267 * @param oy offset Y
1268 */
1269void QG_GraphicView::setOffset([[maybe_unused]] const int ox, [[maybe_unused]] const int oy) {
1270 getViewPort()->setOffsetX(ox);
1271 getViewPort()->setOffsetY(oy);
1272 // need to adjust offset control for scrollbars when setting graphicview offset
1273 adjustOffsetControls();
1274}
1275
1276void QG_GraphicView::layerActivated(RS_Layer* layer) {
1277 const bool applyLayerToSelectedEntities = LC_GET_ONE_BOOLRS_Settings::instance()->readBoolSingle("Modify", "ModifyEntitiesToActiveLayer");
1278
1279 if (applyLayerToSelectedEntities) {
1280 RS_Graphic* graphic = getGraphic();
1281 if (graphic != nullptr) {
1282 const auto doc = getDocument();
1283 const auto selection = doc->getSelection();
1284 if (!selection->isEmpty()) {
1285 QList<RS_Entity*> selected;
1286 selection->collectSelectedEntities(selected);
1287 if (!selected.isEmpty()) {
1288 doc->undoableModify(getViewPort(), [selected, layer](LC_DocumentModificationBatch& ctx)-> bool {
1289 for (const auto en : std::as_const(selected)) {
1290 if (en != nullptr && en->isAlive()) {
1291 RS_Entity* clone = en->clone();
1292 clone->setLayer(layer);
1293 clone->setPen(en->getPen(false));
1294 clone->clearSelectionFlag();
1295 clone->update();
1296 ctx += clone;
1297 ctx -= en;
1298 }
1299 }
1300 ctx.dontSetActiveLayerAndPen();
1301 return true;
1302 }, [this]([[maybe_unused]] LC_DocumentModificationBatch& ctx, RS_Document* d)-> void {
1303 RS_Selection::unselectAllInDocument(d, getViewPort());
1304 });
1305 }
1306 }
1307
1308 graphic->updateInserts();
1309 doc->calculateBorders();
1310 doc->clearSelectionFlag();
1311 redraw(RS2::RedrawDrawing);
1312 }
1313 }
1314}
1315
1316/**
1317 * Handles paint events by redrawing the graphic in this view.
1318 * usually that's very fast since we only paint the buffer we
1319 * have from the last call..
1320 */
1321void QG_GraphicView::paintEvent(QPaintEvent*) {
1322 getRenderer()->render();
1323}
1324
1325#define HIDE_SELECT_CURSORfalse false
1326
1327void QG_GraphicView::loadSettings() {
1328 RS_GraphicView::loadSettings();
1329
1330 {
1331 LC_GROUP_GUARDauto _guard_settings = RS_Settings::instance()->beginGroupGuard("Appearance");
1332 const int zoomFactor1000 = LC_GET_INTRS_Settings::instance()->readInt("ScrollZoomFactor", 1137);
1333 m_scrollZoomFactor = zoomFactor1000 / 1000.0;
1334
1335 m_ucsHighlightData->maxBlinkNumber = LC_GET_INTRS_Settings::instance()->readInt("UCSHighlightBlinkCount", 10) * 2;
1336 // one blink includes both for visible and invisible phase
1337 m_ucsHighlightData->timerInterval = LC_GET_INTRS_Settings::instance()->readInt("UCSHighlightBlinkDelay", 250);
1338 }
1339
1340 {
1341 LC_GROUP_GUARDauto _guard_settings = RS_Settings::instance()->beginGroupGuard("Defaults");
1342 m_invertZoomDirection = LC_GET_ONE_BOOLRS_Settings::instance()->readBoolSingle("Defaults", "InvertZoomDirection");
1343 m_invertHorizontalScroll = LC_GET_BOOLRS_Settings::instance()->readBool("WheelScrollInvertH");
1344 m_invertVerticalScroll = LC_GET_BOOLRS_Settings::instance()->readBool("WheelScrollInvertV");
1345 }
1346
1347 m_allowScrollAndMoveAdjustByKeys = LC_GET_ONE_BOOLRS_Settings::instance()->readBoolSingle("Keyboard", "AllowScrollMoveAdjustByKeys", true);
1348
1349 LC_GROUPRS_Settings::instance()->beginGroup("Appearance");
1350 {
1351 m_cursorHiding = LC_GET_BOOLRS_Settings::instance()->readBool("cursor_hiding", false);
1352 bool showSnapIndicatorLines = LC_GET_BOOLRS_Settings::instance()->readBool("indicator_lines_state", true);
1353 bool showSnapIndicatorShape = LC_GET_BOOLRS_Settings::instance()->readBool("indicator_shape_state", true);
Value stored to 'showSnapIndicatorShape' during its initialization is never read
1354 if (HIDE_SELECT_CURSORfalse) {
1355 // potentially, select cursor may be also hidden and so snapper will be used instead of cursor.
1356 // however, this will require review and modifications of significant amount of actions, so
1357 // probably I'll return to this later. In such case, the code within this "if" will be handy for such support
1358 m_selectCursorHiding = m_cursorHiding && (showSnapIndicatorLines || showSnapIndicatorShape);
1359 }
1360 m_selectCursorHiding = false;
1361 }
1362 LC_GROUP_ENDRS_Settings::instance()->endGroup();
1363 m_ucsMarkOptions->loadSettings();
1364
1365 LC_GROUPRS_Settings::instance()->beginGroup("Colors");
1366 {
1367 const RS_Color bgColor(LC_GET_STRRS_Settings::instance()->readStr("RelativePositionAssistantBackground", RS_Settings::RELATIVE_POSITION_BACKGROUND));
1368 const RS_Color txtColor(LC_GET_STRRS_Settings::instance()->readStr("RelativePositionAssistantText", RS_Settings::RELATIVE_POSITION_BACKGROUND));
1369 m_relativePointWidgetHolder->setWidgetColors(bgColor, txtColor);
1370 }
1371 LC_GROUP_ENDRS_Settings::instance()->endGroup();
1372
1373 LC_GROUPRS_Settings::instance()->beginGroup("RelativePositionAssistant");
1374 {
1375 const int fontSize = LC_GET_INTRS_Settings::instance()->readInt("FontSize", 10);
1376 const QString fontName = LC_GET_STRRS_Settings::instance()->readStr("FontName", "Helvetica");
1377 m_relativePointWidgetHolder->setFont(fontName, fontSize);
1378 }
1379 LC_GROUP_ENDRS_Settings::instance()->endGroup();
1380}
1381
1382void QG_GraphicView::setAntialiasing(const bool state) const {
1383 getRenderer()->setAntialiasing(state);
1384}
1385
1386bool QG_GraphicView::isAntialiasing() const {
1387 return getRenderer()->isAntialiasing();
1388}
1389
1390bool QG_GraphicView::isDraftMode() const {
1391 const auto* viewRenderer = dynamic_cast<LC_GraphicViewRenderer*>(getRenderer());
1392 return (viewRenderer != nullptr) ? viewRenderer->isDraftMode() : false;
1393}
1394
1395void QG_GraphicView::setDraftMode(const bool dm) {
1396 auto* viewRenderer = dynamic_cast<LC_GraphicViewRenderer*>(getRenderer());
1397 if (viewRenderer != nullptr) {
1398 viewRenderer->setDraftMode(dm);
1399 redraw();
1400 }
1401}
1402
1403void QG_GraphicView::setDraftLinesMode(const bool mode) const {
1404 auto* viewRenderer = dynamic_cast<LC_GraphicViewRenderer*>(getRenderer());
1405 if (viewRenderer != nullptr) {
1406 viewRenderer->setLineWidthScaling(mode);
1407 }
1408}
1409
1410bool QG_GraphicView::isDraftLinesMode() const {
1411 const auto* viewRenderer = dynamic_cast<LC_GraphicViewRenderer*>(getRenderer());
1412 if (viewRenderer != nullptr) {
1413 return !viewRenderer->getLineWidthScaling();
1414 }
1415 return false;
1416}
1417
1418void QG_GraphicView::addScrollbars() {
1419 m_scrollbars = true;
1420
1421 m_hScrollBar = new QG_ScrollBar(Qt::Horizontal, this);
1422 m_vScrollBar = new QG_ScrollBar(Qt::Vertical, this);
1423 m_layout = new QGridLayout(this);
1424
1425 setOffset(50, 50);
1426
1427#if QT_VERSION((6<<16)|(9<<8)|(0)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0))
1428 m_layout->setContentsMargins(QMargins{});
1429#else
1430 layout->setMargin(0);
1431#endif
1432 m_layout->setSpacing(0);
1433 m_layout->setColumnStretch(0, 1);
1434 m_layout->setColumnStretch(1, 0);
1435 m_layout->setColumnStretch(2, 0);
1436 m_layout->setRowStretch(0, 1);
1437 m_layout->setRowStretch(1, 0);
1438
1439 m_hScrollBar->setSingleStep(50);
1440 m_hScrollBar->setCursor(Qt::ArrowCursor);
1441 m_layout->addWidget(m_hScrollBar, 1, 0);
1442 connect(m_hScrollBar, &QG_ScrollBar::valueChanged, this, &QG_GraphicView::slotHScrolled);
1443
1444 m_vScrollBar->setSingleStep(50);
1445 m_vScrollBar->setCursor(Qt::ArrowCursor);
1446 m_layout->addWidget(m_vScrollBar, 0, 1);
1447 connect(m_vScrollBar, &QG_ScrollBar::valueChanged, this, &QG_GraphicView::slotVScrolled);
1448}
1449
1450bool QG_GraphicView::hasScrollbars() const {
1451 return m_scrollbars;
1452}
1453
1454void QG_GraphicView::setCursorHiding(const bool state) {
1455 m_cursorHiding = state;
1456}
1457
1458void QG_GraphicView::setCurrentQAction(QAction* q_action) {
1459 getEventHandler()->setQAction(q_action);
1460
1461 if (m_recentActions.contains(q_action)) {
1462 m_recentActions.removeOne(q_action);
1463 }
1464 m_recentActions.prepend(q_action);
1465}
1466
1467void QG_GraphicView::startAutoPanTimer(const QMouseEvent* event) {
1468 if (event == nullptr) {
1469 return;
1470 }
1471 const RS_Vector cadArea_minCoord(0., 0.);
1472 const RS_Vector cadArea_maxCoord(getWidth(), getHeight());
1473 const LC_Rect cadArea_actual(cadArea_minCoord, cadArea_maxCoord);
1474 const LC_Rect cadArea_unprobed(cadArea_minCoord + m_panData->probedAreaOffset, cadArea_maxCoord - m_panData->probedAreaOffset);
1475
1476 RS_Vector mouseCoord{event->position()};
1477 mouseCoord.y = cadArea_actual.height() - mouseCoord.y;
1478
1479 const RS_Vector cadArea_centerPoint((cadArea_minCoord + cadArea_maxCoord) / 2.0);
1480 RS_Vector offset = mouseCoord - cadArea_centerPoint;
1481 offset = {std::abs(offset.x) - cadArea_unprobed.width() / 2., std::abs(offset.y) - cadArea_unprobed.height() / 2.};
1482 offset = {std::max(offset.x, 1.), std::max(offset.y, 1.)};
1483
1484 const double panOffset_angle{cadArea_centerPoint.angleTo(mouseCoord)};
1485
1486 /* It would be better if the below value was calculated in the code that deals with resizing the CAD area. */
1487 const double quarterAngle = cadArea_centerPoint.angleTo(cadArea_actual.upperRightCorner());
1488
1489 double percentageFactor;
1490
1491 if (((panOffset_angle > quarterAngle) && (panOffset_angle <= (M_PI3.14159265358979323846 - quarterAngle))) || ((panOffset_angle > (quarterAngle + M_PI3.14159265358979323846)) && (
1492 panOffset_angle <= (M_PI3.14159265358979323846 + M_PI3.14159265358979323846 - quarterAngle)))) {
1493 percentageFactor = (std::abs((mouseCoord - cadArea_centerPoint).y) - cadArea_unprobed.height() / 2.0) / (cadArea_actual.height() /
1494 2.0 - cadArea_unprobed.height() / 2.0);
1495 }
1496 else {
1497 percentageFactor = (std::abs((mouseCoord - cadArea_centerPoint).x) - cadArea_unprobed.width() / 2.0) / (cadArea_actual.width() / 2.0
1498 - cadArea_unprobed.width() / 2.0);
1499 }
1500
1501 const double panTimerInterval{
1502 m_panData->panTimerIntervalMinimum + ((m_panData->panTimerIntervalMaximum - m_panData->panTimerIntervalMinimum) * (1.0 -
1503 percentageFactor))
1504 };
1505
1506 offset = RS_Vector::polar(offset.magnitude(), M_PI3.14159265358979323846 - panOffset_angle);
1507 m_panData->panOffset = {static_cast<int>(offset.x), static_cast<int>(offset.y)};
1508
1509 if (m_panData->panTimer != nullptr) {
1510 m_panData->panTimer->setInterval(panTimerInterval);
1511 }
1512 else {
1513 m_panData->start(panTimerInterval, *this);
1514 }
1515
1516 if (RS_DEBUGRS_Debug::instance()->getLevel() >= RS_Debug::D_INFORMATIONAL) {
1517 std::cout << " CAD area centre point = " << cadArea_centerPoint << std::endl <<
1518 " Actual CAD area quarter angle (deg) = " << quarterAngle * 180.0 / M_PI3.14159265358979323846 << std::endl <<
1519 " Percentage factor = " << percentageFactor << std::endl << " Pan offset angle (radians) = " <<
1520 panOffset_angle << std::endl << " Pan offset angle (degrees) = " << panOffset_angle * 180.0 / M_PI3.14159265358979323846 << std::endl <<
1521 " Pan offset vector = " << m_panData->panOffset.x() << ", " << m_panData->panOffset.y() << std::endl
1522 //<< " Pan timer interval (ms) = " << m_panData->panTimer->interfac
1523 << std::endl << " Mouse (cursor) position (adjusted) = " << mouseCoord << std::endl <<
1524 " Mouse position w.r.t. centre point = " << mouseCoord - cadArea_centerPoint << std::endl << std::endl << std::endl;
1525 }
1526}
1527
1528bool QG_GraphicView::isAutoPan(const QMouseEvent* event) const {
1529 if (event == nullptr) {
1530 return false;
1531 }
1532
1533 const bool autopanEnabled = LC_GET_ONE_BOOLRS_Settings::instance()->readBoolSingle("Appearance", "Autopanning");
1534
1535 if (!autopanEnabled) {
1536 return false;
1537 }
1538
1539 const RS_Vector cadArea_minCoord(0., 0.);
1540 const RS_Vector cadArea_maxCoord(getWidth(), getHeight());
1541 const LC_Rect cadArea_actual(cadArea_minCoord, cadArea_maxCoord);
1542 const LC_Rect cadArea_unprobed(cadArea_minCoord + m_panData->probedAreaOffset, cadArea_maxCoord - m_panData->probedAreaOffset);
1543 if (cadArea_unprobed.width() < 0. || cadArea_unprobed.height() < 0.) {
1544 return false;
1545 }
1546
1547 const RS_Vector mouseCoord{event->position()};
1548
1549 if (RS_DEBUGRS_Debug::instance()->getLevel() >= RS_Debug::D_INFORMATIONAL) {
1550 std::cout << " Unprobed CAD area width and height = " << cadArea_unprobed.width() << "/" << cadArea_unprobed.height() << std::endl
1551 << " Actual CAD area width and height = " << cadArea_actual.width() << "/" << cadArea_actual.height() << std::endl <<
1552 " Mouse (cursor) position = " << mouseCoord << std::endl << std::endl;
1553 }
1554
1555 return cadArea_actual.inArea(mouseCoord) && !cadArea_unprobed.inArea(mouseCoord);
1556}
1557
1558void QG_GraphicView::deleteActionContext() const {
1559 delete m_actionContext;
1560}
1561
1562/*
1563 Auto-pans the CAD area.
1564 - by Melwyn Francis Carlo <carlo.melwyn@outlook.com>
1565*/
1566void QG_GraphicView::autoPanStep() const {
1567 // skip first steps to avoid unintensional panning
1568 m_panData->delayCounter = std::min(++m_panData->delayCounter, m_panData->delayCounterMax);
1569 if (m_panData->delayCounter < m_panData->delayCounterMax) {
1570 return;
1571 }
1572
1573 RS_DEBUGRS_Debug::instance()->print(RS_Debug::D_INFORMATIONAL, "%s(): Timer is ticking!", __func__);
1574 getViewPort()->zoomPan(m_panData->panOffset.x(), m_panData->panOffset.y());
1575}
1576
1577QString QG_GraphicView::obtainEntityDescription(RS_Entity* entity, const RS2::EntityDescriptionLevel shortDescription) {
1578 const LC_QuickInfoWidget* entityInfoWidget = QC_ApplicationWindow::getAppWindow()->getEntityInfoWidget();
1579 if (entityInfoWidget != nullptr) {
1580 QString result = entityInfoWidget->getEntityDescription(entity, shortDescription);
1581 return result;
1582 }
1583 return "";
1584}
1585
1586void QG_GraphicView::ucsHighlightStep() {
1587 const auto overlayContainer = getViewPort()->getOverlaysDrawablesContainer(RS2::OverlayGraphics::ActionPreviewEntity);
1588 overlayContainer->clear();
1589 if (m_ucsHighlightData->mayTick()) {
1590 if (m_ucsHighlightData->inVisiblePhase) {
1591 // note - potentially, here we may simply store data for custom ucs mark and create object in renderer....
1592 // that will eliminate storing ucs mark settings in this class
1593 const auto ucsMark = new LC_OverlayUCSMark(m_ucsHighlightData->origin, m_ucsHighlightData->angle, m_ucsHighlightData->forWCS,
1594 m_ucsMarkOptions.get());
1595 overlayContainer->add(ucsMark);
1596 }
1597 else {
1598 }
1599 }
1600 else {
1601 m_ucsHighlightData->stop();
1602 // restore current view position
1603 getViewPort()->justSetOffsetAndFactor(m_ucsHighlightData->savedViewOffset.x, m_ucsHighlightData->savedViewOffset.y,
1604 m_ucsHighlightData->savedViewFactor);
1605 }
1606 redraw(RS2::RedrawOverlay);
1607 update();
1608}
1609
1610void QG_GraphicView::highlightUCSLocation(LC_UCS* ucs) {
1611 if (ucs == nullptr) {
1612 return;
1613 }
1614
1615 const auto viewport = getViewPort();
1616 // save current view position
1617 m_ucsHighlightData->savedViewOffset.x = viewport->getOffsetX();
1618 m_ucsHighlightData->savedViewOffset.y = viewport->getOffsetY();
1619 m_ucsHighlightData->savedViewFactor = viewport->getFactor().x;
1620
1621 const RS_Vector origin = ucs->getOrigin();
1622 const double angle = ucs->getXAxisDirection();
1623
1624 // try to ensure that origin of UCS is visible if it's outside of visible part of drawing
1625 const double AXIS_SIZE = viewport->toUcsDX(20); // fixme - ucs - or toUcsX?
1626 viewport->zoomAutoEnsurePointsIncluded(origin, origin.relative(AXIS_SIZE, angle), origin.relative(AXIS_SIZE, angle + M_PI_21.57079632679489661923));
1627
1628 double uiOriginPointX = 0., uiOriginPointY = 0.;
1629 viewport->toUI(origin, uiOriginPointX, uiOriginPointY);
1630
1631 const double ucsXAxisAngleInUCS = viewport->toUCSAngle(angle);
1632
1633 m_ucsHighlightData->origin = RS_Vector(uiOriginPointX, uiOriginPointY);
1634 m_ucsHighlightData->angle = -ucsXAxisAngleInUCS;
1635 m_ucsHighlightData->forWCS = !ucs->isUCS();
1636 const double timerInterval = m_ucsHighlightData->timerInterval;
1637 m_ucsHighlightData->start(timerInterval, *this);
1638}