File indexing completed on 2024-04-21 03:41:51
0001 /*************************************************************************** 0002 * Copyright (C) 2004-2007 by Albert Astals Cid * 0003 * aacid@kde.org * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 ***************************************************************************/ 0010 0011 #include "boxasker.h" 0012 0013 #include <KAcceleratorManager> 0014 #include <KLocalizedString> 0015 0016 #include <QButtonGroup> 0017 #include <QGroupBox> 0018 #include <QEvent> 0019 #include <QLabel> 0020 #include <QLayout> 0021 #include <QRadioButton> 0022 #include <QRandomGenerator> 0023 #include <QPushButton> 0024 #include <QKeyEvent> 0025 0026 #include "map.h" 0027 #include "settings.h" 0028 0029 static const int NB_CHOICES = 4; 0030 0031 boxAsker::boxAsker(QWidget *parent, KGmap *m, QWidget *w, uint count) : askWidget(parent, m, w, count) 0032 { 0033 p_headWidget = nullptr; 0034 p_lay = new QVBoxLayout(this); 0035 0036 p_groupBox = new QGroupBox(this); 0037 p_groupLayout = new QGridLayout(p_groupBox); 0038 p_label = new QLabel(this); 0039 0040 p_radioButtons.resize(NB_CHOICES); 0041 p_answerLabels.resize(NB_CHOICES); 0042 for(int i = 0; i < NB_CHOICES; i++) 0043 { 0044 p_answerLabels[i] = new QLabel(QString::number(i +1)); 0045 p_radioButtons[i] = new QRadioButton(p_groupBox); 0046 0047 p_radioButtons[i] -> installEventFilter(this); 0048 connect(p_radioButtons[i], &QAbstractButton::toggled, this, &boxAsker::atLeastOneSelected); 0049 } 0050 p_accept = new QPushButton(); 0051 0052 layoutGroupBox(); 0053 layoutAligned(); 0054 0055 KAcceleratorManager::setNoAccel(this); 0056 0057 p_groupBox -> setFocus(); 0058 } 0059 0060 boxAsker::~boxAsker() 0061 { 0062 if ( p_accept->parent() == nullptr ) 0063 delete p_accept; 0064 } 0065 0066 void boxAsker::updateLayout() 0067 { 0068 layoutGroupBox(); 0069 layoutAligned(); 0070 } 0071 0072 void boxAsker::layoutGroupBox() 0073 { 0074 while ( p_groupLayout->takeAt(0) != nullptr ) { } 0075 0076 int horizAlignCode = kgeographySettings::self() -> questionPlacingScheme() % 3; 0077 Qt::Alignment horizAlignment = horizAlignCode == 0 ? Qt::AlignLeft : horizAlignCode == 1 ? Qt::AlignHCenter : Qt::AlignRight; 0078 p_groupLayout->setColumnStretch(0, horizAlignment == Qt::AlignLeft ? 0 : 1); 0079 p_groupLayout->setColumnStretch(3, horizAlignment == Qt::AlignRight ? 0 : 1); 0080 0081 p_groupLayout -> setHorizontalSpacing(6); 0082 for(int i = 0; i < NB_CHOICES; i++) 0083 { 0084 p_answerLabels[i]->setAlignment(Qt::AlignRight); 0085 p_groupLayout -> addWidget(p_answerLabels[i], i, 0); 0086 p_groupLayout -> addWidget(p_radioButtons[i], i, 1); 0087 } 0088 } 0089 0090 void boxAsker::layoutAligned() 0091 { 0092 while ( p_lay->takeAt(0) != nullptr ) { } 0093 0094 if ( p_headWidget != nullptr ) 0095 p_lay->addWidget(p_headWidget); 0096 0097 int horizAlignCode = kgeographySettings::self() -> questionPlacingScheme() % 3; 0098 Qt::Alignment horizAlignment = horizAlignCode == 0 ? Qt::AlignLeft : horizAlignCode == 1 ? Qt::AlignHCenter : Qt::AlignRight; 0099 int vertAlignCode = kgeographySettings::self() -> questionPlacingScheme() / 3 % 3; 0100 Qt::Alignment vertAlignment = vertAlignCode == 0 ? Qt::AlignTop : vertAlignCode == 1 ? Qt::AlignVCenter : Qt::AlignBottom; 0101 0102 p_label -> setAlignment(horizAlignment); 0103 p_groupBox -> setAlignment(horizAlignment); 0104 0105 p_lay -> addWidget(p_label); 0106 0107 if ( vertAlignment != Qt::AlignTop ) { 0108 p_lay -> addStretch(1); 0109 } 0110 0111 p_lay -> addWidget(p_groupBox, 0); 0112 0113 if ( vertAlignment != Qt::AlignBottom ) { 0114 p_lay -> addStretch(1); 0115 } 0116 0117 if ( kgeographySettings::self() -> waitsForValidation() ) { 0118 p_lay->addWidget(p_accept); 0119 p_accept->show(); 0120 } 0121 else { 0122 if ( p_accept->isVisible() ) 0123 checkAnswer(); 0124 p_accept -> hide(); 0125 } 0126 } 0127 0128 bool boxAsker::eventFilter(QObject *obj, QEvent *event) 0129 { 0130 if ( kgeographySettings::self() -> focusFollowsMouse() && event -> type() == QEvent::Enter) { 0131 if (obj == p_accept) 0132 p_accept -> setFocus(); 0133 else 0134 ((QRadioButton*)obj) -> setFocus(); 0135 return true; 0136 } else { 0137 // pass the event on to the parent class 0138 return QWidget::eventFilter(obj, event); 0139 } 0140 } 0141 0142 void boxAsker::setQuestion(const QString &q) 0143 { 0144 p_label -> setText(q); 0145 } 0146 0147 void boxAsker::keyPressEvent(QKeyEvent *e) 0148 { 0149 // we do this on press because it is done so for 0->1-2->3 and 3->2->1->0 movements 0150 // (those keys are subject to repeat, they have to be treated as press) 0151 if ( e -> key() == Qt::Key_Down && p_radioButtons[NB_CHOICES -1] -> hasFocus() ) 0152 { 0153 if ( p_radioButtons[NB_CHOICES -1] -> isChecked() ) p_radioButtons[0] -> setChecked(true); 0154 p_radioButtons[0] -> setFocus(); 0155 } 0156 else if ( e -> key() == Qt::Key_Up && p_radioButtons[0] -> hasFocus() ) 0157 { 0158 if ( p_radioButtons[0] -> isChecked() ) p_radioButtons[NB_CHOICES -1] -> setChecked(true); 0159 p_radioButtons[NB_CHOICES -1] -> setFocus(); 0160 } 0161 } 0162 0163 void boxAsker::keyReleaseEvent(QKeyEvent *e) 0164 { 0165 if (e -> key() == Qt::Key_Return || e -> key() == Qt::Key_Enter) checkAnswer(); 0166 else if ( e -> key() >= Qt::Key_1 && e -> key() <= (Qt::Key_1 + NB_CHOICES -1) ) 0167 { 0168 p_radioButtons[e -> key() - Qt::Key_1] -> setFocus(); 0169 // we check the box after the focus because the check can trigger immediate destruction of the asker at last question 0170 p_radioButtons[e -> key() - Qt::Key_1] -> setChecked(true); 0171 // next line triggered by previous, no need to go this way, crashes at end. 0172 //if ( ! kgeographySettings::self() -> waitsForValidation() ) checkAnswer(); 0173 } 0174 else askWidget::keyReleaseEvent(e); 0175 } 0176 0177 void boxAsker::nextQuestionHook(const division *div) 0178 { 0179 QString otherDivision; 0180 QStringList auxList; 0181 int j; 0182 0183 for(int i = 0; i < NB_CHOICES; i++) p_radioButtons[i] -> setAutoExclusive(false); 0184 for(int i = 0; i < NB_CHOICES; i++) p_radioButtons[i] -> setChecked(false); 0185 for(int i = 0; i < NB_CHOICES; i++) p_radioButtons[i] -> setText(QString()); 0186 for(int i = 0; i < NB_CHOICES; i++) p_radioButtons[i] -> setIcon(QIcon()); 0187 for(int i = 0; i < NB_CHOICES; i++) p_radioButtons[i] -> setAutoExclusive(true); 0188 0189 p_accept -> setEnabled(false); 0190 0191 auxList << div -> getUntranslatedName (); 0192 0193 // we put the division in a random place 0194 p_position = QRandomGenerator::global()->bounded(NB_CHOICES); 0195 nextBoxAskerQuestionHook(div, p_position, true); 0196 0197 // fill the other names 0198 j = 0; 0199 while (j < NB_CHOICES) 0200 { 0201 if (p_radioButtons[j] -> text().isNull() && p_radioButtons[j] -> icon().isNull()) 0202 { 0203 division *otherDiv; 0204 do 0205 { 0206 otherDiv = p_map -> getRandomDivision(askMode()); 0207 otherDivision = otherDiv -> getUntranslatedName(); 0208 } while (auxList.contains(otherDivision)); 0209 if (nextBoxAskerQuestionHook(otherDiv, j, false)) 0210 ++j; 0211 auxList << otherDivision; 0212 } 0213 else ++j; 0214 } 0215 } 0216 0217 void boxAsker::atLeastOneSelected() 0218 { 0219 if ( ! kgeographySettings::self() -> waitsForValidation() ) 0220 QMetaObject::invokeMethod(this, &boxAsker::checkAnswer, Qt::QueuedConnection); 0221 else 0222 p_accept -> setEnabled(true); 0223 } 0224 0225 void boxAsker::checkAnswer() 0226 { 0227 bool any, correct; 0228 int i; 0229 0230 correct = false; 0231 any = false; 0232 i = 0; 0233 while(!any && i < NB_CHOICES) 0234 { 0235 if (p_radioButtons[i] -> isChecked()) 0236 { 0237 any = true; 0238 correct = (i == p_position); 0239 } 0240 else i++; 0241 } 0242 0243 if (any) 0244 { 0245 setAnswerHook(i); 0246 questionAnswered(correct); 0247 nextQuestion(); 0248 } 0249 } 0250 0251 void boxAsker::init() 0252 { 0253 p_accept -> setText(i18n("&Accept")); 0254 0255 resetAnswers(); 0256 clearAsked(); 0257 nextQuestion(); 0258 0259 p_accept -> disconnect(); 0260 connect(p_accept, &QPushButton::clicked, this, &boxAsker::checkAnswer); 0261 } 0262 0263 void boxAsker::setHeadWidget(QWidget *headWidget) 0264 { 0265 p_headWidget = headWidget; 0266 p_lay -> insertWidget(0, headWidget); 0267 } 0268 0269 #include "moc_boxasker.cpp"