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