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