File indexing completed on 2023-05-30 10:42:10

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