1 #include <stdexcept>
2 //#include <cstdlib>
3 #include "dictdlg.h"
4 #include <lvstring.h>
5 #include <lvref.h>
6 //#include "selector.h"
7 #include <crgui.h>
8 #include <crtrace.h>
9
10
11 #include <lvarray.h>
12 #include <lvstring.h>
13 #include <lvtinydom.h>
14 #include <lvdocview.h>
15 #include "mainwnd.h"
16 #include "viewdlg.h"
17 #include "bgfit.h"
18 #include "t9encoding.h"
19
20 #include <cri18n.h>
21
22
23 //TODO: place TinyDictionary to separate file
CRTinyDict(const lString16 & config)24 CRTinyDict::CRTinyDict( const lString16& config )
25 {
26 lString16 path = config;
27 LVAppendPathDelimiter( path );
28 LVContainerRef dir = LVOpenDirectory( config.c_str() );
29 if ( !dir )
30 dir = LVOpenDirectory( LVExtractPath(config).c_str() );
31 if ( !dir.isNull() ) {
32 int count = dir->GetSize();
33 lString16 indexExt(".index");
34 for ( int i=0; i<count; i++ ) {
35 const LVContainerItemInfo * item = dir->GetObjectInfo( i );
36 if ( !item->IsContainer() ) {
37 lString16 name = item->GetName();
38 if ( name.endsWith( indexExt ) ) {
39 lString16 nameBase = name.substr( 0, name.length() - indexExt.length() );
40 lString16 name1 = nameBase + ".dict";
41 lString16 name2 = nameBase + ".dict.dz";
42 lString16 dataName;
43 int index = -1;
44 for ( int n=0; n<count; n++ ) {
45 const LVContainerItemInfo * item2 = dir->GetObjectInfo( n );
46 if ( !item2->IsContainer() ) {
47 if ( item2->GetName() == name1 || item2->GetName() == name2 ) {
48 index = n;
49 dataName = item2->GetName();
50 break;
51 }
52 }
53 }
54 if ( index>=0 ) {
55 // found pair
56 dicts.add(UnicodeToUtf8(path + name).c_str(), UnicodeToUtf8(path + dataName).c_str());
57 }
58 }
59 }
60 }
61 }
62 CRLog::info( "%d dictionaries opened", dicts.length() );
63 }
64
65
translate(const lString8 & w)66 lString8 CRTinyDict::translate(const lString8 & w)
67 {
68 lString16 s16 = Utf8ToUnicode( w );
69 s16.lowercase();
70 lString8 word = UnicodeToUtf8( s16 );
71 lString8 body;
72 TinyDictResultList results;
73 if ( dicts.length() == 0 ) {
74 // should not happen
75 body << "<title><p>No dictionaries found</p></title>";
76 } else if ( dicts.find(results, word.c_str(), TINY_DICT_OPTION_STARTS_WITH ) ) {
77 for ( int d = 0; d<results.length(); d++ ) {
78 TinyDictWordList * words = results.get(d);
79 if ( words->length()>0 )
80 body << "<title><p>" << _("From dictionary ") << words->getDictionaryName() << ":</p></title>";
81 // for each found word
82 for ( int i=0; i<words->length(); i++ ) {
83 //TinyDictWord * word = words->get(i);
84 const char * article = words->getArticle( i );
85 body << "<code style=\"text-align: left; text-indent: 0; font-size: 22\">";
86 if ( article ) {
87 body << article;
88 } else {
89 body << _("[cannot read article]");
90 }
91 body << "</code>";
92 if ( i<words->length()-1 )
93 body << "<hr/>";
94 }
95 }
96 } else {
97 body << "<title><p>Article for word " << word << " not found</p></title>";
98 }
99
100 return body;
101 }
102
103
104
105 #define DICT_MIN_WORD_LENGTH 3
106
107 class WordWithRanges
108 {
109 lString16 word;
110 lString16 wordLower;
111 lString8 encoded;
112 LVArray<ldomWord> ranges;
113 public:
getWord()114 const lString16 & getWord() { return word; }
getEncoded()115 const lString8 & getEncoded() { return encoded; }
getRanges()116 LVArray<ldomWord> & getRanges() { return ranges; }
matchEncoded(const lString8 & prefix)117 bool matchEncoded( const lString8 & prefix )
118 {
119 if ( prefix.empty() )
120 return false;
121 return encoded.startsWith( prefix );
122 }
matchWord(const lString16 & prefix)123 bool matchWord( const lString16 & prefix )
124 {
125 if ( prefix.empty() )
126 return false;
127 return word.startsWithNoCase( prefix );
128 }
equals(const lString16 & w)129 bool equals( const lString16 & w )
130 {
131 lString16 w1( w );
132 w1.lowercase();
133 return w1 == wordLower;
134 }
add(const ldomWord & range)135 void add( const ldomWord & range )
136 {
137 ranges.add( range );
138 }
WordWithRanges(const lString16 & w,const lString8 & enc,const ldomWord & range)139 WordWithRanges( const lString16 & w, const lString8 & enc, const ldomWord & range )
140 : word( w ), encoded( enc )
141 {
142 wordLower = w;
143 wordLower.lowercase();
144 ranges.add( range );
145 }
146 };
147
148 class wordlist {
149 LVDocView& docview_;
150 const TEncoding& encoding_;
151 LVPtrVector<WordWithRanges> _words;
152 public:
init()153 void init()
154 {
155 _words.clear();
156 //ldomDocument * doc = docview.getDocument();
157 int pageIndex = -1; //docview.getCurPage();
158 LVRef<ldomXRange> range = docview_.getPageDocumentRange( pageIndex );
159 crtrace trace;
160 if( !range.isNull() ) {
161 LVArray<ldomWord> words;
162 range->getRangeWords(words);
163 for ( int i=0; i<words.length(); i++ ) {
164 lString16 w = words[i].getText();
165 lString8 encoded = encoding_.encode_string( w );
166 if ( w.length() < DICT_MIN_WORD_LENGTH )
167 continue;
168 /*
169 trace << "string " << w <<
170 " encoded as " << encoded << "\n";
171 */
172 int index = -1;
173 for ( int j=0; j<_words.length(); j++ )
174 if ( _words[j]->equals(w) ) {
175 index = j;
176 break;
177 }
178 if ( index>=0 )
179 _words[index]->add( words[i] ); // add range to existing word
180 else
181 _words.add( new WordWithRanges( w, encoded, words[i] ) ); // add new word
182 }
183 }
184 }
185
wordlist(LVDocView & docview,const TEncoding & encoding)186 wordlist(LVDocView& docview, const TEncoding& encoding) :
187 docview_(docview), encoding_( encoding )
188 {
189 init();
190 }
191
match(const lString8 & prefix,LVArray<WordWithRanges * > & result)192 void match( const lString8& prefix, LVArray<WordWithRanges *> & result )
193 {
194 crtrace dumpstr;
195 dumpstr << "match with " << prefix;
196 for( int i=0; i<_words.length(); i++ ) {
197 if( _words[i]->matchEncoded( prefix ) ) {
198 result.add( _words[i] );
199 //dumpstr << " " << i << " " << encoded_words_[i];
200 };
201 };
202 }
203
highlight(WordWithRanges * p)204 void highlight( WordWithRanges * p )
205 {
206 crtrace trace;
207 trace << "Select word " << p->getWord() << " (" << p->getRanges().length() << " occurences) : ";
208 for ( int i=0; i<p->getRanges().length(); i++ ) {
209 trace << "[" << p->getRanges()[i].getStart() << "," << p->getRanges()[i].getEnd() << "] ";
210 }
211 docview_.selectWords( p->getRanges() );
212 }
213 };
214
215 class selector {
216 wordlist words_;
217 int current_;
218 int level_;
219 LVArray<WordWithRanges *> candidates_;
220 LVArray<lString16> encoded_;
221 lString8 prefix_;
222 // LVArray<lString16> keytable_;
223 int repeat_;
224 int last_;
225 public:
reinit()226 void reinit() { words_.init(); candidates_.clear(); }
getPrefix()227 lString8 getPrefix() { return prefix_; }
selector(LVDocView & docview,const TEncoding & encoding)228 selector(LVDocView& docview, const TEncoding& encoding) :
229 words_(docview, encoding),
230 current_(0),
231 level_(0),
232 candidates_(),
233 prefix_(),
234 repeat_(0)
235 {
236 // init_keytable(keytable_);
237 update_candidates();
238 };
239
moveBy(int delta)240 void moveBy( int delta )
241 {
242 current_ += delta;
243 if ( current_ < 0 )
244 current_ = candidates_.length()-1;
245 if ( current_ >= candidates_.length() )
246 current_ = 0;
247 if ( current_ < 0 )
248 current_ = 0;
249 if ( candidates_.length() )
250 words_.highlight(candidates_[current_]);
251 }
252
up()253 void up() { moveBy( -1 ); }
254
down()255 void down() { moveBy( 1 ); }
256
update_candidates()257 bool update_candidates()
258 {
259 CRLog::info("update_candidates() enter\n");
260 LVArray<WordWithRanges *> new_candidates;
261 words_.match( prefix_, new_candidates );
262 CRLog::info("update_candidates() mid\n");
263 current_=0;
264 CRLog::info("update_candidates() mid2\n");
265 if( new_candidates.length() == 0 ) {
266 CRLog::info("nothig to highlight");
267 return false;
268 };
269 candidates_ = new_candidates;
270 words_.highlight( candidates_[current_] );
271 CRLog::info("update_candidates() leave\n");
272 return true;
273 }
274
push_button(int key)275 bool push_button(int key) {
276 crtrace trace("selector::push(): ");
277 lString8 old_prefix = prefix_;
278 prefix_.append(1,static_cast<char>(key));
279 if(update_candidates()){
280 level_++;
281 return true;
282 }
283 prefix_ = old_prefix;
284 return false;
285 }
286
287
pop()288 bool pop()
289 {
290 if (level_==0) {
291 return true;
292 }
293 prefix_ = prefix_.substr(0,prefix_.length()-1);
294 level_--;
295 update_candidates();
296 return false;
297 }
298
get()299 const lString16 get()
300 {
301 if ( current_ >= 0 && current_ < candidates_.length() )
302 return candidates_[current_]->getWord();
303 return lString16::empty_str;
304 }
305 };
306
307
308 class CRT9Keyboard : public BackgroundFitWindow
309 {
310 int _command;
311 lString16 & _buf;
312 TEncoding encoding_;
313 selector selector_;
314 protected:
315
316 virtual void draw();
317
318 public:
319 void setDefaultLayout();
320 void setLayout( CRKeyboardLayoutRef layout );
321
322 CRT9Keyboard(CRGUIWindowManager * wm, CRDocViewWindow * mainwin, int id, lString16 & buffer );
323
324 virtual bool onCommand( int command, int params );
325
326 };
327
328 const lChar16 * defT5encoding[] = {
329 L"", // 0 STUB
330 L"abcde", // 1
331 L"fghij", // 2
332 L"klmno", // 3
333 L"pqrst", // 4
334 L"uvwxyz", // 5
335 NULL
336 };
337
setDefaultLayout()338 void CRT9Keyboard::setDefaultLayout()
339 {
340 encoding_.init(defT5encoding);
341 }
342
setLayout(CRKeyboardLayoutRef layout)343 void CRT9Keyboard::setLayout( CRKeyboardLayoutRef layout )
344 {
345 while ( !selector_.pop() )
346 ;
347 _mainwin->getDocView()->clearSelection();
348
349 if ( !layout.isNull() ) {
350 encoding_.set( layout->tXKeyboard->getItems() );
351 }
352 if ( encoding_.length()==0 )
353 setDefaultLayout();
354 selector_.reinit();
355 setDirty();
356 }
357
CRT9Keyboard(CRGUIWindowManager * wm,CRDocViewWindow * mainwin,int id,lString16 & buffer)358 CRT9Keyboard::CRT9Keyboard(CRGUIWindowManager * wm, CRDocViewWindow * mainwin, int id, lString16 & buffer )
359 : BackgroundFitWindow(wm, mainwin)
360 , _command( id )
361 , _buf( buffer )
362 , encoding_(defT5encoding)
363 , selector_(*mainwin->getDocView(), encoding_)
364 {
365 _passKeysToParent = false;
366 _passCommandsToParent = false;
367
368 //this->setAccelerators( mainwin->getDialogAccelerators() );
369 setAccelerators( _wm->getAccTables().get("txkeyboard") );
370
371 _rect = _wm->getScreen()->getRect();
372 //_rect.bottom = _rect.top;
373 _rect.top = _rect.bottom - 40;
374 }
375
draw()376 void CRT9Keyboard::draw()
377 {
378 BackgroundFitWindow::draw();
379 CRMenuSkinRef skin = _wm->getSkin()->getMenuSkin( L"#t9input" );
380 CRRectSkinRef shortcutSkin = skin->getItemShortcutSkin();
381 CRRectSkinRef itemSkin = skin->getItemSkin();
382 CRRectSkinRef clientSkin = skin->getClientSkin();
383 LVDrawBuf * buf = _wm->getScreen()->getCanvas().get();
384 skin->draw( *buf, _rect );
385 lString16 prompt = Utf8ToUnicode(selector_.getPrefix());
386 prompt << L"_";
387 skin->draw( *buf, _rect );
388 //buf->FillRect( _rect, 0xAAAAAA );
389 lvRect rect = _rect;
390 lvRect borders = skin->getBorderWidths();
391 rect.shrinkBy( borders );
392 lvRect keyRect = rect;
393 lvPoint minSizeN = shortcutSkin->getMinSize();
394 for ( int i=0; i<encoding_.length(); i++ ) {
395 lString16 txtN = lString16::itoa(i);
396 lString16 txt = encoding_[i];
397 if ( txt.empty() )
398 continue;
399 // label 0..9
400 lvPoint sz = shortcutSkin->measureTextItem( txtN );
401 keyRect.right = keyRect.left + sz.x; //borders.left + borders.right;
402 shortcutSkin->draw( *buf, keyRect );
403 shortcutSkin->drawText( *buf, keyRect, txtN );
404 keyRect.left = keyRect.right;
405 // chars (abc)
406 sz = itemSkin->measureTextItem( txt );
407 keyRect.right = keyRect.left + sz.x; //borders.left + borders.right;
408 itemSkin->draw( *buf, keyRect );
409 itemSkin->drawText( *buf, keyRect, txt );
410 keyRect.left = keyRect.right; //borders.left;
411 }
412 keyRect.right = rect.right;
413 if ( !clientSkin.isNull() && !keyRect.isEmpty() ) {
414 clientSkin->draw( *buf, keyRect );
415 clientSkin->drawText( *buf, keyRect, prompt );
416 }
417 }
418
onCommand(int command,int params)419 bool CRT9Keyboard::onCommand( int command, int params )
420 {
421 switch ( command ) {
422 case MCMD_SELECT_0:
423 case MCMD_SELECT_1:
424 case MCMD_SELECT_2:
425 case MCMD_SELECT_3:
426 case MCMD_SELECT_4:
427 case MCMD_SELECT_5:
428 case MCMD_SELECT_6:
429 case MCMD_SELECT_7:
430 case MCMD_SELECT_8:
431 case MCMD_SELECT_9:
432 selector_.push_button( command - MCMD_SELECT_0 + '0' );
433 setDirty();
434 break;
435 case MCMD_SCROLL_FORWARD:
436 selector_.down();
437 break;
438 case MCMD_KBD_NEXTLAYOUT:
439 setLayout( _wm->getKeyboardLayouts().nextLayout() );
440 break;
441 case MCMD_KBD_PREVLAYOUT:
442 setLayout( _wm->getKeyboardLayouts().prevLayout() );
443 break;
444 case MCMD_SCROLL_BACK:
445 selector_.up();
446 break;
447 case MCMD_OK:
448 {
449 lString8 translated;
450 lString8 output;
451 lString16 src = selector_.get();
452 if ( src.empty() ) {
453 _mainwin->getDocView()->clearSelection();
454 _wm->closeWindow(this);
455 return true;
456 }
457 CRLog::info("Closing dict");
458 _buf = src;
459 _mainwin->getDocView()->clearSelection();
460 _wm->postCommand( _command, 1 );
461 _wm->closeWindow(this);
462 };
463 break;
464 case MCMD_CANCEL:
465 if ( selector_.pop() ) {
466 _mainwin->getDocView()->clearSelection();
467 _wm->closeWindow(this);
468 return true;
469 }
470 setDirty();
471 break;
472 case MCMD_CLEAR:
473 while ( !selector_.pop() )
474 ;
475 _mainwin->getDocView()->clearSelection();
476 setDirty();
477 break;
478 }
479 return true;
480 }
481
showT9Keyboard(CRGUIWindowManager * wm,CRDocViewWindow * mainwin,int id,lString16 & buffer)482 void showT9Keyboard(CRGUIWindowManager * wm, CRDocViewWindow * mainwin, int id, lString16 & buffer)
483 {
484 CRT9Keyboard * dlg = new CRT9Keyboard( wm, mainwin, id, buffer );
485 wm->activateWindow( dlg );
486 }
487
488