]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CGUIEditBox.cpp
a55664e1aa2f7098a54cce4b6b8a1adefb064392
[irrlicht.git] / source / Irrlicht / CGUIEditBox.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "CGUIEditBox.h"\r
6 #ifdef _IRR_COMPILE_WITH_GUI_\r
7 \r
8 #include "IGUISkin.h"\r
9 #include "IGUIEnvironment.h"\r
10 #include "IGUIFont.h"\r
11 #include "IVideoDriver.h"\r
12 #include "rect.h"\r
13 #include "os.h"\r
14 #include "Keycodes.h"\r
15 \r
16 /*\r
17         todo:\r
18         optional scrollbars\r
19         ctrl+left/right to select word\r
20         double click/ctrl click: word select + drag to select whole words, triple click to select line\r
21         optional? dragging selected text\r
22         numerical\r
23 */\r
24 \r
25 namespace irr\r
26 {\r
27 namespace gui\r
28 {\r
29 \r
30 //! constructor\r
31 CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,\r
32                 IGUIEnvironment* environment, IGUIElement* parent, s32 id,\r
33                 const core::rect<s32>& rectangle)\r
34         : IGUIEditBox(environment, parent, id, rectangle), OverwriteMode(false), MouseMarking(false),\r
35         Border(border), Background(true), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),\r
36         OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0),\r
37         Operator(0), BlinkStartTime(0), CursorBlinkTime(350), CursorChar(L"_"), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),\r
38         WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false),\r
39         PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),\r
40         CurrentTextRect(0,0,1,1), FrameRect(rectangle)\r
41 {\r
42         #ifdef _DEBUG\r
43         setDebugName("CGUIEditBox");\r
44         #endif\r
45 \r
46         Text = text;\r
47 \r
48         if (Environment)\r
49                 Operator = Environment->getOSOperator();\r
50 \r
51         if (Operator)\r
52                 Operator->grab();\r
53 \r
54         // this element can be tabbed to\r
55         setTabStop(true);\r
56         setTabOrder(-1);\r
57 \r
58         calculateFrameRect();\r
59         breakText();\r
60 \r
61         calculateScrollPos();\r
62 }\r
63 \r
64 \r
65 //! destructor\r
66 CGUIEditBox::~CGUIEditBox()\r
67 {\r
68         if (OverrideFont)\r
69                 OverrideFont->drop();\r
70 \r
71         if (Operator)\r
72                 Operator->drop();\r
73 }\r
74 \r
75 \r
76 //! Sets another skin independent font.\r
77 void CGUIEditBox::setOverrideFont(IGUIFont* font)\r
78 {\r
79         if (OverrideFont == font)\r
80                 return;\r
81 \r
82         if (OverrideFont)\r
83                 OverrideFont->drop();\r
84 \r
85         OverrideFont = font;\r
86 \r
87         if (OverrideFont)\r
88                 OverrideFont->grab();\r
89 \r
90         breakText();\r
91 }\r
92 \r
93 //! Gets the override font (if any)\r
94 IGUIFont * CGUIEditBox::getOverrideFont() const\r
95 {\r
96         return OverrideFont;\r
97 }\r
98 \r
99 //! Get the font which is used right now for drawing\r
100 IGUIFont* CGUIEditBox::getActiveFont() const\r
101 {\r
102         if ( OverrideFont )\r
103                 return OverrideFont;\r
104         IGUISkin* skin = Environment->getSkin();\r
105         if (skin)\r
106                 return skin->getFont();\r
107         return 0;\r
108 }\r
109 \r
110 //! Sets another color for the text.\r
111 void CGUIEditBox::setOverrideColor(video::SColor color)\r
112 {\r
113         OverrideColor = color;\r
114         OverrideColorEnabled = true;\r
115 }\r
116 \r
117 \r
118 video::SColor CGUIEditBox::getOverrideColor() const\r
119 {\r
120         return OverrideColor;\r
121 }\r
122 \r
123 \r
124 //! Turns the border on or off\r
125 void CGUIEditBox::setDrawBorder(bool border)\r
126 {\r
127         Border = border;\r
128 }\r
129 \r
130 //! Checks if border drawing is enabled\r
131 bool CGUIEditBox::isDrawBorderEnabled() const\r
132 {\r
133         return Border;\r
134 }\r
135 \r
136 //! Sets whether to draw the background\r
137 void CGUIEditBox::setDrawBackground(bool draw)\r
138 {\r
139         Background = draw;\r
140 }\r
141 \r
142 //! Checks if background drawing is enabled\r
143 bool CGUIEditBox::isDrawBackgroundEnabled() const\r
144 {\r
145         return Background;\r
146 }\r
147 \r
148 //! Sets if the text should use the override color or the color in the gui skin.\r
149 void CGUIEditBox::enableOverrideColor(bool enable)\r
150 {\r
151         OverrideColorEnabled = enable;\r
152 }\r
153 \r
154 bool CGUIEditBox::isOverrideColorEnabled() const\r
155 {\r
156         return OverrideColorEnabled;\r
157 }\r
158 \r
159 //! Enables or disables word wrap\r
160 void CGUIEditBox::setWordWrap(bool enable)\r
161 {\r
162         WordWrap = enable;\r
163         breakText();\r
164 }\r
165 \r
166 \r
167 void CGUIEditBox::updateAbsolutePosition()\r
168 {\r
169         core::rect<s32> oldAbsoluteRect(AbsoluteRect);\r
170         IGUIElement::updateAbsolutePosition();\r
171         if ( oldAbsoluteRect != AbsoluteRect )\r
172         {\r
173                 calculateFrameRect();\r
174                 breakText();\r
175                 calculateScrollPos();\r
176         }\r
177 }\r
178 \r
179 \r
180 //! Checks if word wrap is enabled\r
181 bool CGUIEditBox::isWordWrapEnabled() const\r
182 {\r
183         return WordWrap;\r
184 }\r
185 \r
186 \r
187 //! Enables or disables newlines.\r
188 void CGUIEditBox::setMultiLine(bool enable)\r
189 {\r
190         MultiLine = enable;\r
191         breakText();\r
192 }\r
193 \r
194 \r
195 //! Checks if multi line editing is enabled\r
196 bool CGUIEditBox::isMultiLineEnabled() const\r
197 {\r
198         return MultiLine;\r
199 }\r
200 \r
201 \r
202 void CGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)\r
203 {\r
204         PasswordBox = passwordBox;\r
205         if (PasswordBox)\r
206         {\r
207                 PasswordChar = passwordChar;\r
208                 setMultiLine(false);\r
209                 setWordWrap(false);\r
210                 BrokenText.clear();\r
211         }\r
212 }\r
213 \r
214 \r
215 bool CGUIEditBox::isPasswordBox() const\r
216 {\r
217         return PasswordBox;\r
218 }\r
219 \r
220 \r
221 //! Sets text justification\r
222 void CGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)\r
223 {\r
224         HAlign = horizontal;\r
225         VAlign = vertical;\r
226 }\r
227 \r
228 \r
229 //! called if an event happened.\r
230 bool CGUIEditBox::OnEvent(const SEvent& event)\r
231 {\r
232         if (isEnabled())\r
233         {\r
234 \r
235                 switch(event.EventType)\r
236                 {\r
237                 case EET_GUI_EVENT:\r
238                         if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)\r
239                         {\r
240                                 if (event.GUIEvent.Caller == this)\r
241                                 {\r
242                                         MouseMarking = false;\r
243                                         setTextMarkers(0,0);\r
244                                 }\r
245                         }\r
246                         break;\r
247                 case EET_KEY_INPUT_EVENT:\r
248                         if (processKey(event))\r
249                                 return true;\r
250                         break;\r
251                 case EET_MOUSE_INPUT_EVENT:\r
252                         if (processMouse(event))\r
253                                 return true;\r
254                         break;\r
255                 default:\r
256                         break;\r
257                 }\r
258         }\r
259 \r
260         return IGUIElement::OnEvent(event);\r
261 }\r
262 \r
263 \r
264 bool CGUIEditBox::processKey(const SEvent& event)\r
265 {\r
266         if (!event.KeyInput.PressedDown)\r
267                 return false;\r
268 \r
269         bool textChanged = false;\r
270         s32 newMarkBegin = MarkBegin;\r
271         s32 newMarkEnd = MarkEnd;\r
272 \r
273         // control shortcut handling\r
274 \r
275         if (event.KeyInput.Control)\r
276         {\r
277                 // german backlash '\' entered with control + '?'\r
278                 if ( event.KeyInput.Char == '\\' )\r
279                 {\r
280                         inputChar(event.KeyInput.Char);\r
281                         return true;\r
282                 }\r
283 \r
284                 switch(event.KeyInput.Key)\r
285                 {\r
286                 case KEY_KEY_A:\r
287                         // select all\r
288                         newMarkBegin = 0;\r
289                         newMarkEnd = Text.size();\r
290                         break;\r
291                 case KEY_KEY_C:\r
292                         // copy to clipboard\r
293                         if (!PasswordBox && Operator && MarkBegin != MarkEnd)\r
294                         {\r
295                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
296                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
297 \r
298                                 core::stringc s;\r
299                                 s = Text.subString(realmbgn, realmend - realmbgn).c_str();\r
300                                 Operator->copyToClipboard(s.c_str());\r
301                         }\r
302                         break;\r
303                 case KEY_KEY_X:\r
304                         // cut to the clipboard\r
305                         if (!PasswordBox && Operator && MarkBegin != MarkEnd)\r
306                         {\r
307                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
308                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
309 \r
310                                 // copy\r
311                                 core::stringc sc;\r
312                                 sc = Text.subString(realmbgn, realmend - realmbgn).c_str();\r
313                                 Operator->copyToClipboard(sc.c_str());\r
314 \r
315                                 if (isEnabled())\r
316                                 {\r
317                                         // delete\r
318                                         core::stringw s;\r
319                                         s = Text.subString(0, realmbgn);\r
320                                         s.append( Text.subString(realmend, Text.size()-realmend) );\r
321                                         Text = s;\r
322 \r
323                                         CursorPos = realmbgn;\r
324                                         newMarkBegin = 0;\r
325                                         newMarkEnd = 0;\r
326                                         textChanged = true;\r
327                                 }\r
328                         }\r
329                         break;\r
330                 case KEY_KEY_V:\r
331                         if ( !isEnabled() )\r
332                                 break;\r
333 \r
334                         // paste from the clipboard\r
335                         if (Operator)\r
336                         {\r
337                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
338                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
339 \r
340                                 // add new character\r
341                                 const c8* p = Operator->getTextFromClipboard();\r
342                                 if (p)\r
343                                 {\r
344                                         irr::core::stringw widep;\r
345                                         core::multibyteToWString(widep, p);\r
346 \r
347                                         if (MarkBegin == MarkEnd)\r
348                                         {\r
349                                                 // insert text\r
350                                                 core::stringw s = Text.subString(0, CursorPos);\r
351                                                 s.append(widep);\r
352                                                 s.append( Text.subString(CursorPos, Text.size()-CursorPos) );\r
353 \r
354                                                 if (!Max || s.size()<=Max) // thx to Fish FH for fix\r
355                                                 {\r
356                                                         Text = s;\r
357                                                         s = widep;\r
358                                                         CursorPos += s.size();\r
359                                                 }\r
360                                         }\r
361                                         else\r
362                                         {\r
363                                                 // replace text\r
364 \r
365                                                 core::stringw s = Text.subString(0, realmbgn);\r
366                                                 s.append(widep);\r
367                                                 s.append( Text.subString(realmend, Text.size()-realmend) );\r
368 \r
369                                                 if (!Max || s.size()<=Max)  // thx to Fish FH for fix\r
370                                                 {\r
371                                                         Text = s;\r
372                                                         s = widep;\r
373                                                         CursorPos = realmbgn + s.size();\r
374                                                 }\r
375                                         }\r
376                                 }\r
377 \r
378                                 newMarkBegin = 0;\r
379                                 newMarkEnd = 0;\r
380                                 textChanged = true;\r
381                         }\r
382                         break;\r
383                 case KEY_HOME:\r
384                         // move/highlight to start of text\r
385                         if (event.KeyInput.Shift)\r
386                         {\r
387                                 newMarkEnd = CursorPos;\r
388                                 newMarkBegin = 0;\r
389                                 CursorPos = 0;\r
390                         }\r
391                         else\r
392                         {\r
393                                 CursorPos = 0;\r
394                                 newMarkBegin = 0;\r
395                                 newMarkEnd = 0;\r
396                         }\r
397                         break;\r
398                 case KEY_END:\r
399                         // move/highlight to end of text\r
400                         if (event.KeyInput.Shift)\r
401                         {\r
402                                 newMarkBegin = CursorPos;\r
403                                 newMarkEnd = Text.size();\r
404                                 CursorPos = 0;\r
405                         }\r
406                         else\r
407                         {\r
408                                 CursorPos = Text.size();\r
409                                 newMarkBegin = 0;\r
410                                 newMarkEnd = 0;\r
411                         }\r
412                         break;\r
413                 default:\r
414                         return false;\r
415                 }\r
416         }\r
417         // Some special keys - but only handle them if KeyInput.Char is null as on some systems (X11) they might have same key-code as ansi-keys otherwise\r
418         else if (event.KeyInput.Char == 0)\r
419         {\r
420                 switch(event.KeyInput.Key)\r
421                 {\r
422                 case KEY_END:\r
423                         {\r
424                                 s32 p = Text.size();\r
425                                 if (WordWrap || MultiLine)\r
426                                 {\r
427                                         p = getLineFromPos(CursorPos);\r
428                                         p = BrokenTextPositions[p] + (s32)BrokenText[p].size();\r
429                                         if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))\r
430                                                 p-=1;\r
431                                 }\r
432 \r
433                                 if (event.KeyInput.Shift)\r
434                                 {\r
435                                         if (MarkBegin == MarkEnd)\r
436                                                 newMarkBegin = CursorPos;\r
437 \r
438                                         newMarkEnd = p;\r
439                                 }\r
440                                 else\r
441                                 {\r
442                                         newMarkBegin = 0;\r
443                                         newMarkEnd = 0;\r
444                                 }\r
445                                 CursorPos = p;\r
446                                 BlinkStartTime = os::Timer::getTime();\r
447                         }\r
448                         break;\r
449                 case KEY_HOME:\r
450                         {\r
451 \r
452                                 s32 p = 0;\r
453                                 if (WordWrap || MultiLine)\r
454                                 {\r
455                                         p = getLineFromPos(CursorPos);\r
456                                         p = BrokenTextPositions[p];\r
457                                 }\r
458 \r
459                                 if (event.KeyInput.Shift)\r
460                                 {\r
461                                         if (MarkBegin == MarkEnd)\r
462                                                 newMarkBegin = CursorPos;\r
463                                         newMarkEnd = p;\r
464                                 }\r
465                                 else\r
466                                 {\r
467                                         newMarkBegin = 0;\r
468                                         newMarkEnd = 0;\r
469                                 }\r
470                                 CursorPos = p;\r
471                                 BlinkStartTime = os::Timer::getTime();\r
472                         }\r
473                         break;\r
474                 case KEY_LEFT:\r
475 \r
476                         if (event.KeyInput.Shift)\r
477                         {\r
478                                 if (CursorPos > 0)\r
479                                 {\r
480                                         if (MarkBegin == MarkEnd)\r
481                                                 newMarkBegin = CursorPos;\r
482 \r
483                                         newMarkEnd = CursorPos-1;\r
484                                 }\r
485                         }\r
486                         else\r
487                         {\r
488                                 newMarkBegin = 0;\r
489                                 newMarkEnd = 0;\r
490                         }\r
491 \r
492                         if (CursorPos > 0) CursorPos--;\r
493                         BlinkStartTime = os::Timer::getTime();\r
494                         break;\r
495 \r
496                 case KEY_RIGHT:\r
497                         if (event.KeyInput.Shift)\r
498                         {\r
499                                 if (Text.size() > (u32)CursorPos)\r
500                                 {\r
501                                         if (MarkBegin == MarkEnd)\r
502                                                 newMarkBegin = CursorPos;\r
503 \r
504                                         newMarkEnd = CursorPos+1;\r
505                                 }\r
506                         }\r
507                         else\r
508                         {\r
509                                 newMarkBegin = 0;\r
510                                 newMarkEnd = 0;\r
511                         }\r
512 \r
513                         if (Text.size() > (u32)CursorPos) CursorPos++;\r
514                         BlinkStartTime = os::Timer::getTime();\r
515                         break;\r
516                 case KEY_UP:\r
517                         if (MultiLine || (WordWrap && BrokenText.size() > 1) )\r
518                         {\r
519                                 s32 lineNo = getLineFromPos(CursorPos);\r
520                                 s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);\r
521                                 if (lineNo > 0)\r
522                                 {\r
523                                         s32 cp = CursorPos - BrokenTextPositions[lineNo];\r
524                                         if ((s32)BrokenText[lineNo-1].size() < cp)\r
525                                                 CursorPos = BrokenTextPositions[lineNo-1] + core::max_((u32)1, BrokenText[lineNo-1].size())-1;\r
526                                         else\r
527                                                 CursorPos = BrokenTextPositions[lineNo-1] + cp;\r
528                                 }\r
529 \r
530                                 if (event.KeyInput.Shift)\r
531                                 {\r
532                                         newMarkBegin = mb;\r
533                                         newMarkEnd = CursorPos;\r
534                                 }\r
535                                 else\r
536                                 {\r
537                                         newMarkBegin = 0;\r
538                                         newMarkEnd = 0;\r
539                                 }\r
540 \r
541                         }\r
542                         else\r
543                         {\r
544                                 return false;\r
545                         }\r
546                         break;\r
547                 case KEY_DOWN:\r
548                         if (MultiLine || (WordWrap && BrokenText.size() > 1) )\r
549                         {\r
550                                 s32 lineNo = getLineFromPos(CursorPos);\r
551                                 s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);\r
552                                 if (lineNo < (s32)BrokenText.size()-1)\r
553                                 {\r
554                                         s32 cp = CursorPos - BrokenTextPositions[lineNo];\r
555                                         if ((s32)BrokenText[lineNo+1].size() < cp)\r
556                                                 CursorPos = BrokenTextPositions[lineNo+1] + core::max_((u32)1, BrokenText[lineNo+1].size())-1;\r
557                                         else\r
558                                                 CursorPos = BrokenTextPositions[lineNo+1] + cp;\r
559                                 }\r
560 \r
561                                 if (event.KeyInput.Shift)\r
562                                 {\r
563                                         newMarkBegin = mb;\r
564                                         newMarkEnd = CursorPos;\r
565                                 }\r
566                                 else\r
567                                 {\r
568                                         newMarkBegin = 0;\r
569                                         newMarkEnd = 0;\r
570                                 }\r
571 \r
572                         }\r
573                         else\r
574                         {\r
575                                 return false;\r
576                         }\r
577                         break;\r
578                 case KEY_INSERT:\r
579                         if ( !isEnabled() )\r
580                                 break;\r
581 \r
582                         OverwriteMode = !OverwriteMode;\r
583                         break;\r
584                 case KEY_DELETE:\r
585                         if ( !isEnabled() )\r
586                                 break;\r
587 \r
588                         if (keyDelete())\r
589                         {\r
590                                 BlinkStartTime = os::Timer::getTime();\r
591                                 newMarkBegin = 0;\r
592                                 newMarkEnd = 0;\r
593                                 textChanged = true;\r
594                         }\r
595                         break;\r
596                 default:\r
597                         return false;\r
598                 }\r
599         }\r
600         else\r
601         {\r
602                 // default keyboard handling\r
603                 switch(event.KeyInput.Key)\r
604                 {\r
605                 case KEY_RETURN:\r
606                         if (MultiLine)\r
607                         {\r
608                                 inputChar(L'\n');\r
609                         }\r
610                         else\r
611                         {\r
612                                 calculateScrollPos();\r
613                                 sendGuiEvent( EGET_EDITBOX_ENTER );\r
614                         }\r
615                         return true;\r
616 \r
617                 case KEY_BACK:\r
618                         if ( !isEnabled() )\r
619                                 break;\r
620 \r
621                         if (Text.size())\r
622                         {\r
623                                 core::stringw s;\r
624 \r
625                                 if (MarkBegin != MarkEnd)\r
626                                 {\r
627                                         // delete marked text\r
628                                         const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
629                                         const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
630 \r
631                                         s = Text.subString(0, realmbgn);\r
632                                         s.append( Text.subString(realmend, Text.size()-realmend) );\r
633                                         Text = s;\r
634 \r
635                                         CursorPos = realmbgn;\r
636                                 }\r
637                                 else\r
638                                 {\r
639                                         // delete text behind cursor\r
640                                         if (CursorPos>0)\r
641                                                 s = Text.subString(0, CursorPos-1);\r
642                                         else\r
643                                                 s = L"";\r
644                                         s.append( Text.subString(CursorPos, Text.size()-CursorPos) );\r
645                                         Text = s;\r
646                                         --CursorPos;\r
647                                 }\r
648 \r
649                                 if (CursorPos < 0)\r
650                                         CursorPos = 0;\r
651                                 BlinkStartTime = os::Timer::getTime();\r
652                                 newMarkBegin = 0;\r
653                                 newMarkEnd = 0;\r
654                                 textChanged = true;\r
655                         }\r
656                         break;\r
657 \r
658                 case KEY_DELETE:\r
659 \r
660                         // At least on X11 we get a char with 127 when the delete key is pressed.\r
661                         // We get no char when the delete key on numkeys is pressed with numlock off (handled in the other case calling keyDelete as Char is then 0).\r
662                         // We get a keykode != 127 when delete key on numlock is pressed with numlock on.\r
663                         if (event.KeyInput.Char == 127)\r
664                         {\r
665                                 if ( !isEnabled() )\r
666                                         break;\r
667 \r
668                                 if (keyDelete())\r
669                                 {\r
670                                         BlinkStartTime = os::Timer::getTime();\r
671                                         newMarkBegin = 0;\r
672                                         newMarkEnd = 0;\r
673                                         textChanged = true;\r
674                                 }\r
675                                 break;\r
676                         }\r
677                         else\r
678                         {\r
679                                 inputChar(event.KeyInput.Char);\r
680                                 return true;\r
681                         }\r
682 \r
683                 case KEY_ESCAPE:\r
684                 case KEY_TAB:\r
685                 case KEY_SHIFT:\r
686                 case KEY_F1:\r
687                 case KEY_F2:\r
688                 case KEY_F3:\r
689                 case KEY_F4:\r
690                 case KEY_F5:\r
691                 case KEY_F6:\r
692                 case KEY_F7:\r
693                 case KEY_F8:\r
694                 case KEY_F9:\r
695                 case KEY_F10:\r
696                 case KEY_F11:\r
697                 case KEY_F12:\r
698                 case KEY_F13:\r
699                 case KEY_F14:\r
700                 case KEY_F15:\r
701                 case KEY_F16:\r
702                 case KEY_F17:\r
703                 case KEY_F18:\r
704                 case KEY_F19:\r
705                 case KEY_F20:\r
706                 case KEY_F21:\r
707                 case KEY_F22:\r
708                 case KEY_F23:\r
709                 case KEY_F24:\r
710                         // ignore these keys\r
711                         return false;\r
712 \r
713                 default:\r
714                         inputChar(event.KeyInput.Char);\r
715                         return true;\r
716                 }\r
717         }\r
718 \r
719         // Set new text markers\r
720         setTextMarkers( newMarkBegin, newMarkEnd );\r
721 \r
722         // break the text if it has changed\r
723         if (textChanged)\r
724         {\r
725                 breakText();\r
726                 calculateScrollPos();\r
727                 sendGuiEvent(EGET_EDITBOX_CHANGED);\r
728         }\r
729         else\r
730         {\r
731                 calculateScrollPos();\r
732         }\r
733 \r
734         return true;\r
735 }\r
736 \r
737 bool CGUIEditBox::keyDelete()\r
738 {\r
739         if (Text.size() != 0)\r
740         {\r
741                 core::stringw s;\r
742 \r
743                 if (MarkBegin != MarkEnd)\r
744                 {\r
745                         // delete marked text\r
746                         const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
747                         const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
748 \r
749                         s = Text.subString(0, realmbgn);\r
750                         s.append( Text.subString(realmend, Text.size()-realmend) );\r
751                         Text = s;\r
752 \r
753                         CursorPos = realmbgn;\r
754                 }\r
755                 else\r
756                 {\r
757                         // delete text before cursor\r
758                         s = Text.subString(0, CursorPos);\r
759                         s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );\r
760                         Text = s;\r
761                 }\r
762 \r
763                 if (CursorPos > (s32)Text.size())\r
764                         CursorPos = (s32)Text.size();\r
765 \r
766                 return true;\r
767         }\r
768 \r
769         return false;\r
770 }\r
771 \r
772 //! draws the element and its children\r
773 void CGUIEditBox::draw()\r
774 {\r
775         if (!IsVisible)\r
776                 return;\r
777 \r
778         const bool focus = Environment->hasFocus(this);\r
779 \r
780         IGUISkin* skin = Environment->getSkin();\r
781         if (!skin)\r
782                 return;\r
783 \r
784         EGUI_DEFAULT_COLOR bgCol = EGDC_GRAY_EDITABLE;\r
785         if ( isEnabled() )\r
786                 bgCol = focus ? EGDC_FOCUSED_EDITABLE : EGDC_EDITABLE;\r
787 \r
788         if (!Border && Background)\r
789         {\r
790                 skin->draw2DRectangle(this, skin->getColor(bgCol), AbsoluteRect, &AbsoluteClippingRect);\r
791         }\r
792 \r
793         if (Border)\r
794         {\r
795                 // draw the border\r
796                 skin->draw3DSunkenPane(this, skin->getColor(bgCol), false, Background, AbsoluteRect, &AbsoluteClippingRect);\r
797 \r
798                 calculateFrameRect();\r
799         }\r
800 \r
801         core::rect<s32> localClipRect = FrameRect;\r
802         localClipRect.clipAgainst(AbsoluteClippingRect);\r
803 \r
804         // draw the text\r
805 \r
806         IGUIFont* font = getActiveFont();\r
807 \r
808         s32 cursorLine = 0;\r
809         s32 charcursorpos = 0;\r
810 \r
811         if (font)\r
812         {\r
813                 if (LastBreakFont != font)\r
814                 {\r
815                         breakText();\r
816                 }\r
817 \r
818                 // calculate cursor pos\r
819 \r
820                 core::stringw *txtLine = &Text;\r
821                 s32 startPos = 0;\r
822 \r
823                 core::stringw s, s2;\r
824 \r
825                 // get mark position\r
826                 const bool ml = (!PasswordBox && (WordWrap || MultiLine));\r
827                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
828                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
829                 const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;\r
830                 const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;\r
831                 const s32 lineCount = ml ? BrokenText.size() : 1;\r
832 \r
833                 // Save the override color information.\r
834                 // Then, alter it if the edit box is disabled.\r
835                 const bool prevOver = OverrideColorEnabled;\r
836                 const video::SColor prevColor = OverrideColor;\r
837 \r
838                 if (Text.size())\r
839                 {\r
840                         if (!isEnabled() && !OverrideColorEnabled)\r
841                         {\r
842                                 OverrideColorEnabled = true;\r
843                                 OverrideColor = skin->getColor(EGDC_GRAY_TEXT);\r
844                         }\r
845 \r
846                         for (s32 i=0; i < lineCount; ++i)\r
847                         {\r
848                                 setTextRect(i);\r
849 \r
850                                 // clipping test - don't draw anything outside the visible area\r
851                                 core::rect<s32> c = localClipRect;\r
852                                 c.clipAgainst(CurrentTextRect);\r
853                                 if (!c.isValid())\r
854                                         continue;\r
855 \r
856                                 // get current line\r
857                                 if (PasswordBox)\r
858                                 {\r
859                                         if (BrokenText.size() != 1)\r
860                                         {\r
861                                                 BrokenText.clear();\r
862                                                 BrokenText.push_back(core::stringw());\r
863                                         }\r
864                                         if (BrokenText[0].size() != Text.size())\r
865                                         {\r
866                                                 BrokenText[0] = Text;\r
867                                                 for (u32 q = 0; q < Text.size(); ++q)\r
868                                                 {\r
869                                                         BrokenText[0] [q] = PasswordChar;\r
870                                                 }\r
871                                         }\r
872                                         txtLine = &BrokenText[0];\r
873                                         startPos = 0;\r
874                                 }\r
875                                 else\r
876                                 {\r
877                                         txtLine = ml ? &BrokenText[i] : &Text;\r
878                                         startPos = ml ? BrokenTextPositions[i] : 0;\r
879                                 }\r
880 \r
881 \r
882                                 // draw normal text\r
883                                 font->draw(txtLine->c_str(), CurrentTextRect,\r
884                                         OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),\r
885                                         false, true, &localClipRect);\r
886 \r
887                                 // draw mark and marked text\r
888                                 if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)\r
889                                 {\r
890 \r
891                                         s32 mbegin = 0, mend = 0;\r
892                                         s32 lineStartPos = 0, lineEndPos = txtLine->size();\r
893 \r
894                                         if (i == hlineStart)\r
895                                         {\r
896                                                 // highlight start is on this line\r
897                                                 s = txtLine->subString(0, realmbgn - startPos);\r
898                                                 mbegin = font->getDimension(s.c_str()).Width;\r
899 \r
900                                                 // deal with kerning\r
901                                                 mbegin += font->getKerningWidth(\r
902                                                         &((*txtLine)[realmbgn - startPos]),\r
903                                                         realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);\r
904 \r
905                                                 lineStartPos = realmbgn - startPos;\r
906                                         }\r
907                                         if (i == hlineStart + hlineCount - 1)\r
908                                         {\r
909                                                 // highlight end is on this line\r
910                                                 s2 = txtLine->subString(0, realmend - startPos);\r
911                                                 mend = font->getDimension(s2.c_str()).Width;\r
912                                                 lineEndPos = (s32)s2.size();\r
913                                         }\r
914                                         else\r
915                                                 mend = font->getDimension(txtLine->c_str()).Width;\r
916 \r
917                                         CurrentTextRect.UpperLeftCorner.X += mbegin;\r
918                                         CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;\r
919 \r
920                                         // draw mark\r
921                                         skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);\r
922 \r
923                                         // draw marked text\r
924                                         s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);\r
925 \r
926                                         if (s.size())\r
927                                                 font->draw(s.c_str(), CurrentTextRect,\r
928                                                         OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),\r
929                                                         false, true, &localClipRect);\r
930 \r
931                                 }\r
932                         }\r
933 \r
934                         // Return the override color information to its previous settings.\r
935                         OverrideColorEnabled = prevOver;\r
936                         OverrideColor = prevColor;\r
937                 }\r
938 \r
939                 // draw cursor\r
940                 if ( isEnabled() )\r
941                 {\r
942                         if (WordWrap || MultiLine)\r
943                         {\r
944                                 cursorLine = getLineFromPos(CursorPos);\r
945                                 txtLine = &BrokenText[cursorLine];\r
946                                 startPos = BrokenTextPositions[cursorLine];\r
947                         }\r
948                         s = txtLine->subString(0,CursorPos-startPos);\r
949                         charcursorpos = font->getDimension(s.c_str()).Width +\r
950                                 font->getKerningWidth(CursorChar.c_str(), CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);\r
951 \r
952                         if (focus && (CursorBlinkTime == 0 || (os::Timer::getTime() - BlinkStartTime) % (2*CursorBlinkTime) < CursorBlinkTime))\r
953                         {\r
954                                 setTextRect(cursorLine);\r
955                                 CurrentTextRect.UpperLeftCorner.X += charcursorpos;\r
956 \r
957                                 if ( OverwriteMode )\r
958                                 {\r
959                                         core::stringw character = Text.subString(CursorPos,1);\r
960                                         s32 mend = font->getDimension(character.c_str()).Width;\r
961                                         //Make sure the cursor box has at least some width to it\r
962                                         if ( mend <= 0 )\r
963                                                 mend = font->getDimension(CursorChar.c_str()).Width;\r
964                                         CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend;\r
965                                         skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);\r
966                                         font->draw(character.c_str(), CurrentTextRect,\r
967                                                                 OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),\r
968                                                                 false, true, &localClipRect);\r
969                                 }\r
970                                 else\r
971                                 {\r
972                                         font->draw(CursorChar, CurrentTextRect,\r
973                                                 OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),\r
974                                                 false, true, &localClipRect);\r
975                                 }\r
976                         }\r
977                 }\r
978         }\r
979 \r
980         // draw children\r
981         IGUIElement::draw();\r
982 }\r
983 \r
984 \r
985 //! Sets the new caption of this element.\r
986 void CGUIEditBox::setText(const wchar_t* text)\r
987 {\r
988         Text = text;\r
989         if (u32(CursorPos) > Text.size())\r
990                 CursorPos = Text.size();\r
991         HScrollPos = 0;\r
992         breakText();\r
993 }\r
994 \r
995 \r
996 //! Enables or disables automatic scrolling with cursor position\r
997 //! \param enable: If set to true, the text will move around with the cursor position\r
998 void CGUIEditBox::setAutoScroll(bool enable)\r
999 {\r
1000         AutoScroll = enable;\r
1001 }\r
1002 \r
1003 \r
1004 //! Checks to see if automatic scrolling is enabled\r
1005 //! \return true if automatic scrolling is enabled, false if not\r
1006 bool CGUIEditBox::isAutoScrollEnabled() const\r
1007 {\r
1008         return AutoScroll;\r
1009 }\r
1010 \r
1011 \r
1012 //! Gets the area of the text in the edit box\r
1013 //! \return Returns the size in pixels of the text\r
1014 core::dimension2du CGUIEditBox::getTextDimension()\r
1015 {\r
1016         core::rect<s32> ret;\r
1017 \r
1018         setTextRect(0);\r
1019         ret = CurrentTextRect;\r
1020 \r
1021         for (u32 i=1; i < BrokenText.size(); ++i)\r
1022         {\r
1023                 setTextRect(i);\r
1024                 ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);\r
1025                 ret.addInternalPoint(CurrentTextRect.LowerRightCorner);\r
1026         }\r
1027 \r
1028         return core::dimension2du(ret.getSize());\r
1029 }\r
1030 \r
1031 \r
1032 //! Sets the maximum amount of characters which may be entered in the box.\r
1033 //! \param max: Maximum amount of characters. If 0, the character amount is\r
1034 //! infinity.\r
1035 void CGUIEditBox::setMax(u32 max)\r
1036 {\r
1037         Max = max;\r
1038 \r
1039         if (Text.size() > Max && Max != 0)\r
1040                 Text = Text.subString(0, Max);\r
1041 }\r
1042 \r
1043 \r
1044 //! Returns maximum amount of characters, previously set by setMax();\r
1045 u32 CGUIEditBox::getMax() const\r
1046 {\r
1047         return Max;\r
1048 }\r
1049 \r
1050 //! Set the character used for the cursor.\r
1051 /** By default it's "_" */\r
1052 void CGUIEditBox::setCursorChar(const wchar_t cursorChar)\r
1053 {\r
1054         CursorChar[0] = cursorChar;\r
1055 }\r
1056 \r
1057 //! Get the character used for the cursor.\r
1058 wchar_t CGUIEditBox::getCursorChar() const\r
1059 {\r
1060         return CursorChar[0];\r
1061 }\r
1062 \r
1063 //! Set the blinktime for the cursor. 2x blinktime is one full cycle.\r
1064 void CGUIEditBox::setCursorBlinkTime(irr::u32 timeMs)\r
1065 {\r
1066         CursorBlinkTime = timeMs;\r
1067 }\r
1068 \r
1069 //! Get the cursor blinktime\r
1070 irr::u32 CGUIEditBox::getCursorBlinkTime() const\r
1071 {\r
1072         return CursorBlinkTime;\r
1073 }\r
1074 \r
1075 bool CGUIEditBox::processMouse(const SEvent& event)\r
1076 {\r
1077         switch(event.MouseInput.Event)\r
1078         {\r
1079         case irr::EMIE_LMOUSE_LEFT_UP:\r
1080                 if (Environment->hasFocus(this))\r
1081                 {\r
1082                         CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);\r
1083                         if (MouseMarking)\r
1084                         {\r
1085                                 setTextMarkers( MarkBegin, CursorPos );\r
1086                         }\r
1087                         MouseMarking = false;\r
1088                         calculateScrollPos();\r
1089                         return true;\r
1090                 }\r
1091                 break;\r
1092         case irr::EMIE_MOUSE_MOVED:\r
1093                 {\r
1094                         if (MouseMarking)\r
1095                         {\r
1096                                 CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);\r
1097                                 setTextMarkers( MarkBegin, CursorPos );\r
1098                                 calculateScrollPos();\r
1099                                 return true;\r
1100                         }\r
1101                 }\r
1102                 break;\r
1103         case EMIE_LMOUSE_PRESSED_DOWN:\r
1104                 if (!Environment->hasFocus(this))       // can happen when events are manually send to the element\r
1105                 {\r
1106                         BlinkStartTime = os::Timer::getTime();\r
1107                         MouseMarking = true;\r
1108                         CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);\r
1109                         setTextMarkers(CursorPos, CursorPos );\r
1110                         calculateScrollPos();\r
1111                         return true;\r
1112                 }\r
1113                 else\r
1114                 {\r
1115                         if (!AbsoluteClippingRect.isPointInside(\r
1116                                 core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))\r
1117                         {\r
1118                                 return false;\r
1119                         }\r
1120                         else\r
1121                         {\r
1122                                 // move cursor\r
1123                                 CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);\r
1124 \r
1125                                 s32 newMarkBegin = MarkBegin;\r
1126                                 if (!MouseMarking)\r
1127                                         newMarkBegin = CursorPos;\r
1128 \r
1129                                 MouseMarking = true;\r
1130                                 setTextMarkers( newMarkBegin, CursorPos);\r
1131                                 calculateScrollPos();\r
1132                                 return true;\r
1133                         }\r
1134                 }\r
1135         default:\r
1136                 break;\r
1137         }\r
1138 \r
1139         return false;\r
1140 }\r
1141 \r
1142 \r
1143 s32 CGUIEditBox::getCursorPos(s32 x, s32 y)\r
1144 {\r
1145         IGUIFont* font = getActiveFont();\r
1146 \r
1147         const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;\r
1148 \r
1149         core::stringw *txtLine=0;\r
1150         s32 startPos=0;\r
1151         x+=3;\r
1152 \r
1153         for (u32 i=0; i < lineCount; ++i)\r
1154         {\r
1155                 setTextRect(i);\r
1156                 if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)\r
1157                         y = CurrentTextRect.UpperLeftCorner.Y;\r
1158                 if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )\r
1159                         y = CurrentTextRect.LowerRightCorner.Y;\r
1160 \r
1161                 // is it inside this region?\r
1162                 if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)\r
1163                 {\r
1164                         // we've found the clicked line\r
1165                         txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;\r
1166                         startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;\r
1167                         break;\r
1168                 }\r
1169         }\r
1170 \r
1171         if (x < CurrentTextRect.UpperLeftCorner.X)\r
1172                 x = CurrentTextRect.UpperLeftCorner.X;\r
1173 \r
1174         if ( !txtLine )\r
1175                 return 0;\r
1176 \r
1177         s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);\r
1178 \r
1179         // click was on or left of the line\r
1180         if (idx != -1)\r
1181                 return idx + startPos;\r
1182 \r
1183         // click was off the right edge of the line, go to end.\r
1184         return txtLine->size() + startPos;\r
1185 }\r
1186 \r
1187 \r
1188 //! Breaks the single text line.\r
1189 void CGUIEditBox::breakText()\r
1190 {\r
1191         if ((!WordWrap && !MultiLine))\r
1192                 return;\r
1193 \r
1194         BrokenText.clear(); // need to reallocate :/\r
1195         BrokenTextPositions.set_used(0);\r
1196 \r
1197         IGUIFont* font = getActiveFont();\r
1198         if (!font)\r
1199                 return;\r
1200 \r
1201         LastBreakFont = font;\r
1202 \r
1203         core::stringw line;\r
1204         core::stringw word;\r
1205         core::stringw whitespace;\r
1206         s32 lastLineStart = 0;\r
1207         s32 size = Text.size();\r
1208         s32 length = 0;\r
1209         s32 elWidth = RelativeRect.getWidth() - 6;\r
1210         wchar_t c;\r
1211 \r
1212         for (s32 i=0; i<size; ++i)\r
1213         {\r
1214                 c = Text[i];\r
1215                 bool lineBreak = false;\r
1216 \r
1217                 if (c == L'\r') // Mac or Windows breaks\r
1218                 {\r
1219                         lineBreak = true;\r
1220                         c = 0;\r
1221                         if (Text[i+1] == L'\n') // Windows breaks\r
1222                         {\r
1223                                 // TODO: I (Michael) think that we shouldn't change the text given by the user for whatever reason.\r
1224                                 // Instead rework the cursor positioning to be able to handle this (but not in stable release\r
1225                                 // branch as users might already expect this behavior).\r
1226                                 Text.erase(i+1);\r
1227                                 --size;\r
1228                                 if ( CursorPos > i )\r
1229                                         --CursorPos;\r
1230                         }\r
1231                 }\r
1232                 else if (c == L'\n') // Unix breaks\r
1233                 {\r
1234                         lineBreak = true;\r
1235                         c = 0;\r
1236                 }\r
1237 \r
1238                 // don't break if we're not a multi-line edit box\r
1239                 if (!MultiLine)\r
1240                         lineBreak = false;\r
1241 \r
1242                 if (c == L' ' || c == 0 || i == (size-1))\r
1243                 {\r
1244                         // here comes the next whitespace, look if\r
1245                         // we can break the last word to the next line\r
1246                         // We also break whitespace, otherwise cursor would vanish beside the right border.\r
1247                         s32 whitelgth = font->getDimension(whitespace.c_str()).Width;\r
1248                         s32 worldlgth = font->getDimension(word.c_str()).Width;\r
1249 \r
1250                         if (WordWrap && length + worldlgth + whitelgth > elWidth && line.size() > 0)\r
1251                         {\r
1252                                 // break to next line\r
1253                                 length = worldlgth;\r
1254                                 BrokenText.push_back(line);\r
1255                                 BrokenTextPositions.push_back(lastLineStart);\r
1256                                 lastLineStart = i - (s32)word.size();\r
1257                                 line = word;\r
1258                         }\r
1259                         else\r
1260                         {\r
1261                                 // add word to line\r
1262                                 line += whitespace;\r
1263                                 line += word;\r
1264                                 length += whitelgth + worldlgth;\r
1265                         }\r
1266 \r
1267                         word = L"";\r
1268                         whitespace = L"";\r
1269 \r
1270 \r
1271                         if ( c )\r
1272                                 whitespace += c;\r
1273 \r
1274                         // compute line break\r
1275                         if (lineBreak)\r
1276                         {\r
1277                                 line += whitespace;\r
1278                                 line += word;\r
1279                                 BrokenText.push_back(line);\r
1280                                 BrokenTextPositions.push_back(lastLineStart);\r
1281                                 lastLineStart = i+1;\r
1282                                 line = L"";\r
1283                                 word = L"";\r
1284                                 whitespace = L"";\r
1285                                 length = 0;\r
1286                         }\r
1287                 }\r
1288                 else\r
1289                 {\r
1290                         // yippee this is a word..\r
1291                         word += c;\r
1292                 }\r
1293         }\r
1294 \r
1295         line += whitespace;\r
1296         line += word;\r
1297         BrokenText.push_back(line);\r
1298         BrokenTextPositions.push_back(lastLineStart);\r
1299 }\r
1300 \r
1301 // TODO: that function does interpret VAlign according to line-index (indexed line is placed on top-center-bottom)\r
1302 // but HAlign according to line-width (pixels) and not by row.\r
1303 // Intuitively I suppose HAlign handling is better as VScrollPos should handle the line-scrolling.\r
1304 // But please no one change this without also rewriting (and this time fucking testing!!!) autoscrolling (I noticed this when fixing the old autoscrolling).\r
1305 void CGUIEditBox::setTextRect(s32 line)\r
1306 {\r
1307         if ( line < 0 )\r
1308                 return;\r
1309 \r
1310         IGUIFont* font = getActiveFont();\r
1311         if (!font)\r
1312                 return;\r
1313 \r
1314         core::dimension2du d;\r
1315 \r
1316         // get text dimension\r
1317         const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;\r
1318         if (WordWrap || MultiLine)\r
1319         {\r
1320                 d = font->getDimension(BrokenText[line].c_str());\r
1321         }\r
1322         else\r
1323         {\r
1324                 d = font->getDimension(Text.c_str());\r
1325                 d.Height = AbsoluteRect.getHeight();\r
1326         }\r
1327         d.Height += font->getKerningHeight();\r
1328 \r
1329         // justification\r
1330         switch (HAlign)\r
1331         {\r
1332         case EGUIA_CENTER:\r
1333                 // align to h centre\r
1334                 CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);\r
1335                 CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);\r
1336                 break;\r
1337         case EGUIA_LOWERRIGHT:\r
1338                 // align to right edge\r
1339                 CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;\r
1340                 CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();\r
1341                 break;\r
1342         default:\r
1343                 // align to left edge\r
1344                 CurrentTextRect.UpperLeftCorner.X = 0;\r
1345                 CurrentTextRect.LowerRightCorner.X = d.Width;\r
1346 \r
1347         }\r
1348 \r
1349         switch (VAlign)\r
1350         {\r
1351         case EGUIA_CENTER:\r
1352                 // align to v centre\r
1353                 CurrentTextRect.UpperLeftCorner.Y =\r
1354                         (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;\r
1355                 break;\r
1356         case EGUIA_LOWERRIGHT:\r
1357                 // align to bottom edge\r
1358                 CurrentTextRect.UpperLeftCorner.Y =\r
1359                         FrameRect.getHeight() - lineCount*d.Height + d.Height*line;\r
1360                 break;\r
1361         default:\r
1362                 // align to top edge\r
1363                 CurrentTextRect.UpperLeftCorner.Y = d.Height*line;\r
1364                 break;\r
1365         }\r
1366 \r
1367         CurrentTextRect.UpperLeftCorner.X  -= HScrollPos;\r
1368         CurrentTextRect.LowerRightCorner.X -= HScrollPos;\r
1369         CurrentTextRect.UpperLeftCorner.Y  -= VScrollPos;\r
1370         CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;\r
1371 \r
1372         CurrentTextRect += FrameRect.UpperLeftCorner;\r
1373 \r
1374 }\r
1375 \r
1376 \r
1377 s32 CGUIEditBox::getLineFromPos(s32 pos)\r
1378 {\r
1379         if (!WordWrap && !MultiLine)\r
1380                 return 0;\r
1381 \r
1382         s32 i=0;\r
1383         while (i < (s32)BrokenTextPositions.size())\r
1384         {\r
1385                 if (BrokenTextPositions[i] > pos)\r
1386                         return i-1;\r
1387                 ++i;\r
1388         }\r
1389         return (s32)BrokenTextPositions.size() - 1;\r
1390 }\r
1391 \r
1392 \r
1393 void CGUIEditBox::inputChar(wchar_t c)\r
1394 {\r
1395         if (!isEnabled())\r
1396                 return;\r
1397 \r
1398         if (c != 0)\r
1399         {\r
1400                 {\r
1401                         core::stringw s;\r
1402 \r
1403                         if (MarkBegin != MarkEnd)\r
1404                         {\r
1405                                 // replace marked text\r
1406                                 const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;\r
1407                                 const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;\r
1408 \r
1409                                 s = Text.subString(0, realmbgn);\r
1410                                 s.append(c);\r
1411                                 s.append( Text.subString(realmend, Text.size()-realmend) );\r
1412                                 Text = s;\r
1413                                 CursorPos = realmbgn+1;\r
1414                         }\r
1415                         else if ( OverwriteMode )\r
1416                         {\r
1417                                 //check to see if we are at the end of the text\r
1418                                 if ( (u32)CursorPos != Text.size())\r
1419                                 {\r
1420                                         bool isEOL = (Text[CursorPos] == L'\n' ||Text[CursorPos] == L'\r' );\r
1421                                         if (!isEOL || Text.size() < Max || Max == 0)\r
1422                                         {\r
1423                                                 s = Text.subString(0, CursorPos);\r
1424                                                 s.append(c);\r
1425                                                 if ( isEOL )\r
1426                                                 {\r
1427                                                         //just keep appending to the current line\r
1428                                                         //This follows the behavior of other gui libraries behaviors\r
1429                                                         s.append( Text.subString(CursorPos, Text.size()-CursorPos) );\r
1430                                                 }\r
1431                                                 else\r
1432                                                 {\r
1433                                                         //replace the next character\r
1434                                                         s.append( Text.subString(CursorPos + 1,Text.size() - CursorPos + 1));\r
1435                                                 }\r
1436                                                 Text = s;\r
1437                                                 ++CursorPos;\r
1438                                         }\r
1439                                 }\r
1440                                 else if (Text.size() < Max || Max == 0)\r
1441                                 {\r
1442                                         // add new character because we are at the end of the string\r
1443                                         s = Text.subString(0, CursorPos);\r
1444                                         s.append(c);\r
1445                                         s.append( Text.subString(CursorPos, Text.size()-CursorPos) );\r
1446                                         Text = s;\r
1447                                         ++CursorPos;\r
1448                                 }\r
1449                         }\r
1450                         else if (Text.size() < Max || Max == 0)\r
1451                         {\r
1452                                 // add new character\r
1453                                 s = Text.subString(0, CursorPos);\r
1454                                 s.append(c);\r
1455                                 s.append( Text.subString(CursorPos, Text.size()-CursorPos) );\r
1456                                 Text = s;\r
1457                                 ++CursorPos;\r
1458                         }\r
1459 \r
1460                         BlinkStartTime = os::Timer::getTime();\r
1461                         setTextMarkers(0, 0);\r
1462                 }\r
1463         }\r
1464         breakText();\r
1465         calculateScrollPos();\r
1466         sendGuiEvent(EGET_EDITBOX_CHANGED);\r
1467 }\r
1468 \r
1469 // calculate autoscroll\r
1470 void CGUIEditBox::calculateScrollPos()\r
1471 {\r
1472         if (!AutoScroll)\r
1473                 return;\r
1474 \r
1475         IGUIFont* font = getActiveFont();\r
1476         if (!font)\r
1477                 return;\r
1478 \r
1479         s32 cursLine = getLineFromPos(CursorPos);\r
1480         if ( cursLine < 0 )\r
1481                 return;\r
1482         setTextRect(cursLine);\r
1483         const bool hasBrokenText = MultiLine || WordWrap;\r
1484 \r
1485         // Check horizonal scrolling\r
1486         // NOTE: Calculations different to vertical scrolling because setTextRect interprets VAlign relative to line but HAlign not relative to row\r
1487         {\r
1488                 // get cursor position\r
1489                 // get cursor area\r
1490                 irr::u32 cursorWidth = font->getDimension(CursorChar.c_str()).Width;\r
1491                 core::stringw *txtLine = hasBrokenText ? &BrokenText[cursLine] : &Text;\r
1492                 s32 cPos = hasBrokenText ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;       // column\r
1493                 s32 cStart = font->getDimension(txtLine->subString(0, cPos).c_str()).Width;             // pixels from text-start\r
1494                 s32 cEnd = cStart + cursorWidth;\r
1495                 s32 txtWidth = font->getDimension(txtLine->c_str()).Width;\r
1496 \r
1497                 if ( txtWidth < FrameRect.getWidth() )\r
1498                 {\r
1499                         // TODO: Needs a clean left and right gap removal depending on HAlign, similar to vertical scrolling tests for top/bottom.\r
1500                         // This check just fixes the case where it was most noticable (text smaller than clipping area).\r
1501 \r
1502                         HScrollPos = 0;\r
1503                         setTextRect(cursLine);\r
1504                 }\r
1505 \r
1506                 if ( CurrentTextRect.UpperLeftCorner.X+cStart < FrameRect.UpperLeftCorner.X )\r
1507                 {\r
1508                         // cursor to the left of the clipping area\r
1509                         HScrollPos -= FrameRect.UpperLeftCorner.X-(CurrentTextRect.UpperLeftCorner.X+cStart);\r
1510                         setTextRect(cursLine);\r
1511 \r
1512                         // TODO: should show more characters to the left when we're scrolling left\r
1513                         //      and the cursor reaches the border.\r
1514                 }\r
1515                 else if ( CurrentTextRect.UpperLeftCorner.X+cEnd > FrameRect.LowerRightCorner.X)\r
1516                 {\r
1517                         // cursor to the right of the clipping area\r
1518                         HScrollPos += (CurrentTextRect.UpperLeftCorner.X+cEnd)-FrameRect.LowerRightCorner.X;\r
1519                         setTextRect(cursLine);\r
1520                 }\r
1521         }\r
1522 \r
1523         // calculate vertical scrolling\r
1524         if (hasBrokenText)\r
1525         {\r
1526                 irr::u32 lineHeight = font->getDimension(L"A").Height + font->getKerningHeight();\r
1527                 // only up to 1 line fits?\r
1528                 if ( lineHeight >= (irr::u32)FrameRect.getHeight() )\r
1529                 {\r
1530                         VScrollPos = 0;\r
1531                         setTextRect(cursLine);\r
1532                         s32 unscrolledPos = CurrentTextRect.UpperLeftCorner.Y;\r
1533                         s32 pivot = FrameRect.UpperLeftCorner.Y;\r
1534                         switch (VAlign)\r
1535                         {\r
1536                                 case EGUIA_CENTER:\r
1537                                         pivot += FrameRect.getHeight()/2;\r
1538                                         unscrolledPos += lineHeight/2;\r
1539                                         break;\r
1540                                 case EGUIA_LOWERRIGHT:\r
1541                                         pivot += FrameRect.getHeight();\r
1542                                         unscrolledPos += lineHeight;\r
1543                                         break;\r
1544                                 default:\r
1545                                         break;\r
1546                         }\r
1547                         VScrollPos = unscrolledPos-pivot;\r
1548                         setTextRect(cursLine);\r
1549                 }\r
1550                 else\r
1551                 {\r
1552                         // First 2 checks are necessary when people delete lines\r
1553                         setTextRect(0);\r
1554                         if ( CurrentTextRect.UpperLeftCorner.Y > FrameRect.UpperLeftCorner.Y && VAlign != EGUIA_LOWERRIGHT)\r
1555                         {\r
1556                                 // first line is leaving a gap on top\r
1557                                 VScrollPos = 0;\r
1558                         }\r
1559                         else if (VAlign != EGUIA_UPPERLEFT)\r
1560                         {\r
1561                                 u32 lastLine = BrokenTextPositions.empty() ? 0 : BrokenTextPositions.size()-1;\r
1562                                 setTextRect(lastLine);\r
1563                                 if ( CurrentTextRect.LowerRightCorner.Y < FrameRect.LowerRightCorner.Y)\r
1564                                 {\r
1565                                         // last line is leaving a gap on bottom\r
1566                                         VScrollPos -= FrameRect.LowerRightCorner.Y-CurrentTextRect.LowerRightCorner.Y;\r
1567                                 }\r
1568                         }\r
1569 \r
1570                         setTextRect(cursLine);\r
1571                         if ( CurrentTextRect.UpperLeftCorner.Y < FrameRect.UpperLeftCorner.Y )\r
1572                         {\r
1573                                 // text above valid area\r
1574                                 VScrollPos -= FrameRect.UpperLeftCorner.Y-CurrentTextRect.UpperLeftCorner.Y;\r
1575                                 setTextRect(cursLine);\r
1576                         }\r
1577                         else if ( CurrentTextRect.LowerRightCorner.Y > FrameRect.LowerRightCorner.Y)\r
1578                         {\r
1579                                 // text below valid area\r
1580                                 VScrollPos += CurrentTextRect.LowerRightCorner.Y-FrameRect.LowerRightCorner.Y;\r
1581                                 setTextRect(cursLine);\r
1582                         }\r
1583                 }\r
1584         }\r
1585 }\r
1586 \r
1587 void CGUIEditBox::calculateFrameRect()\r
1588 {\r
1589         FrameRect = AbsoluteRect;\r
1590         IGUISkin *skin = 0;\r
1591         if (Environment)\r
1592                 skin = Environment->getSkin();\r
1593         if (Border && skin)\r
1594         {\r
1595                 FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;\r
1596                 FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;\r
1597                 FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;\r
1598                 FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;\r
1599         }\r
1600 }\r
1601 \r
1602 //! set text markers\r
1603 void CGUIEditBox::setTextMarkers(s32 begin, s32 end)\r
1604 {\r
1605         if ( begin != MarkBegin || end != MarkEnd )\r
1606         {\r
1607                 MarkBegin = begin;\r
1608                 MarkEnd = end;\r
1609                 sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);\r
1610         }\r
1611 }\r
1612 \r
1613 //! send some gui event to parent\r
1614 void CGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)\r
1615 {\r
1616         if ( Parent )\r
1617         {\r
1618                 SEvent e;\r
1619                 e.EventType = EET_GUI_EVENT;\r
1620                 e.GUIEvent.Caller = this;\r
1621                 e.GUIEvent.Element = 0;\r
1622                 e.GUIEvent.EventType = type;\r
1623 \r
1624                 Parent->OnEvent(e);\r
1625         }\r
1626 }\r
1627 \r
1628 //! Writes attributes of the element.\r
1629 void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const\r
1630 {\r
1631         // IGUIEditBox::serializeAttributes(out,options);\r
1632 \r
1633         out->addBool  ("Border", Border);\r
1634         out->addBool  ("Background", Background);\r
1635         out->addBool  ("OverrideColorEnabled", OverrideColorEnabled );\r
1636         out->addColor ("OverrideColor", OverrideColor);\r
1637         // out->addFont("OverrideFont", OverrideFont);\r
1638         out->addInt   ("MaxChars", Max);\r
1639         out->addBool  ("WordWrap", WordWrap);\r
1640         out->addBool  ("MultiLine", MultiLine);\r
1641         out->addBool  ("AutoScroll", AutoScroll);\r
1642         out->addBool  ("PasswordBox", PasswordBox);\r
1643         core::stringw ch = L" ";\r
1644         ch[0] = PasswordChar;\r
1645         out->addString("PasswordChar", ch.c_str());\r
1646         out->addEnum  ("HTextAlign", HAlign, GUIAlignmentNames);\r
1647         out->addEnum  ("VTextAlign", VAlign, GUIAlignmentNames);\r
1648 \r
1649         IGUIEditBox::serializeAttributes(out,options);\r
1650 }\r
1651 \r
1652 \r
1653 //! Reads attributes of the element\r
1654 void CGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)\r
1655 {\r
1656         IGUIEditBox::deserializeAttributes(in,options);\r
1657 \r
1658         setDrawBorder( in->getAttributeAsBool("Border", Border) );\r
1659         setDrawBackground( in->getAttributeAsBool("Background", Background) );\r
1660         setOverrideColor(in->getAttributeAsColor("OverrideColor", OverrideColor));\r
1661         enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled", OverrideColorEnabled));\r
1662         setMax(in->getAttributeAsInt("MaxChars", Max));\r
1663         setWordWrap(in->getAttributeAsBool("WordWrap", WordWrap));\r
1664         setMultiLine(in->getAttributeAsBool("MultiLine", MultiLine));\r
1665         setAutoScroll(in->getAttributeAsBool("AutoScroll", AutoScroll));\r
1666         core::stringw ch = L" ";\r
1667         ch[0] = PasswordChar;\r
1668         ch = in->getAttributeAsStringW("PasswordChar", ch);\r
1669 \r
1670         if (!ch.size())\r
1671                 setPasswordBox(in->getAttributeAsBool("PasswordBox", PasswordBox));\r
1672         else\r
1673                 setPasswordBox(in->getAttributeAsBool("PasswordBox", PasswordBox), ch[0]);\r
1674 \r
1675         setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames, (s32)HAlign),\r
1676                         (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames, (s32)VAlign));\r
1677 \r
1678         // setOverrideFont(in->getAttributeAsFont("OverrideFont"));\r
1679 }\r
1680 \r
1681 \r
1682 } // end namespace gui\r
1683 } // end namespace irr\r
1684 \r
1685 #endif // _IRR_COMPILE_WITH_GUI_\r
1686 \r