1 /*
2 KBlackBox - A simple game inspired by an emacs module
3
4 SPDX-FileCopyrightText: 1999-2000 Robert Cimrman <cimrman3@students.zcu.cz>
5 SPDX-FileCopyrightText: 2007 Nicolas Roffet <nicolas-kde@roffet.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "kbbscalablegraphicwidget.h"
11
12
13
14 #include <QAction>
15 #include <QFont>
16 #include <QGraphicsScene>
17 #include <QLCDNumber>
18 #include <QResizeEvent>
19
20
21 #include <KGamePopupItem>
22 #include <QIcon>
23 #include <KLocalizedString>
24 #include <QPushButton>
25
26
27 #include "kbbballsonboard.h"
28 #include "kbbgamedoc.h"
29 #include "kbbgraphicsitemball.h"
30 #include "kbbgraphicsitemballrepository.h"
31 #include "kbbgraphicsitemblackbox.h"
32 #include "kbbgraphicsitemcursor.h"
33 #include "kbbgraphicsitemlaser.h"
34 #include "kbbgraphicsitemonbox.h"
35 #include "kbbgraphicsitemray.h"
36 #include "kbbgraphicsitemrayresult.h"
37 #include "kbbgraphicsitemset.h"
38 #include "kbbthememanager.h"
39
40
41
42 //
43 // Constructor / Destructor
44 //
45
KBBScalableGraphicWidget(KBBGameDoc * gameDoc,KBBThemeManager * themeManager,QAction * done)46 KBBScalableGraphicWidget::KBBScalableGraphicWidget(KBBGameDoc* gameDoc, KBBThemeManager* themeManager, QAction* done)
47 {
48 m_gameDoc = gameDoc;
49 m_themeManager = themeManager;
50 m_columns = -2;
51 m_rows = -2;
52 m_pause = false;
53 m_ballNumber = 0;
54 m_doneAction = done;
55 m_cursorFollowsMouse = false;
56
57 setFrameStyle(QFrame::NoFrame);
58 setCacheMode(QGraphicsView::CacheBackground);
59 setMinimumSize(QSize(MINIMUM_SIZE, MINIMUM_SIZE));
60
61
62 m_scene = new QGraphicsScene( 0, 0, 2*BORDER_SIZE, 2*BORDER_SIZE, this );
63
64 m_blackbox = new KBBGraphicsItemBlackBox(this, m_scene, m_themeManager, false);
65 m_blackbox->setKBBScalableGraphicWidget(this);
66 m_balls = new KBBGraphicsItemSet(m_scene);
67 m_cursor = new KBBGraphicsItemCursor(this, m_themeManager);
68 connect(m_cursor, &KBBGraphicsItemCursor::cursorAtNewPosition, this, &KBBScalableGraphicWidget::cursorAtNewPosition);
69 m_markersNothing = new KBBGraphicsItemSet(m_scene);
70 m_ballsSolution = new KBBGraphicsItemSet(m_scene);
71 m_ballsUnsure = new KBBGraphicsItemSet(m_scene);
72 m_lasers = new KBBGraphicsItemSet(m_scene);
73 m_rayResults = new KBBGraphicsItemSet(m_scene);
74
75 m_playerRay = new KBBGraphicsItemRay(playerRay, m_scene, m_themeManager);
76 m_solutionRay = new KBBGraphicsItemRay(solutionRay, m_scene, m_themeManager);
77
78 // Information message about the score
79 m_infoScore = new KGamePopupItem();
80 m_infoScore->setMessageIcon(QPixmap()); // No icon, because they are no scalable.
81 m_scene->addItem(m_infoScore); // it hides itself by default
82
83 m_ballRepository = new KBBGraphicsItemBallRepository(this, themeManager);
84
85 setScene(m_scene);
86
87 m_doneButton = new QPushButton(m_doneAction->text(), this);
88 m_doneButton->setIcon(QIcon(m_doneAction->icon()));
89 m_doneButton->setWhatsThis(m_doneAction->whatsThis());
90 connect(m_doneButton, &QPushButton::clicked, m_doneAction, &QAction::trigger);
91 QFont font;
92 font.setPointSize(m_doneButton->font().pointSize()+2);
93 font.setBold(true);
94 m_doneButton->setFont(font);
95
96 m_score = new QLCDNumber(3, this);
97 m_score->setFrameStyle(QFrame::NoFrame);
98 m_score->setMaximumWidth(m_doneButton->width());
99 m_score->setFixedHeight((int)(1.5*m_doneButton->height()));
100 m_score->setToolTip(i18n("Score"));
101 m_score->setWhatsThis(i18n("<qt><p>This is <b>your score</b>. You should try to get the lowest possible.</p><p>The score increases:<ul><li>with time: <b>1 point</b> per second.</li><li>with the use of lasers:<ul><li><b>3 points</b> if the laser beam hits a ball or exits at the entry point,</li><li><b>9 points</b> if it exits at another entry point.</li></ul></li></ul></p><p>Your score is set to <b>999</b> at the end of the game if you make a mistake.</p></qt>"));
102
103 // TODO: not displayed... :(
104 setWhatsThis(i18n("<qt><p>This is the <b>main game area</b>.</p><ul><li>The <b>black box</b> is in the center.</li><li>On the left, there are the <b>balls</b> you have to place over the black box.</li><li>Around the black box, there are <b>lasers</b> that are replaced with <b>interaction information</b> if you use them.</li></ul></qt>"));
105 connect(m_blackbox, &KBBGraphicsItemBlackBox::hoverMoved, this, &KBBScalableGraphicWidget::hoverMovePosition);
106
107 }
108
109
~KBBScalableGraphicWidget()110 KBBScalableGraphicWidget::~KBBScalableGraphicWidget()
111 {
112 delete m_balls;
113 delete m_markersNothing;
114 delete m_ballsSolution;
115 delete m_ballsUnsure;
116 delete m_lasers;
117 delete m_rayResults;
118
119 delete m_ballRepository;
120
121 delete m_scene;
122 }
123
124
125
126 //
127 // Public
128 //
129
addBall(int boxPosition)130 void KBBScalableGraphicWidget::addBall(int boxPosition)
131 {
132 addBall(boxPosition, KBBGraphicsItemOnBox::NO_POSITION);
133 }
134
135
addBall(int boxPosition,int outsidePosition)136 void KBBScalableGraphicWidget::addBall(int boxPosition, int outsidePosition)
137 {
138 if (!m_pause && m_inputAccepted && (!m_balls->containsVisible(boxPosition))&& (!m_ballsUnsure->containsVisible(boxPosition))) {
139 m_boardBallsPlaced->add(boxPosition);
140 m_balls->insert(new KBBGraphicsItemBall(playerBall, this, m_themeManager, boxPosition, m_columns, m_rows));
141 m_markersNothing->remove(boxPosition);
142 if (outsidePosition==KBBGraphicsItemOnBox::NO_POSITION) {
143 outsidePosition = m_ballRepository->ballToTake();
144 }
145 if (outsidePosition!=KBBGraphicsItemSet::NO_INDEX)
146 m_ballRepository->removeBall(outsidePosition);
147
148 updateDoneButton();
149 }
150 }
151
152
addBallUnsure(const int boxPosition)153 void KBBScalableGraphicWidget::addBallUnsure(const int boxPosition)
154 {
155 addBall(boxPosition);
156 setBallUnsure(boxPosition, true);
157 }
158
159
addMarkerNothing(const int boxPosition)160 void KBBScalableGraphicWidget::addMarkerNothing(const int boxPosition)
161 {
162 if (!m_pause && m_inputAccepted && (!m_markersNothing->containsVisible(boxPosition))) {
163 m_markersNothing->insert(new KBBGraphicsItemOnBox(markerNothing, this, m_themeManager, boxPosition, m_columns, m_rows));
164 m_balls->remove(boxPosition);
165 m_ballsUnsure->remove(boxPosition);
166 m_boardBallsPlaced->remove(boxPosition);
167 }
168 }
169
170
drawRay(const int borderPosition)171 void KBBScalableGraphicWidget::drawRay(const int borderPosition)
172 {
173 if (!m_pause) {
174 if (!m_inputAccepted) {
175 m_solutionRay->draw(m_boardBalls, borderPosition);
176 }
177 m_playerRay->draw(m_boardBallsPlaced, borderPosition);
178 }
179 }
180
181
mouseBorderClick(const int borderPosition)182 void KBBScalableGraphicWidget::mouseBorderClick(const int borderPosition)
183 {
184 useLaser(borderPosition);
185 m_cursor->setBorderPosition(borderPosition);
186 m_cursor->hide();
187 }
188
189
mouseBoxClick(const Qt::MouseButton button,int boxPosition)190 void KBBScalableGraphicWidget::mouseBoxClick(const Qt::MouseButton button, int boxPosition)
191 {
192 m_cursor->setBoxPosition(boxPosition);
193 if (button==Qt::RightButton)
194 switchMarker();
195 else
196 switchBall();
197 m_cursor->hide();
198 }
199
200
moveBall(const int boxPositionFrom,const int boxPositionTo)201 int KBBScalableGraphicWidget::moveBall(const int boxPositionFrom, const int boxPositionTo)
202 {
203 int newPos = positionAfterMovingBall(boxPositionFrom, boxPositionTo);
204
205 if (!m_pause && m_inputAccepted && (!m_balls->containsVisible(boxPositionTo)) && (!m_ballsUnsure->containsVisible(boxPositionTo))) {
206 m_markersNothing->remove(boxPositionTo);
207 if (boxPositionFrom>=m_columns*m_rows) {
208 // ball moved from outside of the board
209 addBall(boxPositionTo, boxPositionFrom);
210 } else {
211 // ball moved from a board position
212 m_boardBallsPlaced->remove(boxPositionFrom);
213 m_boardBallsPlaced->add(boxPositionTo);
214 }
215 }
216
217 return newPos;
218 }
219
220
moveMarkerNothing(const int boxPositionFrom,const int boxPositionTo)221 int KBBScalableGraphicWidget::moveMarkerNothing(const int boxPositionFrom, const int boxPositionTo)
222 {
223 if (!m_pause && m_inputAccepted && (!m_markersNothing->containsVisible(boxPositionTo))) {
224 removeBall(boxPositionTo);
225 return boxPositionTo;
226 } else
227 return boxPositionFrom;
228 }
229
230
newGame(int columns,int rows,int ballNumber)231 void KBBScalableGraphicWidget::newGame(int columns, int rows, int ballNumber)
232 {
233 m_rayNumber = 0;
234 m_boardBallsPlaced = m_gameDoc->m_ballsPlaced;
235 setPause(false);
236 m_ballNumber = ballNumber;
237
238 // remove old ray results, all placed balls, all markers "nothing" and all solutions
239 m_rayResults->clear();
240 m_balls->clear();
241 m_ballsUnsure->clear();
242 m_markersNothing->clear();
243 m_ballsSolution->clear();
244
245 // Reorganize lasers
246 if ((columns!=m_columns) || (rows!=m_rows)) {
247 // not the same amount of lasers: We can destroy them and create some new ones
248 m_lasers->clear();
249 for (int i=0; i<2*(columns + rows); i++)
250 m_lasers->insert(new KBBGraphicsItemLaser(this, m_themeManager, i, columns, rows));
251 } else {
252 // same amount of lasers: We "recycle" them. (Just destroying them and re-creating them is not working fine: some lasers remain hidden until the next resize... Strange bug with QGraphicsView...)
253 for (int i=0; i<2*(m_columns + m_rows); i++)
254 m_lasers->setVisible(i, true);
255 }
256
257 m_ballRepository->newGame(columns, rows, ballNumber);
258
259 // set the new size if needed
260 if (m_columns!=columns || m_rows!=rows) {
261 m_columns = columns;
262 m_rows = rows;
263 m_blackbox->setSize(m_columns, m_rows);
264 m_cursor->setBoardSize(m_columns, m_rows);
265 m_scene->setSceneRect(m_ballRepository->x() - RATIO, 0, m_columns*RATIO + 2*BORDER_SIZE - m_ballRepository->x() + RATIO, m_rows*RATIO + 2*BORDER_SIZE);
266 }
267 resizeEvent(nullptr);
268 setInputAccepted(true);
269 }
270
271
popupText(const QString & text,int time)272 void KBBScalableGraphicWidget::popupText(const QString& text, int time)
273 {
274 if (text.isEmpty())
275 m_infoScore->forceHide();
276 else {
277 m_infoScore->setMessageTimeout(time);
278 m_infoScore->showMessage(text, KGamePopupItem::TopLeft, KGamePopupItem::ReplacePrevious);
279 }
280 }
281
282
positionAfterMovingBall(const int boxPositionFrom,const int boxPositionTo) const283 int KBBScalableGraphicWidget::positionAfterMovingBall(const int boxPositionFrom, const int boxPositionTo) const
284 {
285 if (!m_pause && m_inputAccepted && (!m_balls->containsVisible(boxPositionTo)) && (!m_ballsUnsure->containsVisible(boxPositionTo))) {
286 return boxPositionTo;
287 } else
288 return boxPositionFrom;
289 }
290
291
setPause(bool state)292 void KBBScalableGraphicWidget::setPause(bool state)
293 {
294 m_pause = state;
295 for (int i=0;i<2*(m_rows+m_columns);i++) {
296 if (m_rayResults->containsVisible(i))
297 m_rayResults->item(i)->setPause(state);
298 }
299
300 updateDoneButton();
301 }
302
303
resizeEvent(QResizeEvent *)304 void KBBScalableGraphicWidget::resizeEvent( QResizeEvent* )
305 {
306 // 1. Compute the size of m_rectBackground
307 const qreal sW = m_scene->width();
308 const qreal sH = m_scene->height();
309 const qreal wW = width();
310 const qreal wH = height();
311 const qreal offset = (sH+sW)/100 ;
312 if (sH*wW > sW*wH) {
313 // The widget is larger than the scene
314 qreal w = wW*sH/wH;
315 m_rectBackground.setRect((sW-w)/2-offset+m_ballRepository->x()-RATIO, -offset, w + 2*offset, sH + 2*offset);
316 } else {
317 // The scene is larger than the widget (or as large)
318 qreal h = wH*sW/wW;
319 m_rectBackground.setRect(-offset+m_ballRepository->x()-RATIO, (sH-h)/2-offset, sW + 2*offset, h + 2*offset);
320 }
321
322 // 2. Resize the scene to fit in the widget
323 fitInView(m_ballRepository->x()-RATIO, 0, m_columns*RATIO + 2*BORDER_SIZE - m_ballRepository->x() + RATIO, m_rows*RATIO + 2*BORDER_SIZE, Qt::KeepAspectRatio);
324
325
326 m_doneButton->move(OFFSET_DONE_BUTTON, height() - m_doneButton->height() - OFFSET_DONE_BUTTON);
327 m_score->move(OFFSET_DONE_BUTTON, height() - m_score->height() - m_doneButton->height() - 3*OFFSET_DONE_BUTTON);
328 }
329
330
removeAllBalls()331 void KBBScalableGraphicWidget::removeAllBalls()
332 {
333 for (int i=0;i<m_columns*m_rows;i++) {
334 removeBall(i);
335 }
336 }
337
338
removeBall(const int boxPosition)339 void KBBScalableGraphicWidget::removeBall(const int boxPosition)
340 {
341 if (!m_pause && m_inputAccepted) {
342 m_balls->remove(boxPosition);
343 m_ballsUnsure->remove(boxPosition);
344 m_boardBallsPlaced->remove(boxPosition);
345 m_ballRepository->fillBallsOutside(m_gameDoc->m_ballsPlaced->count());
346
347 updateDoneButton();
348 }
349 }
350
351
removeRay()352 void KBBScalableGraphicWidget::removeRay()
353 {
354 m_playerRay->hide();
355 m_solutionRay->hide();
356 }
357
358
scene()359 QGraphicsScene* KBBScalableGraphicWidget::scene()
360 {
361 return m_scene;
362 }
363
364
setScore(int score)365 void KBBScalableGraphicWidget::setScore(int score)
366 {
367 m_score->display(score);
368 }
369
370
solve(const bool continueGame)371 void KBBScalableGraphicWidget::solve(const bool continueGame)
372 {
373 m_boardBalls = m_gameDoc->m_balls;
374
375 setInputAccepted(continueGame);
376 if (!continueGame)
377 setPause(false);
378
379 for (int i=0; i<(m_columns * m_rows); i++) {
380 if ((m_balls->containsVisible(i) || m_ballsUnsure->containsVisible(i)) && m_boardBalls->contains(i)) {
381 m_ballsSolution->remove(i); // For the sandbox mode: a solution ball could already be here.
382 m_ballsSolution->insert(new KBBGraphicsItemBall(rightPlayerBall, this, m_themeManager, i, m_columns, m_rows));
383 }
384 if ((m_balls->containsVisible(i) || m_ballsUnsure->containsVisible(i)) && !m_boardBalls->contains(i))
385 m_ballsSolution->insert(new KBBGraphicsItemOnBox(wrongPlayerBall, this, m_themeManager, i, m_columns, m_rows));
386 if (!m_balls->containsVisible(i) && !m_ballsUnsure->containsVisible(i) && m_boardBalls->contains(i))
387 m_ballsSolution->insert(new KBBGraphicsItemBall(solutionBall, this, m_themeManager, i, m_columns, m_rows));
388 }
389 }
390
391
392
393 //
394 // Public slots
395 //
396
cursorAtNewPosition(int borderPosition)397 void KBBScalableGraphicWidget::cursorAtNewPosition(int borderPosition)
398 {
399 removeRay();
400 if ((borderPosition!=KBBGraphicsItemCursor::NO_POSITION) && m_cursor->isVisible())
401 drawRay(borderPosition);
402
403 // highlight
404 for (int i=0;i<2*(m_rows+m_columns);i++) {
405 if (m_rayResults->containsVisible(i))
406 m_rayResults->item(i)->highlight(false);
407 }
408 if (m_rayResults->containsVisible(borderPosition))
409 m_rayResults->item(borderPosition)->highlightBoth(true);
410
411 }
412
413
keyboardEnter()414 void KBBScalableGraphicWidget::keyboardEnter()
415 {
416 if (m_cursor->isVisible()) {
417 if (m_cursor->borderPosition() != KBBGraphicsItemCursor::NO_POSITION)
418 useLaser(m_cursor->borderPosition());
419 else
420 switchBall();
421 }
422 m_cursor->show();
423 }
424
425
keyboardMoveDown()426 void KBBScalableGraphicWidget::keyboardMoveDown()
427 {
428 if (m_cursor->isVisible())
429 m_cursor->moveDown();
430 m_cursor->show();
431 }
432
433
keyboardMoveLeft()434 void KBBScalableGraphicWidget::keyboardMoveLeft()
435 {
436 if (m_cursor->isVisible())
437 m_cursor->moveLeft();
438 m_cursor->show();
439 }
440
441
keyboardMoveRight()442 void KBBScalableGraphicWidget::keyboardMoveRight()
443 {
444 if (m_cursor->isVisible())
445 m_cursor->moveRight();
446 m_cursor->show();
447 }
448
449
keyboardMoveUp()450 void KBBScalableGraphicWidget::keyboardMoveUp()
451 {
452 if (m_cursor->isVisible())
453 m_cursor->moveUp();
454 m_cursor->show();
455 }
456
457
keyboardSpace()458 void KBBScalableGraphicWidget::keyboardSpace()
459 {
460 if (m_cursor->isVisible()) {
461 if (m_cursor->boxPosition() != KBBGraphicsItemCursor::NO_POSITION)
462 switchMarker();
463 }
464 m_cursor->show();
465 }
466
467
468
469 //
470 // Protected
471 //
472
drawBackground(QPainter * painter,const QRectF &)473 void KBBScalableGraphicWidget::drawBackground(QPainter* painter, const QRectF&)
474 {
475 m_themeManager->svgRenderer()->render(painter, m_themeManager->elementId(background), m_rectBackground);
476 }
477
478
479
480 //
481 // Private
482 //
483
removeMarkerNothing(const int boxPosition)484 void KBBScalableGraphicWidget::removeMarkerNothing(const int boxPosition)
485 {
486 if (!m_pause && m_inputAccepted) {
487 m_markersNothing->remove(boxPosition);
488 }
489 }
490
491
setBallUnsure(const int boxPosition,const bool unsure)492 void KBBScalableGraphicWidget::setBallUnsure(const int boxPosition, const bool unsure)
493 {
494 if (!m_pause && m_inputAccepted) {
495 if (unsure) {
496 m_balls->remove(boxPosition);
497 m_ballsUnsure->insert(new KBBGraphicsItemBall(unsureBall, this, m_themeManager, boxPosition, m_columns, m_rows));
498 } else {
499 m_ballsUnsure->remove(boxPosition);
500 m_balls->insert(new KBBGraphicsItemBall(playerBall, this, m_themeManager, boxPosition, m_columns, m_rows));
501 }
502 }
503 }
504
505
setInputAccepted(bool inputAccepted)506 void KBBScalableGraphicWidget::setInputAccepted(bool inputAccepted)
507 {
508 m_inputAccepted = inputAccepted;
509 if (m_inputAccepted) {
510 setFocusPolicy( Qt::StrongFocus );
511 setFocus();
512 } else {
513 setFocusPolicy( Qt::NoFocus );
514 clearFocus();
515 }
516
517 updateDoneButton();
518 }
519
520
switchBall()521 void KBBScalableGraphicWidget::switchBall()
522 {
523 if ((m_balls->containsVisible(m_cursor->boxPosition())) || (m_ballsUnsure->containsVisible(m_cursor->boxPosition())))
524 removeBall(m_cursor->boxPosition());
525 else if (m_ballRepository->ballToTake() != KBBGraphicsItemSet::NO_INDEX)
526 addBall(m_cursor->boxPosition());
527 }
528
529
switchMarker()530 void KBBScalableGraphicWidget::switchMarker()
531 {
532 if (m_balls->containsVisible(m_cursor->boxPosition()))
533 setBallUnsure(m_cursor->boxPosition(), true);
534 else if (m_markersNothing->containsVisible(m_cursor->boxPosition()))
535 removeMarkerNothing(m_cursor->boxPosition());
536 else{
537 removeBall(m_cursor->boxPosition());
538 addMarkerNothing(m_cursor->boxPosition());
539 }
540 }
541
542
updateDoneButton()543 void KBBScalableGraphicWidget::updateDoneButton()
544 {
545 m_doneButton->setEnabled(m_doneAction->isEnabled());
546 m_doneButton->setToolTip(m_doneAction->toolTip());
547 }
548
549
useLaser(const int incomingPosition)550 void KBBScalableGraphicWidget::useLaser(const int incomingPosition)
551 {
552 if (!m_pause && m_gameDoc->mayShootRay(incomingPosition) && m_inputAccepted && m_lasers->containsVisible(incomingPosition)) {
553 const int outgoingPosition = m_gameDoc->shootRay(incomingPosition);
554
555 KBBGraphicsItemRayResult* inRay;
556 KBBGraphicsItemRayResult* outRay;
557
558 int rayNumberOrReflection = 0;
559 if (outgoingPosition==KBBGameDoc::HIT_POSITION)
560 rayNumberOrReflection = KBBGameDoc::HIT_POSITION;
561 if ((outgoingPosition!=incomingPosition) && (outgoingPosition!=KBBGameDoc::HIT_POSITION)) {
562 m_rayNumber++;
563 m_lasers->setVisible(outgoingPosition, false);
564 m_rayResults->insert(outRay = new KBBGraphicsItemRayResult(this, m_themeManager, m_scene, outgoingPosition, m_columns, m_rows, m_rayNumber));
565 rayNumberOrReflection = m_rayNumber;
566 }
567 m_rayResults->insert(inRay = new KBBGraphicsItemRayResult(this, m_themeManager, m_scene, incomingPosition, m_columns, m_rows, rayNumberOrReflection));
568
569 if ((outgoingPosition!=incomingPosition) && (outgoingPosition!=KBBGameDoc::HIT_POSITION)) {
570 inRay->setOpposite(outRay);
571 outRay->setOpposite(inRay);
572 }
573
574 m_scene->update();
575 m_lasers->setVisible(incomingPosition, false);
576
577 popupText(QString()); // To Remove any displayed text quicker.
578
579 cursorAtNewPosition(incomingPosition);
580 }
581 }
582
hoverMovePosition(int pos)583 void KBBScalableGraphicWidget::hoverMovePosition(int pos)
584 {
585 if (m_cursorFollowsMouse)
586 {
587 m_cursor->show();
588 m_cursor->setBoxPosition(pos);
589 m_cursor->updatePositions();
590 }
591 }
592
toggleCursor()593 void KBBScalableGraphicWidget::toggleCursor()
594 {
595 m_cursorFollowsMouse = !m_cursorFollowsMouse;
596 if (m_cursorFollowsMouse == false){
597 m_cursor->hide();
598 }
599 }
600
cursorOff()601 void KBBScalableGraphicWidget::cursorOff() {
602 m_cursor->hide();
603 }
604
605