doxygen comments
[aoi.git] / src / aoi.hxx
blobfa919ee85c31252cae0df754be9ffbe95b16dc5f
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 // singleton
102 App();
103 App& operator=(App const&) = delete;
104 static App* instance_;
106 /*!
107 * Returns part of the SQL query for DicView.
108 * \param id database id (did) of the searched word
109 * \sa parse_dic_input()
111 inline string q_reading ( const string &id="")
113 string s1 = id.empty() ? "":"(select ";
114 string s2 = id.empty() ? "":(" from d_reading where d_reading.did="+id+") ");
115 return s1 + "group_concat("\
116 "(case freq when 1 then '<reading_freq>'||reading||'</reading_freq>' "\
117 "else '<reading>'||reading||'</reading>' end)||"\
118 "'<rinf>'||inf||'</rinf>',"\
119 "\"<br/>\")" + s2 + "as reading, ";
122 /*!
123 * Returns part of the SQL query for DicView.
124 * \param id database id (did) of the searched word
125 * \sa parse_dic_input()
127 inline string q_kanji ( const string &id="" )
129 string s1 = id.empty() ? "":"(select ";
130 string s2 = id.empty() ? "":(" from d_kanji where d_kanji.did="+id+") ");
131 return s1 + "group_concat("\
132 "(case freq when 1 then '<kanji_freq>'||kanji||'</kanji_freq>' "\
133 "else '<kanji>'||kanji||'</kanji>' end)||"\
134 "'<kinf>'||inf||'</kinf>',"\
135 "\"<br/>\")" + s2 + "as kanji, ";
138 /*!
139 * Returns part of the SQL query for DicView.
140 * \param id database id (did) of the searched word
141 * \sa parse_dic_input()
143 inline string q_sense ( const string &id="" )
145 string s1 = id.empty() ? "":"(select ";
146 string s2 = id.empty() ? "":(" from d_sense where d_sense.did="+id+") ");
147 return s1 + "group_concat("\
148 "(case pos when '' then '' else '<pos>'||pos||'</pos>' end)||"\
149 "(case misc when '' then '' else '<misc>'||misc||'</misc>' end)||"\
150 "gloss||"\
151 "(case field when '' then '' else '<field>'||field||'</field>' end)||"\
152 "(case dial when '' then '' else '<dial>'||dial||'</dial>' end),"\
153 "'<sep/>') " + s2 + " as sense ";
156 public:
157 ~App();
159 //! Returns instance of App.
160 static inline App *get (){
161 if ( !instance_)
162 instance_ = new App();
163 return instance_;
166 //! Returns pointer to Romanization.
167 inline Romanization *rmn () const { return rmn_; };
169 //! Log message.
170 inline void log ( const string &s ) { logger_.msg(s); };
171 //! Log error.
172 inline void log_e ( const string &s ) { logger_.msg(s,Logger::MSG_ERROR); };
173 //! Log warning.
174 inline void log_w ( const string &s ) { logger_.msg(s,Logger::MSG_WARNING); };
175 //! Log debug message.
176 inline void log_d ( const string &s ) { logger_.msg(s,Logger::MSG_DEBUG); };
177 //! Log message.
178 inline void log ( const std::stringstream &s ) { logger_.msg(s); };
179 //! Log error.
180 inline void log_e ( const std::stringstream &s )
181 { logger_.msg(s,Logger::MSG_ERROR); };
182 //! Log warning.
183 inline void log_w ( const std::stringstream &s )
184 { logger_.msg(s,Logger::MSG_WARNING); };
185 //! Log debug message.
186 inline void log_d ( const std::stringstream &s )
187 { logger_.msg(s,Logger::MSG_DEBUG); };
189 //! Returns pointer to UI.
190 inline aoi_ui::GUI *ui() const { return ui_; };
192 //! Call UI::run(), which calls Fl::run()
193 int run ( int argc, char **argv );
196 * Performs database query q.
197 * \param q SQLite query
198 * \param log_query true: q will be logged as debug message
199 * \param replace_separator true: parsers::SEPARATOR_SQL ('|') will be replaced by ", "
200 * \sa SQLite3::query()
201 * \returns data in same format as SQLite3::query()
203 vector<string> query ( const char *q, bool log_query=true,
204 bool replace_separator = true );
206 //! Gets one kanji from database.
207 Kanji db_get_kanji ( const string &kanji );
209 //! Gets one word(record) from database
210 DicWord db_get_word( int did );
212 //! Returns whole config as map.
213 inline std::map<string,aoi_config::Config::Value> get_config_map ()
214 { return cfg_->get_map(); };
215 //! Returns default config map.
216 inline std::map<string,aoi_config::Config::Value> get_config_map_default ()
217 { return cfg_->get_default_map(); };
218 /*!
219 * Sets one key=value config pair.
220 * \sa apply_config()
222 inline void set_config ( const string &key, const string &val )
223 { cfg_->set( key, val ); };
226 * Overrides one config option. Overidden option cant be changed until restart
227 * of the program.
228 * \sa Config::set_override()
230 inline void config_override ( const string &key, const string &val ) {
231 try{
232 cfg_->set_override(key,val);
234 catch ( const std::exception &e ) {
235 log_e("Unknown config: " + key);
240 * Load color from database (string 0xRRGGBB00) and converts it to Fl_Color
241 * (unsigned int);
243 inline Fl_Color get_color ( const string &color ){
244 string s = cfg_->get<string>("color/"+color);
245 std::stringstream ss;
246 ss << std::hex << s.substr(2);
247 Fl_Color c;
248 ss >> c;
249 return c;
253 * Applies config - sets colors, loglevel, fonts, ... Redraws UI.
254 * \sa set_config()
256 void apply_config ();
257 /*!
258 * Gets one config item
259 * \exception std::runtime_error when key does not exist
261 template<class T=string> inline T get_config( const string &s )
262 { return cfg_->get<T>(s); }
264 * Writes current config to database. Calls set_config() for each
265 * item in config map. After that calls init_dicview()
266 * \sa get_config_map()
267 * \sa set_config()
268 * \sa init_dicview()
270 void save_config ( const std::map<string,aoi_config::Config::Value> &newmap={});
272 //! Loads config from database.
273 void load_config ();
276 * Initializes and sets styles in DicView.
277 * \sa DicView
278 * \sa TextStyle
279 * \sa GUI::register_tag_dicview()
281 void init_dicview ();
283 //! Shows alert/error window.
284 void alert ( const string &msg, const string &desc="" );
287 * Checks tables in database. Creates missing.
288 * \sa aoi_config::db_tables
290 void check_tables ();
292 * Checks indexes in database. Creates missing indexes and runs
293 * VACUUM if neccessary.
294 * \sa aoi_config::db_tables
296 void check_indexes ();
298 // Dictionary functions
299 void cb_dic_input ();
300 void on_dic_selected ( int id );
301 void cb_edit_word ();
302 void parse_dic_input ( const char *str );
304 // Kanjidic functions
305 void cb_popup_kanji ( const string &kanji );
306 void cb_kanji_search ();
308 // GUI functions
310 * Sets contents of the listview and listview_items_.
311 * \parameter v preformatted text (output from query()) to be used in aoi_ui::DicView
312 * \todo nokanji
314 void set_listview ( const vector<string> &v );
315 void cb_dicview_rightclick ( int did );
317 void cb_examples ();
318 void cb_manage_db ();
319 void cb_filter_listview ();
321 void cb_set_components ();
323 void cb_download_db();
326 * Used as callback for download button in aoi_ui::ManageDBDialog::cb_download()
327 * \see cb_manage_db() (here it is set as callback)
328 * \see cb_download_db()
330 inline static void scb_download_db ( Fl_Widget *w, void *p )
331 { ((App*)p)->cb_download_db(); }
332 inline static void scb_examples ( Fl_Widget *w, void *p )
333 { ((App*)p)->cb_examples(); }
334 inline static void scb_dic_input ( Fl_Widget *w, void *p )
335 { ((App*)p)->cb_dic_input(); }
336 inline static void scb_filter_listview ( Fl_Widget *w, void *p )
337 { ((App*)p)->cb_filter_listview(); }
338 inline static void scb_manage_db ( Fl_Widget *w, void *p )
339 { ((App*)p)->cb_manage_db(); }
340 inline static void scb_kanji_search ( Fl_Widget *w, void *p )
341 { ((App*)p)->cb_kanji_search(); }
343 * \todo change to direct kanjisearch
345 inline static void scb_kanjiview_select ( Fl_Widget *w, void *p ){
346 aoi_ui::KanjiView *v = (aoi_ui::KanjiView*)w;
347 aoi_ui::KanjiView::Cell *c = v->selected();
348 if ( c ){
349 ((App*)p)->cb_popup_kanji( c->str );
350 // copy to selection buffer (middle mouse)
351 Fl::copy(c->str.c_str(), strlen(c->str.c_str()), 0);
354 inline static void scb_dicview_doubleclick ( Fl_Widget *w, void *p ){
355 ((App*)p)->cb_edit_word(); }
356 inline static void scb_edit_word ( Fl_Widget *w, void *p ){
357 ((App*)p)->cb_edit_word(); }
358 inline static void scb_dicview_rightclick ( Fl_Widget *w, void *p ){
359 aoi_ui::DicView *v = (aoi_ui::DicView*)w;
360 ((App*)p)->cb_dicview_rightclick( v->selected_row_id() );
362 inline static void scb_set_components ( Fl_Widget *w, void *p )
363 { ((App*)p)->cb_set_components(); }
367 class DictionaryInputParser
369 public:
370 enum PartType { STRING, WORD, KANJI };
371 private:
372 vector<std::pair<string,PartType>> parts_;
373 std::stringstream buffer_;
374 PartType type_ = STRING;
375 bool warning_ = false;
377 inline void add () {
378 if ( buffer_.str().empty() ) return;
379 parts_.push_back( { buffer_.str(), type_ } );
380 buffer_.str("");
383 set<string> intersection ( const string &query, const set<string> &current )
385 set<string> characters;
386 // all characters in result
387 for ( string &r: App::get()->query(query.c_str()) ) {
388 for ( string &c: utils::str_to_chars(r.c_str()) ){
389 if ( App::get()->rmn()->is_kanji(c.c_str()) )
390 characters.insert(c);
393 if ( current.empty() )
394 return characters;
395 // intersection
396 vector<string> tmp(current.size());
397 std::set_intersection(
398 current.begin(), current.end(),
399 characters.begin(), characters.end(),
400 tmp.begin()
402 set<string> newset;
403 newset.insert( tmp.begin(), tmp.end() );
404 return newset;
407 public:
408 DictionaryInputParser(){};
409 ~DictionaryInputParser(){};
411 inline bool warning () const { return warning_; };
413 string find_kanji ( const string &s, bool from_words=true )
415 set<string> results;
416 string q;
417 for ( string &w: utils::split_string(s,",") ) {
418 // search SKIP
419 if ( w.size()==1 && utils::isint(w.c_str()) ){
420 int i = std::stoi(w);
421 if ( i > 0 && i < 5 )
422 q = "select group_concat(kanji,'') from k_skip where skip1=" + w + ";";
424 else {
425 // search readings
426 if ( from_words ) {
427 q = "select group_concat("\
428 "(select group_concat(kanji,'') from d_kanji where d_reading.did=d_kanji.did)"\
429 ",'') from d_reading where reading='" + App::get()->rmn()->romaji_to_hiragana(w)
430 + "';";
432 // search yomi
433 else{
434 q = "select group_concat(kanji,'') from k_kanji where "\
435 "kunyomi glob '*" + App::get()->rmn()->romaji_to_hiragana(w) +
436 "*' or onyomi glob '*" + App::get()->rmn()->romaji_to_katakana(w) + "*'";
439 results = intersection( q, results);
441 return utils::to_string(results,"");
444 void check_warning ( const string &s, const string &previous )
446 size_t warning_limit = App::get()->get_config<size_t>("dic/input_parser_warning");
447 size_t n = utils::str_to_chars(s.c_str()).size();
448 if ( warning_ || n <= warning_limit )
449 return;
451 bool is_wildchar = true;
452 for ( string &c: utils::str_to_chars(previous.c_str()) ){
453 if ( c != "?" && c != "*" ){
454 is_wildchar = false;
455 break;
458 warning_ = is_wildchar;
462 string parse ( const char *s )
464 // initialize
465 type_ = STRING;
466 buffer_.str("");
467 parts_.clear();
468 warning_ = false;
470 // scan string
471 size_t i = 0;
472 while ( i < strlen(s) ){
473 switch( s[i] ){
474 case '[': add(); type_ = WORD; break;
475 case '(': add(); type_ = KANJI; break;
476 case ']':
477 case ')': add(); type_ = STRING; break;
478 default:
479 buffer_ << s[i];
481 i++;
483 add();
485 std::stringstream output;
486 for ( size_t j=0; j < parts_.size(); ++j ){
487 auto p = parts_[j];
488 switch ( p.second ){
489 case WORD:
491 string r = find_kanji(p.first,true);
492 check_warning( r, (j==0) ? "":parts_[j-1].first );
493 output << "[" << r << "]";
494 break;
496 case KANJI:
498 string r = find_kanji(p.first,false);
499 check_warning( r, (j==0) ? "":parts_[j-1].first );
500 output << "[" << r << "]";
501 break;
503 default:
504 output << p.first;
508 string ret = output.str();
509 utils::replace_all(ret, "{", "[");
510 utils::replace_all(ret, "}", "]");
511 return ret;
515 } // namespace aoi
516 #endif // _AOI_HXX