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
101 std::set
<string
> config_overrides_
; // values set from commandline, cant be changed in program
104 App
& operator=(App
const&) = delete;
105 static App
* instance_
;
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, ";
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, ";
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)||"\
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 ";
160 //! Returns instance of App.
161 static inline App
*get (){
163 instance_
= new App();
167 //! Returns pointer to Romanization.
168 inline Romanization
*rmn () const { return rmn_
; };
171 inline void log ( const string
&s
) { logger_
.msg(s
); };
173 inline void log_e ( const string
&s
) { logger_
.msg(s
,Logger::MSG_ERROR
); };
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
); };
179 inline void log ( const std::stringstream
&s
) { logger_
.msg(s
); };
181 inline void log_e ( const std::stringstream
&s
)
182 { logger_
.msg(s
,Logger::MSG_ERROR
); };
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(); };
217 * Sets one key=value config pair.
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.
226 void apply_config ();
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()
240 void save_config ( const std::map
<string
,aoi_config::Config::Value
> &newmap
={});
242 //! Loads config from database.
246 * Initializes and sets styles in DicView.
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().
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 ();
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();
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
352 enum PartType
{ STRING
, WORD
, KANJI
};
354 vector
<std::pair
<string
,PartType
>> parts_
;
355 std::stringstream buffer_
;
356 PartType type_
= STRING
;
357 bool warning_
= false;
360 if ( buffer_
.str().empty() ) return;
361 parts_
.push_back( { buffer_
.str(), type_
} );
365 set
<string
> intersection ( const string
&query
, const set
<string
> ¤t
)
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() )
378 vector
<string
> tmp(current
.size());
379 std::set_intersection(
380 current
.begin(), current
.end(),
381 characters
.begin(), characters
.end(),
385 newset
.insert( tmp
.begin(), tmp
.end() );
390 DictionaryInputParser(){};
391 ~DictionaryInputParser(){};
393 inline bool warning () const { return warning_
; };
395 string
find_kanji ( const string
&s
, bool from_words
=true )
399 for ( string
&w
: utils::split_string(s
,",") ) {
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
+ ";";
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
)
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
)
433 bool is_wildchar
= true;
434 for ( string
&c
: utils::str_to_chars(previous
.c_str()) ){
435 if ( c
!= "?" && c
!= "*" ){
440 warning_
= is_wildchar
;
444 string
parse ( const char *s
)
454 while ( i
< strlen(s
) ){
456 case '[': add(); type_
= WORD
; break;
457 case '(': add(); type_
= KANJI
; break;
459 case ')': add(); type_
= STRING
; break;
467 std::stringstream output
;
468 for ( size_t j
=0; j
< parts_
.size(); ++j
){
473 string r
= find_kanji(p
.first
,true);
474 check_warning( r
, (j
==0) ? "":parts_
[j
-1].first
);
475 output
<< "[" << r
<< "]";
480 string r
= find_kanji(p
.first
,false);
481 check_warning( r
, (j
==0) ? "":parts_
[j
-1].first
);
482 output
<< "[" << r
<< "]";
490 string ret
= output
.str();
491 utils::replace_all(ret
, "{", "[");
492 utils::replace_all(ret
, "}", "]");