1 /*
2 This file is part of Kiten, a KDE Japanese Reference Tool
3 SPDX-FileCopyrightText: 2011 Daniel E. Moctezuma <democtezuma@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "kanjibrowserview.h"
9
10 #include "DictKanjidic/dictfilekanjidic.h"
11 #include "DictKanjidic/entrykanjidic.h"
12 #include "dictquery.h"
13 #include "entrylist.h"
14 #include "kanjibrowser.h"
15 #include "kanjibrowserconfig.h"
16
17 #include <QAction>
18 #include <QClipboard>
19 #include <QDebug>
20 #include <KActionCollection>
21 #include <KLocalizedString>
22 #include <KMessageBox>
23
KanjiBrowserView(QWidget * parent)24 KanjiBrowserView::KanjiBrowserView( QWidget *parent )
25 : QWidget( parent )
26 , _currentKanji( 0 )
27 {
28 setupUi( this );
29 loadSettings();
30 }
31
~KanjiBrowserView()32 KanjiBrowserView::~KanjiBrowserView()
33 {
34
35 }
36
changeGrade(const int grade)37 void KanjiBrowserView::changeGrade( const int grade )
38 {
39 _currentGradeList.clear();
40
41 // Indexes of items in the ComboBox:
42 // All Jouyou Kanji Grades: 0
43 // Grade 1: 1
44 // Grade 2: 2
45 // .
46 // .
47 // .
48 // Not in Jouyou list: ComboBox->count() - 1
49
50 if( grade == AllJouyouGrades )
51 {
52 // Add the all the grades found in our list.
53 foreach( const int grd, _gradeList )
54 {
55 _currentGradeList << grd;
56 }
57 }
58 // Here the user selected "Not in Jouyou list".
59 else if( grade == ( _grades->count() - 1 ) )
60 {
61 // Only show the kanji with grade 0 (not in Jouyou).
62 _currentGradeList << 0;
63 }
64 // It seems KANJIDIC doesn't have a G7 (grade 7) kanji.
65 // If the user selects G8 or above, we need to add 1 to the grade
66 // because the index (from the ComboBox) and the grade will be different.
67 else if( grade >= Grade7 )
68 {
69 _currentGradeList << ( grade + 1 );
70 }
71 // Show the kanji with the selected grade.
72 else
73 {
74 _currentGradeList << grade;
75 }
76
77 // Reload our QListWidget widget.
78 reloadKanjiList();
79 }
80
changeStrokeCount(const int strokes)81 void KanjiBrowserView::changeStrokeCount( const int strokes )
82 {
83 _currentStrokesList.clear();
84
85 // Indexes of items in the ComboBox:
86 // No stroke limit: 0
87 // 1 stroke: 1
88 // 2 strokes: 2
89 // .
90 // .
91 // .
92
93 // We don't need to filter any kanji by stroke number.
94 if( strokes == NoStrokeLimit )
95 {
96 // Add all the strokes found to our the list.
97 foreach( const int stroke, _strokesList )
98 {
99 _currentStrokesList << stroke;
100 }
101 }
102 // Show the kanji with the selected number of strokes.
103 else
104 {
105 _currentStrokesList << strokes;
106 }
107
108 // Reload our QListWidget widget.
109 reloadKanjiList();
110 }
111
changeToInfoPage()112 void KanjiBrowserView::changeToInfoPage()
113 {
114 _stackedWidget->setCurrentIndex( Info );
115 }
116
changeToListPage()117 void KanjiBrowserView::changeToListPage()
118 {
119 _stackedWidget->setCurrentIndex( List );
120 }
121
convertToCSS(const QFont & font)122 QString KanjiBrowserView::convertToCSS( const QFont &font )
123 {
124 QString weight;
125 switch( font.weight() )
126 {
127 case QFont::Light:
128 weight = QStringLiteral("lighter");
129 break;
130 case QFont::Normal:
131 weight = QStringLiteral("normal");
132 break;
133 case QFont::Bold:
134 weight = QStringLiteral("bold");
135 break;
136 }
137
138 QString style;
139 switch( font.style() )
140 {
141 case QFont::StyleNormal:
142 style = QStringLiteral("normal");
143 break;
144 case QFont::StyleItalic:
145 style = QStringLiteral("italic");
146 break;
147 case QFont::StyleOblique:
148 style = QStringLiteral("oblique");
149 break;
150 }
151
152 return QString( "font-family:\"%1\";"
153 "font-size:%2px;"
154 "font-weight:%3;"
155 "font-style:%4;" ).arg( font.family() )
156 .arg( font.pointSizeF() )
157 .arg( weight )
158 .arg( style );
159 }
160
loadSettings()161 void KanjiBrowserView::loadSettings()
162 {
163 _kanjiList->setFont( KanjiBrowserConfigSkeleton::self()->kanjiListFont() );
164 _kanjiSize = KanjiBrowserConfigSkeleton::self()->kanjiSize();
165 _kanaFont = KanjiBrowserConfigSkeleton::self()->kanaFont();
166 _labelFont = KanjiBrowserConfigSkeleton::self()->labelFont();
167
168 // Reload the Kanji Information page with the new font changes.
169 if( _currentKanji != nullptr )
170 {
171 showKanjiInformation( _currentKanji );
172 }
173 }
174
reloadKanjiList()175 void KanjiBrowserView::reloadKanjiList()
176 {
177 // Grade and strokes lists have the information of
178 // which kanji we are going to filter.
179 // We just iterate on them to actually do the filtering.
180 QStringList list;
181 foreach( const int strokes, _currentStrokesList )
182 {
183 foreach( const int grade, _currentGradeList )
184 {
185 list.append( _kanji.keys( qMakePair( grade, strokes ) ) );
186 }
187 }
188
189 _kanjiList->clear();
190 _kanjiList->addItems( list );
191
192 // Update our status bar with the number of kanji filtered.
193 statusBarChanged( i18np( "%1 kanji found", "%1 kanji found", _kanjiList->count() ) );
194 }
195
searchKanji(QListWidgetItem * item)196 void KanjiBrowserView::searchKanji( QListWidgetItem *item )
197 {
198 if( _currentKanji != nullptr
199 && item->text() == _currentKanji->getWord() )
200 {
201 return;
202 }
203
204 _goToKanjiInfo->setText( i18n( "About %1", item->text() ) );
205 _copyToClipboard->setText( i18n( "Copy %1 to clipboard", item->text() ) );
206 _copyToClipboard->setVisible( true );
207
208 Entry *result = _parent->_dictFileKanjidic->doSearch( DictQuery( item->text() ) )->first();
209 EntryKanjidic *kanji = static_cast<EntryKanjidic*>( result );
210 _currentKanji = kanji;
211
212 showKanjiInformation( kanji );
213 }
214
setupView(KanjiBrowser * parent,const QHash<QString,QPair<int,int>> & kanji,QList<int> & kanjiGrades,QList<int> & strokeCount)215 void KanjiBrowserView::setupView( KanjiBrowser *parent
216 , const QHash< QString, QPair<int, int> > &kanji
217 , QList<int> &kanjiGrades
218 , QList<int> &strokeCount )
219 {
220 if( kanji.isEmpty() || kanjiGrades.isEmpty() || strokeCount.isEmpty() )
221 {
222 qDebug() << "One or more of our lists are empty (kanji, grades, strokes).";
223 qDebug() << "Could not load the view properly.";
224 KMessageBox::error( this, i18n( "Could not load the necessary kanji information." ) );
225 return;
226 }
227
228 _parent = parent;
229 _kanji = kanji;
230 _gradeList = kanjiGrades;
231 _strokesList = strokeCount;
232
233 QAction *goToKanjiList = _parent->actionCollection()->addAction( QStringLiteral("kanji_list") );
234 goToKanjiList->setText( i18n( "Kanji &List" ) );
235
236 _goToKanjiInfo = _parent->actionCollection()->addAction( QStringLiteral("kanji_info") );
237 _goToKanjiInfo->setText( i18n( "Kanji &Information" ) );
238
239 _copyToClipboard = _parent->actionCollection()->addAction( QStringLiteral("copy_kanji_to_clipboard") );
240 _copyToClipboard->setVisible(false);
241
242 _grades->addItem( i18n( "All Jouyou Kanji grades" ) );
243 foreach( const int &grade, kanjiGrades )
244 {
245 // Grades 9 and above are considered Jinmeiyou.
246 if( grade >= Jinmeiyou )
247 {
248 _grades->addItem( i18n( "Grade %1 (Jinmeiyou)", grade ) );
249 }
250 else
251 {
252 _grades->addItem( i18n( "Grade %1", grade ) );
253 }
254 }
255 _grades->addItem( i18n( "Not in Jouyou list" ) );
256
257 _strokes->addItem( i18n( "No stroke limit" ) );
258 foreach( const int &stroke, strokeCount )
259 {
260 _strokes->addItem( i18np( "%1 stroke", "%1 strokes", stroke ) );
261 }
262
263 connect(_grades, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &KanjiBrowserView::changeGrade);
264 connect(_strokes, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &KanjiBrowserView::changeStrokeCount);
265 connect(_kanjiList, &QListWidget::itemClicked, this, &KanjiBrowserView::searchKanji);
266 connect(_kanjiList, &QListWidget::itemClicked, _goToKanjiInfo, &QAction::triggered);
267 connect(goToKanjiList, &QAction::triggered, this, &KanjiBrowserView::changeToListPage);
268 connect(_goToKanjiInfo, &QAction::triggered, this, &KanjiBrowserView::changeToInfoPage);
269 connect(_copyToClipboard, &QAction::triggered, this, &KanjiBrowserView::toClipboard);
270
271 // Set the current grade (Grade 1).
272 _grades->setCurrentIndex( 1 );
273 // Set the current number of strokes (No stroke limit).
274 // NOTE: we change from '1 stroke' to 'No stroke limit'
275 // to let the ComboBox notice the change and do the filter.
276 _strokes->setCurrentIndex( 1 );
277 _strokes->setCurrentIndex( NoStrokeLimit );
278
279 qDebug() << "Initial setup succeeded!";
280 }
281
showKanjiInformation(const EntryKanjidic * kanji)282 void KanjiBrowserView::showKanjiInformation( const EntryKanjidic *kanji )
283 {
284 // This font is shipped with Kiten and should not be changed as it shows
285 // the stroke order of a kanji.
286 QFont kanjiFont( QStringLiteral("KanjiStrokeOrders") );
287 kanjiFont.setPointSizeF( _kanjiSize.toReal() );
288
289 QString text;
290 text.append( "<html><body><style>" );
291 text.append( QStringLiteral( ".kanji { %1 }" ).arg( convertToCSS( kanjiFont ) ) );
292 text.append( QStringLiteral( ".label { %1 }" ).arg( convertToCSS( _labelFont ) ) );
293 text.append( QStringLiteral( ".kana { %1 }" ).arg( convertToCSS( _kanaFont ) ) );
294 text.append( "</style>" );
295
296 // Put the kanji.
297 text.append( QStringLiteral( "<table><tr><td><p class=\"kanji\">%1</p></td>" )
298 .arg( kanji->getWord() ) );
299
300 // Now the kanji grades and number of strokes.
301 text.append( "<td>" );
302 if( ! kanji->getKanjiGrade().isEmpty() )
303 {
304 text.append( QStringLiteral( "<p class=\"label\">%1 %2</p></br>" )
305 .arg( i18n( "Grade:" ) )
306 .arg( kanji->getKanjiGrade() ) );
307 }
308 text.append( QStringLiteral( "<p class=\"label\">%1 %2</p></td></tr></table>" )
309 .arg( i18n( "Strokes:" ) )
310 .arg( kanji->getStrokesCount() ) );
311
312 // Onyomi readings.
313 if( ! kanji->getOnyomiReadingsList().isEmpty() )
314 {
315 text.append( QString( "<p class=\"label\">%1"
316 "<span class=\"kana\">%2</span></p></br>" )
317 .arg( i18n( "Onyomi: " ) )
318 .arg( kanji->getOnyomiReadings() ) );
319 }
320
321 // Kunyomi readings.
322 if( ! kanji->getKunyomiReadingsList().isEmpty() )
323 {
324 text.append( QString( "<p class=\"label\">%1"
325 "<span class=\"kana\">%2</span></p></br>" )
326 .arg( i18n( "Kunyomi: " ) )
327 .arg( kanji->getKunyomiReadings() ) );
328 }
329
330 // Special readings used in names.
331 if( ! kanji->getInNamesReadingsList().isEmpty() )
332 {
333 text.append( QString( "<p class=\"label\">%1"
334 "<span class=\"kana\">%2</span></p></br>" )
335 .arg( i18n( "In names: " ) )
336 .arg( kanji->getInNamesReadings() ) );
337 }
338
339 // Reading used as radical.
340 if( ! kanji->getAsRadicalReadingsList().isEmpty() )
341 {
342 text.append( QString( "<p class=\"label\">%1"
343 "<span class=\"kana\">%2</span></p></br>" )
344 .arg( i18n( "As radical: " ) )
345 .arg( kanji->getAsRadicalReadings() ) );
346 }
347
348 // Meanings
349 text.append( "<p class=\"label\">" );
350 if( kanji->getMeaningsList().count() == 1 )
351 {
352 text.append( i18n( "Meaning: ") );
353 }
354 else
355 {
356 text.append( i18n( "Meanings: " ) );
357 }
358 text.append( QStringLiteral( "<span class=\"kana\">%1</span></p>" )
359 .arg( kanji->getMeanings() ) );
360
361 // Close remaining tags and set the HTML text.
362 text.append( "</body></html>" );
363 _kanjiInformation->setHtml( text );
364 }
365
toClipboard()366 void KanjiBrowserView::toClipboard()
367 {
368 QClipboard *cb = QApplication::clipboard();
369 cb->setText( _currentKanji->getWord(), QClipboard::Clipboard );
370 cb->setText( _currentKanji->getWord(), QClipboard::Selection );
371 }
372
373