File indexing completed on 2024-03-24 04:04:43
0001 /* 0002 KBlackBox - A simple game inspired by an emacs module 0003 0004 SPDX-FileCopyrightText: 1999-2000 Robert Cimrman <cimrman3@students.zcu.cz> 0005 SPDX-FileCopyrightText: 2007 Nicolas Roffet <nicolas-kde@roffet.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "kbbmainwindow.h" 0011 0012 #include <QFile> 0013 #include <QHBoxLayout> 0014 #include <QPointer> 0015 #include <QWidget> 0016 0017 #include <KActionCollection> 0018 #include <KConfigDialog> 0019 #include <KLocalizedString> 0020 #include <KMessageBox> 0021 0022 #include <KGameClock> 0023 #include <KGameHighScoreDialog> 0024 #include <KGameStandardAction> 0025 0026 #include <QStatusBar> 0027 #include <QIcon> 0028 #include <QStandardPaths> 0029 0030 #include "kbbgamedoc.h" 0031 #include "kbbgraphicsitemtutorialmarker.h" 0032 #include "kbblevelconfigurationwidget.h" 0033 #include "kbbprefs.h" 0034 #include "kbbscalablegraphicwidget.h" 0035 #include "kbbthememanager.h" 0036 #include "kbbtutorial.h" 0037 0038 0039 0040 // 0041 // Constructor / Destructor 0042 // 0043 0044 KBBMainWindow::KBBMainWindow() 0045 { 0046 m_boardEnabled = false; 0047 0048 //Read configuration options 0049 m_customBallNumber = KBBPrefs::balls(); 0050 m_customColumns = KBBPrefs::columns(); 0051 m_customRows = KBBPrefs::rows(); 0052 0053 0054 // Status bar 0055 //QT5 statusBar()->insertPermanentItem("", SRUN, 1); 0056 //QT5 statusBar()->insertPermanentItem(i18n("Time: 00:00"), STIME, 1); 0057 //QT5 statusBar()->insertPermanentItem(i18n("Size: 00 x 00"), SSIZE); 0058 //QT5 statusBar()->setItemAlignment(SRUN, Qt::AlignLeft | Qt::AlignVCenter); 0059 0060 0061 // Difficulty 0062 KGameDifficulty::global()->addStandardLevelRange( 0063 KGameDifficultyLevel::VeryEasy, KGameDifficultyLevel::ExtremelyHard, 0064 KGameDifficultyLevel::Medium //default 0065 ); 0066 KGameDifficulty::global()->addLevel(new KGameDifficultyLevel( 0067 1000, QByteArray("config"), i18nc("Difficulty level title", "Configurable") 0068 )); 0069 KGameDifficultyGUI::init(this); 0070 connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, this, &KBBMainWindow::levelChanged); 0071 0072 0073 // Menu "Game" 0074 KGameStandardAction::gameNew(this, &KBBMainWindow::newGame, actionCollection()); 0075 m_pauseAction = KGameStandardAction::pause(this, &KBBMainWindow::pause, actionCollection()); 0076 QAction* tutorial = actionCollection()->addAction( QStringLiteral( "game_tutorial" )); 0077 tutorial->setText(i18n("Start Tutorial")); 0078 tutorial->setIcon(QIcon::fromTheme( QStringLiteral( "footprint" ))); 0079 tutorial->setToolTip(i18n("Start tutorial")); 0080 tutorial->setWhatsThis(i18n("<qt>The <b>tutorial</b> is a fast, user friendly and interactive way to learn the rules of the game. Start it if you do not know them!</qt>")); 0081 connect(tutorial, &QAction::triggered, this, &KBBMainWindow::startTutorial); 0082 KGameStandardAction::quit(this, &QWidget::close, actionCollection()); 0083 QAction* sandbox = actionCollection()->addAction( QStringLiteral( "game_sandbox" )); 0084 sandbox->setText(i18n("New Sandbox Game")); 0085 sandbox->setToolTip(i18n("Start a new sandbox game")); 0086 sandbox->setWhatsThis(i18n("<qt><p>In a <b>sandbox game</b>, the solution is displayed at the beginning of the game. This is useful to understand the game principles.</p><p>However: after a while, it is not really fun and you should try to start a real game!</p></qt>")); 0087 connect(sandbox, &QAction::triggered, this, &KBBMainWindow::startSandbox); 0088 KGameStandardAction::highscores(this, &KBBMainWindow::showHighscores, actionCollection()); 0089 0090 // Menu "Move" 0091 m_doneAction = actionCollection()->addAction( QStringLiteral( "move_done" )); 0092 m_doneAction->setText(i18nc("This is the last action of a game to check the result, when the user is done.", "Done!")); 0093 m_doneAction->setWhatsThis(i18n("<qt><ul><li>First, you have to place all the balls on the black box. To guess the correct positions of the balls and see how they interact with laser beams, you should use the lasers that are positioned around the black box.</li><li><b>When you think you are done</b>, you should click here.</li></ul><p>Note that it is only possible to click here if you have placed the correct number of balls.</p></qt>")); 0094 m_doneAction->setIcon(QIcon::fromTheme( QStringLiteral( "dialog-ok" ))); 0095 connect(m_doneAction, &QAction::triggered, this, &KBBMainWindow::done); 0096 m_solveAction = KGameStandardAction::solve(this, &KBBMainWindow::solve, actionCollection()); 0097 m_solveAction->setToolTip(i18n("Give up the game")); 0098 m_solveAction->setWhatsThis(i18n("<qt><p>Choose \"<b>Solve</b>\" if you want to give up the current game. The solution will be displayed.</p><p>If you placed all the balls and do not want to give up, choose \"Done!\".</p></qt>")); 0099 0100 // Menu "Settings" 0101 KStandardAction::preferences(this, &KBBMainWindow::settingsDialog, actionCollection()); 0102 m_toggleCursorAction = actionCollection()->addAction( QStringLiteral( "toggle_cursor" )); 0103 m_toggleCursorAction->setText(i18n("Enable highlight under mouse")); 0104 m_toggleCursorAction->setCheckable(true); 0105 const KConfigGroup group = KSharedConfig::openConfig()->group(QStringLiteral("default")); 0106 m_toggleCursorAction->setChecked(group.readEntry<bool>("highlight_enabled", true)); 0107 connect(m_toggleCursorAction, &QAction::triggered, this, &KBBMainWindow::toggleCursor); 0108 0109 // Theme manager 0110 QString svgzFile = KBBPrefs::theme(); 0111 if (!QFile(svgzFile).exists()) 0112 svgzFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("themes/default.svgz")); 0113 m_themeManager = new KBBThemeManager(svgzFile); 0114 0115 0116 // Tutorial widget 0117 m_tutorial = new KBBTutorial(this); 0118 0119 0120 // Board 0121 m_gameDoc = new KBBGameDoc(this, m_tutorial); 0122 connect(m_gameDoc, &KBBGameDoc::updateStats, this, &KBBMainWindow::updateStats); 0123 connect(m_gameDoc, &KBBGameDoc::isRunning, this, &KBBMainWindow::setRunning); 0124 0125 0126 // Game widget 0127 m_gameWidget = new KBBScalableGraphicWidget(m_gameDoc, m_themeManager, m_doneAction); 0128 m_tutorial->setGameWidget(m_gameWidget, new KBBGraphicsItemTutorialMarker(m_gameWidget, m_themeManager, KBBTutorial::COLUMNS, KBBTutorial::ROWS)); 0129 0130 0131 // Central Widget 0132 m_centralWidget = new QWidget(this); 0133 QHBoxLayout *widgetLayout = new QHBoxLayout(); 0134 widgetLayout->setContentsMargins(0, 0, 0, 0); 0135 m_centralWidget->setLayout(widgetLayout); 0136 widgetLayout->addWidget(m_gameWidget); 0137 widgetLayout->addWidget(m_tutorial); 0138 setCentralWidget(m_centralWidget); 0139 0140 0141 // Keyboard only 0142 QAction * action = actionCollection()->addAction( QStringLiteral( "move_down" ) ); 0143 action->setText( i18n("Move Down") ); 0144 connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveDown); 0145 KActionCollection::setDefaultShortcut(action, Qt::Key_Down); 0146 addAction(action); 0147 0148 action = actionCollection()->addAction( QStringLiteral( "move_up" ) ); 0149 action->setText( i18n("Move Up") ); 0150 connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveUp); 0151 KActionCollection::setDefaultShortcut(action,Qt::Key_Up); 0152 addAction(action); 0153 0154 action = actionCollection()->addAction( QStringLiteral( "move_left" ) ); 0155 action->setText( i18n("Move Left") ); 0156 connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveLeft); 0157 KActionCollection::setDefaultShortcut(action, Qt::Key_Left); 0158 addAction(action); 0159 0160 action = actionCollection()->addAction( QStringLiteral( "move_right" ) ); 0161 action->setText( i18n("Move Right") ); 0162 connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveRight); 0163 KActionCollection::setDefaultShortcut(action, Qt::Key_Right); 0164 addAction(action); 0165 0166 action = actionCollection()->addAction( QStringLiteral( "switch_ball" )); 0167 action->setText(i18n("Switch Ball or Shoot Laser")); 0168 connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardEnter); 0169 KActionCollection::setDefaultShortcut(action, Qt::Key_Return); 0170 addAction(action); 0171 0172 action = actionCollection()->addAction( QStringLiteral( "switch_marker" )); 0173 action->setText(i18n("Switch Marker")); 0174 connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardSpace); 0175 KActionCollection::setDefaultShortcut(action, Qt::Key_Space); 0176 addAction(action); 0177 0178 0179 m_gameClock = new KGameClock(this, KGameClock::MinSecOnly); 0180 connect(m_gameClock, &KGameClock::timeChanged, this, &KBBMainWindow::updateStats); 0181 connect(m_gameClock, &KGameClock::timeChanged, m_gameDoc, &KBBGameDoc::timeChanged); 0182 0183 0184 levelChanged(); 0185 0186 setupGUI(); 0187 0188 if (m_toggleCursorAction->isChecked()) 0189 toggleCursor(); 0190 0191 // start a new game 0192 startGame(false); 0193 } 0194 0195 0196 KBBMainWindow::~KBBMainWindow() 0197 { 0198 KBBPrefs::self()->save(); 0199 0200 delete m_gameWidget; 0201 delete m_themeManager; 0202 } 0203 0204 0205 0206 // 0207 // Public slots 0208 // 0209 0210 void KBBMainWindow::levelChanged() 0211 { 0212 KGameDifficultyLevel::StandardLevel level = KGameDifficulty::globalLevel(); 0213 switch(level) { 0214 case KGameDifficultyLevel::VeryEasy: 0215 m_ballNumber = 2; 0216 m_columns = 6; 0217 m_rows = 6; 0218 break; 0219 case KGameDifficultyLevel::Easy: 0220 default: 0221 m_ballNumber = 4; 0222 m_columns = 8; 0223 m_rows = 8; 0224 level = KGameDifficultyLevel::Medium; 0225 break; 0226 case KGameDifficultyLevel::Medium: 0227 m_ballNumber = 6; 0228 m_columns = 10; 0229 m_rows = 10; 0230 break; 0231 case KGameDifficultyLevel::Hard: 0232 m_ballNumber = 8; 0233 m_columns = 12; 0234 m_rows = 12; 0235 break; 0236 case KGameDifficultyLevel::VeryHard: 0237 m_ballNumber = 11; 0238 m_columns = 14; 0239 m_rows = 10; 0240 break; 0241 case KGameDifficultyLevel::ExtremelyHard: 0242 m_ballNumber = 15; 0243 m_columns = 20; 0244 m_rows = 12; 0245 break; 0246 case KGameDifficultyLevel::Custom: 0247 m_gameWidget->popupText(i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Note: You can change<br />the parameters of<br />custom games in the<br />Settings dialog.")); 0248 break; 0249 } 0250 0251 m_level = level; 0252 startGame(m_sandboxMode); 0253 } 0254 0255 0256 void KBBMainWindow::setRunning(bool r) 0257 { 0258 // Difficulty 0259 KGameDifficulty::global()->setGameRunning(r); 0260 0261 // Clock 0262 if (r) { 0263 m_gameClock->resume(); 0264 m_gameDoc->timeChanged(); // It's important to end the current seconde before pausing so that the player cannot cheat with pause. 0265 } else 0266 m_gameClock->pause(); 0267 0268 // Pause 0269 m_pauseAction->setEnabled(r); 0270 } 0271 0272 0273 void KBBMainWindow::updateStats() 0274 { 0275 int ballsLeftToPlace = m_gameDoc->numberOfBallsToPlace() - m_gameDoc->numberOfBallsPlaced(); 0276 0277 m_doneAction->setEnabled(m_solveAction->isEnabled() && (ballsLeftToPlace==0)); 0278 0279 if (ballsLeftToPlace<0) 0280 m_doneAction->setToolTip(i18np("First, you need to remove 1 ball from the black box.", "First, you need to remove %1 balls from the black box.", -ballsLeftToPlace)); 0281 else if (ballsLeftToPlace==0) { 0282 m_doneAction->setToolTip(i18n("To check if you successfully guessed the ball positions, click here!")); 0283 } else if (ballsLeftToPlace>0) { 0284 m_doneAction->setToolTip(i18np("You need to place 1 more ball on the black box.", "You need to place %1 more balls on the black box.", ballsLeftToPlace)); 0285 } 0286 0287 if (!m_boardEnabled) 0288 m_doneAction->setToolTip(i18n("Game over.")); 0289 if (m_pauseAction->isChecked()) 0290 m_doneAction->setToolTip(i18n("Game paused.")); 0291 0292 0293 // Status bar 0294 if (m_tutorial->isVisible()) { 0295 //QT5 statusBar()->changeItem(i18n("Tutorial"), SRUN ); 0296 } 0297 if (!m_tutorial->isVisible()) { 0298 if (m_boardEnabled) { 0299 if (ballsLeftToPlace<0) { 0300 //QT5 statusBar()->changeItem((i18np("1 ball too many!", "%1 balls too many!", -ballsLeftToPlace)), SRUN); 0301 } else if (ballsLeftToPlace==0) { 0302 //QT5 statusBar()->changeItem(i18n("No more balls to place"), SRUN); 0303 } else if (ballsLeftToPlace>0) { 0304 //QT5 statusBar()->changeItem(i18np("1 ball to place", "%1 balls to place", ballsLeftToPlace), SRUN); 0305 } 0306 } else { 0307 //QT5 statusBar()->changeItem(i18n("Game over"), SRUN ); 0308 } 0309 } 0310 0311 //QT5 statusBar()->changeItem(i18n("Time: %1", m_gameClock->timeString()), STIME); 0312 0313 //QT5 statusBar()->changeItem( i18n("Size: %1 x %2", m_gameDoc->columns(), m_gameDoc->rows()), SSIZE ); 0314 0315 0316 // 2. Info Widget 0317 m_gameWidget->setScore(m_gameDoc->score()); 0318 } 0319 0320 0321 0322 // 0323 // Private slots 0324 // 0325 0326 void KBBMainWindow::done() 0327 { 0328 if (m_tutorial->isVisible() && !m_tutorial->maySolve()) { 0329 KMessageBox::information(this, i18n("Clicking on \"Done!\" is the normal way to check the positions of the balls at the end of the game. However, it is not possible in the tutorial to end the game before you reached the last step.\nPlease first finish the tutorial."), i18n("Check positions")); 0330 } else { 0331 solving(); 0332 0333 const int score = m_gameDoc->score(); 0334 QString s; 0335 if (score <= (m_ballNumber*35)) { 0336 s = i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Your final score is: %1.<br />You did really well!", score); 0337 if (m_sandboxMode) 0338 s += QLatin1String("<br /><br />") + i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "But it does not count<br />because <b>it is the sandbox!</b>"); 0339 } else 0340 s = i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Your final score is: %1.<br />I guess you need more practice.", score); 0341 0342 if ((!m_tutorial->isVisible()) && (!m_sandboxMode) && (KGameDifficulty::globalLevel() != KGameDifficultyLevel::Custom) && (score<KBBGameDoc::SCORE_LOST)) { 0343 QPointer<KGameHighScoreDialog> scoreDialog = new KGameHighScoreDialog(KGameHighScoreDialog::Score | KGameHighScoreDialog::Name, this); 0344 scoreDialog->initFromDifficulty(KGameDifficulty::global()); 0345 0346 KGameHighScoreDialog::FieldInfo scoreInfo; 0347 scoreInfo[KGameHighScoreDialog::Score].setNum(score); 0348 if(scoreDialog->addScore(scoreInfo, KGameHighScoreDialog::LessIsMore) != 0) 0349 scoreDialog->exec(); 0350 delete scoreDialog; 0351 } 0352 0353 m_gameWidget->popupText(s); 0354 } 0355 } 0356 0357 0358 void KBBMainWindow::newGame() 0359 { 0360 if (mayAbortGame()) 0361 startGame(false); 0362 } 0363 0364 0365 void KBBMainWindow::pause(bool state) 0366 { 0367 if (state) { 0368 m_gameClock->pause(); 0369 m_gameWidget->popupText(i18n("Game paused.<br />Press \"%1\" to resume.", m_pauseAction->shortcut().toString(QKeySequence::NativeText)), 0); 0370 } else { 0371 m_gameClock->resume(); 0372 m_gameWidget->popupText(QString()); 0373 } 0374 m_solveAction->setEnabled(!state); 0375 0376 updateStats(); 0377 m_gameWidget->setPause(state); 0378 } 0379 0380 0381 void KBBMainWindow::settingsChanged() 0382 { 0383 m_customBallNumber = m_levelConfig->balls(); 0384 m_customColumns = m_levelConfig->columns(); 0385 m_customRows = m_levelConfig->rows(); 0386 0387 if (m_level==KGameDifficultyLevel::Custom) { 0388 bool mayRestart = true; 0389 if (m_gameDoc->gameReallyStarted()) 0390 if (KMessageBox::questionTwoActions(this, 0391 i18n("Do you want to cancel the current custom game and start a new one with the new parameters?"), QString(), 0392 KGuiItem(i18nc("@action:button", "Start New Game"), QStringLiteral("view-refresh")), 0393 KStandardGuiItem::cont()) 0394 == KMessageBox::SecondaryAction) 0395 mayRestart = false; 0396 0397 if (mayRestart) 0398 startGame(m_sandboxMode); 0399 } 0400 } 0401 0402 0403 void KBBMainWindow::settingsDialog() 0404 { 0405 if (!KConfigDialog::showDialog(QStringLiteral("settings"))) { 0406 KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), KBBPrefs::self()); 0407 m_levelConfig = new KBBLevelConfigurationWidget(dialog, m_customBallNumber, m_customColumns, m_customRows, m_themeManager); 0408 dialog->addPage(m_levelConfig, i18n("Custom Game"), QStringLiteral("games-config-custom")); 0409 connect(dialog, &KConfigDialog::settingsChanged, this, &KBBMainWindow::settingsChanged); 0410 //QT5 dialog->setHelp(QString(), "kblackbox"); 0411 dialog->show(); 0412 } 0413 } 0414 0415 0416 void KBBMainWindow::showHighscores() 0417 { 0418 KGameHighScoreDialog scoreDialog(KGameHighScoreDialog::Score | KGameHighScoreDialog::Name, this); 0419 scoreDialog.initFromDifficulty(KGameDifficulty::global()); 0420 scoreDialog.exec(); 0421 } 0422 0423 0424 void KBBMainWindow::solve() 0425 { 0426 if (m_tutorial->isVisible() && !m_tutorial->maySolve()) { 0427 KMessageBox::information(this, i18n("Sorry, you may not give up during the tutorial."), i18n("Solve")); 0428 } else { 0429 if (m_gameDoc->numberOfBallsToPlace()==m_gameDoc->numberOfBallsPlaced()) { 0430 if (KMessageBox::warningContinueCancel(this, i18n("You placed all the balls. Great!\nYou should now click on \"Done!\" to end the game and check if you guessed correctly.\nSo, do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue) 0431 solving(); 0432 } else if (KMessageBox::warningContinueCancel(this, i18np("You should place %1 ball!\n", "You should place %1 balls!\n", m_gameDoc->numberOfBallsToPlace()) + i18np("You have placed %1.\n", "You have placed %1.\n", m_gameDoc->numberOfBallsPlaced()) + i18n("Do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue) 0433 solving(); 0434 } 0435 } 0436 0437 0438 void KBBMainWindow::startSandbox() 0439 { 0440 if (mayAbortGame()) { 0441 startGame(true); 0442 m_gameWidget->popupText(i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Note: In the sandbox mode,<br />the solution is already displayed.<br />Have fun!")); 0443 } 0444 } 0445 0446 0447 void KBBMainWindow::startTutorial() 0448 { 0449 if (mayAbortGame()) { 0450 m_gameDoc->startTutorial(); 0451 m_solveAction->setEnabled(true); 0452 m_pauseAction->setChecked(false); 0453 KGameDifficulty::global()->setEditable(false); 0454 0455 // Reset clock but don't start it yet. 0456 m_gameClock->restart(); 0457 m_gameClock->pause(); 0458 0459 updateStats(); 0460 } 0461 } 0462 0463 void KBBMainWindow::toggleCursor() 0464 { 0465 m_gameWidget->toggleCursor(); 0466 0467 KConfigGroup group = KSharedConfig::openConfig()->group(QStringLiteral("default")); 0468 group.writeEntry<bool>("highlight_enabled", m_toggleCursorAction->isChecked()); 0469 } 0470 0471 0472 // 0473 // Private 0474 // 0475 0476 bool KBBMainWindow::mayAbortGame() 0477 { 0478 bool mayAbort = true; 0479 0480 if (m_gameDoc->gameReallyStarted()) 0481 mayAbort = ( KMessageBox::warningContinueCancel(nullptr, i18n("This will be the end of the current game!"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::Continue ); 0482 0483 return mayAbort; 0484 } 0485 0486 0487 void KBBMainWindow::solving() 0488 { 0489 m_boardEnabled = false; 0490 m_solveAction->setEnabled(false); 0491 m_doneAction->setEnabled(false); 0492 m_gameDoc->gameOver(); 0493 m_gameWidget->solve(false); 0494 updateStats(); 0495 } 0496 0497 0498 void KBBMainWindow::startGame(bool sandboxMode) 0499 { 0500 if (m_level==KGameDifficultyLevel::Custom) { 0501 m_ballNumber = m_customBallNumber; 0502 m_columns = m_customColumns; 0503 m_rows = m_customRows; 0504 } 0505 0506 m_boardEnabled = true; 0507 m_sandboxMode = sandboxMode; 0508 0509 m_solveAction->setEnabled(true); 0510 m_pauseAction->setChecked(false); 0511 KGameDifficulty::global()->setEditable(true); 0512 m_tutorial->hide(); 0513 m_gameDoc->newGame(m_ballNumber, m_columns, m_rows); 0514 m_gameWidget->newGame(m_columns, m_rows, m_ballNumber); 0515 if (m_sandboxMode) 0516 m_gameWidget->solve(true); 0517 0518 // Reset clock but don't start it yet. 0519 m_gameClock->restart(); 0520 m_gameClock->pause(); 0521 0522 updateStats(); 0523 } 0524 0525 #include "moc_kbbmainwindow.cpp"