]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiEditBox.cpp
Use "Aux1" key name consistently everywhere
[dragonfireclient.git] / src / gui / guiEditBox.cpp
1 /*
2 Minetest
3 Copyright (C) 2021 Minetest
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "guiEditBox.h"
21
22 #include "IGUISkin.h"
23 #include "IGUIEnvironment.h"
24 #include "IGUIFont.h"
25
26 #include "porting.h"
27
28 GUIEditBox::~GUIEditBox()
29 {
30         if (m_override_font)
31                 m_override_font->drop();
32
33         if (m_operator)
34                 m_operator->drop();
35
36         if (m_vscrollbar)
37                 m_vscrollbar->drop();
38 }
39
40 void GUIEditBox::setOverrideFont(IGUIFont *font)
41 {
42         if (m_override_font == font)
43                 return;
44
45         if (m_override_font)
46                 m_override_font->drop();
47
48         m_override_font = font;
49
50         if (m_override_font)
51                 m_override_font->grab();
52
53         breakText();
54 }
55
56 //! Get the font which is used right now for drawing
57 IGUIFont *GUIEditBox::getActiveFont() const
58 {
59         if (m_override_font)
60                 return m_override_font;
61         IGUISkin *skin = Environment->getSkin();
62         if (skin)
63                 return skin->getFont();
64         return 0;
65 }
66
67 //! Sets another color for the text.
68 void GUIEditBox::setOverrideColor(video::SColor color)
69 {
70         m_override_color = color;
71         m_override_color_enabled = true;
72 }
73
74 video::SColor GUIEditBox::getOverrideColor() const
75 {
76         return m_override_color;
77 }
78
79 //! Sets if the text should use the overide color or the color in the gui skin.
80 void GUIEditBox::enableOverrideColor(bool enable)
81 {
82         m_override_color_enabled = enable;
83 }
84
85 //! Enables or disables word wrap
86 void GUIEditBox::setWordWrap(bool enable)
87 {
88         m_word_wrap = enable;
89         breakText();
90 }
91
92 //! Enables or disables newlines.
93 void GUIEditBox::setMultiLine(bool enable)
94 {
95         m_multiline = enable;
96 }
97
98 //! Enables or disables automatic scrolling with cursor position
99 //! \param enable: If set to true, the text will move around with the cursor position
100 void GUIEditBox::setAutoScroll(bool enable)
101 {
102         m_autoscroll = enable;
103 }
104
105 void GUIEditBox::setPasswordBox(bool password_box, wchar_t password_char)
106 {
107         m_passwordbox = password_box;
108         if (m_passwordbox) {
109                 m_passwordchar = password_char;
110                 setMultiLine(false);
111                 setWordWrap(false);
112                 m_broken_text.clear();
113         }
114 }
115
116 //! Sets text justification
117 void GUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
118 {
119         m_halign = horizontal;
120         m_valign = vertical;
121 }
122
123 //! Sets the new caption of this element.
124 void GUIEditBox::setText(const wchar_t *text)
125 {
126         Text = text;
127         if (u32(m_cursor_pos) > Text.size())
128                 m_cursor_pos = Text.size();
129         m_hscroll_pos = 0;
130         breakText();
131 }
132
133 //! Sets the maximum amount of characters which may be entered in the box.
134 //! \param max: Maximum amount of characters. If 0, the character amount is
135 //! infinity.
136 void GUIEditBox::setMax(u32 max)
137 {
138         m_max = max;
139
140         if (Text.size() > m_max && m_max != 0)
141                 Text = Text.subString(0, m_max);
142 }
143
144 //! Gets the area of the text in the edit box
145 //! \return Returns the size in pixels of the text
146 core::dimension2du GUIEditBox::getTextDimension()
147 {
148         core::rect<s32> ret;
149
150         setTextRect(0);
151         ret = m_current_text_rect;
152
153         for (u32 i = 1; i < m_broken_text.size(); ++i) {
154                 setTextRect(i);
155                 ret.addInternalPoint(m_current_text_rect.UpperLeftCorner);
156                 ret.addInternalPoint(m_current_text_rect.LowerRightCorner);
157         }
158
159         return core::dimension2du(ret.getSize());
160 }
161
162 //! Turns the border on or off
163 void GUIEditBox::setDrawBorder(bool border)
164 {
165         m_border = border;
166 }
167
168 void GUIEditBox::setWritable(bool can_write_text)
169 {
170         m_writable = can_write_text;
171 }
172
173 //! set text markers
174 void GUIEditBox::setTextMarkers(s32 begin, s32 end)
175 {
176         if (begin != m_mark_begin || end != m_mark_end) {
177                 m_mark_begin = begin;
178                 m_mark_end = end;
179                 sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
180         }
181 }
182
183 //! send some gui event to parent
184 void GUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
185 {
186         if (Parent) {
187                 SEvent e;
188                 e.EventType = EET_GUI_EVENT;
189                 e.GUIEvent.Caller = this;
190                 e.GUIEvent.Element = 0;
191                 e.GUIEvent.EventType = type;
192
193                 Parent->OnEvent(e);
194         }
195 }
196
197 //! called if an event happened.
198 bool GUIEditBox::OnEvent(const SEvent &event)
199 {
200         if (isEnabled()) {
201
202                 switch (event.EventType) {
203                 case EET_GUI_EVENT:
204                         if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
205                                 if (event.GUIEvent.Caller == this) {
206                                         m_mouse_marking = false;
207                                         setTextMarkers(0, 0);
208                                 }
209                         }
210                         break;
211                 case EET_KEY_INPUT_EVENT: {
212 #if (defined(__linux__) || defined(__FreeBSD__)) || defined(__DragonFly__)
213                         // ################################################################
214                         // ValkaTR:
215                         // This part is the difference from the original intlGUIEditBox
216                         // It converts UTF-8 character into a UCS-2 (wchar_t)
217                         wchar_t wc = L'_';
218                         mbtowc(&wc, (char *)&event.KeyInput.Char,
219                                         sizeof(event.KeyInput.Char));
220
221                         // printf( "char: %lc (%u)  \r\n", wc, wc );
222
223                         SEvent irrevent(event);
224                         irrevent.KeyInput.Char = wc;
225                         // ################################################################
226
227                         if (processKey(irrevent))
228                                 return true;
229 #else
230                         if (processKey(event))
231                                 return true;
232 #endif // defined(linux)
233
234                         break;
235                 }
236                 case EET_MOUSE_INPUT_EVENT:
237                         if (processMouse(event))
238                                 return true;
239                         break;
240                 default:
241                         break;
242                 }
243         }
244
245         return IGUIElement::OnEvent(event);
246 }
247
248 bool GUIEditBox::processKey(const SEvent &event)
249 {
250         if (!m_writable) {
251                 return false;
252         }
253
254         if (!event.KeyInput.PressedDown)
255                 return false;
256
257         bool text_changed = false;
258         s32 new_mark_begin = m_mark_begin;
259         s32 new_mark_end = m_mark_end;
260
261         // control shortcut handling
262         if (event.KeyInput.Control) {
263
264                 // german backlash '\' entered with control + '?'
265                 if (event.KeyInput.Char == '\\') {
266                         inputChar(event.KeyInput.Char);
267                         return true;
268                 }
269
270                 switch (event.KeyInput.Key) {
271                 case KEY_KEY_A:
272                         // select all
273                         new_mark_begin = 0;
274                         new_mark_end = Text.size();
275                         break;
276                 case KEY_KEY_C:
277                         onKeyControlC(event);
278                         break;
279                 case KEY_KEY_X:
280                         text_changed = onKeyControlX(event, new_mark_begin, new_mark_end);
281                         break;
282                 case KEY_KEY_V:
283                         text_changed = onKeyControlV(event, new_mark_begin, new_mark_end);
284                         break;
285                 case KEY_HOME:
286                         // move/highlight to start of text
287                         if (event.KeyInput.Shift) {
288                                 new_mark_end = m_cursor_pos;
289                                 new_mark_begin = 0;
290                                 m_cursor_pos = 0;
291                         } else {
292                                 m_cursor_pos = 0;
293                                 new_mark_begin = 0;
294                                 new_mark_end = 0;
295                         }
296                         break;
297                 case KEY_END:
298                         // move/highlight to end of text
299                         if (event.KeyInput.Shift) {
300                                 new_mark_begin = m_cursor_pos;
301                                 new_mark_end = Text.size();
302                                 m_cursor_pos = 0;
303                         } else {
304                                 m_cursor_pos = Text.size();
305                                 new_mark_begin = 0;
306                                 new_mark_end = 0;
307                         }
308                         break;
309                 default:
310                         return false;
311                 }
312         } else {
313                 switch (event.KeyInput.Key) {
314                 case KEY_END: {
315                         s32 p = Text.size();
316                         if (m_word_wrap || m_multiline) {
317                                 p = getLineFromPos(m_cursor_pos);
318                                 p = m_broken_text_positions[p] +
319                                     (s32)m_broken_text[p].size();
320                                 if (p > 0 && (Text[p - 1] == L'\r' ||
321                                                              Text[p - 1] == L'\n'))
322                                         p -= 1;
323                         }
324
325                         if (event.KeyInput.Shift) {
326                                 if (m_mark_begin == m_mark_end)
327                                         new_mark_begin = m_cursor_pos;
328
329                                 new_mark_end = p;
330                         } else {
331                                 new_mark_begin = 0;
332                                 new_mark_end = 0;
333                         }
334                         m_cursor_pos = p;
335                         m_blink_start_time = porting::getTimeMs();
336                 } break;
337                 case KEY_HOME: {
338
339                         s32 p = 0;
340                         if (m_word_wrap || m_multiline) {
341                                 p = getLineFromPos(m_cursor_pos);
342                                 p = m_broken_text_positions[p];
343                         }
344
345                         if (event.KeyInput.Shift) {
346                                 if (m_mark_begin == m_mark_end)
347                                         new_mark_begin = m_cursor_pos;
348                                 new_mark_end = p;
349                         } else {
350                                 new_mark_begin = 0;
351                                 new_mark_end = 0;
352                         }
353                         m_cursor_pos = p;
354                         m_blink_start_time = porting::getTimeMs();
355                 } break;
356                 case KEY_RETURN:
357                         if (m_multiline) {
358                                 inputChar(L'\n');
359                         } else {
360                                 calculateScrollPos();
361                                 sendGuiEvent(EGET_EDITBOX_ENTER);
362                         }
363                         return true;
364                 case KEY_LEFT:
365                         if (event.KeyInput.Shift) {
366                                 if (m_cursor_pos > 0) {
367                                         if (m_mark_begin == m_mark_end)
368                                                 new_mark_begin = m_cursor_pos;
369
370                                         new_mark_end = m_cursor_pos - 1;
371                                 }
372                         } else {
373                                 new_mark_begin = 0;
374                                 new_mark_end = 0;
375                         }
376
377                         if (m_cursor_pos > 0)
378                                 m_cursor_pos--;
379                         m_blink_start_time = porting::getTimeMs();
380                         break;
381                 case KEY_RIGHT:
382                         if (event.KeyInput.Shift) {
383                                 if (Text.size() > (u32)m_cursor_pos) {
384                                         if (m_mark_begin == m_mark_end)
385                                                 new_mark_begin = m_cursor_pos;
386
387                                         new_mark_end = m_cursor_pos + 1;
388                                 }
389                         } else {
390                                 new_mark_begin = 0;
391                                 new_mark_end = 0;
392                         }
393
394                         if (Text.size() > (u32)m_cursor_pos)
395                                 m_cursor_pos++;
396                         m_blink_start_time = porting::getTimeMs();
397                         break;
398                 case KEY_UP:
399                         if (!onKeyUp(event, new_mark_begin, new_mark_end)) {
400                                 return false;
401                         }
402                         break;
403                 case KEY_DOWN:
404                         if (!onKeyDown(event, new_mark_begin, new_mark_end)) {
405                                 return false;
406                         }
407                         break;
408                 case KEY_BACK:
409                         text_changed = onKeyBack(event, new_mark_begin, new_mark_end);
410                         break;
411
412                 case KEY_DELETE:
413                         text_changed = onKeyDelete(event, new_mark_begin, new_mark_end);
414                         break;
415
416                 case KEY_ESCAPE:
417                 case KEY_TAB:
418                 case KEY_SHIFT:
419                 case KEY_F1:
420                 case KEY_F2:
421                 case KEY_F3:
422                 case KEY_F4:
423                 case KEY_F5:
424                 case KEY_F6:
425                 case KEY_F7:
426                 case KEY_F8:
427                 case KEY_F9:
428                 case KEY_F10:
429                 case KEY_F11:
430                 case KEY_F12:
431                 case KEY_F13:
432                 case KEY_F14:
433                 case KEY_F15:
434                 case KEY_F16:
435                 case KEY_F17:
436                 case KEY_F18:
437                 case KEY_F19:
438                 case KEY_F20:
439                 case KEY_F21:
440                 case KEY_F22:
441                 case KEY_F23:
442                 case KEY_F24:
443                         // ignore these keys
444                         return false;
445
446                 default:
447                         inputChar(event.KeyInput.Char);
448                         return true;
449                 }
450         }
451
452         // Set new text markers
453         setTextMarkers(new_mark_begin, new_mark_end);
454
455         // break the text if it has changed
456         if (text_changed) {
457                 breakText();
458                 sendGuiEvent(EGET_EDITBOX_CHANGED);
459         }
460
461         calculateScrollPos();
462
463         return true;
464 }
465
466 bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
467 {
468         // clang-format off
469         if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
470                 s32 lineNo = getLineFromPos(m_cursor_pos);
471                 s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
472                         (m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
473                 if (lineNo > 0) {
474                         s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
475                         if ((s32)m_broken_text[lineNo - 1].size() < cp) {
476                                 m_cursor_pos = m_broken_text_positions[lineNo - 1] +
477                                         core::max_((u32)1, m_broken_text[lineNo - 1].size()) - 1;
478                         }
479                         else
480                                 m_cursor_pos = m_broken_text_positions[lineNo - 1] + cp;
481                 }
482
483                 if (event.KeyInput.Shift) {
484                         mark_begin = mb;
485                         mark_end = m_cursor_pos;
486                 } else {
487                         mark_begin = 0;
488                         mark_end = 0;
489                 }
490
491                 return true;
492         }
493
494         // clang-format on
495         return false;
496 }
497
498 bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
499 {
500         // clang-format off
501         if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
502                 s32 lineNo = getLineFromPos(m_cursor_pos);
503                 s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
504                         (m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
505                 if (lineNo < (s32)m_broken_text.size() - 1) {
506                         s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
507                         if ((s32)m_broken_text[lineNo + 1].size() < cp) {
508                                 m_cursor_pos = m_broken_text_positions[lineNo + 1] +
509                                         core::max_((u32)1, m_broken_text[lineNo + 1].size()) - 1;
510                         }
511                         else
512                                 m_cursor_pos = m_broken_text_positions[lineNo + 1] + cp;
513                 }
514
515                 if (event.KeyInput.Shift) {
516                         mark_begin = mb;
517                         mark_end = m_cursor_pos;
518                 } else {
519                         mark_begin = 0;
520                         mark_end = 0;
521                 }
522
523                 return true;
524         }
525
526         // clang-format on
527         return false;
528 }
529
530 void GUIEditBox::onKeyControlC(const SEvent &event)
531 {
532         // copy to clipboard
533         if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
534                 return;
535
536         const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
537         const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
538
539         core::stringc s;
540         s = Text.subString(realmbgn, realmend - realmbgn).c_str();
541         m_operator->copyToClipboard(s.c_str());
542 }
543
544 bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_end)
545 {
546         // First copy to clipboard
547         onKeyControlC(event);
548
549         if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
550                 return false;
551
552         const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
553         const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
554
555         // Now remove from box if enabled
556         if (isEnabled()) {
557                 // delete
558                 core::stringw s;
559                 s = Text.subString(0, realmbgn);
560                 s.append(Text.subString(realmend, Text.size() - realmend));
561                 Text = s;
562
563                 m_cursor_pos = realmbgn;
564                 mark_begin = 0;
565                 mark_end = 0;
566                 return true;
567         }
568
569         return false;
570 }
571
572 bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_end)
573 {
574         if (!isEnabled())
575                 return false;
576
577         // paste from the clipboard
578         if (!m_operator)
579                 return false;
580
581         const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
582         const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
583
584         // add new character
585         if (const c8 *p = m_operator->getTextFromClipboard()) {
586                 if (m_mark_begin == m_mark_end) {
587                         // insert text
588                         core::stringw s = Text.subString(0, m_cursor_pos);
589                         s.append(p);
590                         s.append(Text.subString(
591                                         m_cursor_pos, Text.size() - m_cursor_pos));
592
593                         if (!m_max || s.size() <= m_max) {
594                                 Text = s;
595                                 s = p;
596                                 m_cursor_pos += s.size();
597                         }
598                 } else {
599                         // replace text
600
601                         core::stringw s = Text.subString(0, realmbgn);
602                         s.append(p);
603                         s.append(Text.subString(realmend, Text.size() - realmend));
604
605                         if (!m_max || s.size() <= m_max) {
606                                 Text = s;
607                                 s = p;
608                                 m_cursor_pos = realmbgn + s.size();
609                         }
610                 }
611         }
612
613         mark_begin = 0;
614         mark_end = 0;
615         return true;
616 }
617
618 bool GUIEditBox::onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end)
619 {
620         if (!isEnabled() || Text.empty())
621                 return false;
622
623         core::stringw s;
624
625         if (m_mark_begin != m_mark_end) {
626                 // delete marked text
627                 const s32 realmbgn =
628                                 m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
629                 const s32 realmend =
630                                 m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
631
632                 s = Text.subString(0, realmbgn);
633                 s.append(Text.subString(realmend, Text.size() - realmend));
634                 Text = s;
635
636                 m_cursor_pos = realmbgn;
637         } else {
638                 // delete text behind cursor
639                 if (m_cursor_pos > 0)
640                         s = Text.subString(0, m_cursor_pos - 1);
641                 else
642                         s = L"";
643                 s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
644                 Text = s;
645                 --m_cursor_pos;
646         }
647
648         if (m_cursor_pos < 0)
649                 m_cursor_pos = 0;
650         m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
651         mark_begin = 0;
652         mark_end = 0;
653         return true;
654 }
655
656 bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end)
657 {
658         if (!isEnabled() || Text.empty())
659                 return false;
660
661         core::stringw s;
662
663         if (m_mark_begin != m_mark_end) {
664                 // delete marked text
665                 const s32 realmbgn =
666                                 m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
667                 const s32 realmend =
668                                 m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
669
670                 s = Text.subString(0, realmbgn);
671                 s.append(Text.subString(realmend, Text.size() - realmend));
672                 Text = s;
673
674                 m_cursor_pos = realmbgn;
675         } else {
676                 // delete text before cursor
677                 s = Text.subString(0, m_cursor_pos);
678                 s.append(Text.subString(
679                                 m_cursor_pos + 1, Text.size() - m_cursor_pos - 1));
680                 Text = s;
681         }
682
683         if (m_cursor_pos > (s32)Text.size())
684                 m_cursor_pos = (s32)Text.size();
685
686         m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
687         mark_begin = 0;
688         mark_end = 0;
689         return true;
690 }
691
692 void GUIEditBox::inputChar(wchar_t c)
693 {
694         if (!isEnabled() || !m_writable)
695                 return;
696
697         if (c != 0) {
698                 if (Text.size() < m_max || m_max == 0) {
699                         core::stringw s;
700
701                         if (m_mark_begin != m_mark_end) {
702                                 // clang-format off
703                                 // replace marked text
704                                 s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
705                                 s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
706
707                                 s = Text.subString(0, real_begin);
708                                 s.append(c);
709                                 s.append(Text.subString(real_end, Text.size() - real_end));
710                                 Text = s;
711                                 m_cursor_pos = real_begin + 1;
712                                 // clang-format on
713                         } else {
714                                 // add new character
715                                 s = Text.subString(0, m_cursor_pos);
716                                 s.append(c);
717                                 s.append(Text.subString(m_cursor_pos,
718                                                 Text.size() - m_cursor_pos));
719                                 Text = s;
720                                 ++m_cursor_pos;
721                         }
722
723                         m_blink_start_time = porting::getTimeMs();
724                         setTextMarkers(0, 0);
725                 }
726         }
727         breakText();
728         sendGuiEvent(EGET_EDITBOX_CHANGED);
729         calculateScrollPos();
730 }
731
732 bool GUIEditBox::processMouse(const SEvent &event)
733 {
734         switch (event.MouseInput.Event) {
735         case irr::EMIE_LMOUSE_LEFT_UP:
736                 if (Environment->hasFocus(this)) {
737                         m_cursor_pos = getCursorPos(
738                                         event.MouseInput.X, event.MouseInput.Y);
739                         if (m_mouse_marking) {
740                                 setTextMarkers(m_mark_begin, m_cursor_pos);
741                         }
742                         m_mouse_marking = false;
743                         calculateScrollPos();
744                         return true;
745                 }
746                 break;
747         case irr::EMIE_MOUSE_MOVED: {
748                 if (m_mouse_marking) {
749                         m_cursor_pos = getCursorPos(
750                                         event.MouseInput.X, event.MouseInput.Y);
751                         setTextMarkers(m_mark_begin, m_cursor_pos);
752                         calculateScrollPos();
753                         return true;
754                 }
755         } break;
756         case EMIE_LMOUSE_PRESSED_DOWN:
757
758                 if (!Environment->hasFocus(this)) {
759                         m_blink_start_time = porting::getTimeMs();
760                         m_mouse_marking = true;
761                         m_cursor_pos = getCursorPos(
762                                         event.MouseInput.X, event.MouseInput.Y);
763                         setTextMarkers(m_cursor_pos, m_cursor_pos);
764                         calculateScrollPos();
765                         return true;
766                 } else {
767                         if (!AbsoluteClippingRect.isPointInside(core::position2d<s32>(
768                                             event.MouseInput.X, event.MouseInput.Y))) {
769                                 return false;
770                         } else {
771                                 // move cursor
772                                 m_cursor_pos = getCursorPos(
773                                                 event.MouseInput.X, event.MouseInput.Y);
774
775                                 s32 newMarkBegin = m_mark_begin;
776                                 if (!m_mouse_marking)
777                                         newMarkBegin = m_cursor_pos;
778
779                                 m_mouse_marking = true;
780                                 setTextMarkers(newMarkBegin, m_cursor_pos);
781                                 calculateScrollPos();
782                                 return true;
783                         }
784                 }
785         case EMIE_MOUSE_WHEEL:
786                 if (m_vscrollbar && m_vscrollbar->isVisible()) {
787                         s32 pos = m_vscrollbar->getPos();
788                         s32 step = m_vscrollbar->getSmallStep();
789                         m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
790                         return true;
791                 }
792                 break;
793         default:
794                 break;
795         }
796
797         return false;
798 }
799
800 s32 GUIEditBox::getLineFromPos(s32 pos)
801 {
802         if (!m_word_wrap && !m_multiline)
803                 return 0;
804
805         s32 i = 0;
806         while (i < (s32)m_broken_text_positions.size()) {
807                 if (m_broken_text_positions[i] > pos)
808                         return i - 1;
809                 ++i;
810         }
811         return (s32)m_broken_text_positions.size() - 1;
812 }
813
814 void GUIEditBox::updateVScrollBar()
815 {
816         if (!m_vscrollbar) {
817                 return;
818         }
819
820         // OnScrollBarChanged(...)
821         if (m_vscrollbar->getPos() != m_vscroll_pos) {
822                 s32 deltaScrollY = m_vscrollbar->getPos() - m_vscroll_pos;
823                 m_current_text_rect.UpperLeftCorner.Y -= deltaScrollY;
824                 m_current_text_rect.LowerRightCorner.Y -= deltaScrollY;
825
826                 s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
827                 if (scrollymax != m_vscrollbar->getMax()) {
828                         // manage a newline or a deleted line
829                         m_vscrollbar->setMax(scrollymax);
830                         m_vscrollbar->setPageSize(s32(getTextDimension().Height));
831                         calculateScrollPos();
832                 } else {
833                         // manage a newline or a deleted line
834                         m_vscroll_pos = m_vscrollbar->getPos();
835                 }
836         }
837
838         // check if a vertical scrollbar is needed ?
839         if (getTextDimension().Height > (u32)m_frame_rect.getHeight()) {
840                 m_frame_rect.LowerRightCorner.X -= m_scrollbar_width;
841
842                 s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
843                 if (scrollymax != m_vscrollbar->getMax()) {
844                         m_vscrollbar->setMax(scrollymax);
845                         m_vscrollbar->setPageSize(s32(getTextDimension().Height));
846                 }
847
848                 if (!m_vscrollbar->isVisible()) {
849                         m_vscrollbar->setVisible(true);
850                 }
851         } else {
852                 if (m_vscrollbar->isVisible()) {
853                         m_vscrollbar->setVisible(false);
854                         m_vscroll_pos = 0;
855                         m_vscrollbar->setPos(0);
856                         m_vscrollbar->setMax(1);
857                         m_vscrollbar->setPageSize(s32(getTextDimension().Height));
858                 }
859         }
860 }
861
862 void GUIEditBox::deserializeAttributes(
863                 io::IAttributes *in, io::SAttributeReadWriteOptions *options = 0)
864 {
865         IGUIEditBox::deserializeAttributes(in, options);
866
867         setOverrideColor(in->getAttributeAsColor("OverrideColor"));
868         enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
869         setMax(in->getAttributeAsInt("MaxChars"));
870         setWordWrap(in->getAttributeAsBool("WordWrap"));
871         setMultiLine(in->getAttributeAsBool("MultiLine"));
872         setAutoScroll(in->getAttributeAsBool("AutoScroll"));
873         core::stringw ch = in->getAttributeAsStringW("PasswordChar");
874
875         if (ch.empty())
876                 setPasswordBox(in->getAttributeAsBool("PasswordBox"));
877         else
878                 setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
879
880         setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration(
881                                          "HTextAlign", GUIAlignmentNames),
882                         (EGUI_ALIGNMENT)in->getAttributeAsEnumeration(
883                                         "VTextAlign", GUIAlignmentNames));
884
885         setWritable(in->getAttributeAsBool("Writable"));
886         // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
887 }
888
889 //! Writes attributes of the element.
890 void GUIEditBox::serializeAttributes(
891                 io::IAttributes *out, io::SAttributeReadWriteOptions *options = 0) const
892 {
893         // IGUIEditBox::serializeAttributes(out,options);
894
895         out->addBool("OverrideColorEnabled", m_override_color_enabled);
896         out->addColor("OverrideColor", m_override_color);
897         // out->addFont("OverrideFont",m_override_font);
898         out->addInt("MaxChars", m_max);
899         out->addBool("WordWrap", m_word_wrap);
900         out->addBool("MultiLine", m_multiline);
901         out->addBool("AutoScroll", m_autoscroll);
902         out->addBool("PasswordBox", m_passwordbox);
903         core::stringw ch = L" ";
904         ch[0] = m_passwordchar;
905         out->addString("PasswordChar", ch.c_str());
906         out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
907         out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
908         out->addBool("Writable", m_writable);
909
910         IGUIEditBox::serializeAttributes(out, options);
911 }