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