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 *LOG_FILE
= "aoi.log";
30 const char SENSE_SEARCH_CHAR
= ':';
32 App
* App::instance_
= nullptr;
37 logger_
.filename(LOG_FILE
);
38 logger_
.loglevel(Logger::MSG_DEBUG
);
40 cfg_
= new aoi_config::Config();
41 ui_
= new aoi_ui::GUI(this);
42 rmn_
= new Romanization();
44 ui_
->progress(0,"Opening database...");
47 db_
= new SQLite3::SQLite3( get_config("db/file_main").c_str() );
49 catch (SQLite3::CantOpenDatabase
&e
){
50 log_e( "Aoi: Can't open database '" + string(e
.what()) + "'.");
54 ss
<< "ATTACH DATABASE '" << get_config("db/file_user") << "'as user;";
55 query(ss
.str().c_str());
57 ui_
->progress( 30, "Checking tables..." );
59 ui_
->progress( 60, "Checking indexes..." );
66 // ui_->progress(90, "DEBUG query..." );
67 // parse_dic_input("ana*");
69 // ui_->cb_toggle_group(nullptr);
72 ui_
->progress( 98, "Initializing fonts..." );
73 ui_
->fontname_kanji( get_config("font/kanji") );
74 ui_
->help_file( get_config("sources/help_index") );
79 auto q
= query("select val from aoi where key='jmdict_version'");
80 string jmdict_version
= (q
.empty()) ? "NONE":q
[0];
81 q
= query("select val from aoi where key='kanjidic_version'");
82 string kanjidic_version
= q
.empty() ? "NONE":q
[0];
83 if ( jmdict_version
== "NONE" || kanjidic_version
== "NONE" )
88 logger_
.loglevel(Logger::MSG_DEBUG
);
104 void App::init_dicview ()
106 // initialize textstyles
107 TextStyle
style_default(FL_HELVETICA
,1.2);
108 TextStyle
style_reading(aoi_ui::FONT_KANJI
,1.5);
109 TextStyle style_reading_freq
= style_reading
;
110 style_reading_freq
.color
= get_color("frequent");
111 TextStyle
style_kanji(aoi_ui::FONT_KANJI
,1.7);
112 TextStyle style_kanji_freq
= style_kanji
;
113 style_kanji_freq
.color
= get_color("frequent");
114 TextStyle
style_inf(FL_HELVETICA_ITALIC
,0.8,get_color("pos"));
115 style_inf
.offset_y
= 3;
116 TextStyle style_pos
= style_inf
;
117 TextStyle style_misc
= style_pos
;
118 style_misc
.color
= get_color("misc");
119 TextStyle style_field
= style_pos
;
120 style_field
.color
= get_color("field");
121 TextStyle style_dial
= style_pos
;
123 // register TextStyles
124 ui_
->register_tag_dicview( "default", style_default
);
125 ui_
->register_tag_dicview( "reading", style_reading
);
126 ui_
->register_tag_dicview( "reading_freq", style_reading_freq
);
127 ui_
->register_tag_dicview( "kanji", style_kanji
);
128 ui_
->register_tag_dicview( "kanji_freq", style_kanji_freq
);
129 ui_
->register_tag_dicview( "kinf", style_inf
);
130 ui_
->register_tag_dicview( "rinf", style_inf
);
131 ui_
->register_tag_dicview( "pos", style_pos
);
132 ui_
->register_tag_dicview( "misc", style_misc
);
133 ui_
->register_tag_dicview( "field", style_field
);
134 ui_
->register_tag_dicview( "dial", style_dial
);
138 vector
<string
> App::query ( const char *q
, bool log_query
, bool replace_separator
)
141 log_e("App::query(): Database does not exist.");
145 vector
<string
> result
;
149 result
= db_
->query(q
);
151 catch (SQLite3::DatabaseError
&e
){
152 log_e("App: DatabaseError:: " + string(e
.what()) + string("\nQuery: ")
153 + string(e
.query()) );
154 ui_
->cursor_default();
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
, SEPARATOR_SQL
, ", ");
161 ui_
->cursor_default();
166 void App::cb_set_components ()
168 if ( curr_components_
.empty() )
171 using aoi_ui::ComponentView
;
174 vector
<string
> included
= ui_
->components_include();
175 vector
<string
> excluded
= ui_
->components_exclude();
177 vector
<ComponentView::Cell
> v
;
178 for ( string
&s
: included
)
179 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_1
) );
180 for ( string
&s
: excluded
)
181 v
.push_back( ComponentView::Cell(s
,ComponentView::CELL_SELECTED_2
) );
183 // sort by occurences
184 if ( !ui_
->sort_components_by_strokes() ){
185 for ( auto mi
= curr_components_
.rbegin(); mi
!=curr_components_
.rend(); ++mi
){
186 if ( !utils::is_in( included
, mi
->second
)
187 && !utils::is_in( excluded
, mi
->second
) )
188 v
.push_back( ComponentView::Cell(mi
->second
) );
193 vector
<string
> comps_by_strokes
;
194 for ( auto mi
: curr_components_
){
195 if ( mi
.first
< get_config
<int>("knj/min_compo_count") )
197 if ( !utils::is_in( included
, mi
.second
)
198 && !utils::is_in( excluded
, mi
.second
) )
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");
234 for ( size_t i
=0; i
<res
.size(); i
=i
+2 ){
236 if ( res
[i
+1].empty() )
239 strokes
= std::stoi(res
[i
+1]);
240 components_
[res
[i
]] = strokes
;
243 log_d(std::to_string(skipped
)+" components without strokes data.");
247 std::pair
<int,int> strokes
= utils::parse_range( utils::strip( ui_
->strokes() ) );
249 std::pair
<int,int> jlpt
= utils::parse_range( utils::strip( ui_
->jlpt() ) );
251 std::pair
<int,int> grade
= utils::parse_range( utils::strip( ui_
->grade() ) );
254 vector
<string
> skip
= utils::split_string( ui_
->skip() );
255 std::stringstream sskip
;
256 if ( skip
.size() > 0 ){
257 string skip1
= utils::strip(skip
[0].c_str());
258 sskip
<< " S.skip1=" << skip1
;
260 if ( skip
.size() > 1 ){
261 std::pair
<int,int> skip2
= utils::parse_range( utils::strip(skip
[1].c_str()) );
262 sskip
<< " and S.skip2>=" << skip2
.first
<< " and S.skip2<=" << skip2
.second
;
264 if ( skip
.size() > 2 ){
265 std::pair
<int,int> skip3
= utils::parse_range( utils::strip(skip
[2].c_str()) );
266 sskip
<< " and S.skip3>=" << skip3
.first
<< " and S.skip3<=" << skip3
.second
;
268 if ( !sskip
.str().empty() )
273 switch ( ui_
->sort_mode() ){
275 order_by
= "(case freq when 0 then 9999 else freq end) asc";
278 order_by
= "(case freq when 0 then 9999 else freq end) desc";
280 case SORT_STROKES_ASC
:
281 order_by
= "strokes asc";
283 case SORT_STROKES_DESC
:
284 order_by
= "strokes desc";
288 vector
<string
> components_include
= ui_
->components_include();
289 vector
<string
> components_exclude
= ui_
->components_exclude();
290 std::stringstream comps
;
291 for ( auto c
: components_include
)
292 comps
<< " and components like '%" << c
<< "%'";
293 for ( auto c
: components_exclude
)
294 comps
<< " and components not like '%" << c
<< "%'";
295 printf("%s\n",comps
.str().c_str());
298 if ( get_config
<bool>("knj/jis208_only") )
299 jis208
= " flags glob '*jis208*' and ";
302 std::stringstream ss
;
303 ss
<< "select distinct "
304 << "K.kanji,freq,components,flags "
305 << " from k_kanji as K"
306 << (sskip
.str().empty() ? " where ":", k_skip as S where K.kanji=S.kanji and")
309 << " strokes>=" << strokes
.first
<< " and strokes<=" << strokes
.second
310 << " and jlpt>=" << jlpt
.first
<< " and jlpt<=" << jlpt
.second
311 << " and grade>=" << grade
.first
<< " and grade<=" << grade
.second
313 << " order by " << order_by
;
317 vector
<string
> q
= query( ss
.str().c_str(), true, false );
318 vector
<aoi_ui::KanjiView::Cell
> data
;
319 vector
<string
> components
;
321 for ( size_t i
=0; i
<q
.size(); i
+=4 ){
323 int freq
= std::stoi(q
[i
+1]);
325 aoi_ui::KanjiView::Cell(
327 (freq
>0)? get_color("frequent"):-1
330 components
.push_back(q
[i
+2]);
331 for ( string
&s
: utils::split_string( q
[i
+3], SEPARATOR_SQL
) )
335 log_d("Groups: " + utils::to_string(flags
));
337 ui_
->set_kanjiview( data
);
339 sprintf( b
, "%d results", db_
->result_rows() );
340 ui_
->set_kanji_results( b
);
342 utils::Histogram
<string
> histogram
;
343 for ( string
&s
: components
)
344 histogram
.add( utils::str_to_chars(s
.c_str()) );
345 curr_components_
= histogram
.sorted();
351 void App::alert ( const string
&msg
, const string
&desc
)
353 std::stringstream ss
;
356 ss
<< "\n\nDetails:\n" << desc
;
358 ui_
->alert(ss
.str());
362 int App::run ( int argc
, char **argv
)
364 return ui_
->run(argc
,argv
);
368 void App::check_tables ()
370 for ( auto &dbit
: aoi_config::db_tables
) {
371 // no need for vacuum here, it will be done by check_indexes()
372 log("Checking tables in database: " + string(dbit
.first
));
374 q
<< "SELECT name FROM " << dbit
.first
<< "." << "sqlite_master WHERE type='table'";
375 vector
<string
> existing
= query(q
.str().c_str());
376 // CREATE TABLE name ( column1 TYPE, column2 TYPE, ... )
377 for ( auto &table
: dbit
.second
){
378 if ( utils::is_in(existing
, string(table
.first
)) )
380 log("Creating table " + string(table
.first
));
382 for ( auto &column
: table
.second
){
383 std::stringstream sstr
;
384 sstr
<< column
.name
<< " " << column
.type
;
385 v
.push_back(sstr
.str());
387 std::stringstream ss
;
388 ss
<< "CREATE TABLE " << dbit
.first
<< "." << table
.first
389 << " (" << to_string(v
,",") << ");\n";
390 query(ss
.str().c_str());
397 void App::check_indexes ()
399 bool do_vacuum
= false;
400 log("Checking indexes...");
401 for ( auto &dbit
: aoi_config::db_tables
) {
403 q
<< "SELECT name FROM " << dbit
.first
<< "." << "sqlite_master WHERE type='index'";
404 vector
<string
> existing
= query(q
.str().c_str());
405 // CREATE INDEX idx_table_column ON table ( column ASC )
406 for ( auto &mi
: dbit
.second
){ // tables
407 for ( auto &c
: mi
.second
){ // columns
408 std::stringstream idx_name
;
409 idx_name
<< "idx_" << mi
.first
<< "_" << c
.name
;
410 if ( c
.index
&& !utils::is_in( existing
, idx_name
.str() ) ){
411 log(string("Creating index ") + idx_name
.str());
412 std::stringstream ss
;
413 ss
<< "CREATE INDEX " << idx_name
.str() << " ON " << mi
.first
414 << "(" << c
.name
<< " " << c
.sort
<< ")";
415 query(ss
.str().c_str());
427 void App::cb_dic_input ()
429 parse_dic_input( ui_
->get_dic_input() );
430 ui_
->reset_filters();
434 void App::on_dic_selected ( int id
)
436 log_d("App::on_dic_selected()");
440 void App::cb_download_db ()
442 log_d("Download DB");
443 string path
= ui_
->download_dialog( get_config("sources/url_database") );
444 if ( !path
.empty() && !utils::file_exists( path
) ){
445 log_w("App::cb_download_db(): Not a file: "+path
);
451 void App::cb_edit_word ()
453 std::stringstream ss
;
454 int id
= ui_
->dicview_selected_rowid();
455 ss
<< "App::edit_word( " << id
<< " )";
457 ui_
->edit_word( db_get_word(id
));
461 void App::cb_popup_kanji ( const string
&kanji
)
463 std::stringstream ss
;
464 ss
<< "App::on_kanji_clicked()" << kanji
;
465 Kanji k
= db_get_kanji(kanji
);
466 ui_
->popup_kanji( k
);
467 // XXX: this should be somewhere else (it is ilogical here)
468 ui_
->highlight_components( k
.components() );
473 void App::set_listview ( const vector
<string
> &v
)
475 if ( !listview_items_
.empty() ) listview_items_
.clear();
477 vector
<int> cell_ids
;
479 while ( i
< v
.size() ) {
480 int cell_id
= std::stoi(v
[i
]); // jmdict id
481 cell_ids
.push_back(cell_id
);
482 cell_ids
.push_back(cell_id
);
483 cell_ids
.push_back(cell_id
);
486 for ( string
&elt
: utils::split_string( v
[i
+1], ",") )
487 if ( elt
.size() > 0 )
488 pos
.insert(utils::strip(elt
.c_str()));
489 d
.push_back( v
[i
+2] ); // reading
490 d
.push_back( v
[i
+3] ); // kanji
491 d
.push_back( v
[i
+4] ); // sense
492 listview_items_
.push_back( {cell_id
, pos
, v
[i
+2], v
[i
+3], v
[i
+4]} );
496 sprintf( buff
, "%d results", db_
->result_rows() );
497 ui_
->set_dic_results( buff
);
498 ui_
->set_listview(d
,cell_ids
);
502 void App::parse_dic_input ( const char *str
)
504 log_d(string("App::parse_dic_input: \"") +string(str
) + string("\""));
506 string stripped
= utils::strip(str
);
508 if ( stripped
.empty() )
511 const char *s
= stripped
.c_str();
514 if ( s
[0] != SENSE_SEARCH_CHAR
){
515 DictionaryInputParser p
;
520 "Too broad search.\n"\
521 "Your computer may become unresponsible for a long time.\n"\
527 if ( res
== 1 ) // Cancel
531 if ( !strchr(s
,'*') && !strchr(s
,'?') && !strchr(s
,'[') && !strchr(s
,'{')
537 if ( s
[0] == SENSE_SEARCH_CHAR
){
539 << "group_concat(pos) as pos,"
540 << q_reading("d_sense.did")
541 << q_kanji("d_sense.did")
543 << " from d_sense where gloss glob '*" << stripped
.substr(1) << "*'"
546 else if ( rmn_
->contains_kanji( qq
.c_str() ) ) {
547 q
<< "select d_kanji.did as did,"
548 << "(select group_concat(pos) from d_sense where d_sense.did = d_kanji.did) as pos,"
550 << q_reading("d_kanji.did")
551 << q_sense("d_kanji.did")
552 << "from d_kanji where "
553 << "kanji glob '" << rmn_
->romaji_to_hiragana(qq
.c_str()) << "' "
554 << " or kanji glob '" << rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
555 << " group by did order by d_kanji.freq desc, d_kanji.kanji asc";
558 q
<< "select d_reading.did as did,"
559 << "(select group_concat(pos) from d_sense where d_sense.did = d_reading.did) as pos,"
561 << q_kanji("d_reading.did")
562 << q_sense("d_reading.did")
563 << "from d_reading where "
564 << "reading glob '" << rmn_
->romaji_to_hiragana(qq
.c_str())
565 << "' or reading glob '"<< rmn_
->romaji_to_katakana(qq
.c_str()) << "' "
566 << " group by did order by d_reading.freq desc, d_reading.reading asc";
568 set_listview(query(q
.str().c_str()));
572 void App::cb_examples ()
574 log_d("cb_examples():" + std::to_string(ui_
->dicview_selected_rowid()));
575 DicWord w
= db_get_word( ui_
->dicview_selected_rowid() );
576 if ( w
.k_ele().empty() )
579 // returns: japanese sentece, english sentence, string to be highlighted
581 << " (select text from sentences where id=sid) as jp,"
582 << " (select text from sentences where id=mid) as en,"
585 << " from indices where headword='" << w
.k_ele()[0].kanji()
586 << "' order by good_example desc;";
587 vector
<string
> res
= query(q
.str().c_str());
588 std::stringstream ss
;
589 for ( size_t i
=0; i
<res
.size(); i
+=4 ){
590 bool good_example
= std::stoi(res
[i
+2]);
591 ss
<< "<font face=\"symbol\"";
593 ss
<< " color=\"blue\"";
594 ss
<< ">" << res
[i
] << "</font><br>";
595 if ( !res
[i
+1].empty() )
596 ss
<< res
[i
+1] << "<br>";
600 ui_
->show_html(ss
.str());
604 DicWord
App::db_get_word ( int id
)
607 std::stringstream ss
;
610 ss
<< "select kid,kanji,inf,freq from d_kanji where did=" << id
<<";";
611 vector
<string
> res
= query( ss
.str().c_str() );
612 for ( size_t i
=0; i
<res
.size(); i
+=4 ){
613 int kid
= std::stoi(res
[i
]);
614 string kanji
= res
[i
+1];
615 vector
<string
> inf
= utils::split_string(res
[i
+2],SEPARATOR_SQL
);
616 bool freq
= std::stoi(res
[i
+3]);
617 w
.k_ele( ElementKanji( kid
, kanji
, inf
, freq
) );
623 ss
<< "select rid,reading,inf,nokanji,freq from d_reading where did=" << id
<< ";";
624 res
= query( ss
.str().c_str() );
625 for ( size_t i
=0; i
<res
.size(); i
+=5 ){
626 int rid
= std::stoi(res
[i
]);
627 string reading
= res
[i
+1];
628 vector
<string
> inf
= utils::split_string(res
[i
+2],SEPARATOR_SQL
);
629 bool nokanji
= std::stoi(res
[i
+3]);
630 bool freq
= std::stoi(res
[i
+4]);
631 w
.r_ele( ElementReading( rid
, reading
, nokanji
, {/*restr*/}, inf
, freq
) );
637 ss
<< "select sid,gloss,xref,ant,inf,pos,field,misc,dial from d_sense where did=" << id
<< ";";
638 res
= query( ss
.str().c_str() );
639 for ( size_t i
=0; i
<res
.size(); i
+=9 ){
640 int sid
= std::stoi(res
[i
]);
641 vector
<string
> gloss
= utils::split_string(res
[i
+1],SEPARATOR_SQL
);
642 vector
<string
> xref
= utils::split_string(res
[i
+2],SEPARATOR_SQL
);
643 vector
<string
> ant
= utils::split_string(res
[i
+3],SEPARATOR_SQL
);
644 vector
<string
> inf
= utils::split_string(res
[i
+4],SEPARATOR_SQL
);
645 vector
<string
> pos
= utils::split_string(res
[i
+5],SEPARATOR_SQL
);
646 vector
<string
> field
= utils::split_string(res
[i
+6],SEPARATOR_SQL
);
647 vector
<string
> misc
= utils::split_string(res
[i
+7],SEPARATOR_SQL
);
648 vector
<string
> dial
= utils::split_string(res
[i
+8],SEPARATOR_SQL
);
649 w
.s_ele( ElementSense( sid
, gloss
, {/*stagk*/}, {/*stagr*/}, pos
, xref
,
650 ant
, field
, misc
, dial
, inf
) );
657 Kanji
App::db_get_kanji ( const string
&kanji
)
659 log("App::db_get_kanji()");
661 q
<< "select kanji,strokes,ucs, rad_classic, rad_nelson, "
662 << "jlpt, grade, freq, onyomi, kunyomi, nanori, meaning,flags,components "
663 << "from k_kanji where kanji='" << kanji
<< "';";
665 vector
<string
> res
= query(q
.str().c_str());
667 kk
.strokes(std::stoi(res
[1]));
669 kk
.rad_classic(std::stoi(res
[3]));
670 kk
.rad_nelson( (res
[4].empty()) ? -1:std::stoi(res
[4]));
671 kk
.jlpt(std::stoi(res
[5]));
672 kk
.grade(std::stoi(res
[6]));
673 kk
.freq(std::stoi(res
[7]));
674 kk
.onyomi( utils::split_string(res
[8],SEPARATOR_SQL
) );
675 kk
.kunyomi( utils::split_string(res
[9],SEPARATOR_SQL
) );
676 kk
.nanori( utils::split_string(res
[10],SEPARATOR_SQL
) );
677 kk
.meaning( utils::split_string(res
[11],SEPARATOR_SQL
) );
678 kk
.flags( utils::split_string(res
[12],SEPARATOR_SQL
) );
679 kk
.components( res
[13] );
681 string qq
= "select skip1,skip2,skip3,misclass from k_skip where kanji='"+kanji
+"';";
682 vector
<string
> res2
= query(qq
.c_str());
683 if ( res2
.size() % 4 != 0 ){
684 std::stringstream ss
;
685 ss
<< "Wrong SKIP count. Kanji: " << kanji
686 << "Query result size: " << res2
.size()
687 << " (should be 4,8 or 12). SKIP not loaded.";
691 for ( size_t i
=0; i
< res2
.size(); i
+=4 )
692 kk
.skip( res2
[i
], res2
[i
+1], res2
[i
+2], res2
[i
+3] );
697 void App::cb_filter_listview ()
699 vector
<string
> pos
= ui_
->listview_filters();
700 log_d("App::cb_filter_listview(): pos: " + utils::to_string(pos
));
701 bool filter_expr
= ( utils::is_in( pos
, string("expr") ) );
702 bool filter_noun
= ( utils::is_in( pos
, string("noun") ) );
703 bool filter_verb
= ( utils::is_in( pos
, string("verb") ) );
704 bool filter_adj
= ( utils::is_in( pos
, string("adj") ) );
708 for ( auto &elt
: listview_items_
){
709 auto start
= elt
.pos
.begin();
710 auto end
= elt
.pos
.end();
711 if ( filter_expr
&& std::find( start
, end
, "exp") == end
)
713 if ( filter_noun
&& std::find( start
, end
, "n" ) == end
)
715 if ( filter_adj
&& std::find_if( start
, end
,
716 [](const string
&s
){ return strncmp(s
.c_str(),"adj",3)==0;} ) == end
)
718 if ( filter_verb
&& std::find_if( start
, end
,
719 [](const string
&s
){ return strncmp(s
.c_str(),"v",1)==0;} ) == end
)
721 ids
.push_back( elt
.did
);
722 ids
.push_back( elt
.did
);
723 ids
.push_back( elt
.did
);
724 data
.push_back ( elt
.reading
);
725 data
.push_back ( elt
.kanji
);
726 data
.push_back ( elt
.sense
);
729 ui_
->set_listview( data
, ids
);
730 std::stringstream ss
;
731 ss
<< n
<< " results";
732 if ( listview_items_
.size() != n
)
733 ss
<< " (" << listview_items_
.size()-n
<< " hidden)";
735 ui_
->set_dic_results( ss
.str() );
739 void App::cb_dicview_rightclick ( int did
)
741 string q
= "select group_concat(kanji,'') from d_kanji where did="+std::to_string(did
);
742 vector
<string
> res
= query( q
.c_str() );
744 for ( string
&c
: utils::str_to_chars(res
[0].c_str()) )
745 if ( App::get()->rmn()->is_kanji(c
.c_str()) )
747 ui_
->dicview_menu( did
, vector
<string
>(kanji
.begin(),kanji
.end()), false, true );
751 void App::apply_config ()
753 ui_
->font_base_size( get_config
<int>("font/base_size"));
754 logger_
.loglevel( get_config("log/level") );
760 void App::cb_manage_db ()
762 auto q
= query("select key, val from aoi");
764 std::map
<string
,string
> mm
;
765 for ( size_t i
=0; i
<q
.size(); i
+=2 )
768 auto *d
= ui_
->dlg_manage_db();
769 d
->main_version( mm
["db_version"]);
770 d
->main_created( mm
["db_created"]);
771 d
->main_ver_jmdict( mm
["jmdict_version"]);
772 d
->main_ver_kanjidic( mm
["kanjidic_version"]);
773 d
->main_ver_kradfile( mm
["kradfile_version"]);
774 d
->main_ver_tatoeba( mm
["tatoeba_version"]);
775 d
->user_checked_against("WHO KNOWS");
776 d
->cb_download( scb_download_db
, (void*)this );
781 void App::load_config ()
783 log("Loading config...");
784 vector
<string
> res
= query("select key,val from config;");
785 for ( size_t i
=0; i
< res
.size(); i
+=2 ){
786 set_config( res
[i
], res
[i
+1] );
792 void App::save_config ( const std::map
<string
,aoi_config::Config::Value
> &newmap
)
794 log("Saving config...");
796 typedef std::pair
<string
,aoi_config::Config::Value
> cfg_pair
;
798 // merge new and old config
799 for ( const cfg_pair
&p
: newmap
)
800 set_config( p
.first
, p
.second
.val
);
803 // prepare SQL script
804 std::stringstream ss
;
805 ss
<< "BEGIN TRANSACTION;\n";
806 ss
<< "DROP TABLE IF EXISTS user.config;\n";
807 ss
<< "CREATE TABLE user.config (key TEXT, val TEXT);\n";
808 for ( const cfg_pair
&p
: get_config_map() )
809 ss
<< "INSERT INTO user.config (key,val) VALUES('"
810 << p
.first
<< "','" << p
.second
.val
<< "');\n";
811 ss
<< "END TRANSACTION;\n";
813 query(ss
.str().c_str());
815 // apply new fonts and colors
820 // check keys in oldconfig not present in newmap