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