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 //! See App::load_dbscript() and App::App()
30 const char *DB_BACKUP_FILE
= "db.backup";
31 const char *LOG_FILE
= "aoi.log";
32 const char SENSE_SEARCH_CHAR
= ':';
34 App
* App::instance_
= nullptr;
39 logger_
.filename(LOG_FILE
);
40 logger_
.loglevel(Logger::MSG_DEBUG
);
42 db_
= nullptr; // NECESSARY ( if db_ undefined then SEGFAULT in open_database() )
43 cfg_
= new aoi_config::Config();
44 ui_
= new aoi_ui::GUI(this);
45 rmn_
= new Romanization();
47 if ( utils::file_exists( DB_BACKUP_FILE
) ){
48 log_w("Backup copy of the database found.");
50 ss
<< "Backup copy of the database found. Delete it?\n"
51 << "WARNING: You should check whether the current database\n"
52 << "works before deleting the backup.";
53 if ( ui_
->choice(ss
.str(), "Delete", "No") == 0 ){
54 log("Deleting the backup copy of the database.");
55 remove(DB_BACKUP_FILE
);
59 ui_
->progress(0,"Opening database...");
62 ui_
->progress( 30, "Checking tables..." );
64 ui_
->progress( 60, "Checking indexes..." );
71 // ui_->progress(90, "DEBUG query..." );
72 parse_dic_input("ana*");
74 // ui_->cb_toggle_group(nullptr);
77 ui_
->progress( 98, "Initializing fonts..." );
78 ui_
->fontname_kanji( get_config("font/kanji") );
79 ui_
->help_file( get_config("sources/help_index") );
84 auto q
= query("select val from aoi where key='jmdict_version'");
85 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
86 q
= query("select val from aoi where key='kanjidic_version'");
87 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
88 if ( jmdict_version
== "NONE" || kanjidic_version
== "NONE" )
93 logger_
.loglevel(Logger::MSG_DEBUG
);
109 void App::init_dicview ()
111 // initialize textstyles
112 TextStyle
style_default(FL_HELVETICA
,1.2);
113 TextStyle
style_reading(aoi_ui::FONT_KANJI
,1.5);
114 TextStyle style_reading_freq
= style_reading
;
115 style_reading_freq
.color
= get_color("frequent");
116 TextStyle
style_kanji(aoi_ui::FONT_KANJI
,1.7);
117 TextStyle style_kanji_freq
= style_kanji
;
118 style_kanji_freq
.color
= get_color("frequent");
119 TextStyle
style_inf(FL_HELVETICA_ITALIC
,0.8,get_color("pos"));
120 style_inf
.offset_y
= 3;
121 TextStyle style_pos
= style_inf
;
122 TextStyle style_misc
= style_pos
;
123 style_misc
.color
= get_color("misc");
124 TextStyle style_field
= style_pos
;
125 style_field
.color
= get_color("field");
126 TextStyle style_dial
= style_pos
;
128 // register TextStyles
129 ui_
->register_tag_dicview( "default", style_default
);
130 ui_
->register_tag_dicview( "reading", style_reading
);
131 ui_
->register_tag_dicview( "reading_freq", style_reading_freq
);
132 ui_
->register_tag_dicview( "kanji", style_kanji
);
133 ui_
->register_tag_dicview( "kanji_freq", style_kanji_freq
);
134 ui_
->register_tag_dicview( "kinf", style_inf
);
135 ui_
->register_tag_dicview( "rinf", style_inf
);
136 ui_
->register_tag_dicview( "pos", style_pos
);
137 ui_
->register_tag_dicview( "misc", style_misc
);
138 ui_
->register_tag_dicview( "field", style_field
);
139 ui_
->register_tag_dicview( "dial", style_dial
);
143 vector
<string
> App::query ( const char *q
, bool log_query
, bool replace_separator
)
146 log_e("App::query(): Database does not exist.");
150 vector
<string
> result
;
154 result
= db_
->query(q
);
156 catch (SQLite3::DatabaseError
&e
){
157 log_e("App: DatabaseError:: " + string(e
.what()) + string("\nQuery: ")
158 + string(e
.query()) );
159 ui_
->cursor_default();
161 string msg
= std::to_string(db_
->result_rows()) + " results";
162 log( "App::query(): " + msg
);
163 if ( replace_separator
)
164 for ( string
&s
: result
)
165 utils::replace_all(s
, SEPARATOR_SQL
, ", ");
166 ui_
->cursor_default();
171 void App::open_database ()
175 db_
= new SQLite3::SQLite3( get_config("db/file_main").c_str() );
178 db_
->open(get_config("db/file_main").c_str());
181 catch (SQLite3::CantOpenDatabase
&e
){
182 log_e( "Aoi: Can't open database '" + string(e
.what()) + "'.");
185 std::stringstream ss
;
186 ss
<< "ATTACH DATABASE '" << get_config("db/file_user") << "'as user;";
187 db_
->query(ss
.str().c_str());
191 void App::cb_set_components ()
193 if ( curr_components_
.empty() )
196 using aoi_ui::ComponentView
;
199 vector
<string
> included
= ui_
->components_include();
200 vector
<string
> excluded
= ui_
->components_exclude();
202 vector
<ComponentView::Cell
> v
;
203 for ( string
&s
: included
)
204 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_1
) );
205 for ( string
&s
: excluded
)
206 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_2
) );
208 // sort by occurences
209 if ( !ui_
->sort_components_by_strokes() ){
210 for ( auto mi
= curr_components_
.rbegin(); mi
!=curr_components_
.rend(); ++mi
){
211 if ( !utils::is_in( included
, mi
->second
)
212 && !utils::is_in( excluded
, mi
->second
) )
213 v
.push_back( ComponentView::Cell(mi
->second
) );
218 vector
<string
> comps_by_strokes
;
219 for ( auto mi
: curr_components_
){
220 if ( mi
.first
< get_config
<int>("knj/min_compo_count") )
222 if ( !utils::is_in( included
, mi
.second
)
223 && !utils::is_in( excluded
, mi
.second
) )
224 comps_by_strokes
.push_back(mi
.second
);
227 struct SortByStrokes
{
228 map
<string
,int> comps
;
229 SortByStrokes( const map
<string
,int> &c
): comps(c
){};
230 bool operator() (const string
&c1
, const string
&c2
){
231 return ( this->comps
[c1
] < this->comps
[c2
] );
235 std::sort( comps_by_strokes
.begin(), comps_by_strokes
.end(), sbc
);
238 for ( string
&s
: comps_by_strokes
){
239 int curr_strokes
= components_
[s
];
240 if ( prev_strokes
!= curr_strokes
)
241 v
.push_back( ComponentView::Cell( std::to_string(curr_strokes
),
242 ComponentView::CELL_LABEL
) );
243 v
.push_back( ComponentView::Cell(s
) );
244 prev_strokes
= curr_strokes
;
248 ui_
->set_components( v
);
252 void App::cb_kanji_search ()
255 if ( components_
.empty() ){
256 log("Loading components...");
257 vector
<string
> res
= query("select component, strokes from components");
259 for ( size_t i
=0; i
<res
.size(); i
=i
+2 ){
261 if ( res
[i
+1].empty() )
264 strokes
= std::stoi(res
[i
+1]);
265 components_
[res
[i
]] = strokes
;
268 log_d(std::to_string(skipped
)+" components without strokes data.");
272 std::pair
<int,int> strokes
= utils::parse_range( utils::strip( ui_
->strokes() ) );
274 std::pair
<int,int> jlpt
= utils::parse_range( utils::strip( ui_
->jlpt() ) );
276 std::pair
<int,int> grade
= utils::parse_range( utils::strip( ui_
->grade() ) );
279 vector
<string
> skip
= utils::split_string( ui_
->skip() );
280 std::stringstream sskip
;
281 if ( skip
.size() > 0 ){
282 string skip1
= utils::strip(skip
[0].c_str());
283 sskip
<< " S.skip1=" << skip1
;
285 if ( skip
.size() > 1 ){
286 std::pair
<int,int> skip2
= utils::parse_range( utils::strip(skip
[1].c_str()) );
287 sskip
<< " and S.skip2>=" << skip2
.first
<< " and S.skip2<=" << skip2
.second
;
289 if ( skip
.size() > 2 ){
290 std::pair
<int,int> skip3
= utils::parse_range( utils::strip(skip
[2].c_str()) );
291 sskip
<< " and S.skip3>=" << skip3
.first
<< " and S.skip3<=" << skip3
.second
;
293 if ( !sskip
.str().empty() )
298 switch ( ui_
->sort_mode() ){
300 order_by
= "(case freq when 0 then 9999 else freq end) asc";
303 order_by
= "(case freq when 0 then 9999 else freq end) desc";
305 case SORT_STROKES_ASC
:
306 order_by
= "strokes asc";
308 case SORT_STROKES_DESC
:
309 order_by
= "strokes desc";
313 vector
<string
> components_include
= ui_
->components_include();
314 vector
<string
> components_exclude
= ui_
->components_exclude();
315 std::stringstream comps
;
316 for ( auto c
: components_include
)
317 comps
<< " and components like '%" << c
<< "%'";
318 for ( auto c
: components_exclude
)
319 comps
<< " and components not like '%" << c
<< "%'";
320 printf("%s\n",comps
.str().c_str());
323 if ( get_config
<bool>("knj/jis208_only") )
324 jis208
= " flags glob '*jis208*' and ";
327 std::stringstream ss
;
328 ss
<< "select distinct "
329 << "K.kanji,freq,components,flags "
330 << " from k_kanji as K"
331 << (sskip
.str().empty() ? " where ":", k_skip as S where K.kanji=S.kanji and")
334 << " strokes>=" << strokes
.first
<< " and strokes<=" << strokes
.second
335 << " and jlpt>=" << jlpt
.first
<< " and jlpt<=" << jlpt
.second
336 << " and grade>=" << grade
.first
<< " and grade<=" << grade
.second
338 << " order by " << order_by
;
342 vector
<string
> q
= query( ss
.str().c_str(), true, false );
343 vector
<aoi_ui::KanjiView::Cell
> data
;
344 vector
<string
> components
;
346 for ( size_t i
=0; i
<q
.size(); i
+=4 ){
348 int freq
= std::stoi(q
[i
+1]);
350 aoi_ui::KanjiView::Cell(
352 (freq
>0)? get_color("frequent"):-1
355 components
.push_back(q
[i
+2]);
356 for ( string
&s
: utils::split_string( q
[i
+3], SEPARATOR_SQL
) )
360 log_d("Groups: " + utils::to_string(flags
));
362 ui_
->set_kanjiview( data
);
364 sprintf( b
, "%d results", db_
->result_rows() );
365 ui_
->set_kanji_results( b
);
367 utils::Histogram
<string
> histogram
;
368 for ( string
&s
: components
)
369 histogram
.add( utils::str_to_chars(s
.c_str()) );
370 curr_components_
= histogram
.sorted();
376 void App::alert ( const string
&msg
, const string
&desc
)
378 std::stringstream ss
;
381 ss
<< "\n\nDetails:\n" << desc
;
383 ui_
->alert(ss
.str());
387 int App::run ( int argc
, char **argv
)
389 return ui_
->run(argc
,argv
);
393 void App::check_tables ()
395 for ( auto &dbit
: aoi_config::db_tables
) {
396 // no need for vacuum here, it will be done by check_indexes()
397 log("Checking tables in database: " + string(dbit
.first
));
399 q
<< "SELECT name FROM " << dbit
.first
<< "." << "sqlite_master WHERE type='table'";
400 vector
<string
> existing
= query(q
.str().c_str());
401 // CREATE TABLE name ( column1 TYPE, column2 TYPE, ... )
402 for ( auto &table
: dbit
.second
){
403 if ( utils::is_in(existing
, string(table
.first
)) )
405 log("Creating table " + string(table
.first
));
407 for ( auto &column
: table
.second
){
408 std::stringstream sstr
;
409 sstr
<< column
.name
<< " " << column
.type
;
410 v
.push_back(sstr
.str());
412 std::stringstream ss
;
413 ss
<< "CREATE TABLE " << dbit
.first
<< "." << table
.first
414 << " (" << to_string(v
,",") << ");\n";
415 query(ss
.str().c_str());
422 void App::check_indexes ()
424 bool do_vacuum
= false;
425 log("Checking indexes...");
426 for ( auto &dbit
: aoi_config::db_tables
) {
428 q
<< "SELECT name FROM " << dbit
.first
<< "." << "sqlite_master WHERE type='index'";
429 vector
<string
> existing
= query(q
.str().c_str());
430 // CREATE INDEX idx_table_column ON table ( column ASC )
431 for ( auto &mi
: dbit
.second
){ // tables
432 for ( auto &c
: mi
.second
){ // columns
433 std::stringstream idx_name
;
434 idx_name
<< "idx_" << mi
.first
<< "_" << c
.name
;
435 if ( c
.index
&& !utils::is_in( existing
, idx_name
.str() ) ){
436 log(string("Creating index ") + idx_name
.str());
437 std::stringstream ss
;
438 ss
<< "CREATE INDEX " << idx_name
.str() << " ON " << mi
.first
439 << "(" << c
.name
<< " " << c
.sort
<< ")";
440 query(ss
.str().c_str());
452 void App::load_dbscript ( const char *fname
)
454 const char *TEMP_DBSCRIPT
= "dbscript.temp";
455 const char *TEMP_DATABASE
= "temp.db";
456 const int LINE_BUFFER
= 4096;
458 // hide manage db_dialog if visible
459 auto *d
= ui_
->dlg_manage_db();
460 if ( d
&& d
->visible() )
463 log("Loading dbscript: "+string(fname
));
465 log("Decompressing file (if necessary)...");
466 utils::gzip_decompress_file( fname
, TEMP_DBSCRIPT
);
468 log("Opening temporary database.");
469 remove(TEMP_DATABASE
);
470 SQLite3::SQLite3
db(TEMP_DATABASE
);
474 char line
[LINE_BUFFER
];
475 f
.open(TEMP_DBSCRIPT
);
476 // n lines should be > 2e6 -> percent=n*2e6/100
479 if ( n
% 5000 == 0 ){
481 snprintf( b
, 64, "Loading line: %d", n
);
482 ui_
->progress(n
/float(2.5e4
), b
);
484 f
.getline( line
, LINE_BUFFER
);
490 log("Closing temporary database...");
492 remove(TEMP_DBSCRIPT
);
494 log("Closing old database.");
497 string dbfile
= get_config("db/file_main");
499 log("Renaming old database to main.db.bckp.");
500 rename( dbfile
.c_str(), DB_BACKUP_FILE
);
502 log("Renaming new database.");
503 rename( TEMP_DATABASE
, dbfile
.c_str() );
505 log("Switching to the new database");
508 ui_
->progress(90, "Checking tables...");
510 ui_
->progress(95, "Checking indexes...");
513 ui_
->progress_hide();
514 // show informations about db
519 void App::cb_dic_input ()
521 parse_dic_input( ui_
->get_dic_input() );
522 ui_
->reset_filters();
526 //void App::on_dic_selected ( int id )
528 // log_d("App::on_dic_selected()");
532 void App::cb_download_db ()
534 log_d("Download DB");
535 string path
= ui_
->download_dialog( get_config("sources/url_database") );
536 if ( !path
.empty() && !utils::file_exists( path
) ){
537 log_w("App::cb_download_db(): Not a file: "+path
);
541 log("Closing old database.");
544 string dbfile
= get_config("db/file_main");
546 log("Renaming old database to main.db.bckp.");
547 rename( dbfile
.c_str(), DB_BACKUP_FILE
);
549 log("Decompressing downloaded file");
550 utils::gzip_decompress_file( path
.c_str(), dbfile
.c_str() );
552 log("Switching to the new database");
555 ui_
->progress(10, "Checking tables...");
557 ui_
->progress(50, "Checking indexes...");
560 remove( path
.c_str() );
561 ui_
->progress_hide();
562 // show informations about db
567 void App::cb_edit_word ()
569 std::stringstream ss
;
570 int id
= ui_
->dicview_selected_rowid();
571 ss
<< "App::edit_word( " << id
<< " )";
573 ui_
->edit_word( db_get_word(id
));
577 void App::cb_popup_kanji ( const string
&kanji
)
579 std::stringstream ss
;
580 ss
<< "App::on_kanji_clicked()" << kanji
;
581 Kanji k
= db_get_kanji(kanji
);
582 ui_
->popup_kanji( k
);
583 // XXX: ? this should be somewhere else (it is not logical here)
584 ui_
->highlight_components( k
.components() );
589 void App::set_listview ( const vector
<string
> &v
)
591 if ( !listview_items_
.empty() ) listview_items_
.clear();
593 vector
<int> cell_ids
;
595 while ( i
< v
.size() ) {
596 int cell_id
= std::stoi(v
[i
]); // jmdict id
597 cell_ids
.push_back(cell_id
);
598 cell_ids
.push_back(cell_id
);
599 cell_ids
.push_back(cell_id
);
602 for ( string
&elt
: utils::split_string( v
[i
+1], ",") )
603 if ( elt
.size() > 0 )
604 pos
.insert(utils::strip(elt
.c_str()));
605 d
.push_back( v
[i
+2] ); // reading
606 d
.push_back( v
[i
+3] ); // kanji
607 d
.push_back( v
[i
+4] ); // sense
608 listview_items_
.push_back( {cell_id
, pos
, v
[i
+2], v
[i
+3], v
[i
+4]} );
612 sprintf( buff
, "%d results", db_
->result_rows() );
613 ui_
->set_dic_results( buff
);
614 ui_
->set_listview(d
,cell_ids
);
618 void App::parse_dic_input ( const char *str
)
620 log_d(string("App::parse_dic_input: \"") +string(str
) + string("\""));
622 string stripped
= utils::strip(str
);
624 if ( stripped
.empty() )
627 const char *s
= stripped
.c_str();
630 if ( s
[0] != SENSE_SEARCH_CHAR
){
631 DictionaryInputParser p
;
635 int res
= ui_
->choice(
636 "Too broad search.\n"\
637 "Your computer may become unresponsible for a long time.\n"\
642 if ( res
== 1 ) // Cancel
646 // append * at the end of the simple string (just text and nothing else)
647 if ( !strchr(s
,'*') && !strchr(s
,'?') && !strchr(s
,'[') && !strchr(s
,'{')
653 if ( s
[0] == SENSE_SEARCH_CHAR
){
655 << "group_concat(pos) as pos,"
656 << q_reading("d_sense.did")
657 << q_kanji("d_sense.did")
659 << " from d_sense where gloss glob '*" << stripped
.substr(1) << "*'"
662 else if ( rmn_
->contains_kanji( qq
.c_str() ) ) {
663 q
<< "select d_kanji.did as did,"
664 << "(select group_concat(pos) from d_sense where d_sense.did = d_kanji.did) as pos,"
666 << q_reading("d_kanji.did")
667 << q_sense("d_kanji.did")
668 << "from d_kanji where "
669 << "kanji glob '" << rmn_
->romaji_to_hiragana(qq
.c_str()) << "' "
670 << " or kanji glob '" << rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
671 << " group by did order by d_kanji.freq desc, d_kanji.kanji asc";
674 q
<< "select d_reading.did as did,"
675 << "(select group_concat(pos) from d_sense where d_sense.did = d_reading.did) as pos,"
677 << q_kanji("d_reading.did")
678 << q_sense("d_reading.did")
679 << "from d_reading where "
680 << "reading glob '" << rmn_
->romaji_to_hiragana(qq
.c_str())
681 << "' or reading glob '"<< rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
682 << " group by did order by d_reading.freq desc, d_reading.reading asc";
684 set_listview(query(q
.str().c_str()));
688 void App::cb_examples ()
690 log_d("cb_examples():" + std::to_string(ui_
->dicview_selected_rowid()));
691 DicWord w
= db_get_word( ui_
->dicview_selected_rowid() );
692 if ( w
.k_ele().empty() )
695 // returns: japanese sentence, english sentence, string to be highlighted
697 << " (select text from sentences where id=sid) as jp,"
698 << " (select text from sentences where id=mid) as en,"
701 << " from indices where headword='" << w
.k_ele()[0].kanji()
702 << "' order by good_example desc;";
703 vector
<string
> res
= query(q
.str().c_str());
704 std::stringstream ss
;
705 for ( size_t i
=0; i
<res
.size(); i
+=4 ){
706 bool good_example
= std::stoi(res
[i
+2]);
707 ss
<< " <br><font face=\"symbol\"";
709 ss
<< " size=\"5\" color=\"blue\"";
710 ss
<< ">" << res
[i
] << "</font><br>";
711 if ( !res
[i
+1].empty() )
712 ss
<< res
[i
+1] << "<br>";
716 ui_
->show_html(ss
.str());
720 DicWord
App::db_get_word ( int id
)
723 std::stringstream ss
;
726 ss
<< "select kid,kanji,inf,freq from d_kanji where did=" << id
<<";";
727 vector
<string
> res
= query( ss
.str().c_str() );
728 for ( size_t i
=0; i
<res
.size(); i
+=4 ){
729 int kid
= std::stoi(res
[i
]);
730 string kanji
= res
[i
+1];
731 vector
<string
> inf
= utils::split_string(res
[i
+2],SEPARATOR_SQL
);
732 bool freq
= std::stoi(res
[i
+3]);
733 w
.k_ele( ElementKanji( kid
, kanji
, inf
, freq
) );
739 ss
<< "select rid,reading,inf,nokanji,freq from d_reading where did=" << id
<< ";";
740 res
= query( ss
.str().c_str() );
741 for ( size_t i
=0; i
<res
.size(); i
+=5 ){
742 int rid
= std::stoi(res
[i
]);
743 string reading
= res
[i
+1];
744 vector
<string
> inf
= utils::split_string(res
[i
+2],SEPARATOR_SQL
);
745 bool nokanji
= std::stoi(res
[i
+3]);
746 bool freq
= std::stoi(res
[i
+4]);
747 w
.r_ele( ElementReading( rid
, reading
, nokanji
, {/*restr*/}, inf
, freq
) );
753 ss
<< "select sid,gloss,xref,ant,inf,pos,field,misc,dial from d_sense where did=" << id
<< ";";
754 res
= query( ss
.str().c_str() );
755 for ( size_t i
=0; i
<res
.size(); i
+=9 ){
756 int sid
= std::stoi(res
[i
]);
757 vector
<string
> gloss
= utils::split_string(res
[i
+1],SEPARATOR_SQL
);
758 vector
<string
> xref
= utils::split_string(res
[i
+2],SEPARATOR_SQL
);
759 vector
<string
> ant
= utils::split_string(res
[i
+3],SEPARATOR_SQL
);
760 vector
<string
> inf
= utils::split_string(res
[i
+4],SEPARATOR_SQL
);
761 vector
<string
> pos
= utils::split_string(res
[i
+5],SEPARATOR_SQL
);
762 vector
<string
> field
= utils::split_string(res
[i
+6],SEPARATOR_SQL
);
763 vector
<string
> misc
= utils::split_string(res
[i
+7],SEPARATOR_SQL
);
764 vector
<string
> dial
= utils::split_string(res
[i
+8],SEPARATOR_SQL
);
765 w
.s_ele( ElementSense( sid
, gloss
, {/*stagk*/}, {/*stagr*/}, pos
, xref
,
766 ant
, field
, misc
, dial
, inf
) );
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],SEPARATOR_SQL
) );
791 kk
.kunyomi( utils::split_string(res
[9],SEPARATOR_SQL
) );
792 kk
.nanori( utils::split_string(res
[10],SEPARATOR_SQL
) );
793 kk
.meaning( utils::split_string(res
[11],SEPARATOR_SQL
) );
794 kk
.flags( utils::split_string(res
[12],SEPARATOR_SQL
) );
795 kk
.components( res
[13] );
797 string qq
= "select skip1,skip2,skip3,misclass from k_skip where kanji='"
799 vector
<string
> res2
= query(qq
.c_str());
800 if ( res2
.size() % 4 != 0 ){
801 std::stringstream ss
;
802 ss
<< "Wrong SKIP count. Kanji: " << kanji
803 << "Query result size: " << res2
.size()
804 << " (should be 4,8 or 12). SKIP not loaded.";
808 for ( size_t i
=0; i
< res2
.size(); i
+=4 )
809 kk
.skip( res2
[i
], res2
[i
+1], res2
[i
+2], res2
[i
+3] );
814 void App::cb_filter_listview ()
816 vector
<string
> pos
= ui_
->listview_filters();
817 log_d("App::cb_filter_listview(): pos: " + utils::to_string(pos
));
818 bool filter_expr
= ( utils::is_in( pos
, string("expr") ) );
819 bool filter_noun
= ( utils::is_in( pos
, string("noun") ) );
820 bool filter_verb
= ( utils::is_in( pos
, string("verb") ) );
821 bool filter_adj
= ( utils::is_in( pos
, string("adj") ) );
825 for ( auto &elt
: listview_items_
){
826 auto start
= elt
.pos
.begin();
827 auto end
= elt
.pos
.end();
828 if ( filter_expr
&& std::find( start
, end
, "exp") == end
)
830 if ( filter_noun
&& std::find( start
, end
, "n" ) == end
)
832 if ( filter_adj
&& std::find_if( start
, end
,
833 [](const string
&s
){ return strncmp(s
.c_str(),"adj",3)==0;} ) == end
)
835 if ( filter_verb
&& std::find_if( start
, end
,
836 [](const string
&s
){ return strncmp(s
.c_str(),"v",1)==0;} ) == end
)
838 ids
.push_back( elt
.did
);
839 ids
.push_back( elt
.did
);
840 ids
.push_back( elt
.did
);
841 data
.push_back ( elt
.reading
);
842 data
.push_back ( elt
.kanji
);
843 data
.push_back ( elt
.sense
);
846 ui_
->set_listview( data
, ids
);
847 std::stringstream ss
;
848 ss
<< n
<< " results";
849 if ( listview_items_
.size() != n
)
850 ss
<< " (" << listview_items_
.size()-n
<< " hidden)";
852 ui_
->set_dic_results( ss
.str() );
856 void App::cb_dicview_rightclick ( int did
)
858 string q
= "select group_concat(kanji,'') from d_kanji where did="
859 + std::to_string(did
);
860 vector
<string
> res
= query( q
.c_str() );
862 for ( string
&c
: utils::str_to_chars(res
[0].c_str()) )
863 if ( App::get()->rmn()->is_kanji(c
.c_str()) )
865 ui_
->dicview_menu( did
, vector
<string
>(kanji
.begin(),kanji
.end()), false, true );
869 void App::apply_config ()
871 ui_
->font_base_size( get_config
<int>("font/base_size"));
872 logger_
.loglevel( get_config("log/level") );
878 void App::cb_manage_db ()
880 auto q
= query("select key, val from aoi");
882 std::map
<string
,string
> mm
;
883 for ( size_t i
=0; i
<q
.size(); i
+=2 )
886 auto *d
= ui_
->dlg_manage_db();
887 d
->main_version( mm
["db_version"]);
888 d
->main_created( mm
["db_created"]);
889 d
->main_ver_jmdict( mm
["jmdict_version"]);
890 d
->main_ver_kanjidic( mm
["kanjidic_version"]);
891 d
->main_ver_kradfile( mm
["kradfile_version"]);
892 d
->main_ver_tatoeba( mm
["tatoeba_version"]);
893 d
->user_checked_against("UNKNOWN");
894 d
->cb_download( scb_download_db
, (void*)this );
899 void App::load_config ()
901 log("Loading config...");
902 vector
<string
> res
= query("select key,val from config;");
903 for ( size_t i
=0; i
< res
.size(); i
+=2 ){
904 set_config( res
[i
], res
[i
+1] );
910 void App::save_config ( const std::map
<string
,aoi_config::Config::Value
> &newmap
)
912 log("Saving config...");
914 typedef std::pair
<string
,aoi_config::Config::Value
> cfg_pair
;
916 // merge new and old config
917 for ( const cfg_pair
&p
: newmap
)
918 set_config( p
.first
, p
.second
.val
);
921 // prepare SQL script
922 std::stringstream ss
;
923 ss
<< "BEGIN TRANSACTION;\n";
924 ss
<< "DROP TABLE IF EXISTS user.config;\n";
925 ss
<< "CREATE TABLE user.config (key TEXT, val TEXT);\n";
926 for ( const cfg_pair
&p
: get_config_map() )
927 ss
<< "INSERT INTO user.config (key,val) VALUES('"
928 << p
.first
<< "','" << p
.second
.val
<< "');\n";
929 ss
<< "END TRANSACTION;\n";
931 query(ss
.str().c_str());
933 // apply new fonts and colors
938 //////////////////////////////////////////////////////////////////////////
939 // DictionaryInputParser
941 set
<string
> DictionaryInputParser::intersection ( const string
&query
,
942 const set
<string
> ¤t
)
944 set
<string
> characters
;
945 // all characters in result
946 for ( string
&r
: App::get()->query(query
.c_str()) ) {
947 for ( string
&c
: utils::str_to_chars(r
.c_str()) ){
948 if ( App::get()->rmn()->is_kanji(c
.c_str()) )
949 characters
.insert(c
);
952 if ( current
.empty() )
955 vector
<string
> tmp(current
.size());
956 std::set_intersection(
957 current
.begin(), current
.end(),
958 characters
.begin(), characters
.end(),
962 newset
.insert( tmp
.begin(), tmp
.end() );
967 string
DictionaryInputParser::find_kanji ( const string
&s
, bool from_words
)
971 for ( string
&w
: utils::split_string(s
,",") ) {
973 if ( w
.size()==1 && utils::isint(w
.c_str()) ){
974 int i
= std::stoi(w
);
975 if ( i
> 0 && i
< 5 )
976 q
= "select group_concat(kanji,'') from k_skip where skip1=" + w
+ ";";
981 q
= "select group_concat("\
982 "(select group_concat(kanji,'') from d_kanji where d_reading.did=d_kanji.did)"\
983 ",'') from d_reading where reading='" + App::get()->rmn()->romaji_to_hiragana(w
)
988 q
= "select group_concat(kanji,'') from k_kanji where "\
989 "kunyomi glob '*" + App::get()->rmn()->romaji_to_hiragana(w
) +
990 "*' or onyomi glob '*" + App::get()->rmn()->romaji_to_katakana(w
) + "*'";
993 results
= intersection( q
, results
);
995 return utils::to_string(results
,"");
999 void DictionaryInputParser::check_warning ( const string
&s
, const string
&previous
)
1001 size_t warning_limit
= App::get()->get_config
<size_t>("dic/input_parser_warning");
1002 size_t n
= utils::str_to_chars(s
.c_str()).size();
1003 if ( warning_
|| n
<= warning_limit
)
1006 bool is_wildchar
= true;
1007 for ( string
&c
: utils::str_to_chars(previous
.c_str()) ){
1008 if ( c
!= "?" && c
!= "*" ){
1009 is_wildchar
= false;
1013 warning_
= is_wildchar
;
1017 string
DictionaryInputParser::parse ( const char *s
)
1027 while ( i
< strlen(s
) ){
1029 case '[': add(); type_
= WORD
; break;
1030 case '(': add(); type_
= KANJI
; break;
1032 case ')': add(); type_
= STRING
; break;
1040 std::stringstream output
;
1041 for ( size_t j
=0; j
< parts_
.size(); ++j
){
1043 switch ( p
.second
){
1046 string r
= find_kanji(p
.first
,true);
1047 check_warning( r
, (j
==0) ? "":parts_
[j
-1].first
);
1048 output
<< "[" << r
<< "]";
1053 string r
= find_kanji(p
.first
,false);
1054 check_warning( r
, (j
==0) ? "":parts_
[j
-1].first
);
1055 output
<< "[" << r
<< "]";
1063 string ret
= output
.str();
1064 utils::replace_all(ret
, "{", "[");
1065 utils::replace_all(ret
, "}", "]");