File indexing completed on 2024-04-21 03:42:01

0001 /*
0002     This file is part of Kiten, a KDE Japanese Reference Tool
0003     SPDX-FileCopyrightText: 2006 Joseph Kerian <jkerian@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 /**
0009  * Future Plans:
0010  *
0011  * Design a custom QGridLayout to rearrange buttons dynamically to resize
0012  * Design multiple radical file handling
0013  * Icon set for displaying radicals outside of this bizarre unicode section
0014  * Radical decomposition implementation
0015  */
0016 
0017 #include "buttongrid.h"
0018 
0019 #include <KLocalizedString>
0020 
0021 #include <QGridLayout>
0022 #include <QLabel>
0023 #include <QString>
0024 
0025 ButtonGrid::ButtonGrid(QWidget *parent, RadicalFile *radicalInfo)
0026     : QWidget(parent)
0027     , CurrentMode(Selection)
0028     , m_radicalInfo(radicalInfo)
0029     , m_sortByFrequency(false)
0030 {
0031     if (m_radicalInfo) {
0032         buildRadicalButtons();
0033     }
0034 }
0035 
0036 void ButtonGrid::buildRadicalButtons()
0037 {
0038     if (layout()) {
0039         // Instead of iterating over our children and deleting them one by one, we
0040         // simply reparent the previous layout to a temporary widget; when it goes
0041         // out of scope, everything will be automatically deleted.
0042         // Thanks to https://stackoverflow.com/a/10439207 for the tip!
0043         QWidget().setLayout(layout());
0044     }
0045 
0046     // Setup the grid
0047     auto grid = new QGridLayout(this);
0048 
0049     // Now make labels
0050     for (unsigned int i = 0; i < number_of_radical_columns; i++) {
0051         QString headerString = QString::number(i + 1);
0052         if (i == (number_of_radical_columns - 1)) {
0053             headerString.append(QStringLiteral("+"));
0054         }
0055         auto header = new QLabel(headerString, this);
0056         header->setAlignment(Qt::AlignHCenter);
0057         grid->addWidget(header, 0, i);
0058     }
0059 
0060     // Get a list of radicals (organized by strokes)
0061     QMultiMap<int, Radical> *radicalMap = m_radicalInfo->mapRadicalsByStrokes(number_of_radical_columns);
0062     QList<int> radicalStrokeCounts = radicalMap->uniqueKeys();
0063     // Now create all the buttons
0064     for (int strokeCount : radicalStrokeCounts) {
0065         //(0-based column index)
0066         unsigned int column_index = strokeCount - 1;
0067         int row_index = 1;
0068 
0069         QList<Radical> radicals = radicalMap->values(strokeCount);
0070         std::sort(radicals.begin(), radicals.end(), m_sortByFrequency ? Radical::compareFrequencies : Radical::compareIndices);
0071         for (const Radical &radical : radicals) {
0072             // Make the button
0073             auto button = new RadicalButton(radical.toString(), this);
0074             grid->addWidget(button, row_index++, column_index);
0075             // Bind slots/signals for this button
0076             connect(button, &RadicalButton::userClicked, this, &ButtonGrid::radicalClicked);
0077             connect(this, &ButtonGrid::clearButtonSelections, button, &RadicalButton::resetButton);
0078 
0079             // Add this button to our list
0080             m_buttons.insert(radical.toString(), button);
0081         }
0082     }
0083     delete radicalMap;
0084     setLayout(grid);
0085 
0086     updateButtons();
0087 }
0088 
0089 void ButtonGrid::setSortByFrequency(bool enable)
0090 {
0091     if (m_sortByFrequency != enable) {
0092         m_sortByFrequency = enable;
0093         buildRadicalButtons();
0094     }
0095 }
0096 
0097 void ButtonGrid::clearSelections()
0098 {
0099     m_selectedRadicals.clear();
0100     Q_EMIT clearButtonSelections();
0101 }
0102 
0103 void ButtonGrid::radicalClicked(const QString &newrad, RadicalButton::ButtonStatus newStatus)
0104 {
0105     if (newStatus == RadicalButton::Related) {
0106         // TODO: Do something fancy
0107     } else if (newStatus == RadicalButton::Normal || newStatus == RadicalButton::Selected) {
0108         CurrentMode = Selection;
0109 
0110         if (newStatus == RadicalButton::Normal) {
0111             m_selectedRadicals.remove(newrad);
0112             if (m_selectedRadicals.isEmpty()) {
0113                 Q_EMIT signalChangeStatusbar(i18n("No Radicals Selected"));
0114             }
0115         } else {
0116             m_selectedRadicals.insert(newrad);
0117         }
0118 
0119         updateButtons();
0120     }
0121 }
0122 
0123 void ButtonGrid::updateButtons()
0124 {
0125     if (!m_radicalInfo) {
0126         return;
0127     }
0128     // Special Case/Early exit: no radicals selected
0129     if (m_selectedRadicals.isEmpty()) {
0130         QList<Kanji> blankList;
0131         for (RadicalButton *button : m_buttons) {
0132             button->setStatus(RadicalButton::Normal);
0133         }
0134 
0135         Q_EMIT possibleKanji(blankList);
0136         return;
0137     }
0138 
0139     // Figure out what our kanji possibilities are
0140     QSet<Kanji> kanjiSet = m_radicalInfo->kanjiContainingRadicals(m_selectedRadicals);
0141 
0142     // Convert to a list, sort, and tell the world!
0143     QList<Kanji> kanjiList = kanjiSet.values();
0144     std::sort(kanjiList.begin(), kanjiList.end());
0145     Q_EMIT possibleKanji(kanjiList);
0146 
0147     // Do the announcement of the selected radical list
0148     QStringList radicalList(m_selectedRadicals.values());
0149     Q_EMIT signalChangeStatusbar(i18n("Selected Radicals: ") + radicalList.join(QLatin1String(", ")));
0150 
0151     // Now figure out what our remaining radical possibilities are
0152     QSet<QString> remainingRadicals = m_radicalInfo->radicalsInKanji(kanjiSet);
0153     // Remove the already selected ones
0154     remainingRadicals -= m_selectedRadicals;
0155 
0156     // Now go through and set status appropriately
0157     QHash<QString, RadicalButton *>::iterator i = m_buttons.begin();
0158     while (i != m_buttons.end()) {
0159         if (m_selectedRadicals.contains(i.key())) {
0160             i.value()->setStatus(RadicalButton::Selected);
0161         } else if (remainingRadicals.contains(i.key())) {
0162             i.value()->setStatus(RadicalButton::Normal);
0163         } else {
0164             i.value()->setStatus(RadicalButton::NotAppropriate);
0165         }
0166 
0167         ++i;
0168     }
0169 }
0170 
0171 #include "moc_buttongrid.cpp"