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