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