]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/intlGUIEditBox.cpp
0c596d3b94885b8eed7c7eddc674f20593601b60
[dragonfireclient.git] / src / gui / intlGUIEditBox.cpp
1 // 11.11.2011 11:11 ValkaTR
2 //
3 // This is a copy of intlGUIEditBox from the irrlicht, but with a
4 // fix in the OnEvent function, which doesn't allowed input of
5 // other keyboard layouts than latin-1
6 //
7 // Characters like: ä ö ü õ ы й ю я ъ № € ° ...
8 //
9 // This fix is only needed for linux, because of a bug
10 // in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht
11 //
12 // Also locale in the programm should not be changed to
13 // a "C", "POSIX" or whatever, it should be set to "",
14 // or XLookupString will return nothing for the international
15 // characters.
16 //
17 // From the "man setlocale":
18 //
19 // On startup of the main program, the portable "C" locale
20 // is selected as default.  A  program  may  be  made
21 // portable to all locales by calling:
22 //
23 //           setlocale(LC_ALL, "");
24 //
25 //       after  program initialization....
26 //
27
28 // Copyright (C) 2002-2013 Nikolaus Gebhardt
29 // This file is part of the "Irrlicht Engine".
30 // For conditions of distribution and use, see copyright notice in irrlicht.h
31
32 #include <util/numeric.h>
33 #include "intlGUIEditBox.h"
34
35 #if defined(_IRR_COMPILE_WITH_GUI_) && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 || defined(__ANDROID__)
36
37 #include "IGUISkin.h"
38 #include "IGUIEnvironment.h"
39 #include "IGUIFont.h"
40 #include "IVideoDriver.h"
41 //#include "irrlicht/os.cpp"
42 #include "porting.h"
43 //#include "Keycodes.h"
44 #include "log.h"
45
46 /*
47         todo:
48         optional scrollbars
49         ctrl+left/right to select word
50         double click/ctrl click: word select + drag to select whole words, triple click to select line
51         optional? dragging selected text
52         numerical
53 */
54
55 namespace irr
56 {
57 namespace gui
58 {
59
60 //! constructor
61 intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
62                 IGUIEnvironment* environment, IGUIElement* parent, s32 id,
63                 const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
64         : IGUIEditBox(environment, parent, id, rectangle),
65         Border(border), FrameRect(rectangle),
66         m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable)
67 {
68         #ifdef _DEBUG
69         setDebugName("intlintlGUIEditBox");
70         #endif
71
72         Text = text;
73
74         if (Environment)
75                 Operator = Environment->getOSOperator();
76
77         if (Operator)
78                 Operator->grab();
79
80         // this element can be tabbed to
81         setTabStop(true);
82         setTabOrder(-1);
83
84         IGUISkin *skin = 0;
85         if (Environment)
86                 skin = Environment->getSkin();
87         if (Border && skin)
88         {
89                 FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
90                 FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
91                 FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
92                 FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
93         }
94
95         if (skin && has_vscrollbar) {
96                 m_scrollbar_width = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
97
98                 if (m_scrollbar_width > 0) {
99                         createVScrollBar();
100                 }
101         }
102
103         breakText();
104
105         calculateScrollPos();
106         setWritable(writable);
107 }
108
109
110 //! destructor
111 intlGUIEditBox::~intlGUIEditBox()
112 {
113         if (OverrideFont)
114                 OverrideFont->drop();
115
116         if (Operator)
117                 Operator->drop();
118 }
119
120
121 //! Sets another skin independent font.
122 void intlGUIEditBox::setOverrideFont(IGUIFont* font)
123 {
124         if (OverrideFont == font)
125                 return;
126
127         if (OverrideFont)
128                 OverrideFont->drop();
129
130         OverrideFont = font;
131
132         if (OverrideFont)
133                 OverrideFont->grab();
134
135         breakText();
136 }
137
138 IGUIFont * intlGUIEditBox::getOverrideFont() const
139 {
140         return OverrideFont;
141 }
142
143 //! Get the font which is used right now for drawing
144 IGUIFont* intlGUIEditBox::getActiveFont() const
145 {
146         if ( OverrideFont )
147                 return OverrideFont;
148         IGUISkin* skin = Environment->getSkin();
149         if (skin)
150                 return skin->getFont();
151         return 0;
152 }
153
154 //! Sets another color for the text.
155 void intlGUIEditBox::setOverrideColor(video::SColor color)
156 {
157         OverrideColor = color;
158         OverrideColorEnabled = true;
159 }
160
161 video::SColor intlGUIEditBox::getOverrideColor() const
162 {
163         return OverrideColor;
164 }
165
166 //! Turns the border on or off
167 void intlGUIEditBox::setDrawBorder(bool border)
168 {
169         Border = border;
170 }
171
172 //! Sets whether to draw the background
173 void intlGUIEditBox::setDrawBackground(bool draw)
174 {
175 }
176
177 //! Sets if the text should use the overide color or the color in the gui skin.
178 void intlGUIEditBox::enableOverrideColor(bool enable)
179 {
180         OverrideColorEnabled = enable;
181 }
182
183 bool intlGUIEditBox::isOverrideColorEnabled() const
184 {
185         return OverrideColorEnabled;
186 }
187
188 //! Enables or disables word wrap
189 void intlGUIEditBox::setWordWrap(bool enable)
190 {
191         WordWrap = enable;
192         breakText();
193 }
194
195
196 void intlGUIEditBox::updateAbsolutePosition()
197 {
198     core::rect<s32> oldAbsoluteRect(AbsoluteRect);
199         IGUIElement::updateAbsolutePosition();
200         if ( oldAbsoluteRect != AbsoluteRect )
201         {
202         breakText();
203         }
204 }
205
206
207 //! Checks if word wrap is enabled
208 bool intlGUIEditBox::isWordWrapEnabled() const
209 {
210         return WordWrap;
211 }
212
213
214 //! Enables or disables newlines.
215 void intlGUIEditBox::setMultiLine(bool enable)
216 {
217         MultiLine = enable;
218 }
219
220
221 //! Checks if multi line editing is enabled
222 bool intlGUIEditBox::isMultiLineEnabled() const
223 {
224         return MultiLine;
225 }
226
227
228 void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
229 {
230         PasswordBox = passwordBox;
231         if (PasswordBox)
232         {
233                 PasswordChar = passwordChar;
234                 setMultiLine(false);
235                 setWordWrap(false);
236                 BrokenText.clear();
237         }
238 }
239
240
241 bool intlGUIEditBox::isPasswordBox() const
242 {
243         return PasswordBox;
244 }
245
246
247 //! Sets text justification
248 void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
249 {
250         HAlign = horizontal;
251         VAlign = vertical;
252 }
253
254
255 //! called if an event happened.
256 bool intlGUIEditBox::OnEvent(const SEvent& event)
257 {
258         if (IsEnabled)
259         {
260
261                 switch(event.EventType)
262                 {
263                 case EET_GUI_EVENT:
264                         if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
265                         {
266                                 if (event.GUIEvent.Caller == this)
267                                 {
268                                         MouseMarking = false;
269                                         setTextMarkers(0,0);
270                                 }
271                         }
272                         break;
273                 case EET_KEY_INPUT_EVENT:
274         {
275 #if (defined(__linux__) || defined(__FreeBSD__))
276             // ################################################################
277                         // ValkaTR:
278             // This part is the difference from the original intlGUIEditBox
279             // It converts UTF-8 character into a UCS-2 (wchar_t)
280             wchar_t wc = L'_';
281             mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
282
283             //printf( "char: %lc (%u)  \r\n", wc, wc );
284
285             SEvent irrevent(event);
286             irrevent.KeyInput.Char = wc;
287             // ################################################################
288
289                         if (processKey(irrevent))
290                                 return true;
291 #else
292                         if (processKey(event))
293                                 return true;
294 #endif // defined(linux)
295
296                         break;
297         }
298                 case EET_MOUSE_INPUT_EVENT:
299                         if (processMouse(event))
300                                 return true;
301                         break;
302                 default:
303                         break;
304                 }
305         }
306
307         return IGUIElement::OnEvent(event);
308 }
309
310
311 bool intlGUIEditBox::processKey(const SEvent& event)
312 {
313         if (!event.KeyInput.PressedDown)
314                 return false;
315
316         bool textChanged = false;
317         s32 newMarkBegin = MarkBegin;
318         s32 newMarkEnd = MarkEnd;
319
320         // control shortcut handling
321
322         if (event.KeyInput.Control)
323         {
324                 // german backlash '\' entered with control + '?'
325                 if ( event.KeyInput.Char == '\\' )
326                 {
327                         inputChar(event.KeyInput.Char);
328                         return true;
329                 }
330
331                 switch(event.KeyInput.Key)
332                 {
333                 case KEY_KEY_A:
334                         // select all
335                         newMarkBegin = 0;
336                         newMarkEnd = Text.size();
337                         break;
338                 case KEY_KEY_C:
339                         // copy to clipboard
340                         if (!PasswordBox && Operator && MarkBegin != MarkEnd)
341                         {
342                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
343                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
344
345                                 core::stringc s;
346                                 s = Text.subString(realmbgn, realmend - realmbgn).c_str();
347                                 Operator->copyToClipboard(s.c_str());
348                         }
349                         break;
350                 case KEY_KEY_X:
351                         // cut to the clipboard
352                         if (!PasswordBox && Operator && MarkBegin != MarkEnd) {
353                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
354                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
355
356                                 // copy
357                                 core::stringc sc;
358                                 sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
359                                 Operator->copyToClipboard(sc.c_str());
360
361                                 if (IsEnabled && m_writable) {
362                                         // delete
363                                         core::stringw s;
364                                         s = Text.subString(0, realmbgn);
365                                         s.append( Text.subString(realmend, Text.size()-realmend) );
366                                         Text = s;
367
368                                         CursorPos = realmbgn;
369                                         newMarkBegin = 0;
370                                         newMarkEnd = 0;
371                                         textChanged = true;
372                                 }
373                         }
374                         break;
375                 case KEY_KEY_V:
376                         if (!IsEnabled || !m_writable)
377                                 break;
378
379                         // paste from the clipboard
380                         if (Operator)
381                         {
382                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
383                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
384
385                                 // add new character
386                                 const c8* p = Operator->getTextFromClipboard();
387                                 if (p)
388                                 {
389                                         if (MarkBegin == MarkEnd)
390                                         {
391                                                 // insert text
392                                                 core::stringw s = Text.subString(0, CursorPos);
393                                                 s.append(p);
394                                                 s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
395
396                                                 if (!Max || s.size()<=Max) // thx to Fish FH for fix
397                                                 {
398                                                         Text = s;
399                                                         s = p;
400                                                         CursorPos += s.size();
401                                                 }
402                                         }
403                                         else
404                                         {
405                                                 // replace text
406
407                                                 core::stringw s = Text.subString(0, realmbgn);
408                                                 s.append(p);
409                                                 s.append( Text.subString(realmend, Text.size()-realmend) );
410
411                                                 if (!Max || s.size()<=Max)  // thx to Fish FH for fix
412                                                 {
413                                                         Text = s;
414                                                         s = p;
415                                                         CursorPos = realmbgn + s.size();
416                                                 }
417                                         }
418                                 }
419
420                                 newMarkBegin = 0;
421                                 newMarkEnd = 0;
422                                 textChanged = true;
423                         }
424                         break;
425                 case KEY_HOME:
426                         // move/highlight to start of text
427                         if (event.KeyInput.Shift)
428                         {
429                                 newMarkEnd = CursorPos;
430                                 newMarkBegin = 0;
431                                 CursorPos = 0;
432                         }
433                         else
434                         {
435                                 CursorPos = 0;
436                                 newMarkBegin = 0;
437                                 newMarkEnd = 0;
438                         }
439                         break;
440                 case KEY_END:
441                         // move/highlight to end of text
442                         if (event.KeyInput.Shift)
443                         {
444                                 newMarkBegin = CursorPos;
445                                 newMarkEnd = Text.size();
446                                 CursorPos = 0;
447                         }
448                         else
449                         {
450                                 CursorPos = Text.size();
451                                 newMarkBegin = 0;
452                                 newMarkEnd = 0;
453                         }
454                         break;
455                 default:
456                         return false;
457                 }
458         }
459         // default keyboard handling
460         else
461         switch(event.KeyInput.Key)
462         {
463         case KEY_END:
464                 {
465                         s32 p = Text.size();
466                         if (WordWrap || MultiLine)
467                         {
468                                 p = getLineFromPos(CursorPos);
469                                 p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
470                                 if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
471                                         p-=1;
472                         }
473
474                         if (event.KeyInput.Shift)
475                         {
476                                 if (MarkBegin == MarkEnd)
477                                         newMarkBegin = CursorPos;
478
479                                 newMarkEnd = p;
480                         }
481                         else
482                         {
483                                 newMarkBegin = 0;
484                                 newMarkEnd = 0;
485                         }
486                         CursorPos = p;
487                         BlinkStartTime = porting::getTimeMs();
488                 }
489                 break;
490         case KEY_HOME:
491                 {
492
493                         s32 p = 0;
494                         if (WordWrap || MultiLine)
495                         {
496                                 p = getLineFromPos(CursorPos);
497                                 p = BrokenTextPositions[p];
498                         }
499
500                         if (event.KeyInput.Shift)
501                         {
502                                 if (MarkBegin == MarkEnd)
503                                         newMarkBegin = CursorPos;
504                                 newMarkEnd = p;
505                         }
506                         else
507                         {
508                                 newMarkBegin = 0;
509                                 newMarkEnd = 0;
510                         }
511                         CursorPos = p;
512                         BlinkStartTime = porting::getTimeMs();
513                 }
514                 break;
515         case KEY_RETURN:
516                 if (MultiLine)
517                 {
518                         inputChar(L'\n');
519                         return true;
520                 }
521                 else
522                 {
523                     sendGuiEvent( EGET_EDITBOX_ENTER );
524                 }
525                 break;
526         case KEY_LEFT:
527
528                 if (event.KeyInput.Shift)
529                 {
530                         if (CursorPos > 0)
531                         {
532                                 if (MarkBegin == MarkEnd)
533                                         newMarkBegin = CursorPos;
534
535                                 newMarkEnd = CursorPos-1;
536                         }
537                 }
538                 else
539                 {
540                         newMarkBegin = 0;
541                         newMarkEnd = 0;
542                 }
543
544                 if (CursorPos > 0) CursorPos--;
545                 BlinkStartTime = porting::getTimeMs();
546                 break;
547
548         case KEY_RIGHT:
549                 if (event.KeyInput.Shift)
550                 {
551                         if (Text.size() > (u32)CursorPos)
552                         {
553                                 if (MarkBegin == MarkEnd)
554                                         newMarkBegin = CursorPos;
555
556                                 newMarkEnd = CursorPos+1;
557                         }
558                 }
559                 else
560                 {
561                         newMarkBegin = 0;
562                         newMarkEnd = 0;
563                 }
564
565                 if (Text.size() > (u32)CursorPos) CursorPos++;
566                 BlinkStartTime = porting::getTimeMs();
567                 break;
568         case KEY_UP:
569                 if (MultiLine || (WordWrap && BrokenText.size() > 1) )
570                 {
571                         s32 lineNo = getLineFromPos(CursorPos);
572                         s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
573                         if (lineNo > 0)
574                         {
575                                 s32 cp = CursorPos - BrokenTextPositions[lineNo];
576                                 if ((s32)BrokenText[lineNo-1].size() < cp)
577                                         CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
578                                 else
579                                         CursorPos = BrokenTextPositions[lineNo-1] + cp;
580                         }
581
582                         if (event.KeyInput.Shift)
583                         {
584                                 newMarkBegin = mb;
585                                 newMarkEnd = CursorPos;
586                         }
587                         else
588                         {
589                                 newMarkBegin = 0;
590                                 newMarkEnd = 0;
591                         }
592
593                 }
594                 else
595                 {
596                         return false;
597                 }
598                 break;
599         case KEY_DOWN:
600                 if (MultiLine || (WordWrap && BrokenText.size() > 1) )
601                 {
602                         s32 lineNo = getLineFromPos(CursorPos);
603                         s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
604                         if (lineNo < (s32)BrokenText.size()-1)
605                         {
606                                 s32 cp = CursorPos - BrokenTextPositions[lineNo];
607                                 if ((s32)BrokenText[lineNo+1].size() < cp)
608                                         CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
609                                 else
610                                         CursorPos = BrokenTextPositions[lineNo+1] + cp;
611                         }
612
613                         if (event.KeyInput.Shift)
614                         {
615                                 newMarkBegin = mb;
616                                 newMarkEnd = CursorPos;
617                         }
618                         else
619                         {
620                                 newMarkBegin = 0;
621                                 newMarkEnd = 0;
622                         }
623
624                 }
625                 else
626                 {
627                         return false;
628                 }
629                 break;
630
631         case KEY_BACK:
632                 if (!this->IsEnabled || !m_writable)
633                         break;
634
635                 if (!Text.empty()) {
636                         core::stringw s;
637
638                         if (MarkBegin != MarkEnd)
639                         {
640                                 // delete marked text
641                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
642                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
643
644                                 s = Text.subString(0, realmbgn);
645                                 s.append( Text.subString(realmend, Text.size()-realmend) );
646                                 Text = s;
647
648                                 CursorPos = realmbgn;
649                         }
650                         else
651                         {
652                                 // delete text behind cursor
653                                 if (CursorPos>0)
654                                         s = Text.subString(0, CursorPos-1);
655                                 else
656                                         s = L"";
657                                 s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
658                                 Text = s;
659                                 --CursorPos;
660                         }
661
662                         if (CursorPos < 0)
663                                 CursorPos = 0;
664                         BlinkStartTime = porting::getTimeMs();
665                         newMarkBegin = 0;
666                         newMarkEnd = 0;
667                         textChanged = true;
668                 }
669                 break;
670         case KEY_DELETE:
671                 if (!this->IsEnabled || !m_writable)
672                         break;
673
674                 if (!Text.empty()) {
675                         core::stringw s;
676
677                         if (MarkBegin != MarkEnd)
678                         {
679                                 // delete marked text
680                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
681                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
682
683                                 s = Text.subString(0, realmbgn);
684                                 s.append( Text.subString(realmend, Text.size()-realmend) );
685                                 Text = s;
686
687                                 CursorPos = realmbgn;
688                         }
689                         else
690                         {
691                                 // delete text before cursor
692                                 s = Text.subString(0, CursorPos);
693                                 s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
694                                 Text = s;
695                         }
696
697                         if (CursorPos > (s32)Text.size())
698                                 CursorPos = (s32)Text.size();
699
700                         BlinkStartTime = porting::getTimeMs();
701                         newMarkBegin = 0;
702                         newMarkEnd = 0;
703                         textChanged = true;
704                 }
705                 break;
706
707         case KEY_ESCAPE:
708         case KEY_TAB:
709         case KEY_SHIFT:
710         case KEY_F1:
711         case KEY_F2:
712         case KEY_F3:
713         case KEY_F4:
714         case KEY_F5:
715         case KEY_F6:
716         case KEY_F7:
717         case KEY_F8:
718         case KEY_F9:
719         case KEY_F10:
720         case KEY_F11:
721         case KEY_F12:
722         case KEY_F13:
723         case KEY_F14:
724         case KEY_F15:
725         case KEY_F16:
726         case KEY_F17:
727         case KEY_F18:
728         case KEY_F19:
729         case KEY_F20:
730         case KEY_F21:
731         case KEY_F22:
732         case KEY_F23:
733         case KEY_F24:
734                 // ignore these keys
735                 return false;
736
737         default:
738                 inputChar(event.KeyInput.Char);
739                 return true;
740         }
741
742     // Set new text markers
743     setTextMarkers( newMarkBegin, newMarkEnd );
744
745         // break the text if it has changed
746         if (textChanged)
747         {
748                 breakText();
749                 sendGuiEvent(EGET_EDITBOX_CHANGED);
750         }
751
752         calculateScrollPos();
753
754         return true;
755 }
756
757
758 //! draws the element and its children
759 void intlGUIEditBox::draw()
760 {
761         if (!IsVisible)
762                 return;
763
764         const bool focus = Environment->hasFocus(this);
765
766         IGUISkin* skin = Environment->getSkin();
767         if (!skin)
768                 return;
769
770         FrameRect = AbsoluteRect;
771
772         // draw the border
773
774         if (Border)
775         {
776                 if (m_writable) {
777                         skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
778                                 false, true, FrameRect, &AbsoluteClippingRect);
779                 }
780
781                 FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
782                 FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
783                 FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
784                 FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
785         }
786
787         updateVScrollBar();
788         core::rect<s32> localClipRect = FrameRect;
789         localClipRect.clipAgainst(AbsoluteClippingRect);
790
791         // draw the text
792
793         IGUIFont* font = OverrideFont;
794         if (!OverrideFont)
795                 font = skin->getFont();
796
797         s32 cursorLine = 0;
798         s32 charcursorpos = 0;
799
800         if (font)
801         {
802                 if (LastBreakFont != font)
803                 {
804                         breakText();
805                 }
806
807                 // calculate cursor pos
808
809                 core::stringw *txtLine = &Text;
810                 s32 startPos = 0;
811
812                 core::stringw s, s2;
813
814                 // get mark position
815                 const bool ml = (!PasswordBox && (WordWrap || MultiLine));
816                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
817                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
818                 const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
819                 const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
820                 const s32 lineCount = ml ? BrokenText.size() : 1;
821
822                 // Save the override color information.
823                 // Then, alter it if the edit box is disabled.
824                 const bool prevOver = OverrideColorEnabled;
825                 const video::SColor prevColor = OverrideColor;
826
827                 if (!Text.empty()) {
828                         if (!IsEnabled && !OverrideColorEnabled)
829                         {
830                                 OverrideColorEnabled = true;
831                                 OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
832                         }
833
834                         for (s32 i=0; i < lineCount; ++i)
835                         {
836                                 setTextRect(i);
837
838                                 // clipping test - don't draw anything outside the visible area
839                                 core::rect<s32> c = localClipRect;
840                                 c.clipAgainst(CurrentTextRect);
841                                 if (!c.isValid())
842                                         continue;
843
844                                 // get current line
845                                 if (PasswordBox)
846                                 {
847                                         if (BrokenText.size() != 1)
848                                         {
849                                                 BrokenText.clear();
850                                                 BrokenText.push_back(core::stringw());
851                                         }
852                                         if (BrokenText[0].size() != Text.size())
853                                         {
854                                                 BrokenText[0] = Text;
855                                                 for (u32 q = 0; q < Text.size(); ++q)
856                                                 {
857                                                         BrokenText[0] [q] = PasswordChar;
858                                                 }
859                                         }
860                                         txtLine = &BrokenText[0];
861                                         startPos = 0;
862                                 }
863                                 else
864                                 {
865                                         txtLine = ml ? &BrokenText[i] : &Text;
866                                         startPos = ml ? BrokenTextPositions[i] : 0;
867                                 }
868
869
870                                 // draw normal text
871                                 font->draw(txtLine->c_str(), CurrentTextRect,
872                                         OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
873                                         false, true, &localClipRect);
874
875                                 // draw mark and marked text
876                                 if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
877                                 {
878
879                                         s32 mbegin = 0, mend = 0;
880                                         s32 lineStartPos = 0, lineEndPos = txtLine->size();
881
882                                         if (i == hlineStart)
883                                         {
884                                                 // highlight start is on this line
885                                                 s = txtLine->subString(0, realmbgn - startPos);
886                                                 mbegin = font->getDimension(s.c_str()).Width;
887
888                                                 // deal with kerning
889                                                 mbegin += font->getKerningWidth(
890                                                         &((*txtLine)[realmbgn - startPos]),
891                                                         realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
892
893                                                 lineStartPos = realmbgn - startPos;
894                                         }
895                                         if (i == hlineStart + hlineCount - 1)
896                                         {
897                                                 // highlight end is on this line
898                                                 s2 = txtLine->subString(0, realmend - startPos);
899                                                 mend = font->getDimension(s2.c_str()).Width;
900                                                 lineEndPos = (s32)s2.size();
901                                         }
902                                         else
903                                                 mend = font->getDimension(txtLine->c_str()).Width;
904
905                                         CurrentTextRect.UpperLeftCorner.X += mbegin;
906                                         CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
907
908                                         // draw mark
909                                         skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
910
911                                         // draw marked text
912                                         s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
913
914                                         if (!s.empty())
915                                                 font->draw(s.c_str(), CurrentTextRect,
916                                                         OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
917                                                         false, true, &localClipRect);
918
919                                 }
920                         }
921
922                         // Return the override color information to its previous settings.
923                         OverrideColorEnabled = prevOver;
924                         OverrideColor = prevColor;
925                 }
926
927                 // draw cursor
928
929                 if (WordWrap || MultiLine)
930                 {
931                         cursorLine = getLineFromPos(CursorPos);
932                         txtLine = &BrokenText[cursorLine];
933                         startPos = BrokenTextPositions[cursorLine];
934                 }
935                 s = txtLine->subString(0,CursorPos-startPos);
936                 charcursorpos = font->getDimension(s.c_str()).Width +
937                         font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
938
939                 if (m_writable) {
940                         if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350) {
941                                 setTextRect(cursorLine);
942                                 CurrentTextRect.UpperLeftCorner.X += charcursorpos;
943
944                                 font->draw(L"_", CurrentTextRect,
945                                         OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
946                                         false, true, &localClipRect);
947                         }
948                 }
949         }
950
951         // draw children
952         IGUIElement::draw();
953 }
954
955
956 //! Sets the new caption of this element.
957 void intlGUIEditBox::setText(const wchar_t* text)
958 {
959         Text = text;
960         if (u32(CursorPos) > Text.size())
961                 CursorPos = Text.size();
962         HScrollPos = 0;
963         breakText();
964 }
965
966
967 //! Enables or disables automatic scrolling with cursor position
968 //! \param enable: If set to true, the text will move around with the cursor position
969 void intlGUIEditBox::setAutoScroll(bool enable)
970 {
971         AutoScroll = enable;
972 }
973
974
975 //! Checks to see if automatic scrolling is enabled
976 //! \return true if automatic scrolling is enabled, false if not
977 bool intlGUIEditBox::isAutoScrollEnabled() const
978 {
979         return AutoScroll;
980 }
981
982
983 //! Gets the area of the text in the edit box
984 //! \return Returns the size in pixels of the text
985 core::dimension2du intlGUIEditBox::getTextDimension()
986 {
987         core::rect<s32> ret;
988
989         setTextRect(0);
990         ret = CurrentTextRect;
991
992         for (u32 i=1; i < BrokenText.size(); ++i)
993         {
994                 setTextRect(i);
995                 ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
996                 ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
997         }
998
999         return core::dimension2du(ret.getSize());
1000 }
1001
1002
1003 //! Sets the maximum amount of characters which may be entered in the box.
1004 //! \param max: Maximum amount of characters. If 0, the character amount is
1005 //! infinity.
1006 void intlGUIEditBox::setMax(u32 max)
1007 {
1008         Max = max;
1009
1010         if (Text.size() > Max && Max != 0)
1011                 Text = Text.subString(0, Max);
1012 }
1013
1014
1015 //! Returns maximum amount of characters, previously set by setMax();
1016 u32 intlGUIEditBox::getMax() const
1017 {
1018         return Max;
1019 }
1020
1021
1022 bool intlGUIEditBox::processMouse(const SEvent& event)
1023 {
1024         switch(event.MouseInput.Event)
1025         {
1026         case irr::EMIE_LMOUSE_LEFT_UP:
1027                 if (Environment->hasFocus(this))
1028                 {
1029                         CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
1030                         if (MouseMarking)
1031                         {
1032                             setTextMarkers( MarkBegin, CursorPos );
1033                         }
1034                         MouseMarking = false;
1035                         calculateScrollPos();
1036                         return true;
1037                 }
1038                 break;
1039         case irr::EMIE_MOUSE_MOVED:
1040                 {
1041                         if (MouseMarking)
1042                         {
1043                                 CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
1044                                 setTextMarkers( MarkBegin, CursorPos );
1045                                 calculateScrollPos();
1046                                 return true;
1047                         }
1048                 }
1049                 break;
1050         case EMIE_LMOUSE_PRESSED_DOWN:
1051                 if (!Environment->hasFocus(this))
1052                 {
1053                         BlinkStartTime = porting::getTimeMs();
1054                         MouseMarking = true;
1055                         CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
1056                         setTextMarkers(CursorPos, CursorPos );
1057                         calculateScrollPos();
1058                         return true;
1059                 }
1060                 else
1061                 {
1062                         if (!AbsoluteClippingRect.isPointInside(
1063                                 core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
1064                                 return false;
1065                         }
1066
1067
1068                         // move cursor
1069                         CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
1070
1071                         s32 newMarkBegin = MarkBegin;
1072                         if (!MouseMarking)
1073                                 newMarkBegin = CursorPos;
1074
1075                         MouseMarking = true;
1076                         setTextMarkers( newMarkBegin, CursorPos);
1077                         calculateScrollPos();
1078                         return true;
1079                 }
1080                 break;
1081         case EMIE_MOUSE_WHEEL:
1082                 if (m_vscrollbar && m_vscrollbar->isVisible()) {
1083                         s32 pos = m_vscrollbar->getPos();
1084                         s32 step = m_vscrollbar->getSmallStep();
1085                         m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
1086                 }
1087                 break;
1088         default:
1089                 break;
1090         }
1091
1092         return false;
1093 }
1094
1095
1096 s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
1097 {
1098         IGUIFont* font = OverrideFont;
1099         IGUISkin* skin = Environment->getSkin();
1100         if (!OverrideFont)
1101                 font = skin->getFont();
1102
1103         const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
1104
1105         core::stringw *txtLine = NULL;
1106         s32 startPos = 0;
1107         u32 curr_line_idx = 0;
1108         x += 3;
1109
1110         for (; curr_line_idx < lineCount; ++curr_line_idx) {
1111                 setTextRect(curr_line_idx);
1112                 if (curr_line_idx == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
1113                         y = CurrentTextRect.UpperLeftCorner.Y;
1114                 if (curr_line_idx == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y)
1115                         y = CurrentTextRect.LowerRightCorner.Y;
1116
1117                 // is it inside this region?
1118                 if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) {
1119                         // we've found the clicked line
1120                         txtLine = (WordWrap || MultiLine) ? &BrokenText[curr_line_idx] : &Text;
1121                         startPos = (WordWrap || MultiLine) ? BrokenTextPositions[curr_line_idx] : 0;
1122                         break;
1123                 }
1124         }
1125
1126         if (x < CurrentTextRect.UpperLeftCorner.X)
1127                 x = CurrentTextRect.UpperLeftCorner.X;
1128         else if (x > CurrentTextRect.LowerRightCorner.X)
1129                 x = CurrentTextRect.LowerRightCorner.X;
1130
1131         s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);
1132         // Special handling for last line, if we are on limits, add 1 extra shift because idx
1133         // will be the last char, not null char of the wstring
1134         if (curr_line_idx == lineCount - 1 && x == CurrentTextRect.LowerRightCorner.X)
1135                 idx++;
1136
1137         return rangelim(idx + startPos, 0, S32_MAX);
1138 }
1139
1140
1141 //! Breaks the single text line.
1142 void intlGUIEditBox::breakText()
1143 {
1144         IGUISkin* skin = Environment->getSkin();
1145
1146         if ((!WordWrap && !MultiLine) || !skin)
1147                 return;
1148
1149         BrokenText.clear(); // need to reallocate :/
1150         BrokenTextPositions.set_used(0);
1151
1152         IGUIFont* font = OverrideFont;
1153         if (!OverrideFont)
1154                 font = skin->getFont();
1155
1156         if (!font)
1157                 return;
1158
1159         LastBreakFont = font;
1160
1161         core::stringw line;
1162         core::stringw word;
1163         core::stringw whitespace;
1164         s32 lastLineStart = 0;
1165         s32 size = Text.size();
1166         s32 length = 0;
1167         s32 elWidth = RelativeRect.getWidth() - 6;
1168         wchar_t c;
1169
1170         for (s32 i=0; i<size; ++i)
1171         {
1172                 c = Text[i];
1173                 bool lineBreak = false;
1174
1175                 if (c == L'\r') // Mac or Windows breaks
1176                 {
1177                         lineBreak = true;
1178                         c = ' ';
1179                         if (Text[i+1] == L'\n') // Windows breaks
1180                         {
1181                                 Text.erase(i+1);
1182                                 --size;
1183                         }
1184                 }
1185                 else if (c == L'\n') // Unix breaks
1186                 {
1187                         lineBreak = true;
1188                         c = ' ';
1189                 }
1190
1191                 // don't break if we're not a multi-line edit box
1192                 if (!MultiLine)
1193                         lineBreak = false;
1194
1195                 if (c == L' ' || c == 0 || i == (size-1))
1196                 {
1197                         if (!word.empty()) {
1198                                 // here comes the next whitespace, look if
1199                                 // we can break the last word to the next line.
1200                                 s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
1201                                 s32 worldlgth = font->getDimension(word.c_str()).Width;
1202
1203                                 if (WordWrap && length + worldlgth + whitelgth > elWidth)
1204                                 {
1205                                         // break to next line
1206                                         length = worldlgth;
1207                                         BrokenText.push_back(line);
1208                                         BrokenTextPositions.push_back(lastLineStart);
1209                                         lastLineStart = i - (s32)word.size();
1210                                         line = word;
1211                                 }
1212                                 else
1213                                 {
1214                                         // add word to line
1215                                         line += whitespace;
1216                                         line += word;
1217                                         length += whitelgth + worldlgth;
1218                                 }
1219
1220                                 word = L"";
1221                                 whitespace = L"";
1222                         }
1223
1224                         whitespace += c;
1225
1226                         // compute line break
1227                         if (lineBreak)
1228                         {
1229                                 line += whitespace;
1230                                 line += word;
1231                                 BrokenText.push_back(line);
1232                                 BrokenTextPositions.push_back(lastLineStart);
1233                                 lastLineStart = i+1;
1234                                 line = L"";
1235                                 word = L"";
1236                                 whitespace = L"";
1237                                 length = 0;
1238                         }
1239                 }
1240                 else
1241                 {
1242                         // yippee this is a word..
1243                         word += c;
1244                 }
1245         }
1246
1247         line += whitespace;
1248         line += word;
1249         BrokenText.push_back(line);
1250         BrokenTextPositions.push_back(lastLineStart);
1251 }
1252
1253
1254 void intlGUIEditBox::setTextRect(s32 line)
1255 {
1256         core::dimension2du d;
1257
1258         IGUISkin* skin = Environment->getSkin();
1259         if (!skin)
1260                 return;
1261
1262         IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
1263
1264         if (!font)
1265                 return;
1266
1267         // get text dimension
1268         const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
1269         if (WordWrap || MultiLine)
1270         {
1271                 d = font->getDimension(BrokenText[line].c_str());
1272         }
1273         else
1274         {
1275                 d = font->getDimension(Text.c_str());
1276                 d.Height = AbsoluteRect.getHeight();
1277         }
1278         d.Height += font->getKerningHeight();
1279
1280         // justification
1281         switch (HAlign)
1282         {
1283         case EGUIA_CENTER:
1284                 // align to h centre
1285                 CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
1286                 CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
1287                 break;
1288         case EGUIA_LOWERRIGHT:
1289                 // align to right edge
1290                 CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
1291                 CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
1292                 break;
1293         default:
1294                 // align to left edge
1295                 CurrentTextRect.UpperLeftCorner.X = 0;
1296                 CurrentTextRect.LowerRightCorner.X = d.Width;
1297
1298         }
1299
1300         switch (VAlign)
1301         {
1302         case EGUIA_CENTER:
1303                 // align to v centre
1304                 CurrentTextRect.UpperLeftCorner.Y =
1305                         (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
1306                 break;
1307         case EGUIA_LOWERRIGHT:
1308                 // align to bottom edge
1309                 CurrentTextRect.UpperLeftCorner.Y =
1310                         FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
1311                 break;
1312         default:
1313                 // align to top edge
1314                 CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
1315                 break;
1316         }
1317
1318         CurrentTextRect.UpperLeftCorner.X  -= HScrollPos;
1319         CurrentTextRect.LowerRightCorner.X -= HScrollPos;
1320         CurrentTextRect.UpperLeftCorner.Y  -= VScrollPos;
1321         CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
1322
1323         CurrentTextRect += FrameRect.UpperLeftCorner;
1324
1325 }
1326
1327
1328 s32 intlGUIEditBox::getLineFromPos(s32 pos)
1329 {
1330         if (!WordWrap && !MultiLine)
1331                 return 0;
1332
1333         s32 i=0;
1334         while (i < (s32)BrokenTextPositions.size())
1335         {
1336                 if (BrokenTextPositions[i] > pos)
1337                         return i-1;
1338                 ++i;
1339         }
1340         return (s32)BrokenTextPositions.size() - 1;
1341 }
1342
1343
1344 void intlGUIEditBox::inputChar(wchar_t c)
1345 {
1346         if (!IsEnabled || !m_writable)
1347                 return;
1348
1349         if (c != 0)
1350         {
1351                 if (Text.size() < Max || Max == 0)
1352                 {
1353                         core::stringw s;
1354
1355                         if (MarkBegin != MarkEnd)
1356                         {
1357                                 // replace marked text
1358                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
1359                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
1360
1361                                 s = Text.subString(0, realmbgn);
1362                                 s.append(c);
1363                                 s.append( Text.subString(realmend, Text.size()-realmend) );
1364                                 Text = s;
1365                                 CursorPos = realmbgn+1;
1366                         }
1367                         else
1368                         {
1369                                 // add new character
1370                                 s = Text.subString(0, CursorPos);
1371                                 s.append(c);
1372                                 s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
1373                                 Text = s;
1374                                 ++CursorPos;
1375                         }
1376
1377                         BlinkStartTime = porting::getTimeMs();
1378                         setTextMarkers(0, 0);
1379                 }
1380         }
1381         breakText();
1382         sendGuiEvent(EGET_EDITBOX_CHANGED);
1383         calculateScrollPos();
1384 }
1385
1386
1387 void intlGUIEditBox::calculateScrollPos()
1388 {
1389         if (!AutoScroll)
1390                 return;
1391
1392         // calculate horizontal scroll position
1393         s32 cursLine = getLineFromPos(CursorPos);
1394         setTextRect(cursLine);
1395
1396         // don't do horizontal scrolling when wordwrap is enabled.
1397         if (!WordWrap)
1398         {
1399                 // get cursor position
1400                 IGUISkin* skin = Environment->getSkin();
1401                 if (!skin)
1402                         return;
1403                 IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
1404                 if (!font)
1405                         return;
1406
1407                 core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
1408                 s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
1409
1410                 s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
1411                         font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
1412
1413                 s32 cEnd = cStart + font->getDimension(L"_ ").Width;
1414
1415                 if (FrameRect.LowerRightCorner.X < cEnd)
1416                         HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
1417                 else if (FrameRect.UpperLeftCorner.X > cStart)
1418                         HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
1419                 else
1420                         HScrollPos = 0;
1421
1422                 // todo: adjust scrollbar
1423         }
1424
1425         if (!WordWrap && !MultiLine)
1426                 return;
1427
1428         // vertical scroll position
1429         if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y)
1430                 VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards
1431         else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y)
1432                 VScrollPos += CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y; // scrolling upwards
1433
1434         // todo: adjust scrollbar
1435         if (m_vscrollbar)
1436                 m_vscrollbar->setPos(VScrollPos);
1437 }
1438
1439 //! set text markers
1440 void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
1441 {
1442     if ( begin != MarkBegin || end != MarkEnd )
1443     {
1444         MarkBegin = begin;
1445         MarkEnd = end;
1446         sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
1447     }
1448 }
1449
1450 //! send some gui event to parent
1451 void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
1452 {
1453         if ( Parent )
1454         {
1455         SEvent e;
1456         e.EventType = EET_GUI_EVENT;
1457         e.GUIEvent.Caller = this;
1458         e.GUIEvent.Element = 0;
1459         e.GUIEvent.EventType = type;
1460
1461         Parent->OnEvent(e);
1462         }
1463 }
1464
1465 //! Create a vertical scrollbar
1466 void intlGUIEditBox::createVScrollBar()
1467 {
1468         s32 fontHeight = 1;
1469
1470         if (OverrideFont) {
1471                 fontHeight = OverrideFont->getDimension(L"").Height;
1472         } else {
1473                 if (IGUISkin* skin = Environment->getSkin()) {
1474                         if (IGUIFont* font = skin->getFont()) {
1475                                 fontHeight = font->getDimension(L"").Height;
1476                         }
1477                 }
1478         }
1479
1480         RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4;
1481
1482         irr::core::rect<s32> scrollbarrect = FrameRect;
1483         scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
1484         m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
1485         m_vscrollbar->setVisible(false);
1486         m_vscrollbar->setSmallStep(3 * fontHeight);
1487         m_vscrollbar->setLargeStep(10 * fontHeight);
1488 }
1489
1490 //! Update the vertical scrollbar (visibilty & scroll position)
1491 void intlGUIEditBox::updateVScrollBar()
1492 {
1493         if (!m_vscrollbar)
1494                 return;
1495
1496         // OnScrollBarChanged(...)
1497         if (m_vscrollbar->getPos() != VScrollPos) {
1498                 s32 deltaScrollY = m_vscrollbar->getPos() - VScrollPos;
1499                 CurrentTextRect.UpperLeftCorner.Y -= deltaScrollY;
1500                 CurrentTextRect.LowerRightCorner.Y -= deltaScrollY;
1501
1502                 s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
1503                 if (scrollymax != m_vscrollbar->getMax()) {
1504                         // manage a newline or a deleted line
1505                         m_vscrollbar->setMax(scrollymax);
1506                         calculateScrollPos();
1507                 } else {
1508                         // manage a newline or a deleted line
1509                         VScrollPos = m_vscrollbar->getPos();
1510                 }
1511         }
1512
1513         // check if a vertical scrollbar is needed ?
1514         if (getTextDimension().Height > (u32) FrameRect.getHeight()) {
1515                 s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
1516                 if (scrollymax != m_vscrollbar->getMax()) {
1517                         m_vscrollbar->setMax(scrollymax);
1518                 }
1519
1520                 if (!m_vscrollbar->isVisible() && MultiLine) {
1521                         AbsoluteRect.LowerRightCorner.X -= m_scrollbar_width;
1522
1523                         m_vscrollbar->setVisible(true);
1524                 }
1525         } else {
1526                 if (m_vscrollbar->isVisible()) {
1527                         AbsoluteRect.LowerRightCorner.X += m_scrollbar_width;
1528
1529                         VScrollPos = 0;
1530                         m_vscrollbar->setPos(0);
1531                         m_vscrollbar->setMax(1);
1532                         m_vscrollbar->setVisible(false);
1533                 }
1534         }
1535 }
1536
1537 void intlGUIEditBox::setWritable(bool can_write_text)
1538 {
1539         m_writable = can_write_text;
1540 }
1541
1542 //! Writes attributes of the element.
1543 void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
1544 {
1545         // IGUIEditBox::serializeAttributes(out,options);
1546
1547         out->addBool  ("OverrideColorEnabled",OverrideColorEnabled );
1548         out->addColor ("OverrideColor",       OverrideColor);
1549         // out->addFont("OverrideFont",OverrideFont);
1550         out->addInt   ("MaxChars",            Max);
1551         out->addBool  ("WordWrap",            WordWrap);
1552         out->addBool  ("MultiLine",           MultiLine);
1553         out->addBool  ("AutoScroll",          AutoScroll);
1554         out->addBool  ("PasswordBox",         PasswordBox);
1555         core::stringw ch = L" ";
1556         ch[0] = PasswordChar;
1557         out->addString("PasswordChar",        ch.c_str());
1558         out->addEnum  ("HTextAlign",          HAlign, GUIAlignmentNames);
1559         out->addEnum  ("VTextAlign",          VAlign, GUIAlignmentNames);
1560         out->addBool  ("Writable",            m_writable);
1561
1562         IGUIEditBox::serializeAttributes(out,options);
1563 }
1564
1565
1566 //! Reads attributes of the element
1567 void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
1568 {
1569         IGUIEditBox::deserializeAttributes(in,options);
1570
1571         setOverrideColor(in->getAttributeAsColor("OverrideColor"));
1572         enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
1573         setMax(in->getAttributeAsInt("MaxChars"));
1574         setWordWrap(in->getAttributeAsBool("WordWrap"));
1575         setMultiLine(in->getAttributeAsBool("MultiLine"));
1576         setAutoScroll(in->getAttributeAsBool("AutoScroll"));
1577         core::stringw ch = in->getAttributeAsStringW("PasswordChar");
1578
1579         if (ch.empty())
1580                 setPasswordBox(in->getAttributeAsBool("PasswordBox"));
1581         else
1582                 setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
1583
1584         setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
1585                         (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
1586
1587         setWritable(in->getAttributeAsBool("Writable"));
1588         // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
1589 }
1590
1591
1592 } // end namespace gui
1593 } // end namespace irr
1594
1595 #endif // _IRR_COMPILE_WITH_GUI_