File indexing completed on 2024-04-21 03:41:59
0001 /* 0002 This file is part of Kiten, a KDE Japanese Reference Tool 0003 SPDX-FileCopyrightText: 2011 Daniel E. Moctezuma <democtezuma@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kanjibrowserview.h" 0009 0010 #include "DictKanjidic/dictfilekanjidic.h" 0011 #include "DictKanjidic/entrykanjidic.h" 0012 #include "dictquery.h" 0013 #include "entrylist.h" 0014 #include "kanjibrowser.h" 0015 #include "kanjibrowserconfig.h" 0016 #include "searchdialog.h" 0017 0018 #include <KActionCollection> 0019 #include <KLocalizedString> 0020 #include <KMessageBox> 0021 #include <QAction> 0022 #include <QClipboard> 0023 0024 KanjiBrowserView::KanjiBrowserView(QWidget *parent) 0025 : QWidget(parent) 0026 , _currentKanji(nullptr) 0027 { 0028 setupUi(this); 0029 loadSettings(); 0030 } 0031 0032 void KanjiBrowserView::changeGrade(const int grade) 0033 { 0034 _currentGradeList.clear(); 0035 0036 // Indexes of items in the ComboBox: 0037 // All Jouyou Kanji Grades: 0 0038 // Grade 1: 1 0039 // Grade 2: 2 0040 // . 0041 // . 0042 // . 0043 // Not in Jouyou list: ComboBox->count() - 1 0044 0045 if (grade == AllJouyouGrades) { 0046 // Add the all the grades found in our list. 0047 for (const int grd : _gradeList) { 0048 _currentGradeList << grd; 0049 } 0050 } 0051 // Here the user selected "Not in Jouyou list". 0052 else if (grade == (_grades->count() - 1)) { 0053 // Only show the kanji with grade 0 (not in Jouyou). 0054 _currentGradeList << 0; 0055 } 0056 // It seems KANJIDIC doesn't have a G7 (grade 7) kanji. 0057 // If the user selects G8 or above, we need to add 1 to the grade 0058 // because the index (from the ComboBox) and the grade will be different. 0059 else if (grade >= Grade7) { 0060 _currentGradeList << (grade + 1); 0061 } 0062 // Show the kanji with the selected grade. 0063 else { 0064 _currentGradeList << grade; 0065 } 0066 0067 // Reload our QListWidget widget. 0068 reloadKanjiList(); 0069 } 0070 0071 void KanjiBrowserView::changeStrokeCount(const int strokes) 0072 { 0073 _currentStrokesList.clear(); 0074 0075 // Indexes of items in the ComboBox: 0076 // No stroke limit: 0 0077 // 1 stroke: 1 0078 // 2 strokes: 2 0079 // . 0080 // . 0081 // . 0082 0083 // We don't need to filter any kanji by stroke number. 0084 if (strokes == NoStrokeLimit) { 0085 // Add all the strokes found to our the list. 0086 for (const int stroke : _strokesList) { 0087 _currentStrokesList << stroke; 0088 } 0089 } 0090 // Show the kanji with the selected number of strokes. 0091 else { 0092 _currentStrokesList << strokes; 0093 } 0094 0095 // Reload our QListWidget widget. 0096 reloadKanjiList(); 0097 } 0098 0099 void KanjiBrowserView::changeToInfoPage() 0100 { 0101 _stackedWidget->setCurrentIndex(Info); 0102 } 0103 0104 void KanjiBrowserView::changeToListPage() 0105 { 0106 _stackedWidget->setCurrentIndex(List); 0107 } 0108 0109 QString KanjiBrowserView::convertToCSS(const QFont &font) 0110 { 0111 QString weight; 0112 // TODO: these have numeric values in Qt we could plug into the CSS here 0113 switch (font.weight()) { 0114 case QFont::Thin: 0115 case QFont::ExtraLight: 0116 case QFont::Light: 0117 weight = QStringLiteral("lighter"); 0118 break; 0119 case QFont::Medium: 0120 case QFont::Normal: 0121 weight = QStringLiteral("normal"); 0122 break; 0123 case QFont::DemiBold: 0124 case QFont::ExtraBold: 0125 case QFont::Black: 0126 case QFont::Bold: 0127 weight = QStringLiteral("bold"); 0128 break; 0129 } 0130 0131 QString style; 0132 switch (font.style()) { 0133 case QFont::StyleNormal: 0134 style = QStringLiteral("normal"); 0135 break; 0136 case QFont::StyleItalic: 0137 style = QStringLiteral("italic"); 0138 break; 0139 case QFont::StyleOblique: 0140 style = QStringLiteral("oblique"); 0141 break; 0142 } 0143 0144 return QStringLiteral( 0145 "font-family:\"%1\";" 0146 "font-size:%2px;" 0147 "font-weight:%3;" 0148 "font-style:%4;") 0149 .arg(font.family()) 0150 .arg(font.pointSizeF()) 0151 .arg(weight) 0152 .arg(style); 0153 } 0154 0155 void KanjiBrowserView::loadSettings() 0156 { 0157 _kanjiList->setFont(KanjiBrowserConfigSkeleton::self()->kanjiListFont()); 0158 _kanjiSize = KanjiBrowserConfigSkeleton::self()->kanjiSize(); 0159 _kanaFont = KanjiBrowserConfigSkeleton::self()->kanaFont(); 0160 _labelFont = KanjiBrowserConfigSkeleton::self()->labelFont(); 0161 0162 // Reload the Kanji Information page with the new font changes. 0163 if (_currentKanji != nullptr) { 0164 showKanjiInformation(_currentKanji); 0165 } 0166 } 0167 0168 void KanjiBrowserView::reloadKanjiList() 0169 { 0170 // Grade and strokes lists have the information of 0171 // which kanji we are going to filter. 0172 // We just iterate on them to actually do the filtering. 0173 QStringList list; 0174 for (const int strokes : _currentStrokesList) { 0175 for (const int grade : _currentGradeList) { 0176 list.append(_kanji.keys(qMakePair(grade, strokes))); 0177 } 0178 } 0179 0180 _kanjiList->clear(); 0181 _kanjiList->addItems(list); 0182 0183 // Update our status bar with the number of kanji filtered. 0184 statusBarChanged(i18np("%1 kanji found", "%1 kanji found", _kanjiList->count())); 0185 } 0186 0187 void KanjiBrowserView::searchKanji(const QString &term) 0188 { 0189 if (_currentKanji != nullptr && term == _currentKanji->getWord()) { 0190 return; 0191 } 0192 0193 if (auto result = _parent->_dictFileKanjidic->doSearch(DictQuery(term)); result != nullptr && !result->isEmpty()) { 0194 auto kanji = dynamic_cast<EntryKanjidic *>(result->first()); 0195 _currentKanji = kanji; 0196 0197 _goToKanjiInfo->setText(i18n("About %1", _currentKanji->getWord())); 0198 _copyToClipboard->setText(i18n("Copy %1 to clipboard", _currentKanji->getWord())); 0199 _copyToClipboard->setVisible(true); 0200 0201 showKanjiInformation(kanji); 0202 _goToKanjiInfo->triggered(); 0203 } 0204 } 0205 0206 void KanjiBrowserView::setupView(KanjiBrowser *parent, const QHash<QString, QPair<int, int>> &kanji, QList<int> &kanjiGrades, QList<int> &strokeCount) 0207 { 0208 if (kanji.isEmpty() || kanjiGrades.isEmpty() || strokeCount.isEmpty()) { 0209 qDebug() << "One or more of our lists are empty (kanji, grades, strokes)."; 0210 qDebug() << "Could not load the view properly."; 0211 KMessageBox::error(this, i18n("Could not load the necessary kanji information.")); 0212 return; 0213 } 0214 0215 _parent = parent; 0216 _kanji = kanji; 0217 _gradeList = kanjiGrades; 0218 _strokesList = strokeCount; 0219 0220 QAction *goToKanjiList = _parent->actionCollection()->addAction(QStringLiteral("kanji_list")); 0221 goToKanjiList->setText(i18n("Kanji &List")); 0222 0223 _goToKanjiInfo = _parent->actionCollection()->addAction(QStringLiteral("kanji_info")); 0224 _goToKanjiInfo->setText(i18n("Kanji &Information")); 0225 0226 _searchKanjiAction = _parent->actionCollection()->addAction(QStringLiteral("search")); 0227 _searchKanjiAction->setIcon(QIcon::fromTheme(QStringLiteral("search"))); 0228 _searchKanjiAction->setText(i18n("&Search…")); 0229 0230 _copyToClipboard = _parent->actionCollection()->addAction(QStringLiteral("copy_kanji_to_clipboard")); 0231 _copyToClipboard->setVisible(false); 0232 0233 _grades->addItem(i18n("All Jouyou Kanji grades")); 0234 for (const int &grade : kanjiGrades) { 0235 // Grades 9 and above are considered Jinmeiyou. 0236 if (grade >= Jinmeiyou) { 0237 _grades->addItem(i18n("Grade %1 (Jinmeiyou)", grade)); 0238 } else { 0239 _grades->addItem(i18n("Grade %1", grade)); 0240 } 0241 } 0242 _grades->addItem(i18n("Not in Jouyou list")); 0243 0244 _strokes->addItem(i18n("No stroke limit")); 0245 for (const int &stroke : strokeCount) { 0246 _strokes->addItem(i18np("%1 stroke", "%1 strokes", stroke)); 0247 } 0248 0249 connect(_grades, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &KanjiBrowserView::changeGrade); 0250 connect(_strokes, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &KanjiBrowserView::changeStrokeCount); 0251 connect(_kanjiList, &QListWidget::itemClicked, this, [this](QListWidgetItem *item) { 0252 searchKanji(item->text()); 0253 }); 0254 connect(_kanjiList, &QListWidget::itemClicked, _goToKanjiInfo, [this] { 0255 _goToKanjiInfo->triggered(); 0256 }); 0257 connect(goToKanjiList, &QAction::triggered, this, &KanjiBrowserView::changeToListPage); 0258 connect(_goToKanjiInfo, &QAction::triggered, this, &KanjiBrowserView::changeToInfoPage); 0259 connect(_copyToClipboard, &QAction::triggered, this, &KanjiBrowserView::toClipboard); 0260 connect(_searchKanjiAction, &QAction::triggered, this, &KanjiBrowserView::openSearchDialog); 0261 0262 // Set the current grade (Grade 1). 0263 _grades->setCurrentIndex(1); 0264 // Set the current number of strokes (No stroke limit). 0265 // NOTE: we change from '1 stroke' to 'No stroke limit' 0266 // to let the ComboBox notice the change and do the filter. 0267 _strokes->setCurrentIndex(1); 0268 _strokes->setCurrentIndex(NoStrokeLimit); 0269 0270 qDebug() << "Initial setup succeeded!"; 0271 } 0272 0273 void KanjiBrowserView::showKanjiInformation(const EntryKanjidic *kanji) 0274 { 0275 // This font is shipped with Kiten and should not be changed as it shows 0276 // the stroke order of a kanji. 0277 QFont kanjiFont(QStringLiteral("KanjiStrokeOrders")); 0278 kanjiFont.setPointSizeF(_kanjiSize.toReal()); 0279 0280 QString text; 0281 text.append(QStringLiteral("<html><body><style>")); 0282 text.append(QStringLiteral(".kanji { %1 }").arg(convertToCSS(kanjiFont))); 0283 text.append(QStringLiteral(".label { %1 }").arg(convertToCSS(_labelFont))); 0284 text.append(QStringLiteral(".kana { %1 }").arg(convertToCSS(_kanaFont))); 0285 text.append(QStringLiteral("</style>")); 0286 0287 // Put the kanji. 0288 text.append(QStringLiteral("<table><tr><td><p class=\"kanji\">%1</p></td>").arg(kanji->getWord())); 0289 0290 // Now the kanji grades and number of strokes. 0291 text.append(QStringLiteral("<td>")); 0292 if (!kanji->getKanjiGrade().isEmpty()) { 0293 text.append(QStringLiteral("<p class=\"label\">%1 %2</p></br>").arg(i18n("Grade:")).arg(kanji->getKanjiGrade())); 0294 } 0295 text.append(QStringLiteral("<p class=\"label\">%1 %2</p></td></tr></table>").arg(i18n("Strokes:")).arg(kanji->getStrokesCount())); 0296 0297 // Onyomi readings. 0298 if (!kanji->getOnyomiReadingsList().isEmpty()) { 0299 text.append(QStringLiteral("<p class=\"label\">%1" 0300 "<span class=\"kana\">%2</span></p></br>") 0301 .arg(i18n("Onyomi: ")) 0302 .arg(kanji->getOnyomiReadings())); 0303 } 0304 0305 // Kunyomi readings. 0306 if (!kanji->getKunyomiReadingsList().isEmpty()) { 0307 text.append(QStringLiteral("<p class=\"label\">%1" 0308 "<span class=\"kana\">%2</span></p></br>") 0309 .arg(i18n("Kunyomi: ")) 0310 .arg(kanji->getKunyomiReadings())); 0311 } 0312 0313 // Special readings used in names. 0314 if (!kanji->getInNamesReadingsList().isEmpty()) { 0315 text.append(QStringLiteral("<p class=\"label\">%1" 0316 "<span class=\"kana\">%2</span></p></br>") 0317 .arg(i18n("In names: ")) 0318 .arg(kanji->getInNamesReadings())); 0319 } 0320 0321 // Reading used as radical. 0322 if (!kanji->getAsRadicalReadingsList().isEmpty()) { 0323 text.append(QStringLiteral("<p class=\"label\">%1" 0324 "<span class=\"kana\">%2</span></p></br>") 0325 .arg(i18n("As radical: ")) 0326 .arg(kanji->getAsRadicalReadings())); 0327 } 0328 0329 // Meanings 0330 text.append(QStringLiteral("<p class=\"label\">")); 0331 if (kanji->getMeaningsList().count() == 1) { 0332 text.append(i18n("Meaning: ")); 0333 } else { 0334 text.append(i18n("Meanings: ")); 0335 } 0336 text.append(QStringLiteral("<span class=\"kana\">%1</span></p>").arg(kanji->getMeanings())); 0337 0338 // Close remaining tags and set the HTML text. 0339 text.append(QStringLiteral("</body></html>")); 0340 _kanjiInformation->setHtml(text); 0341 } 0342 0343 void KanjiBrowserView::toClipboard() 0344 { 0345 QClipboard *cb = QApplication::clipboard(); 0346 cb->setText(_currentKanji->getWord(), QClipboard::Clipboard); 0347 cb->setText(_currentKanji->getWord(), QClipboard::Selection); 0348 } 0349 0350 void KanjiBrowserView::openSearchDialog() 0351 { 0352 auto dialog = new SearchDialog(this); 0353 connect(dialog, &SearchDialog::search, this, [this, dialog](const QString &query) { 0354 searchKanji(query); 0355 dialog->close(); 0356 }); 0357 dialog->show(); 0358 } 0359 0360 #include "moc_kanjibrowserview.cpp"