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