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