]> git.lizzy.rs Git - minetest.git/blob - src/gui/guiEditBox.cpp
e25c9ace39f9da61d13232f822ef545419e90e9d
[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                 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                 case EET_STRING_INPUT_EVENT:
221                         inputString(*event.StringInput.Str);
222                         return true;
223                 default:
224                         break;
225                 }
226         }
227
228         return IGUIElement::OnEvent(event);
229 }
230
231 bool GUIEditBox::processKey(const SEvent &event)
232 {
233         if (!event.KeyInput.PressedDown)
234                 return false;
235
236         bool text_changed = false;
237         s32 new_mark_begin = m_mark_begin;
238         s32 new_mark_end = m_mark_end;
239
240         // control shortcut handling
241         if (event.KeyInput.Control) {
242
243                 // german backlash '\' entered with control + '?'
244                 if (event.KeyInput.Char == '\\') {
245                         inputChar(event.KeyInput.Char);
246                         return true;
247                 }
248
249                 switch (event.KeyInput.Key) {
250                 case KEY_KEY_A:
251                         // select all
252                         new_mark_begin = 0;
253                         new_mark_end = Text.size();
254                         break;
255                 case KEY_KEY_C:
256                         onKeyControlC(event);
257                         break;
258                 case KEY_KEY_X:
259                         text_changed = onKeyControlX(event, new_mark_begin, new_mark_end);
260                         break;
261                 case KEY_KEY_V:
262                         text_changed = onKeyControlV(event, new_mark_begin, new_mark_end);
263                         break;
264                 case KEY_HOME:
265                         // move/highlight to start of text
266                         if (event.KeyInput.Shift) {
267                                 new_mark_end = m_cursor_pos;
268                                 new_mark_begin = 0;
269                                 m_cursor_pos = 0;
270                         } else {
271                                 m_cursor_pos = 0;
272                                 new_mark_begin = 0;
273                                 new_mark_end = 0;
274                         }
275                         break;
276                 case KEY_END:
277                         // move/highlight to end of text
278                         if (event.KeyInput.Shift) {
279                                 new_mark_begin = m_cursor_pos;
280                                 new_mark_end = Text.size();
281                                 m_cursor_pos = 0;
282                         } else {
283                                 m_cursor_pos = Text.size();
284                                 new_mark_begin = 0;
285                                 new_mark_end = 0;
286                         }
287                         break;
288                 default:
289                         return false;
290                 }
291         } else {
292                 switch (event.KeyInput.Key) {
293                 case KEY_END: {
294                         s32 p = Text.size();
295                         if (m_word_wrap || m_multiline) {
296                                 p = getLineFromPos(m_cursor_pos);
297                                 p = m_broken_text_positions[p] +
298                                     (s32)m_broken_text[p].size();
299                                 if (p > 0 && (Text[p - 1] == L'\r' ||
300                                                              Text[p - 1] == L'\n'))
301                                         p -= 1;
302                         }
303
304                         if (event.KeyInput.Shift) {
305                                 if (m_mark_begin == m_mark_end)
306                                         new_mark_begin = m_cursor_pos;
307
308                                 new_mark_end = p;
309                         } else {
310                                 new_mark_begin = 0;
311                                 new_mark_end = 0;
312                         }
313                         m_cursor_pos = p;
314                         m_blink_start_time = porting::getTimeMs();
315                 } break;
316                 case KEY_HOME: {
317
318                         s32 p = 0;
319                         if (m_word_wrap || m_multiline) {
320                                 p = getLineFromPos(m_cursor_pos);
321                                 p = m_broken_text_positions[p];
322                         }
323
324                         if (event.KeyInput.Shift) {
325                                 if (m_mark_begin == m_mark_end)
326                                         new_mark_begin = m_cursor_pos;
327                                 new_mark_end = p;
328                         } else {
329                                 new_mark_begin = 0;
330                                 new_mark_end = 0;
331                         }
332                         m_cursor_pos = p;
333                         m_blink_start_time = porting::getTimeMs();
334                 } break;
335                 case KEY_RETURN:
336                         if (m_multiline) {
337                                 inputChar(L'\n');
338                         } else {
339                                 calculateScrollPos();
340                                 sendGuiEvent(EGET_EDITBOX_ENTER);
341                         }
342                         return true;
343                 case KEY_LEFT:
344                         if (event.KeyInput.Shift) {
345                                 if (m_cursor_pos > 0) {
346                                         if (m_mark_begin == m_mark_end)
347                                                 new_mark_begin = m_cursor_pos;
348
349                                         new_mark_end = m_cursor_pos - 1;
350                                 }
351                         } else {
352                                 new_mark_begin = 0;
353                                 new_mark_end = 0;
354                         }
355
356                         if (m_cursor_pos > 0)
357                                 m_cursor_pos--;
358                         m_blink_start_time = porting::getTimeMs();
359                         break;
360                 case KEY_RIGHT:
361                         if (event.KeyInput.Shift) {
362                                 if (Text.size() > (u32)m_cursor_pos) {
363                                         if (m_mark_begin == m_mark_end)
364                                                 new_mark_begin = m_cursor_pos;
365
366                                         new_mark_end = m_cursor_pos + 1;
367                                 }
368                         } else {
369                                 new_mark_begin = 0;
370                                 new_mark_end = 0;
371                         }
372
373                         if (Text.size() > (u32)m_cursor_pos)
374                                 m_cursor_pos++;
375                         m_blink_start_time = porting::getTimeMs();
376                         break;
377                 case KEY_UP:
378                         if (!onKeyUp(event, new_mark_begin, new_mark_end)) {
379                                 return false;
380                         }
381                         break;
382                 case KEY_DOWN:
383                         if (!onKeyDown(event, new_mark_begin, new_mark_end)) {
384                                 return false;
385                         }
386                         break;
387                 case KEY_BACK:
388                         text_changed = onKeyBack(event, new_mark_begin, new_mark_end);
389                         break;
390
391                 case KEY_DELETE:
392                         text_changed = onKeyDelete(event, new_mark_begin, new_mark_end);
393                         break;
394
395                 case KEY_ESCAPE:
396                 case KEY_TAB:
397                 case KEY_SHIFT:
398                 case KEY_F1:
399                 case KEY_F2:
400                 case KEY_F3:
401                 case KEY_F4:
402                 case KEY_F5:
403                 case KEY_F6:
404                 case KEY_F7:
405                 case KEY_F8:
406                 case KEY_F9:
407                 case KEY_F10:
408                 case KEY_F11:
409                 case KEY_F12:
410                 case KEY_F13:
411                 case KEY_F14:
412                 case KEY_F15:
413                 case KEY_F16:
414                 case KEY_F17:
415                 case KEY_F18:
416                 case KEY_F19:
417                 case KEY_F20:
418                 case KEY_F21:
419                 case KEY_F22:
420                 case KEY_F23:
421                 case KEY_F24:
422                         // ignore these keys
423                         return false;
424
425                 default:
426                         inputChar(event.KeyInput.Char);
427                         return true;
428                 }
429         }
430
431         // Set new text markers
432         setTextMarkers(new_mark_begin, new_mark_end);
433
434         // break the text if it has changed
435         if (text_changed) {
436                 breakText();
437                 sendGuiEvent(EGET_EDITBOX_CHANGED);
438         }
439
440         calculateScrollPos();
441
442         return true;
443 }
444
445 bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
446 {
447         // clang-format off
448         if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
449                 s32 lineNo = getLineFromPos(m_cursor_pos);
450                 s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
451                         (m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
452                 if (lineNo > 0) {
453                         s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
454                         if ((s32)m_broken_text[lineNo - 1].size() < cp) {
455                                 m_cursor_pos = m_broken_text_positions[lineNo - 1] +
456                                         core::max_((u32)1, m_broken_text[lineNo - 1].size()) - 1;
457                         }
458                         else
459                                 m_cursor_pos = m_broken_text_positions[lineNo - 1] + cp;
460                 }
461
462                 if (event.KeyInput.Shift) {
463                         mark_begin = mb;
464                         mark_end = m_cursor_pos;
465                 } else {
466                         mark_begin = 0;
467                         mark_end = 0;
468                 }
469
470                 return true;
471         }
472
473         // clang-format on
474         return false;
475 }
476
477 bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
478 {
479         // clang-format off
480         if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
481                 s32 lineNo = getLineFromPos(m_cursor_pos);
482                 s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
483                         (m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
484                 if (lineNo < (s32)m_broken_text.size() - 1) {
485                         s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
486                         if ((s32)m_broken_text[lineNo + 1].size() < cp) {
487                                 m_cursor_pos = m_broken_text_positions[lineNo + 1] +
488                                         core::max_((u32)1, m_broken_text[lineNo + 1].size()) - 1;
489                         }
490                         else
491                                 m_cursor_pos = m_broken_text_positions[lineNo + 1] + cp;
492                 }
493
494                 if (event.KeyInput.Shift) {
495                         mark_begin = mb;
496                         mark_end = m_cursor_pos;
497                 } else {
498                         mark_begin = 0;
499                         mark_end = 0;
500                 }
501
502                 return true;
503         }
504
505         // clang-format on
506         return false;
507 }
508
509 void GUIEditBox::onKeyControlC(const SEvent &event)
510 {
511         // copy to clipboard
512         if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
513                 return;
514
515         const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
516         const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
517
518         std::string s = stringw_to_utf8(Text.subString(realmbgn, realmend - realmbgn));
519         m_operator->copyToClipboard(s.c_str());
520 }
521
522 bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_end)
523 {
524         // First copy to clipboard
525         onKeyControlC(event);
526
527         if (!m_writable)
528                 return false;
529
530         if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
531                 return false;
532
533         const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
534         const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
535
536         // Now remove from box if enabled
537         if (isEnabled()) {
538                 // delete
539                 core::stringw s;
540                 s = Text.subString(0, realmbgn);
541                 s.append(Text.subString(realmend, Text.size() - realmend));
542                 Text = s;
543
544                 m_cursor_pos = realmbgn;
545                 mark_begin = 0;
546                 mark_end = 0;
547                 return true;
548         }
549
550         return false;
551 }
552
553 bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_end)
554 {
555         if (!isEnabled() || !m_writable)
556                 return false;
557
558         // paste from the clipboard
559         if (!m_operator)
560                 return false;
561
562         const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
563         const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
564
565         // add new character
566         if (const c8 *p = m_operator->getTextFromClipboard()) {
567                 core::stringw inserted_text = utf8_to_stringw(p);
568                 if (m_mark_begin == m_mark_end) {
569                         // insert text
570                         core::stringw s = Text.subString(0, m_cursor_pos);
571                         s.append(inserted_text);
572                         s.append(Text.subString(
573                                         m_cursor_pos, Text.size() - m_cursor_pos));
574
575                         if (!m_max || s.size() <= m_max) {
576                                 Text = s;
577                                 m_cursor_pos += inserted_text.size();
578                         }
579                 } else {
580                         // replace text
581
582                         core::stringw s = Text.subString(0, realmbgn);
583                         s.append(inserted_text);
584                         s.append(Text.subString(realmend, Text.size() - realmend));
585
586                         if (!m_max || s.size() <= m_max) {
587                                 Text = s;
588                                 m_cursor_pos = realmbgn + inserted_text.size();
589                         }
590                 }
591         }
592
593         mark_begin = 0;
594         mark_end = 0;
595         return true;
596 }
597
598 bool GUIEditBox::onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end)
599 {
600         if (!isEnabled() || Text.empty() || !m_writable)
601                 return false;
602
603         core::stringw s;
604
605         if (m_mark_begin != m_mark_end) {
606                 // delete marked text
607                 const s32 realmbgn =
608                                 m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
609                 const s32 realmend =
610                                 m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
611
612                 s = Text.subString(0, realmbgn);
613                 s.append(Text.subString(realmend, Text.size() - realmend));
614                 Text = s;
615
616                 m_cursor_pos = realmbgn;
617         } else {
618                 // delete text behind cursor
619                 if (m_cursor_pos > 0)
620                         s = Text.subString(0, m_cursor_pos - 1);
621                 else
622                         s = L"";
623                 s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
624                 Text = s;
625                 --m_cursor_pos;
626         }
627
628         if (m_cursor_pos < 0)
629                 m_cursor_pos = 0;
630         m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
631         mark_begin = 0;
632         mark_end = 0;
633         return true;
634 }
635
636 bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end)
637 {
638         if (!isEnabled() || Text.empty() || !m_writable)
639                 return false;
640
641         core::stringw s;
642
643         if (m_mark_begin != m_mark_end) {
644                 // delete marked text
645                 const s32 realmbgn =
646                                 m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
647                 const s32 realmend =
648                                 m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
649
650                 s = Text.subString(0, realmbgn);
651                 s.append(Text.subString(realmend, Text.size() - realmend));
652                 Text = s;
653
654                 m_cursor_pos = realmbgn;
655         } else {
656                 // delete text before cursor
657                 s = Text.subString(0, m_cursor_pos);
658                 s.append(Text.subString(
659                                 m_cursor_pos + 1, Text.size() - m_cursor_pos - 1));
660                 Text = s;
661         }
662
663         if (m_cursor_pos > (s32)Text.size())
664                 m_cursor_pos = (s32)Text.size();
665
666         m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
667         mark_begin = 0;
668         mark_end = 0;
669         return true;
670 }
671
672 void GUIEditBox::inputChar(wchar_t c)
673 {
674         if (c == 0)
675                 return;
676         core::stringw s(&c, 1);
677         inputString(s);
678 }
679
680 void GUIEditBox::inputString(const core::stringw &str)
681 {
682         if (!isEnabled() || !m_writable)
683                 return;
684
685         u32 len = str.size();
686         if (Text.size()+len <= m_max || m_max == 0) {
687                 core::stringw s;
688                 if (m_mark_begin != m_mark_end) {
689                         // replace marked text
690                         s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
691                         s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
692
693                         s = Text.subString(0, real_begin);
694                         s.append(str);
695                         s.append(Text.subString(real_end, Text.size() - real_end));
696                         Text = s;
697                         m_cursor_pos = real_begin + len;
698                 } else {
699                         // append string
700                         s = Text.subString(0, m_cursor_pos);
701                         s.append(str);
702                         s.append(Text.subString(m_cursor_pos,
703                                         Text.size() - m_cursor_pos));
704                         Text = s;
705                         m_cursor_pos += len;
706                 }
707
708                 m_blink_start_time = porting::getTimeMs();
709                 setTextMarkers(0, 0);
710         }
711
712         breakText();
713         sendGuiEvent(EGET_EDITBOX_CHANGED);
714         calculateScrollPos();
715 }
716
717 bool GUIEditBox::processMouse(const SEvent &event)
718 {
719         switch (event.MouseInput.Event) {
720         case irr::EMIE_LMOUSE_LEFT_UP:
721                 if (Environment->hasFocus(this)) {
722                         m_cursor_pos = getCursorPos(
723                                         event.MouseInput.X, event.MouseInput.Y);
724                         if (m_mouse_marking) {
725                                 setTextMarkers(m_mark_begin, m_cursor_pos);
726                         }
727                         m_mouse_marking = false;
728                         calculateScrollPos();
729                         return true;
730                 }
731                 break;
732         case irr::EMIE_MOUSE_MOVED: {
733                 if (m_mouse_marking) {
734                         m_cursor_pos = getCursorPos(
735                                         event.MouseInput.X, event.MouseInput.Y);
736                         setTextMarkers(m_mark_begin, m_cursor_pos);
737                         calculateScrollPos();
738                         return true;
739                 }
740         } break;
741         case EMIE_LMOUSE_PRESSED_DOWN:
742
743                 if (!Environment->hasFocus(this)) {
744                         m_blink_start_time = porting::getTimeMs();
745                         m_mouse_marking = true;
746                         m_cursor_pos = getCursorPos(
747                                         event.MouseInput.X, event.MouseInput.Y);
748                         setTextMarkers(m_cursor_pos, m_cursor_pos);
749                         calculateScrollPos();
750                         return true;
751                 } else {
752                         if (!AbsoluteClippingRect.isPointInside(core::position2d<s32>(
753                                             event.MouseInput.X, event.MouseInput.Y))) {
754                                 return false;
755                         } else {
756                                 // move cursor
757                                 m_cursor_pos = getCursorPos(
758                                                 event.MouseInput.X, event.MouseInput.Y);
759
760                                 s32 newMarkBegin = m_mark_begin;
761                                 if (!m_mouse_marking)
762                                         newMarkBegin = m_cursor_pos;
763
764                                 m_mouse_marking = true;
765                                 setTextMarkers(newMarkBegin, m_cursor_pos);
766                                 calculateScrollPos();
767                                 return true;
768                         }
769                 }
770         case EMIE_MOUSE_WHEEL:
771                 if (m_vscrollbar && m_vscrollbar->isVisible()) {
772                         s32 pos = m_vscrollbar->getPos();
773                         s32 step = m_vscrollbar->getSmallStep();
774                         m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
775                         return true;
776                 }
777                 break;
778         default:
779                 break;
780         }
781
782         return false;
783 }
784
785 s32 GUIEditBox::getLineFromPos(s32 pos)
786 {
787         if (!m_word_wrap && !m_multiline)
788                 return 0;
789
790         s32 i = 0;
791         while (i < (s32)m_broken_text_positions.size()) {
792                 if (m_broken_text_positions[i] > pos)
793                         return i - 1;
794                 ++i;
795         }
796         return (s32)m_broken_text_positions.size() - 1;
797 }
798
799 void GUIEditBox::updateVScrollBar()
800 {
801         if (!m_vscrollbar) {
802                 return;
803         }
804
805         // OnScrollBarChanged(...)
806         if (m_vscrollbar->getPos() != m_vscroll_pos) {
807                 s32 deltaScrollY = m_vscrollbar->getPos() - m_vscroll_pos;
808                 m_current_text_rect.UpperLeftCorner.Y -= deltaScrollY;
809                 m_current_text_rect.LowerRightCorner.Y -= deltaScrollY;
810
811                 s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
812                 if (scrollymax != m_vscrollbar->getMax()) {
813                         // manage a newline or a deleted line
814                         m_vscrollbar->setMax(scrollymax);
815                         m_vscrollbar->setPageSize(s32(getTextDimension().Height));
816                         calculateScrollPos();
817                 } else {
818                         // manage a newline or a deleted line
819                         m_vscroll_pos = m_vscrollbar->getPos();
820                 }
821         }
822
823         // check if a vertical scrollbar is needed ?
824         if (getTextDimension().Height > (u32)m_frame_rect.getHeight()) {
825                 m_frame_rect.LowerRightCorner.X -= m_scrollbar_width;
826
827                 s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
828                 if (scrollymax != m_vscrollbar->getMax()) {
829                         m_vscrollbar->setMax(scrollymax);
830                         m_vscrollbar->setPageSize(s32(getTextDimension().Height));
831                 }
832
833                 if (!m_vscrollbar->isVisible()) {
834                         m_vscrollbar->setVisible(true);
835                 }
836         } else {
837                 if (m_vscrollbar->isVisible()) {
838                         m_vscrollbar->setVisible(false);
839                         m_vscroll_pos = 0;
840                         m_vscrollbar->setPos(0);
841                         m_vscrollbar->setMax(1);
842                         m_vscrollbar->setPageSize(s32(getTextDimension().Height));
843                 }
844         }
845 }