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/>.
21 Pouzivat vyhradne query()
22 Veskere SQL jen v tomto souboru
24 pozor na funkce vyzadujici ui_
25 mely by nejdrive testovat nullptr
35 #include "datatypes.hxx"
37 #include "romanization.hxx"
39 #include "sqlite3.hxx"
41 #include "parsers.hxx"
42 #include "gui_dicview.hxx"
43 #include "gui_kanjiview.hxx"
59 enum KANJI_SORT_MODE
{
68 * One item (line) in listview (dictionary results).
69 * \sa cb_filter_listview()
74 // vector<string> flags;
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
) {};
87 * Main class of the program. Singleton. Manages database, logger, config and UI.
88 * Provides functions for UI's callbacks.
93 class SQLite3::SQLite3
*db_
;
94 class Romanization
*rmn_
;
95 class aoi_ui::GUI
*ui_
;
96 class aoi_config::Config
*cfg_
;
98 vector
<DicViewItem
> listview_items_
;
99 map
<string
,int> components_
; // component: stroke_count
100 std::multimap
<int,string
> curr_components_
; // frequency:component
103 App
& operator=(App
const&) = delete;
104 static App
* instance_
;
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, ";
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, ";
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)||"\
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 ";
159 //! Returns instance of App.
160 static inline App
*get (){
162 instance_
= new App();
166 //! Returns pointer to Romanization.
167 inline Romanization
*rmn () const { return rmn_
; };
170 inline void log ( const string
&s
) { logger_
.msg(s
); };
172 inline void log_e ( const string
&s
) { logger_
.msg(s
,Logger::MSG_ERROR
); };
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
); };
178 inline void log ( const std::stringstream
&s
) { logger_
.msg(s
); };
180 inline void log_e ( const std::stringstream
&s
)
181 { logger_
.msg(s
,Logger::MSG_ERROR
); };
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(); };
219 * Sets one key=value config pair.
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
228 * \sa Config::set_override()
230 inline void config_override ( const string
&key
, const string
&val
) {
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
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);
253 * Applies config - sets colors, loglevel, fonts, ... Redraws UI.
256 void apply_config ();
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()
270 void save_config ( const std::map
<string
,aoi_config::Config::Value
> &newmap
={});
272 //! Loads config from database.
276 * Initializes and sets styles in DicView.
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 ();
310 * Sets contents of the listview and listview_items_.
311 * \parameter v preformatted text (output from query()) to be used in aoi_ui::DicView
314 void set_listview ( const vector
<string
> &v
);
315 void cb_dicview_rightclick ( int did
);
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();
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
370 enum PartType
{ STRING
, WORD
, KANJI
};
372 vector
<std::pair
<string
,PartType
>> parts_
;
373 std::stringstream buffer_
;
374 PartType type_
= STRING
;
375 bool warning_
= false;
378 if ( buffer_
.str().empty() ) return;
379 parts_
.push_back( { buffer_
.str(), type_
} );
383 set
<string
> intersection ( const string
&query
, const set
<string
> ¤t
)
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() )
396 vector
<string
> tmp(current
.size());
397 std::set_intersection(
398 current
.begin(), current
.end(),
399 characters
.begin(), characters
.end(),
403 newset
.insert( tmp
.begin(), tmp
.end() );
408 DictionaryInputParser(){};
409 ~DictionaryInputParser(){};
411 inline bool warning () const { return warning_
; };
413 string
find_kanji ( const string
&s
, bool from_words
=true )
417 for ( string
&w
: utils::split_string(s
,",") ) {
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
+ ";";
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
)
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
)
451 bool is_wildchar
= true;
452 for ( string
&c
: utils::str_to_chars(previous
.c_str()) ){
453 if ( c
!= "?" && c
!= "*" ){
458 warning_
= is_wildchar
;
462 string
parse ( const char *s
)
472 while ( i
< strlen(s
) ){
474 case '[': add(); type_
= WORD
; break;
475 case '(': add(); type_
= KANJI
; break;
477 case ')': add(); type_
= STRING
; break;
485 std::stringstream output
;
486 for ( size_t j
=0; j
< parts_
.size(); ++j
){
491 string r
= find_kanji(p
.first
,true);
492 check_warning( r
, (j
==0) ? "":parts_
[j
-1].first
);
493 output
<< "[" << r
<< "]";
498 string r
= find_kanji(p
.first
,false);
499 check_warning( r
, (j
==0) ? "":parts_
[j
-1].first
);
500 output
<< "[" << r
<< "]";
508 string ret
= output
.str();
509 utils::replace_all(ret
, "{", "[");
510 utils::replace_all(ret
, "}", "]");