2 Copyright 2013 Karel Matas
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <FL/fl_ask.H>
26 using aoi_ui::TextStyle
;
27 using utils::to_string
;
29 const char *MANAGEDB_ITEM_KANJIDIC
= "Kanjidic";
30 const char *MANAGEDB_ITEM_JMDICT
= "JMdict";
31 const char *MANAGEDB_ITEM_COMPONENTS
= "Components";
32 const char *TEMP_JMDICT
= "gztemp.jmdict";
33 const char *TEMP_KANJIDIC
= "gztemp.kanjidic";
34 const char *TEMP_COMPONENTS
= "gztemp.components";
35 const char *LOG_FILE
= "aoi.log";
37 App
* App::instance_
= nullptr;
42 logger_
.filename(LOG_FILE
);
43 logger_
.loglevel(Logger::MSG_DEBUG
);
45 cfg_
= new aoi_config::Config();
46 ui_
= new aoi_ui::GUI(this);
47 rmn_
= new Romanization();
49 ui_
->progress(0,"Opening database...");
52 db_
= new SQLite3::SQLite3( cfg_
->get
<string
>("db/file_main").c_str() );
54 catch (SQLite3::CantOpenDatabase
&e
){
55 log_e( "Aoi: Can't open database '" + string(e
.what()) + "'.");
58 ui_
->progress( 30, "Checking tables..." );
60 ui_
->progress( 60, "Checking indexes..." );
67 ui_
->progress(90, "DEBUG query..." );
68 // parse_dic_input("ana*");
70 // ui_->cb_toggle_group(nullptr);
73 ui_
->progress( 98, "Initializing fonts..." );
74 ui_
->fontname_kanji( cfg_
->get("font/kanji") );
75 ui_
->help_file( cfg_
->get("sources/help_index") );
80 auto q
= query("select val from aoi where key='jmdict_version'");
81 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
82 q
= query("select val from aoi where key='kanjidic_version'");
83 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
84 if ( jmdict_version
== "NONE" || kanjidic_version
== "NONE" )
101 void App::init_dicview ()
103 // initialize textstyles
104 TextStyle
style_default(FL_HELVETICA
,1.2);
105 TextStyle
style_reading(aoi_ui::FONT_KANJI
,1.5);
106 TextStyle style_reading_freq
= style_reading
;
107 style_reading_freq
.color
= get_config
<int>("color/frequent");
108 TextStyle
style_kanji(aoi_ui::FONT_KANJI
,1.7);
109 TextStyle style_kanji_freq
= style_kanji
;
110 style_kanji_freq
.color
= get_config
<int>("color/frequent");
111 TextStyle
style_inf(FL_HELVETICA_ITALIC
,0.8,get_config
<int>("color/pos"));
112 style_inf
.offset_y
= 3;
113 TextStyle style_pos
= style_inf
;
114 TextStyle style_misc
= style_pos
;
115 style_misc
.color
= get_config
<int>("color/misc");
116 TextStyle style_field
= style_pos
;
117 style_field
.color
= get_config
<int>("color/field");
118 TextStyle style_dial
= style_pos
;
120 // register TextStyles
121 ui_
->register_tag_dicview( "default", style_default
);
122 ui_
->register_tag_dicview( "reading", style_reading
);
123 ui_
->register_tag_dicview( "reading_freq", style_reading_freq
);
124 ui_
->register_tag_dicview( "kanji", style_kanji
);
125 ui_
->register_tag_dicview( "kanji_freq", style_kanji_freq
);
126 ui_
->register_tag_dicview( "kinf", style_inf
);
127 ui_
->register_tag_dicview( "rinf", style_inf
);
128 ui_
->register_tag_dicview( "pos", style_pos
);
129 ui_
->register_tag_dicview( "misc", style_misc
);
130 ui_
->register_tag_dicview( "field", style_field
);
131 ui_
->register_tag_dicview( "dial", style_dial
);
135 vector
<string
> App::query ( const char *q
, bool log_query
, bool replace_separator
)
138 log_e("App::query(): Database does not exist.");
141 vector
<string
> result
;
145 result
= db_
->query(q
);
147 catch (SQLite3::DatabaseError
&e
){
148 log_e("App: DatabaseError:: " + string(e
.what()) + string("\nQuery: ")
149 + string(e
.query()) );
151 string msg
= std::to_string(db_
->result_rows()) + " results";
152 log( "App::query(): " + msg
);
153 if ( replace_separator
)
154 for ( string
&s
: result
)
155 utils::replace_all(s
, parsers::SEPARATOR_SQL
, ", ");
160 void App::cb_set_components ()
162 if ( curr_components_
.empty() )
165 using aoi_ui::ComponentView
;
168 vector
<string
> included
= ui_
->components_include();
169 vector
<string
> excluded
= ui_
->components_exclude();
171 vector
<ComponentView::Cell
> v
;
172 for ( string
&s
: included
)
173 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_1
) );
174 for ( string
&s
: excluded
)
175 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_2
) );
177 // sort by occurences
178 if ( !ui_
->sort_components_by_strokes() ){
179 for ( auto mi
= curr_components_
.rbegin(); mi
!=curr_components_
.rend(); ++mi
){
180 if ( !utils::is_in( included
, mi
->second
)
181 && !utils::is_in( excluded
, mi
->second
) )
182 v
.push_back( ComponentView::Cell(mi
->second
) );
187 vector
<string
> comps_by_strokes
;
188 for ( auto mi
: curr_components_
){
189 if ( mi
.first
< get_config
<int>("knj/min_compo_count") )
191 comps_by_strokes
.push_back(mi
.second
);
194 struct SortByStrokes
{
195 map
<string
,int> comps
;
196 SortByStrokes( const map
<string
,int> &c
): comps(c
){};
197 bool operator() (const string
&c1
, const string
&c2
){
198 return ( this->comps
[c1
] < this->comps
[c2
] );
202 std::sort( comps_by_strokes
.begin(), comps_by_strokes
.end(), sbc
);
205 for ( string
&s
: comps_by_strokes
){
206 int curr_strokes
= components_
[s
];
207 if ( prev_strokes
!= curr_strokes
)
208 v
.push_back( ComponentView::Cell( std::to_string(curr_strokes
),
209 ComponentView::CELL_LABEL
) );
210 v
.push_back( ComponentView::Cell(s
) );
211 prev_strokes
= curr_strokes
;
215 ui_
->set_components( v
);
219 void App::cb_kanji_search ()
222 if ( components_
.empty() ){
223 log("Loading components...");
224 vector
<string
> res
= query("select component, strokes from components");
225 for ( size_t i
=0; i
<res
.size(); i
=i
+2 ){
226 components_
[res
[i
]] = std::stoi(res
[i
+1]);
231 std::pair
<int,int> strokes
= utils::parse_range( utils::strip( ui_
->strokes() ) );
233 std::pair
<int,int> jlpt
= utils::parse_range( utils::strip( ui_
->jlpt() ) );
235 std::pair
<int,int> grade
= utils::parse_range( utils::strip( ui_
->grade() ) );
240 vector
<string
> skip
= utils::split_string( ui_
->skip() );
241 std::stringstream sskip
;
242 if ( skip
.size() > 0 ){
243 string skip1
= utils::strip(skip
[0].c_str());
244 sskip
<< " S.skip1=" << skip1
;
246 if ( skip
.size() > 1 ){
247 std::pair
<int,int> skip2
= utils::parse_range( utils::strip(skip
[1].c_str()) );
248 sskip
<< " and S.skip2>=" << skip2
.first
<< " and S.skip2<=" << skip2
.second
;
250 if ( skip
.size() > 2 ){
251 std::pair
<int,int> skip3
= utils::parse_range( utils::strip(skip
[2].c_str()) );
252 sskip
<< " and S.skip3>=" << skip3
.first
<< " and S.skip3<=" << skip3
.second
;
254 if ( !sskip
.str().empty() )
259 switch ( ui_
->sort_mode() ){
261 order_by
= "(case freq when 0 then 9999 else freq end) asc";
264 order_by
= "(case freq when 0 then 9999 else freq end) desc";
266 case SORT_STROKES_ASC
:
267 order_by
= "strokes asc";
269 case SORT_STROKES_DESC
:
270 order_by
= "strokes desc";
274 vector
<string
> components_include
= ui_
->components_include();
275 vector
<string
> components_exclude
= ui_
->components_exclude();
276 std::stringstream comps
;
277 for ( auto c
: components_include
)
278 comps
<< " and components like '%" << c
<< "%'";
279 for ( auto c
: components_exclude
)
280 comps
<< " and components not like '%" << c
<< "%'";
281 printf("%s\n",comps
.str().c_str());
284 if ( get_config
<bool>("knj/jis208_only") )
285 jis208
= " flags glob '*jis208*' and ";
288 std::stringstream ss
;
289 ss
<< "select distinct "
290 << "K.kanji,freq,components,flags "
291 << " from k_kanji as K, k_skip as S where K.kanji=S.kanji and "
294 << " strokes>=" << strokes
.first
<< " and strokes<=" << strokes
.second
295 << " and jlpt>=" << jlpt
.first
<< " and jlpt<=" << jlpt
.second
296 << " and grade>=" << grade
.first
<< " and grade<=" << grade
.second
298 << " order by " << order_by
;
302 vector
<string
> q
= query( ss
.str().c_str(), true, false );
303 vector
<aoi_ui::KanjiView::Cell
> data
;
304 vector
<string
> components
;
306 for ( size_t i
=0; i
<q
.size(); i
+=4 ){
308 int freq
= std::stoi(q
[i
+1]);
310 aoi_ui::KanjiView::Cell(
312 (freq
>0)? get_config
<int>("color/frequent"):-1
315 components
.push_back(q
[i
+2]);
316 for ( string
&s
: utils::split_string( q
[i
+3], parsers::SEPARATOR_SQL
) )
320 log_d("Groups: " + utils::to_string(flags
));
322 ui_
->set_kanjiview( data
);
324 sprintf( b
, "%d results", db_
->result_rows() );
325 ui_
->set_kanji_results( b
);
327 utils::Histogram
<string
> histogram
;
328 for ( string
&s
: components
)
329 histogram
.add( utils::str_to_chars(s
.c_str()) );
330 curr_components_
= histogram
.sorted();
336 void App::alert ( const string
&msg
, const string
&desc
)
338 std::stringstream ss
;
341 ss
<< "\n\nDetails:\n" << desc
;
343 ui_
->alert(ss
.str());
347 int App::run ( int argc
, char **argv
)
349 return ui_
->run(argc
,argv
);
353 void App::parse_kanjidic ( const char *fname
, bool delete_source
)
355 if ( !utils::file_exists( fname
) ){
356 log_e("App::parse_kanjidic(): File does not exist: " + string(fname
));
359 ui_
->progress(0, "Loading kanjidic...");
360 log("Loading kanjidic...");
362 const char *SEP
= parsers::SEPARATOR_SQL
;
366 ui_
->progress(1, "Creating parser...");
367 std::stringstream msg
;
368 msg
<< "Decompressing file: " << fname
<< " -> " << TEMP_KANJIDIC
;
372 log_d("Waiting 3 s before decompressing...");
374 log_d("Continue...");
377 utils::gzip_decompress_file( fname
, TEMP_KANJIDIC
);
378 parsers::KanjidicParser
p(TEMP_KANJIDIC
);
379 auto kanji
= p
.get_entry();
381 query( "DROP TABLE IF EXISTS k_kanji;"
382 "DROP TABLE IF EXISTS components;"
383 "DROP TABLE IF EXISTS k_skip;"
384 "DELETE FROM aoi WHERE key='kanjidic_version';" );
388 std::stringstream ss
;
389 ss
<< "BEGIN TRANSACTION;\n";
390 ss
<< "INSERT INTO aoi (key,val) VALUES ('kanjidic_version','"
391 << p
.get_version() << "');\n";
392 while ( kanji
.kanji() != "" ){
394 if ( n_kanji
% 100 == 0)
395 // percent = 100 * n_kanji/13108
396 ui_
->progress( n_kanji
/14, std::to_string(n_kanji
)+string(" kanji loaded"));
397 ss
<< "INSERT INTO k_kanji "
398 << "(kanji,ucs,onyomi,kunyomi,meaning,nanori,flags,jlpt,grade,freq,strokes,"
399 << "rad_classic,rad_nelson,components)"
401 << kanji
.kanji() << "','"
402 << kanji
.ucs() << "','"
403 << to_string(kanji
.onyomi(),SEP
) << "','"
404 << to_string(kanji
.kunyomi(),SEP
) << "','"
405 << SQLite3::escape(to_string(kanji
.meaning(),SEP
)) << "','"
406 << to_string(kanji
.nanori(),SEP
) << "','"
407 << to_string(kanji
.flags(),SEP
) << "',"
408 << kanji
.jlpt() << ","
409 << kanji
.grade() << ","
410 << kanji
.freq() << ","
411 << kanji
.strokes() << ","
412 << kanji
.rad_classic() << ","
413 << kanji
.rad_nelson() << ","
416 for ( SKIP
&s
: kanji
.skip() ) {
417 ss
<< "INSERT INTO k_skip (kanji,skip1,skip2,skip3,misclass) VALUES('"
418 << kanji
.kanji() << "'," << s
.s1
<< "," << s
.s2
<< "," << s
.s3
419 << ",'" << s
.misclass
<< "');\n";
421 kanji
= p
.get_entry();
423 ss
<< "END TRANSACTION;\n";
424 log("Writing to db ...");
427 // log_d("Writing file 'script.kanjidic.sql'...");
428 // std::ofstream f ("script.kanjidic.sql");
432 ui_
->progress(95, "Writing to database...");
433 query(ss
.str().c_str(),false);
435 catch ( utils::ParsingError
&e
){
436 ui_
->progress_hide();
437 std::string msg
= "App::parse_jmdict(): ParsingError: ";
442 ui_
->progress(98, "Checking indexes...");
444 ui_
->progress_hide();
445 remove( TEMP_KANJIDIC
);
451 void App::parse_jmdict ( const char *fname
, bool delete_source
)
453 if ( !utils::file_exists( fname
) ){
454 log_e("App::parse_jmdict(): File does not exist: " + string(fname
));
458 ui_
->progress( 0, "Loading JMdict..." );
459 log("Loading JMdict...");
460 std::stringstream ss
;
461 ss
<< "BEGIN TRANSACTION;\n";
464 for ( auto &mi
: aoi_config::db_tables
){
465 ss
<< "DROP TABLE IF EXISTS " << mi
.first
<< ";\n"
466 << "CREATE TABLE " << mi
.first
<< " ( ";
467 for ( size_t i
=0; i
<mi
.second
.size(); i
++ )
468 ss
<< mi
.second
[i
].name
<< " " << mi
.second
[i
].type
469 << ((i
==mi
.second
.size()-1) ? "":",");
476 ui_
->progress(1, "Creating parser...");
478 utils::gzip_decompress_file( fname
, TEMP_JMDICT
);
479 parsers::JmdictParser
jmp(TEMP_JMDICT
);
480 const char *SEP
= parsers::SEPARATOR_SQL
;
483 ss
<< "INSERT INTO aoi (key,val) VALUES ('jmdict_version', '"
484 << jmp
.get_version() << "');\n";
485 log("jmdict_version: "+jmp
.get_version());
488 for ( std::pair
<string
,string
> elt
: jmp
.get_entities() ){
489 ss
<< "INSERT INTO entities (abbr,desc) VALUES ('" << SQLite3::escape(elt
.first
)
490 << "','" << SQLite3::escape(elt
.second
) << "');\n";
494 parsers::JmdictEntry entry
= jmp
.get_entry();
495 while ( entry
.did
!= -1 ) {
496 if ( n_entries
% 1000 == 0 ){
497 // entries is about 180 000 -> percent = 100*n/180000 ~ n/2000
498 ui_
->progress( n_entries
/2000, std::to_string(n_entries
)+string(" entries loaded") );
504 for ( parsers::ReadingElement
&rele
: entry
.r_ele
){
505 ss
<< "INSERT INTO d_reading (rid,did,reading,inf,nokanji,freq) VALUES ("
506 << rele
.rid
<< "," << entry
.did
<< ",'" << rele
.reading
<< "','"
507 << to_string(rele
.inf
,SEP
) << "',"
508 << rele
.nokanji
<< "," << rele
.freq
<< ");\n";
512 for ( parsers::KanjiElement
&kele
: entry
.k_ele
){
513 ss
<< "INSERT INTO d_kanji (kid,did,kanji,inf,freq) VALUES ("
514 << kele
.kid
<< "," << entry
.did
<< ",'" << kele
.kanji
<< "','"
515 << to_string(kele
.inf
,SEP
) << "'," << kele
.freq
<< ");\n";
518 for ( parsers::SenseElement
&sele
: entry
.s_ele
){
519 ss
<< "INSERT INTO d_sense (sid,did,gloss,xref,ant,inf,pos,field,misc,dial) VALUES ("
520 << sele
.sid
<< "," << entry
.did
<< ",'"
521 << SQLite3::escape(to_string(sele
.gloss
,SEP
)) << "','"
522 << to_string(sele
.xref
,SEP
) << "','"
523 << to_string(sele
.ant
,SEP
) << "','"
524 << SQLite3::escape(to_string(sele
.s_inf
,SEP
)) << "','"
525 << to_string(sele
.pos
,SEP
) << "','"
526 << to_string(sele
.field
,SEP
) << "','"
527 << to_string(sele
.misc
,SEP
) << "','"
528 << to_string(sele
.dial
,SEP
) << "'"
534 entry
= jmp
.get_entry();
537 catch ( utils::ParsingError
&e
){
538 ui_
->progress_hide();
539 std::string msg
= "App::parse_jmdict(): ParsingError: ";
545 log(std::to_string(n_entries
) + " entries processed.");
547 ss
<< "END TRANSACTION;\n";
548 // no need for vacuum here - check_index() will perform it eventually (see below)
551 // log_d("Writing file 'script.jmdict.sql'...");
552 // std::ofstream f ("script.jmdict.sql");
557 ui_
->progress( 95, "Creating database... ");
558 log("Creating database...");
560 // don't log query (ifdef DEBUG then SQL script is already created)
561 query(ss
.str().c_str(),false);
563 catch ( SQLite3::DatabaseError
&e
){
564 string msg
= "App::parse_jmdict(): DatabaseError: ";
569 ui_
->progress( 99, "Checking indexes... ");
570 log("Creating database...");
572 ui_
->progress_hide();
573 log("Deleting temporary file...");
580 void App::check_tables ()
582 // no need for vacuum here, it will be done by check_indexes()
583 log("Checking tables");
584 vector
<string
> existing
= query("SELECT name FROM sqlite_master WHERE type='table'");
585 // CREATE TABLE name ( column1 TYPE, column2 TYPE, ... )
586 for ( auto &table
: aoi_config::db_tables
){
587 if ( utils::is_in(existing
, string(table
.first
)) )
589 log("Creating table " + string(table
.first
));
591 for ( auto &column
: table
.second
){
592 std::stringstream sstr
;
593 sstr
<< column
.name
<< " " << column
.type
;
594 v
.push_back(sstr
.str());
596 std::stringstream ss
;
597 ss
<< "CREATE TABLE " << table
.first
<< " (" << to_string(v
,",") << ");\n";
598 query(ss
.str().c_str());
604 void App::check_indexes ()
606 log("Checking indexes...");
607 bool do_vacuum
= false;
608 vector
<string
> existing
= query("SELECT name FROM sqlite_master WHERE type='index'");
609 // CREATE INDEX idx_table_column ON table ( column ASC )
610 for ( auto &mi
: aoi_config::db_tables
){ // tables
611 for ( auto &c
: mi
.second
){ // columns
612 std::stringstream idx_name
;
613 idx_name
<< "idx_" << mi
.first
<< "_" << c
.name
;
614 if ( c
.index
&& !utils::is_in( existing
, idx_name
.str() ) ){
615 log(string("Creating index ") + idx_name
.str());
616 std::stringstream ss
;
617 ss
<< "CREATE INDEX " << idx_name
.str() << " ON " << mi
.first
618 << "(" << c
.name
<< " " << c
.sort
<< ")";
619 query(ss
.str().c_str());
631 void App::cb_dic_input ()
633 parse_dic_input( ui_
->get_dic_input() );
634 ui_
->reset_filters();
638 void App::on_dic_selected ( int id
)
640 log_d("App::on_dic_selected()");
643 void App::cb_edit_word ( int id
)
645 std::stringstream ss
;
646 ss
<< "App::edit_word( " << id
<< " )";
652 void App::cb_popup_kanji ( const string
&kanji
)
654 std::stringstream ss
;
655 ss
<< "App::on_kanji_clicked()" << kanji
;
656 Kanji k
= db_get_kanji(kanji
);
657 ui_
->popup_kanji( k
);
658 // XXX: this should be somewhere else (it is not logical here)
659 ui_
->highlight_components( k
.components() );
664 void App::set_listview ( const vector
<string
> &v
)
667 if ( !listview_items_
.empty() ) listview_items_
.clear();
669 vector
<int> cell_ids
;
671 while ( i
< v
.size() ) {
672 int cell_id
= std::stoi(v
[i
]); // jmdict id
673 cell_ids
.push_back(cell_id
);
674 cell_ids
.push_back(cell_id
);
675 cell_ids
.push_back(cell_id
);
678 for ( string
&elt
: utils::split_string( v
[i
+1], ",") )
679 if ( elt
.size() > 0 )
680 pos
.insert(utils::strip(elt
.c_str()));
681 d
.push_back( v
[i
+2] ); // reading
682 d
.push_back( v
[i
+3] ); // kanji
683 d
.push_back( v
[i
+4] ); // sense
684 // printf("%s%s%s\n", v[i+2].c_str(), v[i+3].c_str(), v[i+4].c_str() );
685 listview_items_
.push_back( {cell_id
, pos
, v
[i
+2], v
[i
+3], v
[i
+4]} );
689 sprintf( buff
, "%d results", db_
->result_rows() );
690 ui_
->set_dic_results( buff
);
691 ui_
->set_listview(d
,cell_ids
);
695 void App::parse_dic_input ( const char *s
)
697 log_d(string("App::parse_dic_input: \"") +string(s
) + string("\""));
699 DictionaryInputParser p
;
700 string qq
= p
.parse(s
);
704 "Too broad search.\n"\
705 "Your computer may become unresponsible for a long time.\n"\
711 if ( res
== 1 ) // Cancel
715 if ( !strchr(s
,'*') && !strchr(s
,'?') && !strchr(s
,'[') && !strchr(s
,'{')
720 if ( rmn_
->contains_kanji( qq
.c_str() ) ) {
721 q
<< "select d_kanji.did as did,"
722 << "(select group_concat(pos) from d_sense where d_sense.did = d_kanji.did) as pos,"
724 << q_reading("d_kanji.did")
725 << q_sense("d_kanji.did")
726 << "from d_kanji where "
727 << "kanji glob '" << rmn_
->romaji_to_hiragana(qq
.c_str()) << "' "
728 << " or kanji glob '" << rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
729 << " group by did order by d_kanji.freq desc, d_kanji.kanji asc";
732 q
<< "select d_reading.did as did,"
733 << "(select group_concat(pos) from d_sense where d_sense.did = d_reading.did) as pos,"
735 << q_kanji("d_reading.did")
736 << q_sense("d_reading.did")
737 << "from d_reading where "
738 << "reading glob '" << rmn_
->romaji_to_hiragana(qq
.c_str())
739 << "' or reading glob '"<< rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
740 << " group by did order by d_reading.freq desc, d_reading.reading asc";
742 set_listview(query(q
.str().c_str()));
747 Kanji
App::db_get_kanji ( const string
&kanji
)
749 log("App::db_get_kanji()");
751 q
<< "select kanji,strokes,ucs, rad_classic, rad_nelson, "
752 << "jlpt, grade, freq, onyomi, kunyomi, nanori, meaning,flags,components "
753 << "from k_kanji where kanji='" << kanji
<< "';";
755 vector
<string
> res
= query(q
.str().c_str());
757 kk
.strokes(std::stoi(res
[1]));
759 kk
.rad_classic(std::stoi(res
[3]));
760 kk
.rad_nelson( (res
[4].empty()) ? -1:std::stoi(res
[4]));
761 kk
.jlpt(std::stoi(res
[5]));
762 kk
.grade(std::stoi(res
[6]));
763 kk
.freq(std::stoi(res
[7]));
764 kk
.onyomi( utils::split_string(res
[8],parsers::SEPARATOR_SQL
) );
765 kk
.kunyomi( utils::split_string(res
[9],parsers::SEPARATOR_SQL
) );
766 kk
.nanori( utils::split_string(res
[10],parsers::SEPARATOR_SQL
) );
767 kk
.meaning( utils::split_string(res
[11],parsers::SEPARATOR_SQL
) );
768 kk
.flags( utils::split_string(res
[12],parsers::SEPARATOR_SQL
) );
769 kk
.components( res
[13] );
771 string qq
= "select skip1,skip2,skip3,misclass from k_skip where kanji='"+kanji
+"';";
772 vector
<string
> res2
= query(qq
.c_str());
773 if ( res2
.size() % 4 != 0 ){
774 std::stringstream ss
;
775 ss
<< "Wrong SKIP count. Kanji: " << kanji
776 << "Query result size: " << res2
.size()
777 << " (should be 4,8 or 12). SKIP not loaded.";
781 for ( size_t i
=0; i
< res2
.size(); i
+=4 )
782 kk
.skip( res2
[i
], res2
[i
+1], res2
[i
+2], res2
[i
+3] );
787 void App::cb_file_jmdict ( bool download
)
790 const char *path
= nullptr;
793 fname
= ui_
->download_dialog( get_config("sources/url_jmdict") );
794 log_d("Download finished...");
795 path
= fname
.c_str();
798 path
= ui_
->open_file_dialog();
803 log("Selected file: " + string(path
));
804 parse_jmdict(path
, download
);
805 auto label
= ui()->dlg_manage_db()->item(MANAGEDB_ITEM_JMDICT
);
807 label
->version("Changed",FL_BLUE
);
809 log_w("Can't find item " + string(MANAGEDB_ITEM_JMDICT
) + " in ManageDBDialog.");
813 void App::cb_file_kanjidic ( bool download
)
816 const char *path
= nullptr;
819 fname
= ui_
->download_dialog( get_config("sources/url_kanjidic") );
820 log_d("Download finished...");
821 path
= fname
.c_str();
824 path
= ui_
->open_file_dialog();
829 log("Selected file: " + string(path
));
830 parse_kanjidic(path
,download
);
831 auto label
= ui_
->dlg_manage_db()->item(MANAGEDB_ITEM_KANJIDIC
);
833 label
->version("Changed",FL_BLUE
);
835 log_w("Can't find item "+ string(MANAGEDB_ITEM_KANJIDIC
) +" in ManageDBDialog.");
839 void App::cb_file_components ( bool download
)
842 const char *path
= nullptr;
845 fname
= ui_
->download_dialog( get_config("sources/url_components") );
846 log_d("Download finished...");
847 path
= fname
.c_str();
850 path
= ui_
->open_file_dialog();
855 log("Selected file: " + string(path
));
856 utils::gzip_decompress_file( path
, TEMP_COMPONENTS
);
858 db_
->script( TEMP_COMPONENTS
);
860 catch ( SQLite3::DatabaseError
&e
) {
861 alert(e
.what(), e
.query() );
863 catch ( std::exception
&e
) {
867 auto label
= ui_
->dlg_manage_db()->item(MANAGEDB_ITEM_COMPONENTS
);
869 label
->version("Changed",FL_BLUE
);
871 log_w("Can't find item " + string(MANAGEDB_ITEM_COMPONENTS
)
872 + " in ManageDBDialog.");
873 remove(TEMP_COMPONENTS
);
878 void App::cb_filter_listview ()
880 vector
<string
> pos
= ui_
->listview_filters();
881 log_d("App::cb_filter_listview(): pos: " + utils::to_string(pos
));
882 bool filter_expr
= ( utils::is_in( pos
, string("expr") ) );
883 bool filter_noun
= ( utils::is_in( pos
, string("noun") ) );
884 bool filter_verb
= ( utils::is_in( pos
, string("verb") ) );
885 bool filter_adj
= ( utils::is_in( pos
, string("adj") ) );
889 for ( auto &elt
: listview_items_
){
890 auto start
= elt
.pos
.begin();
891 auto end
= elt
.pos
.end();
892 if ( filter_expr
&& std::find( start
, end
, "exp") == end
)
894 if ( filter_noun
&& std::find( start
, end
, "n" ) == end
)
896 if ( filter_adj
&& std::find_if( start
, end
,
897 [](const string
&s
){ return strncmp(s
.c_str(),"adj",3)==0;} ) == end
)
899 if ( filter_verb
&& std::find_if( start
, end
,
900 [](const string
&s
){ return strncmp(s
.c_str(),"v",1)==0;} ) == end
)
902 ids
.push_back( elt
.did
);
903 ids
.push_back( elt
.did
);
904 ids
.push_back( elt
.did
);
905 data
.push_back ( elt
.reading
);
906 data
.push_back ( elt
.kanji
);
907 data
.push_back ( elt
.sense
);
910 ui_
->set_listview( data
, ids
);
911 std::stringstream ss
;
912 ss
<< n
<< " results";
913 if ( listview_items_
.size() != n
)
914 ss
<< " (" << listview_items_
.size()-n
<< " hidden)";
916 ui_
->set_dic_results( ss
.str() );
920 void App::cb_dicview_rightclick ( int did
)
923 string q
= "select group_concat(kanji,'') from d_kanji where did="+std::to_string(did
);
924 vector
<string
> res
= query( q
.c_str() );
926 for ( string
&c
: utils::str_to_chars(res
[0].c_str()) )
927 if ( App::get()->rmn()->is_kanji(c
.c_str()) )
929 ui_
->menu_copy( vector
<string
>(kanji
.begin(),kanji
.end()) );
933 void App::set_config ( const string
&key
, const string
&val
)
935 cfg_
->set( key
, val
);
936 if ( key
== "font/base_size")
937 ui_
->font_base_size(std::stoi(val
));
938 else if ( key
== "log/level" )
939 logger_
.loglevel(val
);
940 else if ( key
== "color/foreground" || key
== "color/background" ||
941 key
== "color/background2" || key
== "color/selection")
947 void App::cb_manage_db ()
949 auto q
= query("select val from aoi where key='jmdict_version'");
950 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
951 q
= query("select val from aoi where key='kanjidic_version'");
952 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
953 q
= query("select val from aoi where key='components_version'");
954 string components_version
= q
.empty() ? "NONE":q
[0];
955 auto *d
= ui_
->dlg_manage_db();
957 if ( !d
->item(MANAGEDB_ITEM_JMDICT
) ){
958 col
= ( jmdict_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
959 d
->add_item( MANAGEDB_ITEM_JMDICT
, jmdict_version
,
960 "Japanese-English dictionary. Needed component.",
961 col
, scb_local_file_jmdict
, scb_download_file_jmdict
,
964 if ( !d
->item(MANAGEDB_ITEM_KANJIDIC
) ){
965 col
= ( kanjidic_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
966 d
->add_item( MANAGEDB_ITEM_KANJIDIC
, kanjidic_version
,
967 "Kanji dictionary. Needed component.",
968 col
, scb_local_file_kanjidic
, scb_download_file_kanjidic
,
971 if ( !d
->item(MANAGEDB_ITEM_COMPONENTS
) ){
972 col
= ( components_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
973 d
->add_item( MANAGEDB_ITEM_COMPONENTS
, components_version
,
974 "Graphical components of kanji.",
975 col
, scb_local_file_components
, scb_download_file_components
,
982 void App::load_config ()
984 log("Loading config...");
985 vector
<string
> res
= query("select key,val from config;");
986 for ( size_t i
=0; i
< res
.size(); i
+=2 ){
987 set_config( res
[i
], res
[i
+1] );
992 void App::save_config ( const std::map
<string
,aoi_config::Config::Value
> &newmap
)
994 log("Saving config...");
996 typedef std::pair
<string
,aoi_config::Config::Value
> cfg_pair
;
998 // merge new and old config
999 for ( const cfg_pair
&p
: newmap
)
1000 set_config( p
.first
, p
.second
.val
);
1002 // prepare SQL script
1003 std::stringstream ss
;
1004 ss
<< "BEGIN TRANSACTION;\n";
1005 for ( const cfg_pair
&p
: get_config_map() )
1006 ss
<< "REPLACE INTO config (key,val) VALUES('"
1007 << p
.first
<< "','" << p
.second
.val
<< "');\n";
1008 ss
<< "END TRANSACTION;\n";
1010 query(ss
.str().c_str());
1012 // apply new fonts and colors
1017 // check keys in oldconfig not present in newmap