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