LICENCE,README and ROADMAP added
[aoi.git] / src / aoi.hxx
blobb93c46c57b8a2a7457283e0605f4ae18fd72cab6
1 /*
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/>.
17 #ifndef _AOI_HXX
18 #define _AOI_HXX
21 Pouzivat vyhradne query()
22 Veskere SQL jen v tomto souboru
24 pozor na funkce vyzadujici ui_
25 mely by nejdrive testovat nullptr
29 #include <cmath>
30 #include <vector>
31 #include <string>
32 #include <set>
34 #include "logger.hxx"
35 #include "datatypes.hxx"
36 #include "utils.hxx"
37 #include "romanization.hxx"
38 #include "config.hxx"
39 #include "sqlite3.hxx"
40 #include "gui.hxx"
41 #include "parsers.hxx"
42 #include "gui_dicview.hxx"
43 #include "gui_kanjiview.hxx"
46 using std::vector;
47 using std::string;
48 using std::set;
50 namespace aoi_ui {
51 class GUI;
54 namespace aoi {
56 /*!
57 * \sa cb_kanji_search
59 enum KANJI_SORT_MODE {
60 SORT_FREQ_ASC = 0,
61 SORT_FREQ_DESC = 1,
62 SORT_STROKES_ASC = 2,
63 SORT_STROKES_DESC = 3
67 /*!
68 * One item (line) in listview (dictionary results).
69 * \sa cb_filter_listview()
71 struct DicViewItem {
72 int did;
73 set<string> pos;
74 // vector<string> flags;
75 string reading;
76 string kanji;
77 string sense;
78 DicViewItem( int id, const set<string> &p, const string &r,
79 const string &k, const string &s )
80 : did(id), pos(p), reading(r), kanji(k), sense(s) {};
86 /*!
87 * Main class of the program. Singleton. Manages database, logger, config and UI.
88 * Provides functions for UI's callbacks.
90 class App
92 private:
93 class SQLite3::SQLite3 *db_;
94 class Romanization *rmn_;
95 class aoi_ui::GUI *ui_;
96 class aoi_config::Config *cfg_;
97 class Logger logger_;
98 vector<DicViewItem> listview_items_;
99 map<string,int> components_; // component: stroke_count
100 std::multimap<int,string> curr_components_; // frequency:component
101 std::set<string> config_overrides_; // values set from commandline, cant be changed in program
102 // singleton
103 App();
104 App& operator=(App const&) = delete;
105 static App* instance_;
107 /*!
108 * Returns part of the SQL query for DicView.
109 * \param id database id (did) of the searched word
110 * \sa parse_dic_input()
112 inline string q_reading ( const string &id="")
114 string s1 = id.empty() ? "":"(select ";
115 string s2 = id.empty() ? "":(" from d_reading where d_reading.did="+id+") ");
116 return s1 + "group_concat("\
117 "(case freq when 1 then '<reading_freq>'||reading||'</reading_freq>' "\
118 "else '<reading>'||reading||'</reading>' end)||"\
119 "'<rinf>'||inf||'</rinf>',"\
120 "\"<br/>\")" + s2 + "as reading, ";
123 /*!
124 * Returns part of the SQL query for DicView.
125 * \param id database id (did) of the searched word
126 * \sa parse_dic_input()
128 inline string q_kanji ( const string &id="" )
130 string s1 = id.empty() ? "":"(select ";
131 string s2 = id.empty() ? "":(" from d_kanji where d_kanji.did="+id+") ");
132 return s1 + "group_concat("\
133 "(case freq when 1 then '<kanji_freq>'||kanji||'</kanji_freq>' "\
134 "else '<kanji>'||kanji||'</kanji>' end)||"\
135 "'<kinf>'||inf||'</kinf>',"\
136 "\"<br/>\")" + s2 + "as kanji, ";
139 /*!
140 * Returns part of the SQL query for DicView.
141 * \param id database id (did) of the searched word
142 * \sa parse_dic_input()
144 inline string q_sense ( const string &id="" )
146 string s1 = id.empty() ? "":"(select ";
147 string s2 = id.empty() ? "":(" from d_sense where d_sense.did="+id+") ");
148 return s1 + "group_concat("\
149 "(case pos when '' then '' else '<pos>'||pos||'</pos>' end)||"\
150 "(case misc when '' then '' else '<misc>'||misc||'</misc>' end)||"\
151 "gloss||"\
152 "(case field when '' then '' else '<field>'||field||'</field>' end)||"\
153 "(case dial when '' then '' else '<dial>'||dial||'</dial>' end),"\
154 "'<sep/>') " + s2 + " as sense ";
157 public:
158 ~App();
160 //! Returns instance of App.
161 static inline App *get (){
162 if ( !instance_)
163 instance_ = new App();
164 return instance_;
167 //! Returns pointer to Romanization.
168 inline Romanization *rmn () const { return rmn_; };
170 //! Log message.
171 inline void log ( const string &s ) { logger_.msg(s); };
172 //! Log error.
173 inline void log_e ( const string &s ) { logger_.msg(s,Logger::MSG_ERROR); };
174 //! Log warning.
175 inline void log_w ( const string &s ) { logger_.msg(s,Logger::MSG_WARNING); };
176 //! Log debug message.
177 inline void log_d ( const string &s ) { logger_.msg(s,Logger::MSG_DEBUG); };
178 //! Log message.
179 inline void log ( const std::stringstream &s ) { logger_.msg(s); };
180 //! Log error.
181 inline void log_e ( const std::stringstream &s )
182 { logger_.msg(s,Logger::MSG_ERROR); };
183 //! Log warning.
184 inline void log_w ( const std::stringstream &s )
185 { logger_.msg(s,Logger::MSG_WARNING); };
186 //! Log debug message.
187 inline void log_d ( const std::stringstream &s )
188 { logger_.msg(s,Logger::MSG_DEBUG); };
190 //! Returns pointer to UI.
191 inline aoi_ui::GUI *ui() const { return ui_; };
193 //! Call UI::run(), which calls Fl::run()
194 int run ( int argc, char **argv );
197 * Performs database query q.
198 * \param q SQLite query
199 * \param log_query true: q will be logged as debug message
200 * \param replace_separator true: parsers::SEPARATOR_SQL ('|') will be replaced by ", "
201 * \sa SQLite3::query()
202 * \returns data in same format as SQLite3::query()
204 vector<string> query ( const char *q, bool log_query=true,
205 bool replace_separator = true );
207 //! Gets ne kanji from database.
208 Kanji db_get_kanji ( const string &kanji );
210 //! Returns whole config as map.
211 inline std::map<string,aoi_config::Config::Value> get_config_map ()
212 { return cfg_->get_map(); };
213 //! Returns default config map.
214 inline std::map<string,aoi_config::Config::Value> get_config_map_default ()
215 { return cfg_->get_default_map(); };
216 /*!
217 * Sets one key=value config pair.
218 * \sa apply_config()
220 inline void set_config ( const string &key, const string &val )
221 { cfg_->set( key, val ); };
223 * Applies config - sets colors, loglevel, fonts, ... Redraws UI.
224 * \sa set_config()
226 void apply_config ();
227 /*!
228 * Gets one config item
229 * \exception std::runtime_error when key does not exist
231 template<class T=string> inline T get_config( const string &s )
232 { return cfg_->get<T>(s); }
234 * Writes current config to database. Calls set_config() for each
235 * item in config map. After that calls init_dicview()
236 * \sa get_config_map()
237 * \sa set_config()
238 * \sa init_dicview()
240 void save_config ( const std::map<string,aoi_config::Config::Value> &newmap={});
242 //! Loads config from database.
243 void load_config ();
246 * Initializes and sets styles in DicView.
247 * \sa DicView
248 * \sa TextStyle
249 * \sa GUI::register_tag_dicview()
251 void init_dicview ();
253 //! Shows alert/error window.
254 inline void alert ( const string &msg, const string &desc="" );
257 * Parses JMDict. File may be GZipped.
258 * \exception utils::ParsingError
259 * \exception SQLite3::DatabaseError
260 * \param fname path to JMDict file
261 * \param delete_source true: delete JMDict file (usefull with downloaded files)
263 void parse_jmdict ( const char *fname, bool delete_source=false );
265 * Parses kanjidic2. Works in the same way as parse_jmdict().
266 * \sa parse_jmdict()
268 void parse_kanjidic ( const char *fname, bool delete_source=false );
270 * Checks tables in database. Creates missing.
271 * \sa aoi_config::db_tables
273 void check_tables ();
275 * Checks indexes in database. Creates missing indexes and runs
276 * VACUUM if neccessary.
277 * \sa aoi_config::db_tables
279 void check_indexes ();
281 // Dictionary functions
282 void cb_dic_input ();
283 void on_dic_selected ( int id );
284 void cb_edit_word ( int id );
285 void parse_dic_input ( const char *str );
287 // Kanjidic functions
288 void cb_popup_kanji ( const string &kanji );
289 void cb_kanji_search ();
291 // GUI functions
292 void set_listview ( const vector<string> &v );
293 void cb_dicview_rightclick ( int did );
295 void cb_manage_db ();
296 void cb_filter_listview ();
298 void cb_set_components ();
300 void cb_file_jmdict ( bool download=false );
301 void cb_file_kanjidic ( bool download=false );
302 void cb_file_components ( bool download=false );
304 inline static void scb_dic_input ( Fl_Widget *w, void *p )
305 { ((App*)p)->cb_dic_input(); }
306 inline static void scb_filter_listview ( Fl_Widget *w, void *p )
307 { ((App*)p)->cb_filter_listview(); }
308 inline static void scb_manage_db ( Fl_Widget *w, void *p )
309 { ((App*)p)->cb_manage_db(); }
310 inline static void scb_kanji_search ( Fl_Widget *w, void *p )
311 { ((App*)p)->cb_kanji_search(); }
312 // TODO: zmenit primo na hledani kanji
313 inline static void scb_kanjiview_leftclick ( Fl_Widget *w, void *p ){
314 aoi_ui::KanjiView *v = (aoi_ui::KanjiView*)w;
315 aoi_ui::KanjiView::Cell *c = v->selected();
316 if ( c ){
317 ((App*)p)->cb_popup_kanji( c->str );
318 // copy to selection buffer (middle mouse)
319 Fl::copy(c->str.c_str(), strlen(c->str.c_str()), 0);
322 inline static void scb_dicview_doubleclick ( Fl_Widget *w, void *p ){
323 aoi_ui::DicView *v = (aoi_ui::DicView*)w;
324 ((App*)p)->cb_edit_word( v->cell_id(v->callback_row(),v->callback_col()) );
326 inline static void scb_dicview_rightclick ( Fl_Widget *w, void *p ){
327 aoi_ui::DicView *v = (aoi_ui::DicView*)w;
328 ((App*)p)->cb_dicview_rightclick( v->selected_row_id() );
332 inline static void scb_local_file_components ( Fl_Widget *w, void *p )
333 { ((App*)p)->cb_file_components(false); };
334 inline static void scb_local_file_kanjidic ( Fl_Widget *w, void *p )
335 { ((App*)p)->cb_file_kanjidic(false);};
336 inline static void scb_local_file_jmdict ( Fl_Widget *w, void *p )
337 { ((App*)p)->cb_file_jmdict(false); };
338 inline static void scb_download_file_components ( Fl_Widget *w, void *p )
339 { ((App*)p)->cb_file_components(true); };
340 inline static void scb_download_file_jmdict( Fl_Widget *w, void *p )
341 { ((App*)p)->cb_file_jmdict(true); };
342 inline static void scb_download_file_kanjidic( Fl_Widget *w, void *p )
343 { ((App*)p)->cb_file_kanjidic(true); };
344 inline static void scb_set_components ( Fl_Widget *w, void *p )
345 { ((App*)p)->cb_set_components(); }
349 class DictionaryInputParser
351 public:
352 enum PartType { STRING, WORD, KANJI };
353 private:
354 vector<std::pair<string,PartType>> parts_;
355 std::stringstream buffer_;
356 PartType type_ = STRING;
357 bool warning_ = false;
359 inline void add () {
360 if ( buffer_.str().empty() ) return;
361 parts_.push_back( { buffer_.str(), type_ } );
362 buffer_.str("");
365 set<string> intersection ( const string &query, const set<string> &current )
367 set<string> characters;
368 // all characters in result
369 for ( string &r: App::get()->query(query.c_str()) ) {
370 for ( string &c: utils::str_to_chars(r.c_str()) ){
371 if ( App::get()->rmn()->is_kanji(c.c_str()) )
372 characters.insert(c);
375 if ( current.empty() )
376 return characters;
377 // intersection
378 vector<string> tmp(current.size());
379 std::set_intersection(
380 current.begin(), current.end(),
381 characters.begin(), characters.end(),
382 tmp.begin()
384 set<string> newset;
385 newset.insert( tmp.begin(), tmp.end() );
386 return newset;
389 public:
390 DictionaryInputParser(){};
391 ~DictionaryInputParser(){};
393 inline bool warning () const { return warning_; };
395 string find_kanji ( const string &s, bool from_words=true )
397 set<string> results;
398 string q;
399 for ( string &w: utils::split_string(s,",") ) {
400 // search SKIP
401 if ( w.size()==1 && utils::isint(w.c_str()) ){
402 int i = std::stoi(w);
403 if ( i > 0 && i < 5 )
404 q = "select group_concat(kanji,'') from k_skip where skip1=" + w + ";";
406 else {
407 // search readings
408 if ( from_words ) {
409 q = "select group_concat("\
410 "(select group_concat(kanji,'') from d_kanji where d_reading.did=d_kanji.did)"\
411 ",'') from d_reading where reading='" + App::get()->rmn()->romaji_to_hiragana(w)
412 + "';";
414 // search yomi
415 else{
416 q = "select group_concat(kanji,'') from k_kanji where "\
417 "kunyomi glob '*" + App::get()->rmn()->romaji_to_hiragana(w) +
418 "*' or onyomi glob '*" + App::get()->rmn()->romaji_to_katakana(w) + "*'";
421 results = intersection( q, results);
423 return utils::to_string(results,"");
426 void check_warning ( const string &s, const string &previous )
428 size_t warning_limit = App::get()->get_config<size_t>("dic/input_parser_warning");
429 size_t n = utils::str_to_chars(s.c_str()).size();
430 if ( warning_ || n <= warning_limit )
431 return;
433 bool is_wildchar = true;
434 for ( string &c: utils::str_to_chars(previous.c_str()) ){
435 if ( c != "?" && c != "*" ){
436 is_wildchar = false;
437 break;
440 warning_ = is_wildchar;
444 string parse ( const char *s )
446 // initialize
447 type_ = STRING;
448 buffer_.str("");
449 parts_.clear();
450 warning_ = false;
452 // scan string
453 size_t i = 0;
454 while ( i < strlen(s) ){
455 switch( s[i] ){
456 case '[': add(); type_ = WORD; break;
457 case '(': add(); type_ = KANJI; break;
458 case ']':
459 case ')': add(); type_ = STRING; break;
460 default:
461 buffer_ << s[i];
463 i++;
465 add();
467 std::stringstream output;
468 for ( size_t j=0; j < parts_.size(); ++j ){
469 auto p = parts_[j];
470 switch ( p.second ){
471 case WORD:
473 string r = find_kanji(p.first,true);
474 check_warning( r, (j==0) ? "":parts_[j-1].first );
475 output << "[" << r << "]";
476 break;
478 case KANJI:
480 string r = find_kanji(p.first,false);
481 check_warning( r, (j==0) ? "":parts_[j-1].first );
482 output << "[" << r << "]";
483 break;
485 default:
486 output << p.first;
490 string ret = output.str();
491 utils::replace_all(ret, "{", "[");
492 utils::replace_all(ret, "}", "]");
493 return ret;
497 } // namespace aoi
498 #endif // _AOI_HXX