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( get_config("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( get_config("font/kanji") );
76 ui_
->help_file( get_config("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_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_color("frequent");
116 TextStyle
style_inf(FL_HELVETICA_ITALIC
,0.8,get_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_color("misc");
121 TextStyle style_field
= style_pos
;
122 style_field
.color
= get_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.");
147 vector
<string
> result
;
151 result
= db_
->query(q
);
153 catch (SQLite3::DatabaseError
&e
){
154 log_e("App: DatabaseError:: " + string(e
.what()) + string("\nQuery: ")
155 + string(e
.query()) );
156 ui_
->cursor_default();
158 string msg
= std::to_string(db_
->result_rows()) + " results";
159 log( "App::query(): " + msg
);
160 if ( replace_separator
)
161 for ( string
&s
: result
)
162 utils::replace_all(s
, parsers::SEPARATOR_SQL
, ", ");
163 ui_
->cursor_default();
168 void App::cb_set_components ()
170 if ( curr_components_
.empty() )
173 using aoi_ui::ComponentView
;
176 vector
<string
> included
= ui_
->components_include();
177 vector
<string
> excluded
= ui_
->components_exclude();
179 vector
<ComponentView::Cell
> v
;
180 for ( string
&s
: included
)
181 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_1
) );
182 for ( string
&s
: excluded
)
183 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_2
) );
185 // sort by occurences
186 if ( !ui_
->sort_components_by_strokes() ){
187 for ( auto mi
= curr_components_
.rbegin(); mi
!=curr_components_
.rend(); ++mi
){
188 if ( !utils::is_in( included
, mi
->second
)
189 && !utils::is_in( excluded
, mi
->second
) )
190 v
.push_back( ComponentView::Cell(mi
->second
) );
195 vector
<string
> comps_by_strokes
;
196 for ( auto mi
: curr_components_
){
197 if ( mi
.first
< get_config
<int>("knj/min_compo_count") )
199 comps_by_strokes
.push_back(mi
.second
);
202 struct SortByStrokes
{
203 map
<string
,int> comps
;
204 SortByStrokes( const map
<string
,int> &c
): comps(c
){};
205 bool operator() (const string
&c1
, const string
&c2
){
206 return ( this->comps
[c1
] < this->comps
[c2
] );
210 std::sort( comps_by_strokes
.begin(), comps_by_strokes
.end(), sbc
);
213 for ( string
&s
: comps_by_strokes
){
214 int curr_strokes
= components_
[s
];
215 if ( prev_strokes
!= curr_strokes
)
216 v
.push_back( ComponentView::Cell( std::to_string(curr_strokes
),
217 ComponentView::CELL_LABEL
) );
218 v
.push_back( ComponentView::Cell(s
) );
219 prev_strokes
= curr_strokes
;
223 ui_
->set_components( v
);
227 void App::cb_kanji_search ()
230 if ( components_
.empty() ){
231 log("Loading components...");
232 vector
<string
> res
= query("select component, strokes from components");
233 for ( size_t i
=0; i
<res
.size(); i
=i
+2 ){
234 components_
[res
[i
]] = std::stoi(res
[i
+1]);
239 std::pair
<int,int> strokes
= utils::parse_range( utils::strip( ui_
->strokes() ) );
241 std::pair
<int,int> jlpt
= utils::parse_range( utils::strip( ui_
->jlpt() ) );
243 std::pair
<int,int> grade
= utils::parse_range( utils::strip( ui_
->grade() ) );
248 vector
<string
> skip
= utils::split_string( ui_
->skip() );
249 std::stringstream sskip
;
250 if ( skip
.size() > 0 ){
251 string skip1
= utils::strip(skip
[0].c_str());
252 sskip
<< " S.skip1=" << skip1
;
254 if ( skip
.size() > 1 ){
255 std::pair
<int,int> skip2
= utils::parse_range( utils::strip(skip
[1].c_str()) );
256 sskip
<< " and S.skip2>=" << skip2
.first
<< " and S.skip2<=" << skip2
.second
;
258 if ( skip
.size() > 2 ){
259 std::pair
<int,int> skip3
= utils::parse_range( utils::strip(skip
[2].c_str()) );
260 sskip
<< " and S.skip3>=" << skip3
.first
<< " and S.skip3<=" << skip3
.second
;
262 if ( !sskip
.str().empty() )
267 switch ( ui_
->sort_mode() ){
269 order_by
= "(case freq when 0 then 9999 else freq end) asc";
272 order_by
= "(case freq when 0 then 9999 else freq end) desc";
274 case SORT_STROKES_ASC
:
275 order_by
= "strokes asc";
277 case SORT_STROKES_DESC
:
278 order_by
= "strokes desc";
282 vector
<string
> components_include
= ui_
->components_include();
283 vector
<string
> components_exclude
= ui_
->components_exclude();
284 std::stringstream comps
;
285 for ( auto c
: components_include
)
286 comps
<< " and components like '%" << c
<< "%'";
287 for ( auto c
: components_exclude
)
288 comps
<< " and components not like '%" << c
<< "%'";
289 printf("%s\n",comps
.str().c_str());
292 if ( get_config
<bool>("knj/jis208_only") )
293 jis208
= " flags glob '*jis208*' and ";
296 std::stringstream ss
;
297 ss
<< "select distinct "
298 << "K.kanji,freq,components,flags "
299 << " from k_kanji as K, k_skip as S where K.kanji=S.kanji and "
302 << " strokes>=" << strokes
.first
<< " and strokes<=" << strokes
.second
303 << " and jlpt>=" << jlpt
.first
<< " and jlpt<=" << jlpt
.second
304 << " and grade>=" << grade
.first
<< " and grade<=" << grade
.second
306 << " order by " << order_by
;
310 vector
<string
> q
= query( ss
.str().c_str(), true, false );
311 vector
<aoi_ui::KanjiView::Cell
> data
;
312 vector
<string
> components
;
314 for ( size_t i
=0; i
<q
.size(); i
+=4 ){
316 int freq
= std::stoi(q
[i
+1]);
318 aoi_ui::KanjiView::Cell(
320 (freq
>0)? get_color("frequent"):-1
323 components
.push_back(q
[i
+2]);
324 for ( string
&s
: utils::split_string( q
[i
+3], parsers::SEPARATOR_SQL
) )
328 log_d("Groups: " + utils::to_string(flags
));
330 ui_
->set_kanjiview( data
);
332 sprintf( b
, "%d results", db_
->result_rows() );
333 ui_
->set_kanji_results( b
);
335 utils::Histogram
<string
> histogram
;
336 for ( string
&s
: components
)
337 histogram
.add( utils::str_to_chars(s
.c_str()) );
338 curr_components_
= histogram
.sorted();
344 void App::alert ( const string
&msg
, const string
&desc
)
346 std::stringstream ss
;
349 ss
<< "\n\nDetails:\n" << desc
;
351 ui_
->alert(ss
.str());
355 int App::run ( int argc
, char **argv
)
357 return ui_
->run(argc
,argv
);
361 void App::parse_kanjidic ( const char *fname
, bool delete_source
)
363 if ( !utils::file_exists( fname
) ){
364 log_e("App::parse_kanjidic(): File does not exist: " + string(fname
));
367 ui_
->progress(0, "Loading kanjidic...");
368 log("Loading kanjidic...");
370 const char *SEP
= parsers::SEPARATOR_SQL
;
374 ui_
->progress(1, "Creating parser...");
375 std::stringstream msg
;
376 msg
<< "Decompressing file: " << fname
<< " -> " << TEMP_KANJIDIC
;
380 utils::gzip_decompress_file( fname
, TEMP_KANJIDIC
);
381 parsers::KanjidicParser
p(TEMP_KANJIDIC
);
382 auto kanji
= p
.get_entry();
384 query( "DROP TABLE IF EXISTS k_kanji;"
385 "DROP TABLE IF EXISTS components;"
386 "DROP TABLE IF EXISTS k_skip;"
387 "DELETE FROM aoi WHERE key='kanjidic_version';" );
391 std::stringstream ss
;
392 ss
<< "BEGIN TRANSACTION;\n";
393 ss
<< "INSERT INTO aoi (key,val) VALUES ('kanjidic_version','"
394 << p
.get_version() << "');\n";
395 while ( kanji
.kanji() != "" ){
397 if ( n_kanji
% 100 == 0)
398 // percent = 100 * n_kanji/13108
399 ui_
->progress( n_kanji
/14, std::to_string(n_kanji
)+string(" kanji loaded"));
400 ss
<< "INSERT INTO k_kanji "
401 << "(kanji,ucs,onyomi,kunyomi,meaning,nanori,flags,jlpt,grade,freq,strokes,"
402 << "rad_classic,rad_nelson,components)"
404 << kanji
.kanji() << "','"
405 << kanji
.ucs() << "','"
406 << to_string(kanji
.onyomi(),SEP
) << "','"
407 << to_string(kanji
.kunyomi(),SEP
) << "','"
408 << SQLite3::escape(to_string(kanji
.meaning(),SEP
)) << "','"
409 << to_string(kanji
.nanori(),SEP
) << "','"
410 << to_string(kanji
.flags(),SEP
) << "',"
411 << kanji
.jlpt() << ","
412 << kanji
.grade() << ","
413 << kanji
.freq() << ","
414 << kanji
.strokes() << ","
415 << kanji
.rad_classic() << ","
416 << kanji
.rad_nelson() << ","
419 for ( SKIP
&s
: kanji
.skip() ) {
420 ss
<< "INSERT INTO k_skip (kanji,skip1,skip2,skip3,misclass) VALUES('"
421 << kanji
.kanji() << "'," << s
.s1
<< "," << s
.s2
<< "," << s
.s3
422 << ",'" << s
.misclass
<< "');\n";
424 kanji
= p
.get_entry();
426 ss
<< "END TRANSACTION;\n";
427 log("Writing to db ...");
430 // log_d("Writing file 'script.kanjidic.sql'...");
431 // std::ofstream f ("script.kanjidic.sql");
435 ui_
->progress(95, "Writing to database...");
436 query(ss
.str().c_str(),false);
438 catch ( utils::ParsingError
&e
){
439 ui_
->cursor_default();
440 ui_
->progress_hide();
441 std::string msg
= "App::parse_kanjidic(): ParsingError: ";
446 ui_
->progress(98, "Checking indexes...");
448 ui_
->progress_hide();
449 remove( TEMP_KANJIDIC
);
452 ui_
->cursor_default();
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...");
484 utils::gzip_decompress_file( fname
, TEMP_JMDICT
);
485 parsers::JmdictParser
jmp(TEMP_JMDICT
);
486 const char *SEP
= parsers::SEPARATOR_SQL
;
489 ss
<< "INSERT INTO aoi (key,val) VALUES ('jmdict_version', '"
490 << jmp
.get_version() << "');\n";
491 log("jmdict_version: "+jmp
.get_version());
494 for ( std::pair
<string
,string
> elt
: jmp
.get_entities() ){
495 ss
<< "INSERT INTO entities (abbr,desc) VALUES ('" << SQLite3::escape(elt
.first
)
496 << "','" << SQLite3::escape(elt
.second
) << "');\n";
500 parsers::JmdictEntry entry
= jmp
.get_entry();
501 while ( entry
.did
!= -1 ) {
502 if ( n_entries
% 1000 == 0 ){
503 // entries is about 180 000 -> percent = 100*n/180000 ~ n/2000
504 ui_
->progress( n_entries
/2000, std::to_string(n_entries
)+string(" entries loaded") );
510 for ( parsers::ReadingElement
&rele
: entry
.r_ele
){
511 ss
<< "INSERT INTO d_reading (rid,did,reading,inf,nokanji,freq) VALUES ("
512 << rele
.rid
<< "," << entry
.did
<< ",'" << rele
.reading
<< "','"
513 << to_string(rele
.inf
,SEP
) << "',"
514 << rele
.nokanji
<< "," << rele
.freq
<< ");\n";
518 for ( parsers::KanjiElement
&kele
: entry
.k_ele
){
519 ss
<< "INSERT INTO d_kanji (kid,did,kanji,inf,freq) VALUES ("
520 << kele
.kid
<< "," << entry
.did
<< ",'" << kele
.kanji
<< "','"
521 << to_string(kele
.inf
,SEP
) << "'," << kele
.freq
<< ");\n";
524 for ( parsers::SenseElement
&sele
: entry
.s_ele
){
525 ss
<< "INSERT INTO d_sense (sid,did,gloss,xref,ant,inf,pos,field,misc,dial) VALUES ("
526 << sele
.sid
<< "," << entry
.did
<< ",'"
527 << SQLite3::escape(to_string(sele
.gloss
,SEP
)) << "','"
528 << to_string(sele
.xref
,SEP
) << "','"
529 << to_string(sele
.ant
,SEP
) << "','"
530 << SQLite3::escape(to_string(sele
.s_inf
,SEP
)) << "','"
531 << to_string(sele
.pos
,SEP
) << "','"
532 << to_string(sele
.field
,SEP
) << "','"
533 << to_string(sele
.misc
,SEP
) << "','"
534 << to_string(sele
.dial
,SEP
) << "'"
540 entry
= jmp
.get_entry();
543 catch ( utils::ParsingError
&e
){
544 ui_
->progress_hide();
545 ui_
->cursor_default();
546 std::string msg
= "App::parse_jmdict(): ParsingError: ";
552 log(std::to_string(n_entries
) + " entries processed.");
554 ss
<< "END TRANSACTION;\n";
555 // no need for vacuum here - check_index() will perform it eventually (see below)
558 // log_d("Writing file 'script.jmdict.sql'...");
559 // std::ofstream f ("script.jmdict.sql");
564 ui_
->progress( 95, "Creating database... ");
565 log("Creating database...");
567 // don't log query (ifdef DEBUG then SQL script is already created)
568 query(ss
.str().c_str(),false);
570 catch ( SQLite3::DatabaseError
&e
){
571 string msg
= "App::parse_jmdict(): DatabaseError: ";
576 ui_
->progress( 99, "Checking indexes... ");
577 log("Creating database...");
579 ui_
->progress_hide();
580 log("Deleting temporary file...");
584 ui_
->cursor_default();
588 void App::check_tables ()
590 // no need for vacuum here, it will be done by check_indexes()
591 log("Checking tables");
592 vector
<string
> existing
= query("SELECT name FROM sqlite_master WHERE type='table'");
593 // CREATE TABLE name ( column1 TYPE, column2 TYPE, ... )
594 for ( auto &table
: aoi_config::db_tables
){
595 if ( utils::is_in(existing
, string(table
.first
)) )
597 log("Creating table " + string(table
.first
));
599 for ( auto &column
: table
.second
){
600 std::stringstream sstr
;
601 sstr
<< column
.name
<< " " << column
.type
;
602 v
.push_back(sstr
.str());
604 std::stringstream ss
;
605 ss
<< "CREATE TABLE " << table
.first
<< " (" << to_string(v
,",") << ");\n";
606 query(ss
.str().c_str());
612 void App::check_indexes ()
614 log("Checking indexes...");
615 bool do_vacuum
= false;
616 vector
<string
> existing
= query("SELECT name FROM sqlite_master WHERE type='index'");
617 // CREATE INDEX idx_table_column ON table ( column ASC )
618 for ( auto &mi
: aoi_config::db_tables
){ // tables
619 for ( auto &c
: mi
.second
){ // columns
620 std::stringstream idx_name
;
621 idx_name
<< "idx_" << mi
.first
<< "_" << c
.name
;
622 if ( c
.index
&& !utils::is_in( existing
, idx_name
.str() ) ){
623 log(string("Creating index ") + idx_name
.str());
624 std::stringstream ss
;
625 ss
<< "CREATE INDEX " << idx_name
.str() << " ON " << mi
.first
626 << "(" << c
.name
<< " " << c
.sort
<< ")";
627 query(ss
.str().c_str());
639 void App::cb_dic_input ()
641 parse_dic_input( ui_
->get_dic_input() );
642 ui_
->reset_filters();
646 void App::on_dic_selected ( int id
)
648 log_d("App::on_dic_selected()");
651 void App::cb_edit_word ( int id
)
653 std::stringstream ss
;
654 ss
<< "App::edit_word( " << id
<< " )";
660 void App::cb_popup_kanji ( const string
&kanji
)
662 std::stringstream ss
;
663 ss
<< "App::on_kanji_clicked()" << kanji
;
664 Kanji k
= db_get_kanji(kanji
);
665 ui_
->popup_kanji( k
);
666 // XXX: this should be somewhere else (it is not logical here)
667 ui_
->highlight_components( k
.components() );
672 void App::set_listview ( const vector
<string
> &v
)
675 if ( !listview_items_
.empty() ) listview_items_
.clear();
677 vector
<int> cell_ids
;
679 while ( i
< v
.size() ) {
680 int cell_id
= std::stoi(v
[i
]); // jmdict id
681 cell_ids
.push_back(cell_id
);
682 cell_ids
.push_back(cell_id
);
683 cell_ids
.push_back(cell_id
);
686 for ( string
&elt
: utils::split_string( v
[i
+1], ",") )
687 if ( elt
.size() > 0 )
688 pos
.insert(utils::strip(elt
.c_str()));
689 d
.push_back( v
[i
+2] ); // reading
690 d
.push_back( v
[i
+3] ); // kanji
691 d
.push_back( v
[i
+4] ); // sense
692 listview_items_
.push_back( {cell_id
, pos
, v
[i
+2], v
[i
+3], v
[i
+4]} );
696 sprintf( buff
, "%d results", db_
->result_rows() );
697 ui_
->set_dic_results( buff
);
698 ui_
->set_listview(d
,cell_ids
);
702 void App::parse_dic_input ( const char *str
)
704 log_d(string("App::parse_dic_input: \"") +string(str
) + string("\""));
706 string stripped
= utils::strip(str
);
708 if ( stripped
.empty() )
711 const char *s
= stripped
.c_str();
714 if ( s
[0] != SENSE_SEARCH_CHAR
){
715 DictionaryInputParser p
;
720 "Too broad search.\n"\
721 "Your computer may become unresponsible for a long time.\n"\
727 if ( res
== 1 ) // Cancel
731 if ( !strchr(s
,'*') && !strchr(s
,'?') && !strchr(s
,'[') && !strchr(s
,'{')
737 if ( s
[0] == SENSE_SEARCH_CHAR
){
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 ( MANAGEDB_FILE_TYPE filetype
, bool download
)
816 const char *item
= nullptr;
818 case MANAGEDB_JMDICT
:
819 url
= get_config("sources/url_jmdict");
820 item
= MANAGEDB_ITEM_JMDICT
;
822 case MANAGEDB_KANJIDIC
:
823 url
= get_config("sources/url_kanjidic");
824 item
= MANAGEDB_ITEM_KANJIDIC
;
826 case MANAGEDB_COMPONENTS
:
827 url
= get_config("sources/url_components");
828 item
= MANAGEDB_ITEM_COMPONENTS
;
831 alert("Unknown MANAGEDB_FILE_TYPE.");
837 fname
= ui_
->download_dialog( url
);
838 log_d("Download finished...");
841 fname
= ui_
->open_file_dialog();
846 log("Selected file: " + fname
);
849 case MANAGEDB_JMDICT
:
850 parse_jmdict(fname
.c_str(), download
);
852 case MANAGEDB_KANJIDIC
:
853 parse_kanjidic(fname
.c_str(), download
);
855 case MANAGEDB_COMPONENTS
:
857 utils::gzip_decompress_file( fname
.c_str(), TEMP_COMPONENTS
);
859 db_
->script( TEMP_COMPONENTS
);
861 catch ( SQLite3::DatabaseError
&e
) {
862 alert(e
.what(), e
.query() );
864 catch ( std::exception
&e
) {
871 auto label
= ui()->dlg_manage_db()->item(item
);
873 label
->version("Changed",FL_BLUE
);
875 log_w("Can't find item " + string(item
) + " in ManageDBDialog.");
877 if ( filetype
== MANAGEDB_COMPONENTS
){
878 remove(TEMP_COMPONENTS
);
879 remove(fname
.c_str());
883 void App::cb_filter_listview ()
885 vector
<string
> pos
= ui_
->listview_filters();
886 log_d("App::cb_filter_listview(): pos: " + utils::to_string(pos
));
887 bool filter_expr
= ( utils::is_in( pos
, string("expr") ) );
888 bool filter_noun
= ( utils::is_in( pos
, string("noun") ) );
889 bool filter_verb
= ( utils::is_in( pos
, string("verb") ) );
890 bool filter_adj
= ( utils::is_in( pos
, string("adj") ) );
894 for ( auto &elt
: listview_items_
){
895 auto start
= elt
.pos
.begin();
896 auto end
= elt
.pos
.end();
897 if ( filter_expr
&& std::find( start
, end
, "exp") == end
)
899 if ( filter_noun
&& std::find( start
, end
, "n" ) == end
)
901 if ( filter_adj
&& std::find_if( start
, end
,
902 [](const string
&s
){ return strncmp(s
.c_str(),"adj",3)==0;} ) == end
)
904 if ( filter_verb
&& std::find_if( start
, end
,
905 [](const string
&s
){ return strncmp(s
.c_str(),"v",1)==0;} ) == end
)
907 ids
.push_back( elt
.did
);
908 ids
.push_back( elt
.did
);
909 ids
.push_back( elt
.did
);
910 data
.push_back ( elt
.reading
);
911 data
.push_back ( elt
.kanji
);
912 data
.push_back ( elt
.sense
);
915 ui_
->set_listview( data
, ids
);
916 std::stringstream ss
;
917 ss
<< n
<< " results";
918 if ( listview_items_
.size() != n
)
919 ss
<< " (" << listview_items_
.size()-n
<< " hidden)";
921 ui_
->set_dic_results( ss
.str() );
925 void App::cb_dicview_rightclick ( int did
)
927 string q
= "select group_concat(kanji,'') from d_kanji where did="+std::to_string(did
);
928 vector
<string
> res
= query( q
.c_str() );
930 for ( string
&c
: utils::str_to_chars(res
[0].c_str()) )
931 if ( App::get()->rmn()->is_kanji(c
.c_str()) )
933 ui_
->menu_copy( vector
<string
>(kanji
.begin(),kanji
.end()) );
937 void App::apply_config ()
939 ui_
->font_base_size( get_config
<int>("font/base_size"));
940 logger_
.loglevel( get_config("log/level") );
946 void App::cb_manage_db ()
948 auto q
= query("select val from aoi where key='jmdict_version'");
949 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
950 q
= query("select val from aoi where key='kanjidic_version'");
951 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
952 q
= query("select val from aoi where key='components_version'");
953 string components_version
= q
.empty() ? "NONE":q
[0];
954 auto *d
= ui_
->dlg_manage_db();
956 if ( !d
->item(MANAGEDB_ITEM_JMDICT
) ){
957 col
= ( jmdict_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
958 d
->add_item( MANAGEDB_ITEM_JMDICT
, jmdict_version
,
959 "Japanese-English dictionary. Needed component.",
960 col
, scb_local_file_jmdict
, scb_download_file_jmdict
,
963 if ( !d
->item(MANAGEDB_ITEM_KANJIDIC
) ){
964 col
= ( kanjidic_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
965 d
->add_item( MANAGEDB_ITEM_KANJIDIC
, kanjidic_version
,
966 "Kanji dictionary. Needed component.",
967 col
, scb_local_file_kanjidic
, scb_download_file_kanjidic
,
970 if ( !d
->item(MANAGEDB_ITEM_COMPONENTS
) ){
971 col
= ( components_version
== "NONE" ) ? FL_RED
:FL_BLUE
;
972 d
->add_item( MANAGEDB_ITEM_COMPONENTS
, components_version
,
973 "Graphical components of kanji.",
974 col
, scb_local_file_components
, scb_download_file_components
,
981 void App::load_config ()
983 log("Loading config...");
984 vector
<string
> res
= query("select key,val from config;");
985 for ( size_t i
=0; i
< res
.size(); i
+=2 ){
986 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
);
1003 // prepare SQL script
1004 std::stringstream ss
;
1005 ss
<< "BEGIN TRANSACTION;\n";
1006 ss
<< "DROP TABLE IF EXISTS config;\n";
1007 ss
<< "CREATE TABLE config (key TEXT, val TEXT);\n";
1008 for ( const cfg_pair
&p
: get_config_map() )
1009 ss
<< "INSERT INTO config (key,val) VALUES('"
1010 << p
.first
<< "','" << p
.second
.val
<< "');\n";
1011 ss
<< "END TRANSACTION;\n";
1013 query(ss
.str().c_str());
1015 // apply new fonts and colors
1020 // check keys in oldconfig not present in newmap