1 /**
2  *  Yudit Unicode Editor Source File
3  *
4  *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License, version 2,
8  *  dated June 1991. See file COPYYING for details.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "swidget/SFreeHand.h"
21 #include "swidget/SIcon.h"
22 #include "swidget/SIconFactory.h"
23 #include "stoolkit/SUtil.h"
24 #include "stoolkit/SEncoder.h"
25 #include "stoolkit/SIO.h"
26 
27 #define SS_LEFT_MARGIN 2
28 
29 static SString SD_NOT_FOUND("???");
30 
SFreeHandListener(void)31 SFreeHandListener::SFreeHandListener(void)
32 {
33 }
~SFreeHandListener()34 SFreeHandListener::~SFreeHandListener()
35 {
36 }
37 /**
38  * A drawing component that lets you make a small
39  * drawing with the mouse.
40  */
SFreeHand(void)41 SFreeHand::SFreeHand (void)
42 {
43   listener = 0;
44   /* top line */
45 
46   titleLabel = new SLabel (translate ("Handwriting Input"));
47   titleLabel->setAlignment (SD_Center);
48   add (titleLabel);
49 
50   strokesLabel = new SLabel (translate ("Strokes:"));
51   strokesLabel->setAlignment (SD_Right);
52   add (strokesLabel);
53 
54   strokesCount = new SLabel ("0");
55   strokesCount->setAlignment (SD_Left);
56   add (strokesCount);
57 
58   /* next line */
59   converters = new SListBox (translate ("Converters"));
60   converters->setListListener (this);
61   add (converters);
62 
63   drawing = new SDrawing ();
64   drawing->setDrawingListener(this);
65   add (drawing);
66 
67   /* next four button are one under the other. */
68 
69   lookupButton = new SButton (translate ("Look-up"),
70        SIconFactory::getIcon("Yes"));
71   lookupButton->setButtonListener (this);
72   lookupButton->setAlignment (SD_Center);
73   add (lookupButton);
74 
75   clearButton = new SButton (translate ("Clear"),
76        SIconFactory::getIcon("Cancel"));
77   clearButton->setButtonListener (this);
78   clearButton->setAlignment (SD_Center);
79   add (clearButton);
80 
81   isDirected = true;
82   directedButton = new SButton (translate ("Directed"));
83   directedButton->setButtonListener (this);
84   directedButton->setAlignment (SD_Center);
85   directedButton->setIcon(SIconFactory::getIcon("CircleOn"));
86   add (directedButton);
87 
88   candidates = new SListBox (translate ("Candidates"));
89   candidates->setListListener (this);
90   add (candidates);
91   forceLayout (SLayout (SDimension (1000, 1000)));
92   recalc ();
93   initConverters();
94   needConversion = false;
95 }
96 
97 /**
98  * Find converters in datapath, with extension .hwd
99  */
100 void
initConverters()101 SFreeHand::initConverters()
102 {
103   SBinHashtable<int> mentioned;
104   SStringVector all;
105   SStringVector sp = SUniMap::getPath ();
106 
107   for (unsigned int i=0; i<sp.size(); i++)
108   {
109     SDir d(sp[i]);
110     SStringVector l = d.list("*.hwd");
111     for (unsigned int j=0; j<l.size(); j++)
112     {
113       SString s = l[j];
114       s.truncate(s.size()-4);
115       if (mentioned.get(s) != 0) continue;
116       mentioned.put (s,1);
117       SString fn = d.getName(); fn.append ("/");
118       fn.append (s);
119       fn.append (".hwd");
120       SFile file (fn);
121       SFileImage im=file.getFileImage();
122       if (im.size() < 10 || (im.array())[0] != '#'
123         || (im.array())[1] != 'H' || (im.array())[2]  != 'W'
124         || (im.array())[3] != 'D'  || (im.array())[4] != ' '
125         || ((im.array())[5]  != '1' && (im.array())[5]  != '2'))
126       {
127         fprintf (stderr,"Bad magic number: %*.*s\n", SSARGS(fn));
128         fprintf (stderr,"Expected: file that starts with '#HWD 1' or '#HWD 2'\n");
129         s.insert (0, SString("?"));
130         s.append (SString("?"));
131         all.append (s);
132       }
133       else
134       {
135         all.append (s);
136       }
137     }
138   }
139   if (all.size())
140   {
141     all.sort();
142     allConverters = all;
143     converters->setText (all);
144     setConverter (all[0]);
145   }
146   else
147   {
148     converters->setText (translate ("not found"));
149     converters->selectItem (0);
150   }
151 }
152 
153 /**
154  * Deletes this drawing. Nothing to do.
155  */
~SFreeHand()156 SFreeHand::~SFreeHand ()
157 {
158 }
159 /*
160 void
161 printl (const SLayout lo)
162 {
163   SLocation l = lo.getLocation();
164   SDimension d = lo.getDimension();
165   fprintf (stderr, "l= (%d,%d) d= (%u,%u)          l2 =(%d,%d)\n",
166     l.x, l.y, d.width, d.height, l.x + (int) d.width, l.y + d.height);
167 }
168 */
169 /**
170  * Recalculate constraints.
171  */
172 void
recalc()173 SFreeHand::recalc ()
174 {
175   int gap = 2;
176   int bgap = 4; /* between buttons */
177 
178   SDimension d = layout.getDimension ();
179   /* we should fit into this layout */
180 
181   // From left to right...
182   SLocation t0 = SLocation (titleLabel->getPreferredSize());
183   SLocation t1 = SLocation (strokesLabel->getPreferredSize());
184   SLocation t2 = SLocation (strokesCount->getPreferredSize());
185 
186   SLocation l0 = SLocation (converters->getPreferredSize());
187   SLocation m0 = SLocation (drawing->getPreferredSize());
188 
189   //SLocation b0 = SLocation (insertButton->getPreferredSize());
190   SLocation b1 = SLocation (lookupButton->getPreferredSize());
191   SLocation b0 = b1; // removed insertButton
192   SLocation b2 = SLocation (directedButton->getPreferredSize());
193   SLocation b3 = SLocation (clearButton->getPreferredSize());
194 
195   SLocation r0 = SLocation (candidates->getPreferredSize());
196   b2.x += 20; /* don't ask */
197   t2.x = t1.x;
198 
199   /* find longest button */
200   int bX = 0;
201   if (b0.x > bX) bX = b0.x;
202   if (b1.x > bX) bX = b1.x;
203   if (b2.x > bX) bX = b2.x;
204   if (b3.x > bX) bX = b3.x;
205   if (l0.x < r0.x + bX + bgap * 2) l0.x = r0.x + bX + bgap * 2;
206 
207   if (l0.y < b0.y + bgap + b1.y + bgap + b2.y)
208   {
209     l0.y = b0.y + bgap + b1.y + bgap + b2.y + bgap + b3.y;
210   }
211   /* assume drawing wants 140 */
212   if (l0.y < 140) l0.y = 140;
213   m0.x = 180; /* a bit more */
214   m0.y = l0.y;
215   r0.y = l0.y;
216   r0.x = r0.x + 20; /* just in case */
217 
218   /* find minimum height */
219   int minY = 2 + t1.y + + gap + l0.y;
220 
221   /* find minimum width, assume drawing wants 100.  */
222   int minX = 2 + l0.x + gap + m0.x + bgap + bX + bgap + r0.x;
223 
224   setMinimumSize (SDimension ((unsigned int)minX, (unsigned int) minY));
225   preferredSize = SDimension ((unsigned int)minX, (unsigned int) minY);
226 
227   /* top */
228   int x = 1;
229   int y = 1;
230   titleLabel->setLayout (SLayout (
231        SLocation (x, y),
232        SLocation (x+t0.x, y + t0.y),
233        SLocation (0, 0),
234        SLocation (0, 0)
235   ));
236 
237   x += (t0.x + 20);
238   strokesLabel->setLayout (SLayout (
239        SLocation (x, y),
240        SLocation (x + t1.x, y + t0.y),
241        SLocation (0, 0),
242        SLocation (0, 0)
243   ));
244   x += (t1.x + gap);
245   strokesCount->setLayout (SLayout (
246        SLocation (x, y),
247        SLocation (x + t2.x,  y + t0.y),
248        SLocation (0, 0),
249        SLocation (0, 0)
250   ));
251   /* new row. */
252   x = 1;
253   y +=  (t1.y + gap);
254 
255   converters->setLayout (SLayout (
256     SLocation (x, y),
257     SLocation (x + l0.x,  y + l0.y),
258     SLocation (0, 0),
259     SLocation (30, 100)
260   ));
261 
262   x += (l0.x + gap);
263 
264   drawing->setLayout (SLayout (
265     SLocation (x, y),
266     SLocation (x + m0.x,  y + m0.y),
267     SLocation (30, 0),
268     SLocation (70, 100)
269   ));
270 
271    /* set buttons later */
272   int butX = x + m0.x;
273 
274   /* right */
275   //int minX = 2 + l0.x + gap + 100 + bgap + bX + bgap + r0.width;
276   x =  minX - 1 - r0.x;
277 
278   candidates->setLayout (SLayout (
279     SLocation (x, y),
280     SLocation (x + r0.x,  y + r0.y),
281     SLocation (80, 0),
282     SLocation (100, 100)
283   ));
284 
285   int tbgap = r0.y - b0.y -bgap - b1.y -bgap -b2.y -bgap -b3.y;
286   if (tbgap < 0) tbgap = 0;
287 
288   /* now buttons between butx .. x- bgap */
289   x = butX + bgap;
290 
291   lookupButton->setLayout (SLayout (
292     SLocation (x, y),
293     SLocation (x + bX,  y + b0.y),
294     SLocation (70, 0),
295     SLocation (80, 0)
296   ));
297   y += (b0.y + bgap);
298 
299   y += ((b1.y + bgap))/2;
300   /* what is this gap ? there was a button here before ) */
301 
302   directedButton->setLayout (SLayout (
303     SLocation (x, y),
304     SLocation (x + bX,  y + b2.y),
305     SLocation (70, 66),
306     SLocation (80, 66)
307   ));
308   y += ((b1.y + bgap))/2 + tbgap;
309 
310 
311   y += (b2.y + bgap);
312 
313   clearButton->setLayout (SLayout (
314     SLocation (x, y),
315     SLocation (x + bX,  y + b3.y),
316     SLocation (70, 100),
317     SLocation (80, 100)
318   ));
319 
320   /* save current */
321   SLayout goodlayout = layout;
322 
323   /* pretend we have this layout */
324   forceLayout (preferredSize);
325 
326   /* accept old layout */
327   setLayout (goodlayout);
328 }
329 
330 /**
331  * ButtonListener
332  */
333 void
buttonPressedAccel(void * source,const SAccelerator * acc)334 SFreeHand::buttonPressedAccel (void* source, const SAccelerator* acc)
335 {
336   if (source == clearButton)
337   {
338     drawing->clear();
339     lookupText.clear();
340     SStringVector v;
341     candidates->setText (v);
342   }
343   else if (source == lookupButton)
344   {
345     convertOne();
346   }
347   if (source == directedButton)
348   {
349     isDirected = !isDirected;
350     const char * str = isDirected?"CircleOn" : "CircleOff";
351     directedButton->setIcon (SIconFactory::getIcon(str));
352     convertOne();
353   }
354 }
355 
356 /**
357  * ItemListener
358  */
359 void
itemSelected(void * source,const SAccelerator * acc)360 SFreeHand::itemSelected (void* source, const SAccelerator* acc)
361 {
362   if (source == candidates->textList)
363   {
364     SString str = candidates->textList->getLastSelectedText();
365     lookupText.clear();
366     if (str == SD_NOT_FOUND) return;
367     lookupText = str;
368     if (lookupText.size()>0 && listener != 0)
369     {
370       listener->freeHandTextChanged (this, lookupText);
371     }
372     window->putClipUTF8 (lookupText);
373   }
374   else
375   {
376     SString str = converters->textList->getLastSelectedText();
377     converter = SHWConverter(str);
378     convertOne();
379   }
380 }
381 
382 
383 /**
384  * There can be only one drawing listener.
385  */
386 void
setFreeHandListener(SFreeHandListener * _listener)387 SFreeHand::setFreeHandListener (SFreeHandListener* _listener)
388 {
389   listener = _listener;
390 }
391 
392 const SString&
getLookupText()393 SFreeHand::getLookupText()
394 {
395   return lookupText;
396 }
397 
398 void
setFont(const SString & font,double fontSize)399 SFreeHand::setFont (const SString& font, double fontSize)
400 {
401   candidates->textList->setFont (font, fontSize);
402   recalc ();
403 }
404 
405 void
setFontSize(double fontSize)406 SFreeHand::setFontSize (double fontSize)
407 {
408   candidates->textList->setFontSize (fontSize);
409   recalc ();
410 }
411 
412 void
setButtonFont(const SString & font,double fontSize)413 SFreeHand::setButtonFont (const SString& font, double fontSize)
414 {
415   lookupButton->setFont (font, fontSize);
416   clearButton->setFont (font, fontSize);
417   directedButton->setFont (font, fontSize);
418 
419   titleLabel->setFont (font, fontSize);
420   strokesLabel->setFont (font, fontSize);
421   strokesCount->setFont (font, fontSize);
422 
423   converters->setFont (font, fontSize);
424   candidates->setFont (font, fontSize);
425   recalc ();
426 }
427 
428 /**
429  * @param bg is the backgroud.
430  */
431 void
setBackground(const SColor & bg)432 SFreeHand::setBackground (const SColor& bg)
433 {
434   SPanel::setBackground (bg);
435 }
436 
437 /**
438  * Set the background of the drawing itself.
439  * @param bg is the backgroud.
440  */
441 void
setDrawingBackground(const SColor & bg)442 SFreeHand::setDrawingBackground (const SColor& bg)
443 {
444   drawing->setTextBackground (bg);
445 }
446 
447 void
setSliderBackground(const SColor & bg)448 SFreeHand::setSliderBackground (const SColor& bg)
449 {
450   candidates->setSliderBackground (bg);
451   converters->setSliderBackground (bg);
452 }
453 
454 /**
455  * Set the foreground of the drawing itself.
456  * @param fg is the foreground
457  * @param fgrecent is the foreground of the last line, being drawn
458  */
459 void
setForeground(const SColor & lrfg,const SColor & rlfg)460 SFreeHand::setForeground (const SColor& lrfg, const SColor& rlfg)
461 {
462   lookupButton->setForeground (lrfg);
463   clearButton->setForeground (lrfg);
464   directedButton->setForeground (lrfg);
465 
466   candidates->setLabelForeground (lrfg);
467   converters->setLabelForeground (lrfg);
468 
469   titleLabel->setForeground (lrfg);
470   strokesLabel->setForeground (lrfg);
471 }
472 
473 /**
474  * Set the foreground of the label
475  * @param fg is the foreground
476  * @param fgrecent is the foreground of the last line, being drawn
477  */
478 void
setLabelForeground(const SColor & lrfg,const SColor & rlfg)479 SFreeHand::setLabelForeground (const SColor& lrfg, const SColor& rlfg)
480 {
481   strokesCount->setForeground (lrfg);
482 }
483 
484 /**
485  * Set the foreground of the drawing itself.
486  * @param fg is the foreground
487  * @param fgrecent is the foreground of the last line, being drawn
488  */
489 void
setDrawingForeground(const SColor & _fg,const SColor & fgrecent)490 SFreeHand::setDrawingForeground (const SColor& _fg, const SColor& fgrecent)
491 {
492   drawing->setForeground (_fg, fgrecent);
493 }
494 
495 /**
496  * Redraw the Component on a canvas
497  * @param canvas is where we redraw this.
498  */
499 void
redraw(SCanvas * canvas,int x,int y,unsigned int width,unsigned int height)500 SFreeHand::redraw (SCanvas *canvas, int x, int y, unsigned int width, unsigned int height)
501 {
502   SPanel::redraw (canvas, x, y, width, height);
503 }
504 
505 void
resize(const SDimension & _size)506 SFreeHand::resize (const SDimension& _size)
507 {
508   SPanel::resize (_size);
509 }
510 void
setConverter(const SString & name)511 SFreeHand::setConverter (const SString& name)
512 {
513   for (unsigned int i=0; i<allConverters.size(); i++)
514   {
515     if (allConverters[i] == name)
516     {
517       converter = SHWConverter(name);
518       drawing->clear();
519       lookupText.clear();
520       candidates->setText (SStringVector());
521       converters->selectItem (i);
522       return;
523     }
524   }
525 }
526 
527 const SString&
getConverter() const528 SFreeHand::getConverter() const
529 {
530   return converter.getName();
531 }
532 
533 bool
isOK() const534 SFreeHand::isOK () const
535 {
536   return converter.isOK();
537 }
538 
539 void
clicked(void * source,int button)540 SFreeHand::clicked (void* source, int button)
541 {
542   if (button==2)
543   {
544     drawing->undo();
545   }
546   if (button==1)
547   {
548     drawing->clear();
549     lookupText.clear();
550     candidates->setText (SString());
551     return;
552   }
553 }
554 
555 void
strokeChanged(void * src,unsigned int newsize)556 SFreeHand::strokeChanged(void* src, unsigned int newsize)
557 {
558   SString str;
559   str.print ((int)newsize);
560   strokesCount->setText (str);
561   candidates->setText (SStringVector());
562   needConversion = true;
563 }
564 
565 void
convertOne()566 SFreeHand::convertOne ()
567 {
568   needConversion  = false;
569   SLineCurves set = drawing->getDrawing();
570   if (set.size()==0)
571   {
572     lookupText = "";
573     candidates->setText (SStringVector());
574     return;
575   }
576   SStringVector v = converter.convert (set, isDirected);
577   if (v.size()==0)
578   {
579     lookupText = "";
580     v.append (SD_NOT_FOUND);
581     candidates->setText (v);
582     return;
583   }
584   lookupText = v[0];
585   candidates->setText (v);
586   window->putClipUTF8 (v[0]);
587 }
588