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";
36 const char SENSE_SEARCH_CHAR
= ':';
38 App
* App::instance_
= nullptr;
43 logger_
.filename(LOG_FILE
);
44 logger_
.loglevel(Logger::MSG_DEBUG
);
46 cfg_
= new aoi_config::Config();
47 ui_
= new aoi_ui::GUI(this);
48 rmn_
= new Romanization();
50 ui_
->progress(0,"Opening database...");
53 db_
= new SQLite3::SQLite3( cfg_
->get
<string
>("db/file_main").c_str() );
55 catch (SQLite3::CantOpenDatabase
&e
){
56 log_e( "Aoi: Can't open database '" + string(e
.what()) + "'.");
59 ui_
->progress( 30, "Checking tables..." );
61 ui_
->progress( 60, "Checking indexes..." );
68 ui_
->progress(90, "DEBUG query..." );
69 // parse_dic_input("ana*");
71 // ui_->cb_toggle_group(nullptr);
74 ui_
->progress( 98, "Initializing fonts..." );
75 ui_
->fontname_kanji( cfg_
->get("font/kanji") );
76 ui_
->help_file( cfg_
->get("sources/help_index") );
81 auto q
= query("select val from aoi where key='jmdict_version'");
82 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
83 q
= query("select val from aoi where key='kanjidic_version'");
84 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
85 if ( jmdict_version
== "NONE" || kanjidic_version
== "NONE" )
90 logger_
.loglevel(Logger::MSG_DEBUG
);
106 void App::init_dicview ()
108 // initialize textstyles
109 TextStyle
style_default(FL_HELVETICA
,1.2);
110 TextStyle
style_reading(aoi_ui::FONT_KANJI
,1.5);
111 TextStyle style_reading_freq
= style_reading
;
112 style_reading_freq
.color
= get_config
<int>("color/frequent");
113 TextStyle
style_kanji(aoi_ui::FONT_KANJI
,1.7);
114 TextStyle style_kanji_freq
= style_kanji
;
115 style_kanji_freq
.color
= get_config
<int>("color/frequent");
116 TextStyle
style_inf(FL_HELVETICA_ITALIC
,0.8,get_config
<int>("color/pos"));
117 style_inf
.offset_y
= 3;
118 TextStyle style_pos
= style_inf
;
119 TextStyle style_misc
= style_pos
;
120 style_misc
.color
= get_config
<int>("color/misc");
121 TextStyle style_field
= style_pos
;
122 style_field
.color
= get_config
<int>("color/field");
123 TextStyle style_dial
= style_pos
;
125 // register TextStyles
126 ui_
->register_tag_dicview( "default", style_default
);
127 ui_
->register_tag_dicview( "reading", style_reading
);
128 ui_
->register_tag_dicview( "reading_freq", style_reading_freq
);
129 ui_
->register_tag_dicview( "kanji", style_kanji
);
130 ui_
->register_tag_dicview( "kanji_freq", style_kanji_freq
);
131 ui_
->register_tag_dicview( "kinf", style_inf
);
132 ui_
->register_tag_dicview( "rinf", style_inf
);
133 ui_
->register_tag_dicview( "pos", style_pos
);
134 ui_
->register_tag_dicview( "misc", style_misc
);
135 ui_
->register_tag_dicview( "field", style_field
);
136 ui_
->register_tag_dicview( "dial", style_dial
);
140 vector
<string
> App::query ( const char *q
, bool log_query
, bool replace_separator
)
143 log_e("App::query(): Database does not exist.");
146 vector
<string
> result
;
150 result
= db_
->query(q
);
152 catch (SQLite3::DatabaseError
&e
){
153 log_e("App: DatabaseError:: " + string(e
.what()) + string("\nQuery: ")
154 + string(e
.query()) );
156 string msg
= std::to_string(db_
->result_rows()) + " results";
157 log( "App::query(): " + msg
);
158 if ( replace_separator
)
159 for ( string
&s
: result
)
160 utils::replace_all(s
, parsers::SEPARATOR_SQL
, ", ");
165 void App::cb_set_components ()
167 if ( curr_components_
.empty() )
170 using aoi_ui::ComponentView
;
173 vector
<string
> included
= ui_
->components_include();
174 vector
<string
> excluded
= ui_
->components_exclude();
176 vector
<ComponentView::Cell
> v
;
177 for ( string
&s
: included
)
178 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_1
) );
179 for ( string
&s
: excluded
)
180 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_2
) );
182 // sort by occurences
183 if ( !ui_
->sort_components_by_strokes() ){
184 for ( auto mi
= curr_components_
.rbegin(); mi
!=curr_components_
.rend(); ++mi
){
185 if ( !utils::is_in( included
, mi
->second
)
186 && !utils::is_in( excluded
, mi
->second
) )
187 v
.push_back( ComponentView::Cell(mi
->second
) );
192 vector
<string
> comps_by_strokes
;
193 for ( auto mi
: curr_components_
){
194 if ( mi
.first
< get_config
<int>("knj/min_compo_count") )
196 comps_by_strokes
.push_back(mi
.second
);
199 struct SortByStrokes
{
200 map
<string
,int> comps
;
201 SortByStrokes( const map
<string
,int> &c
): comps(c
){};
202 bool operator() (const string
&c1
, const string
&c2
){
203 return ( this->comps
[c1
] < this->comps
[c2
] );
207 std::sort( comps_by_strokes
.begin(), comps_by_strokes
.end(), sbc
);
210 for ( string
&s
: comps_by_strokes
){
211 int curr_strokes
= components_
[s
];
212 if ( prev_strokes
!= curr_strokes
)
213 v
.push_back( ComponentView::Cell( std::to_string(curr_strokes
),
214 ComponentView::CELL_LABEL
) );
215 v
.push_back( ComponentView::Cell(s
) );
216 prev_strokes
= curr_strokes
;
220 ui_
->set_components( v
);
224 void App::cb_kanji_search ()
227 if ( components_
.empty() ){
228 log("Loading components...");
229 vector
<string
> res
= query("select component, strokes from components");
230 for ( size_t i
=0; i
<res
.size(); i
=i
+2 ){
231 components_
[res
[i
]] = std::stoi(res
[i
+1]);
236 std::pair
<int,int> strokes
= utils::parse_range( utils::strip( ui_
->strokes() ) );
238 std::pair
<int,int> jlpt
= utils::parse_range( utils::strip( ui_
->jlpt() ) );
240 std::pair
<int,int> grade
= utils::parse_range( utils::strip( ui_
->grade() ) );
245 vector
<string
> skip
= utils::split_string( ui_
->skip() );
246 std::stringstream sskip
;
247 if ( skip
.size() > 0 ){
248 string skip1
= utils::strip(skip
[0].c_str());
249 sskip
<< " S.skip1=" << skip1
;
251 if ( skip
.size() > 1 ){
252 std::pair
<int,int> skip2
= utils::parse_range( utils::strip(skip
[1].c_str()) );
253 sskip
<< " and S.skip2>=" << skip2
.first
<< " and S.skip2<=" << skip2
.second
;
255 if ( skip
.size() > 2 ){
256 std::pair
<int,int> skip3
= utils::parse_range( utils::strip(skip
[2].c_str()) );
257 sskip
<< " and S.skip3>=" << skip3
.first
<< " and S.skip3<=" << skip3
.second
;
259 if ( !sskip
.str().empty() )
264 switch ( ui_
->sort_mode() ){
266 order_by
= "(case freq when 0 then 9999 else freq end) asc";
269 order_by
= "(case freq when 0 then 9999 else freq end) desc";
271 case SORT_STROKES_ASC
:
272 order_by
= "strokes asc";
274 case SORT_STROKES_DESC
:
275 order_by
= "strokes desc";
279 vector
<string
> components_include
= ui_
->components_include();
280 vector
<string
> components_exclude
= ui_
->components_exclude();
281 std::stringstream comps
;
282 for ( auto c
: components_include
)
283 comps
<< " and components like '%" << c
<< "%'";
284 for ( auto c
: components_exclude
)
285 comps
<< " and components not like '%" << c
<< "%'";
286 printf("%s\n",comps
.str().c_str());
289 if ( get_config
<bool>("knj/jis208_only") )
290 jis208
= " flags glob '*jis208*' and ";
293 std::stringstream ss
;
294 ss
<< "select distinct "
295 << "K.kanji,freq,components,flags "
296 << " from k_kanji as K, k_skip as S where K.kanji=S.kanji and "
299 << " strokes>=" << strokes
.first
<< " and strokes<=" << strokes
.second
300 << " and jlpt>=" << jlpt
.first
<< " and jlpt<=" << jlpt
.second
301 << " and grade>=" << grade
.first
<< " and grade<=" << grade
.second
303 << " order by " << order_by
;
307 vector
<string
> q
= query( ss
.str().c_str(), true, false );
308 vector
<aoi_ui::KanjiView::Cell
> data
;
309 vector
<string
> components
;
311 for ( size_t i
=0; i
<q
.size(); i
+=4 ){
313 int freq
= std::stoi(q
[i
+1]);
315 aoi_ui::KanjiView::Cell(
317 (freq
>0)? get_config
<int>("color/frequent"):-1
320 components
.push_back(q
[i
+2]);
321 for ( string
&s
: utils::split_string( q
[i
+3], parsers::SEPARATOR_SQL
) )
325 log_d("Groups: " + utils::to_string(flags
));
327 ui_
->set_kanjiview( data
);
329 sprintf( b
, "%d results", db_
->result_rows() );
330 ui_
->set_kanji_results( b
);
332 utils::Histogram
<string
> histogram
;
333 for ( string
&s
: components
)
334 histogram
.add( utils::str_to_chars(s
.c_str()) );
335 curr_components_
= histogram
.sorted();
341 void App::alert ( const string
&msg
, const string
&desc
)
343 std::stringstream ss
;
346 ss
<< "\n\nDetails:\n" << desc
;
348 ui_
->alert(ss
.str());
352 int App::run ( int argc
, char **argv
)
354 return ui_
->run(argc
,argv
);
358 void App::parse_kanjidic ( const char *fname
, bool delete_source
)
360 if ( !utils::file_exists( fname
) ){
361 log_e("App::parse_kanjidic(): File does not exist: " + string(fname
));
364 ui_
->progress(0, "Loading kanjidic...");
365 log("Loading kanjidic...");
367 const char *SEP
= parsers::SEPARATOR_SQL
;
371 ui_
->progress(1, "Creating parser...");
372 std::stringstream msg
;
373 msg
<< "Decompressing file: " << fname
<< " -> " << TEMP_KANJIDIC
;
377 log_d("Waiting 3 s before decompressing...");
379 log_d("Continue...");
382 utils::gzip_decompress_file( fname
, TEMP_KANJIDIC
);
383 parsers::KanjidicParser
p(TEMP_KANJIDIC
);
384 auto kanji
= p
.get_entry();
386 query( "DROP TABLE IF EXISTS k_kanji;"
387 "DROP TABLE IF EXISTS components;"
388 "DROP TABLE IF EXISTS k_skip;"
389 "DELETE FROM aoi WHERE key='kanjidic_version';" );
393 std::stringstream ss
;
394 ss
<< "BEGIN TRANSACTION;\n";
395 ss
<< "INSERT INTO aoi (key,val) VALUES ('kanjidic_version','"
396 << p
.get_version() << "');\n";
397 while ( kanji
.kanji() != "" ){
399 if ( n_kanji
% 100 == 0)
400 // percent = 100 * n_kanji/13108
401 ui_
->progress( n_kanji
/14, std::to_string(n_kanji
)+string(" kanji loaded"));
402 ss
<< "INSERT INTO k_kanji "
403 << "(kanji,ucs,onyomi,kunyomi,meaning,nanori,flags,jlpt,grade,freq,strokes,"
404 << "rad_classic,rad_nelson,components)"
406 << kanji
.kanji() << "','"
407 << kanji
.ucs() << "','"
408 << to_string(kanji
.onyomi(),SEP
) << "','"
409 << to_string(kanji
.kunyomi(),SEP
) << "','"
410 << SQLite3::escape(to_string(kanji
.meaning(),SEP
)) << "','"
411 << to_string(kanji
.nanori(),SEP
) << "','"
412 << to_string(kanji
.flags(),SEP
) << "',"
413 << kanji
.jlpt() << ","
414 << kanji
.grade() << ","
415 << kanji
.freq() << ","
416 << kanji
.strokes() << ","
417 << kanji
.rad_classic() << ","
418 << kanji
.rad_nelson() << ","
421 for ( SKIP
&s
: kanji
.skip() ) {
422 ss
<< "INSERT INTO k_skip (kanji,skip1,skip2,skip3,misclass) VALUES('"
423 << kanji
.kanji() << "'," << s
.s1
<< "," << s
.s2
<< "," << s
.s3
424 << ",'" << s
.misclass
<< "');\n";
426 kanji
= p
.get_entry();
428 ss
<< "END TRANSACTION;\n";
429 log("Writing to db ...");
432 // log_d("Writing file 'script.kanjidic.sql'...");
433 // std::ofstream f ("script.kanjidic.sql");
437 ui_
->progress(95, "Writing to database...");
438 query(ss
.str().c_str(),false);
440 catch ( utils::ParsingError
&e
){
441 ui_
->progress_hide();
442 std::string msg
= "App::parse_jmdict(): ParsingError: ";
447 ui_
->progress(98, "Checking indexes...");
449 ui_
->progress_hide();
450 remove( TEMP_KANJIDIC
);
456 void App::parse_jmdict ( const char *fname
, bool delete_source
)
458 if ( !utils::file_exists( fname
) ){
459 log_e("App::parse_jmdict(): File does not exist: " + string(fname
));
463 ui_
->progress( 0, "Loading JMdict..." );
464 log("Loading JMdict...");
465 std::stringstream ss
;
466 ss
<< "BEGIN TRANSACTION;\n";
469 for ( auto &mi
: aoi_config::db_tables
){
470 ss
<< "DROP TABLE IF EXISTS " << mi
.first
<< ";\n"
471 << "CREATE TABLE " << mi
.first
<< " ( ";
472 for ( size_t i
=0; i
<mi
.second
.size(); i
++ )
473 ss
<< mi
.second
[i
].name
<< " " << mi
.second
[i
].type
474 << ((i
==mi
.second
.size()-1) ? "":",");
481 ui_
->progress(1, "Creating parser...");
483 utils::gzip_decompress_file( fname
, TEMP_JMDICT
);
484 parsers::JmdictParser
jmp(TEMP_JMDICT
);
485 const char *SEP
= parsers::SEPARATOR_SQL
;
488 ss
<< "INSERT INTO aoi (key,val) VALUES ('jmdict_version', '"
489 << jmp
.get_version() << "');\n";
490 log("jmdict_version: "+jmp
.get_version());
493 for ( std::pair
<string
,string
> elt
: jmp
.get_entities() ){
494 ss
<< "INSERT INTO entities (abbr,desc) VALUES ('" << SQLite3::escape(elt
.first
)
495 << "','" << SQLite3::escape(elt
.second
) << "');\n";
499 parsers::JmdictEntry entry
= jmp
.get_entry();
500 while ( entry
.did
!= -1 ) {
501 if ( n_entries
% 1000 == 0 ){
502 // entries is about 180 000 -> percent = 100*n/180000 ~ n/2000
503 ui_
->progress( n_entries
/2000, std::to_string(n_entries
)+string(" entries loaded") );
509 for ( parsers::ReadingElement
&rele
: entry
.r_ele
){
510 ss
<< "INSERT INTO d_reading (rid,did,reading,inf,nokanji,freq) VALUES ("
511 << rele
.rid
<< "," << entry
.did
<< ",'" << rele
.reading
<< "','"
512 << to_string(rele
.inf
,SEP
) << "',"
513 << rele
.nokanji
<< "," << rele
.freq
<< ");\n";
517 for ( parsers::KanjiElement
&kele
: entry
.k_ele
){
518 ss
<< "INSERT INTO d_kanji (kid,did,kanji,inf,freq) VALUES ("
519 << kele
.kid
<< "," << entry
.did
<< ",'" << kele
.kanji
<< "','"
520 << to_string(kele
.inf
,SEP
) << "'," << kele
.freq
<< ");\n";
523 for ( parsers::SenseElement
&sele
: entry
.s_ele
){
524 ss
<< "INSERT INTO d_sense (sid,did,gloss,xref,ant,inf,pos,field,misc,dial) VALUES ("
525 << sele
.sid
<< "," << entry
.did
<< ",'"
526 << SQLite3::escape(to_string(sele
.gloss
,SEP
)) << "','"
527 << to_string(sele
.xref
,SEP
) << "','"
528 << to_string(sele
.ant
,SEP
) << "','"
529 << SQLite3::escape(to_string(sele
.s_inf
,SEP
)) << "','"
530 << to_string(sele
.pos
,SEP
) << "','"
531 << to_string(sele
.field
,SEP
) << "','"
532 << to_string(sele
.misc
,SEP
) << "','"
533 << to_string(sele
.dial
,SEP
) << "'"
539 entry
= jmp
.get_entry();
542 catch ( utils::ParsingError
&e
){
543 ui_
->progress_hide();
544 std::string msg
= "App::parse_jmdict(): ParsingError: ";
550 log(std::to_string(n_entries
) + " entries processed.");
552 ss
<< "END TRANSACTION;\n";
553 // no need for vacuum here - check_index() will perform it eventually (see below)
556 // log_d("Writing file 'script.jmdict.sql'...");
557 // std::ofstream f ("script.jmdict.sql");
562 ui_
->progress( 95, "Creating database... ");
563 log("Creating database...");
565 // don't log query (ifdef DEBUG then SQL script is already created)
566 query(ss
.str().c_str(),false);
568 catch ( SQLite3::DatabaseError
&e
){
569 string msg
= "App::parse_jmdict(): DatabaseError: ";
574 ui_
->progress( 99, "Checking indexes... ");
575 log("Creating database...");
577 ui_
->progress_hide();
578 log("Deleting temporary file...");
585 void App::check_tables ()
587 // no need for vacuum here, it will be done by check_indexes()
588 log("Checking tables");
589 vector
<string
> existing
= query("SELECT name FROM sqlite_master WHERE type='table'");
590 // CREATE TABLE name ( column1 TYPE, column2 TYPE, ... )
591 for ( auto &table
: aoi_config::db_tables
){
592 if ( utils::is_in(existing
, string(table
.first
)) )
594 log("Creating table " + string(table
.first
));
596 for ( auto &column
: table
.second
){
597 std::stringstream sstr
;
598 sstr
<< column
.name
<< " " << column
.type
;
599 v
.push_back(sstr
.str());
601 std::stringstream ss
;
602 ss
<< "CREATE TABLE " << table
.first
<< " (" << to_string(v
,",") << ");\n";
603 query(ss
.str().c_str());
609 void App::check_indexes ()
611 log("Checking indexes...");
612 bool do_vacuum
= false;
613 vector
<string
> existing
= query("SELECT name FROM sqlite_master WHERE type='index'");
614 // CREATE INDEX idx_table_column ON table ( column ASC )
615 for ( auto &mi
: aoi_config::db_tables
){ // tables
616 for ( auto &c
: mi
.second
){ // columns
617 std::stringstream idx_name
;
618 idx_name
<< "idx_" << mi
.first
<< "_" << c
.name
;
619 if ( c
.index
&& !utils::is_in( existing
, idx_name
.str() ) ){
620 log(string("Creating index ") + idx_name
.str());
621 std::stringstream ss
;
622 ss
<< "CREATE INDEX " << idx_name
.str() << " ON " << mi
.first
623 << "(" << c
.name
<< " " << c
.sort
<< ")";
624 query(ss
.str().c_str());
636 void App::cb_dic_input ()
638 parse_dic_input( ui_
->get_dic_input() );
639 ui_
->reset_filters();
643 void App::on_dic_selected ( int id
)
645 log_d("App::on_dic_selected()");
648 void App::cb_edit_word ( int id
)
650 std::stringstream ss
;
651 ss
<< "App::edit_word( " << id
<< " )";
657 void App::cb_popup_kanji ( const string
&kanji
)
659 std::stringstream ss
;
660 ss
<< "App::on_kanji_clicked()" << kanji
;
661 Kanji k
= db_get_kanji(kanji
);
662 ui_
->popup_kanji( k
);
663 // XXX: this should be somewhere else (it is not logical here)
664 ui_
->highlight_components( k
.components() );
669 void App::set_listview ( const vector
<string
> &v
)
672 if ( !listview_items_
.empty() ) listview_items_
.clear();
674 vector
<int> cell_ids
;
676 while ( i
< v
.size() ) {
677 int cell_id
= std::stoi(v
[i
]); // jmdict id
678 cell_ids
.push_back(cell_id
);
679 cell_ids
.push_back(cell_id
);
680 cell_ids
.push_back(cell_id
);
683 for ( string
&elt
: utils::split_string( v
[i
+1], ",") )
684 if ( elt
.size() > 0 )
685 pos
.insert(utils::strip(elt
.c_str()));
686 d
.push_back( v
[i
+2] ); // reading
687 d
.push_back( v
[i
+3] ); // kanji
688 d
.push_back( v
[i
+4] ); // sense
689 // printf("%s%s%s\n", v[i+2].c_str(), v[i+3].c_str(), v[i+4].c_str() );
690 listview_items_
.push_back( {cell_id
, pos
, v
[i
+2], v
[i
+3], v
[i
+4]} );
694 sprintf( buff
, "%d results", db_
->result_rows() );
695 ui_
->set_dic_results( buff
);
696 ui_
->set_listview(d
,cell_ids
);
700 void App::parse_dic_input ( const char *str
)
702 log_d(string("App::parse_dic_input: \"") +string(str
) + string("\""));
704 string stripped
= utils::strip(str
);
706 if ( stripped
.empty() )
709 const char *s
= stripped
.c_str();
712 if ( s
[0] != SENSE_SEARCH_CHAR
){
713 printf("Search sense.");
714 DictionaryInputParser p
;
719 "Too broad search.\n"\
720 "Your computer may become unresponsible for a long time.\n"\
726 if ( res
== 1 ) // Cancel
730 if ( !strchr(s
,'*') && !strchr(s
,'?') && !strchr(s
,'[') && !strchr(s
,'{')
736 if ( s
[0] == SENSE_SEARCH_CHAR
){
737 printf("Search: %s\n", stripped
.substr(1).c_str() );
739 << "group_concat(pos) as pos,"
740 << q_reading("d_sense.did")
741 << q_kanji("d_sense.did")
743 << " from d_sense where gloss glob '*" << stripped
.substr(1) << "*'"
746 else if ( rmn_
->contains_kanji( qq
.c_str() ) ) {
747 q
<< "select d_kanji.did as did,"
748 << "(select group_concat(pos) from d_sense where d_sense.did = d_kanji.did) as pos,"
750 << q_reading("d_kanji.did")
751 << q_sense("d_kanji.did")
752 << "from d_kanji where "
753 << "kanji glob '" << rmn_
->romaji_to_hiragana(qq
.c_str()) << "' "
754 << " or kanji glob '" << rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
755 << " group by did order by d_kanji.freq desc, d_kanji.kanji asc";
758 q
<< "select d_reading.did as did,"
759 << "(select group_concat(pos) from d_sense where d_sense.did = d_reading.did) as pos,"
761 << q_kanji("d_reading.did")
762 << q_sense("d_reading.did")
763 << "from d_reading where "
764 << "reading glob '" << rmn_
->romaji_to_hiragana(qq
.c_str())
765 << "' or reading glob '"<< rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
766 << " group by did order by d_reading.freq desc, d_reading.reading asc";
768 set_listview(query(q
.str().c_str()));
773 Kanji
App::db_get_kanji ( const string
&kanji
)
775 log("App::db_get_kanji()");
777 q
<< "select kanji,strokes,ucs, rad_classic, rad_nelson, "
778 << "jlpt, grade, freq, onyomi, kunyomi, nanori, meaning,flags,components "
779 << "from k_kanji where kanji='" << kanji
<< "';";
781 vector
<string
> res
= query(q
.str().c_str());
783 kk
.strokes(std::stoi(res
[1]));
785 kk
.rad_classic(std::stoi(res
[3]));
786 kk
.rad_nelson( (res
[4].empty()) ? -1:std::stoi(res
[4]));
787 kk
.jlpt(std::stoi(res
[5]));
788 kk
.grade(std::stoi(res
[6]));
789 kk
.freq(std::stoi(res
[7]));
790 kk
.onyomi( utils::split_string(res
[8],parsers::SEPARATOR_SQL
) );
791 kk
.kunyomi( utils::split_string(res
[9],parsers::SEPARATOR_SQL
) );
792 kk
.nanori( utils::split_string(res
[10],parsers::SEPARATOR_SQL
) );
793 kk
.meaning( utils::split_string(res
[11],parsers::SEPARATOR_SQL
) );
794 kk
.flags( utils::split_string(res
[12],parsers::SEPARATOR_SQL
) );
795 kk
.components( res
[13] );
797 string qq
= "select skip1,skip2,skip3,misclass from k_skip where kanji='"+kanji
+"';";
798 vector
<string
> res2
= query(qq
.c_str());
799 if ( res2
.size() % 4 != 0 ){
800 std::stringstream ss
;
801 ss
<< "Wrong SKIP count. Kanji: " << kanji
802 << "Query result size: " << res2
.size()
803 << " (should be 4,8 or 12). SKIP not loaded.";
807 for ( size_t i
=0; i
< res2
.size(); i
+=4 )
808 kk
.skip( res2
[i
], res2
[i
+1], res2
[i
+2], res2
[i
+3] );
813 void App::cb_file_jmdict ( bool download
)
816 const char *path
= nullptr;
819 fname
= ui_
->download_dialog( get_config("sources/url_jmdict") );
820 log_d("Download finished...");
821 path
= fname
.c_str();
824 path
= ui_
->open_file_dialog();
829 log("Selected file: " + string(path
));
830 parse_jmdict(path
, download
);
831 auto label
= ui()->dlg_manage_db()->item(MANAGEDB_ITEM_JMDICT
);
833 label
->version("Changed",FL_BLUE
);
835 log_w("Can't find item " + string(MANAGEDB_ITEM_JMDICT
) + " in ManageDBDialog.");
839 void App::cb_file_kanjidic ( bool download
)
842 const char *path
= nullptr;
845 fname
= ui_
->download_dialog( get_config("sources/url_kanjidic") );
846 log_d("Download finished...");
847 path
= fname
.c_str();
850 path
= ui_
->open_file_dialog();
855 log("Selected file: " + string(path
));
856 parse_kanjidic(path
,download
);
857 auto label
= ui_
->dlg_manage_db()->item(MANAGEDB_ITEM_KANJIDIC
);
859 label
->version("Changed",FL_BLUE
);
861 log_w("Can't find item "+ string(MANAGEDB_ITEM_KANJIDIC
) +" in ManageDBDialog.");
865 void App::cb_file_components ( bool download
)
868 const char *path
= nullptr;
871 fname
= ui_
->download_dialog( get_config("sources/url_components") );
872 log_d("Download finished...");
873 path
= fname
.c_str();
876 path
= ui_
->open_file_dialog();
881 log("Selected file: " + string(path
));
882 utils::gzip_decompress_file( path
, TEMP_COMPONENTS
);
884 db_
->script( TEMP_COMPONENTS
);
886 catch ( SQLite3::DatabaseError
&e
) {
887 alert(e
.what(), e
.query() );
889 catch ( std::exception
&e
) {
893 auto label
= ui_
->dlg_manage_db()->item(MANAGEDB_ITEM_COMPONENTS
);
895 label
->version("Changed",FL_BLUE
);
897 log_w("Can't find item " + string(MANAGEDB_ITEM_COMPONENTS
)
898 + " in ManageDBDialog.");
899 remove(TEMP_COMPONENTS
);
904 void App::cb_filter_listview ()
906 vector
<string
> pos
= ui_
->listview_filters();
907 log_d("App::cb_filter_listview(): pos: " + utils::to_string(pos
));
908 bool filter_expr
= ( utils::is_in( pos
, string("expr") ) );
909 bool filter_noun
= ( utils::is_in( pos
, string("noun") ) );
910 bool filter_verb
= ( utils::is_in( pos
, string("verb") ) );
911 bool filter_adj
= ( utils::is_in( pos
, string("adj") ) );
915 for ( auto &elt
: listview_items_
){
916 auto start
= elt
.pos
.begin();
917 auto end
= elt
.pos
.end();
918 if ( filter_expr
&& std::find( start
, end
, "exp") == end
)
920 if ( filter_noun
&& std::find( start
, end
, "n" ) == end
)
922 if ( filter_adj
&& std::find_if( start
, end
,
923 [](const string
&s
){ return strncmp(s
.c_str(),"adj",3)==0;} ) == end
)
925 if ( filter_verb
&& std::find_if( start
, end
,
926 [](const string
&s
){ return strncmp(s
.c_str(),"v",1)==0;} ) == end
)
928 ids
.push_back( elt
.did
);
929 ids
.push_back( elt
.did
);
930 ids
.push_back( elt
.did
);
931 data
.push_back ( elt
.reading
);
932 data
.push_back ( elt
.kanji
);
933 data
.push_back ( elt
.sense
);
936 ui_
->set_listview( data
, ids
);
937 std::stringstream ss
;
938 ss
<< n
<< " results";
939 if ( listview_items_
.size() != n
)
940 ss
<< " (" << listview_items_
.size()-n
<< " hidden)";
942 ui_
->set_dic_results( ss
.str() );
946 void App::cb_dicview_rightclick ( int did
)
949 string q
= "select group_concat(kanji,'') from d_kanji where did="+std::to_string(did
);
950 vector
<string
> res
= query( q
.c_str() );
952 for ( string
&c
: utils::str_to_chars(res
[0].c_str()) )
953 if ( App::get()->rmn()->is_kanji(c
.c_str()) )
955 ui_
->menu_copy( vector
<string
>(kanji
.begin(),kanji
.end()) );
959 void App::set_config ( const string
&key
, const string
&val
)
961 cfg_
->set( key
, val
);
962 if ( key
== "font/base_size")
963 ui_
->font_base_size(std::stoi(val
));
964 else if ( key
== "log/level" )
965 logger_
.loglevel(val
);
966 else if ( key
== "color/foreground" || key
== "color/background" ||
967 key
== "color/background2" || key
== "color/selection")
973 void App::cb_manage_db ()
975 auto q
= query("select val from aoi where key='jmdict_version'");
976 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
977 q
= query("select val from aoi where key='kanjidic_version'");
978 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
979 q
= query("select val from aoi where key='components_version'");
980 string components_version
= q
.empty() ? "NONE":q
[0];
981 auto *d
= ui_
->dlg_manage_db();
983 if ( !d
->item(MANAGEDB_ITEM_JMDICT
) ){
984 col
= ( jmdict_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
985 d
->add_item( MANAGEDB_ITEM_JMDICT
, jmdict_version
,
986 "Japanese-English dictionary. Needed component.",
987 col
, scb_local_file_jmdict
, scb_download_file_jmdict
,
990 if ( !d
->item(MANAGEDB_ITEM_KANJIDIC
) ){
991 col
= ( kanjidic_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
992 d
->add_item( MANAGEDB_ITEM_KANJIDIC
, kanjidic_version
,
993 "Kanji dictionary. Needed component.",
994 col
, scb_local_file_kanjidic
, scb_download_file_kanjidic
,
997 if ( !d
->item(MANAGEDB_ITEM_COMPONENTS
) ){
998 col
= ( components_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
999 d
->add_item( MANAGEDB_ITEM_COMPONENTS
, components_version
,
1000 "Graphical components of kanji.",
1001 col
, scb_local_file_components
, scb_download_file_components
,
1008 void App::load_config ()
1010 log("Loading config...");
1011 vector
<string
> res
= query("select key,val from config;");
1012 for ( size_t i
=0; i
< res
.size(); i
+=2 ){
1013 set_config( res
[i
], res
[i
+1] );
1018 void App::save_config ( const std::map
<string
,aoi_config::Config::Value
> &newmap
)
1020 log("Saving config...");
1022 typedef std::pair
<string
,aoi_config::Config::Value
> cfg_pair
;
1024 // merge new and old config
1025 for ( const cfg_pair
&p
: newmap
)
1026 set_config( p
.first
, p
.second
.val
);
1028 // prepare SQL script
1029 std::stringstream ss
;
1030 ss
<< "BEGIN TRANSACTION;\n";
1031 for ( const cfg_pair
&p
: get_config_map() )
1032 ss
<< "REPLACE INTO config (key,val) VALUES('"
1033 << p
.first
<< "','" << p
.second
.val
<< "');\n";
1034 ss
<< "END TRANSACTION;\n";
1036 query(ss
.str().c_str());
1038 // apply new fonts and colors
1043 // check keys in oldconfig not present in newmap