00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "elements/CEGUIMultiLineEditbox.h"
00027 #include "elements/CEGUIScrollbar.h"
00028 #include "CEGUITextUtils.h"
00029 #include "CEGUIImage.h"
00030 #include "CEGUIExceptions.h"
00031
00032
00033
00034 namespace CEGUI
00035 {
00036 const String MultiLineEditbox::EventNamespace("MultiLineEditbox");
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 MultiLineEditboxProperties::ReadOnly MultiLineEditbox::d_readOnlyProperty;
00048 MultiLineEditboxProperties::WordWrap MultiLineEditbox::d_wordWrapProperty;
00049 MultiLineEditboxProperties::CaratIndex MultiLineEditbox::d_caratIndexProperty;
00050 MultiLineEditboxProperties::SelectionStart MultiLineEditbox::d_selectionStartProperty;
00051 MultiLineEditboxProperties::SelectionLength MultiLineEditbox::d_selectionLengthProperty;
00052 MultiLineEditboxProperties::MaxTextLength MultiLineEditbox::d_maxTextLengthProperty;
00053 MultiLineEditboxProperties::NormalTextColour MultiLineEditbox::d_normalTextColourProperty;
00054 MultiLineEditboxProperties::SelectedTextColour MultiLineEditbox::d_selectedTextColourProperty;
00055 MultiLineEditboxProperties::ActiveSelectionColour MultiLineEditbox::d_activeSelectionColourProperty;
00056 MultiLineEditboxProperties::InactiveSelectionColour MultiLineEditbox::d_inactiveSelectionColourProperty;
00057
00058
00059
00060
00061
00062
00063 const String MultiLineEditbox::EventReadOnlyModeChanged( (utf8*)"ReadOnlyChanged" );
00064 const String MultiLineEditbox::EventWordWrapModeChanged( (utf8*)"WordWrapModeChanged" );
00065 const String MultiLineEditbox::EventMaximumTextLengthChanged( (utf8*)"MaximumTextLengthChanged" );
00066 const String MultiLineEditbox::EventCaratMoved( (utf8*)"CaratMoved" );
00067 const String MultiLineEditbox::EventTextSelectionChanged( (utf8*)"TextSelectionChanged" );
00068 const String MultiLineEditbox::EventEditboxFull( (utf8*)"EditboxFullEvent" );
00069 const String MultiLineEditbox::EventVertScrollbarModeChanged( (utf8*)"VertScrollbarModeChanged" );
00070 const String MultiLineEditbox::EventHorzScrollbarModeChanged( (utf8*)"HorzScrollbarModeChanged" );
00071
00072
00073 const argb_t MultiLineEditbox::DefaultNormalTextColour = 0xFFFFFFFF;
00074 const argb_t MultiLineEditbox::DefaultSelectedTextColour = 0xFF000000;
00075 const argb_t MultiLineEditbox::DefaultNormalSelectionColour = 0xFF6060FF;
00076 const argb_t MultiLineEditbox::DefaultInactiveSelectionColour = 0xFF808080;
00077
00078
00079 String MultiLineEditbox::d_lineBreakChars((utf8*)"\n");
00080
00081
00082
00083
00084
00085 MultiLineEditbox::MultiLineEditbox(const String& type, const String& name) :
00086 Window(type, name),
00087 d_readOnly(false),
00088 d_maxTextLen(String::max_size()),
00089 d_caratPos(0),
00090 d_selectionStart(0),
00091 d_selectionEnd(0),
00092 d_dragging(false),
00093 d_dragAnchorIdx(0),
00094 d_wordWrap(true),
00095 d_widestExtent(0.0f),
00096 d_forceVertScroll(false),
00097 d_forceHorzScroll(false),
00098 d_selectionBrush(NULL),
00099 d_normalTextColour(DefaultNormalTextColour),
00100 d_selectTextColour(DefaultSelectedTextColour),
00101 d_selectBrushColour(DefaultNormalSelectionColour),
00102 d_inactiveSelectBrushColour(DefaultInactiveSelectionColour)
00103 {
00104
00105 addMultiLineEditboxEvents();
00106
00107 addMultiLineEditboxProperties();
00108
00109
00110 d_text.append(1, '\n');
00111 }
00112
00113
00114
00115
00116
00117 MultiLineEditbox::~MultiLineEditbox(void)
00118 {
00119 }
00120
00121
00122
00123
00124
00125 void MultiLineEditbox::initialise(void)
00126 {
00127
00128 d_vertScrollbar = createVertScrollbar(getName() + "__auto_vscrollbar__");
00129 d_horzScrollbar = createHorzScrollbar(getName() + "__auto_hscrollbar__");
00130
00131 addChildWindow(d_vertScrollbar);
00132 addChildWindow(d_horzScrollbar);
00133
00134 d_vertScrollbar->subscribeEvent(Scrollbar::EventScrollPositionChanged, Event::Subscriber(&MultiLineEditbox::handle_scrollChange, this));
00135 d_horzScrollbar->subscribeEvent(Scrollbar::EventScrollPositionChanged, Event::Subscriber(&MultiLineEditbox::handle_scrollChange, this));
00136
00137 formatText();
00138 performChildWindowLayout();
00139 }
00140
00141
00142
00143
00144
00145 bool MultiLineEditbox::hasInputFocus(void) const
00146 {
00147 return isActive();
00148 }
00149
00150
00151
00152
00153
00154 size_t MultiLineEditbox::getSelectionStartIndex(void) const
00155 {
00156 return (d_selectionStart != d_selectionEnd) ? d_selectionStart : d_caratPos;
00157 }
00158
00159
00160
00161
00162
00163 size_t MultiLineEditbox::getSelectionEndIndex(void) const
00164 {
00165 return (d_selectionStart != d_selectionEnd) ? d_selectionEnd : d_caratPos;
00166 }
00167
00168
00169
00170
00171
00172 size_t MultiLineEditbox::getSelectionLength(void) const
00173 {
00174 return d_selectionEnd - d_selectionStart;
00175 }
00176
00177
00178
00179
00180
00181 void MultiLineEditbox::addMultiLineEditboxEvents(void)
00182 {
00183 addEvent(EventReadOnlyModeChanged);
00184 addEvent(EventWordWrapModeChanged);
00185 addEvent(EventMaximumTextLengthChanged);
00186 addEvent(EventCaratMoved);
00187 addEvent(EventTextSelectionChanged);
00188 addEvent(EventEditboxFull);
00189 addEvent(EventVertScrollbarModeChanged);
00190 addEvent(EventHorzScrollbarModeChanged);
00191 }
00192
00193
00194
00195
00196
00197 void MultiLineEditbox::populateRenderCache()
00198 {
00199
00200 cacheEditboxBaseImagery();
00201
00202
00203
00204
00205 Rect textarea(getTextRenderArea());
00206
00207 cacheTextLines(textarea);
00208
00209 if (hasInputFocus() && !isReadOnly())
00210 {
00211 cacheCaratImagery(textarea);
00212 }
00213
00214 }
00215
00216
00217
00218
00219
00220 void MultiLineEditbox::setReadOnly(bool setting)
00221 {
00222
00223 if (d_readOnly != setting)
00224 {
00225 d_readOnly = setting;
00226 WindowEventArgs args(this);
00227 onReadOnlyChanged(args);
00228 }
00229
00230 }
00231
00232
00233
00234
00235
00236 void MultiLineEditbox::setCaratIndex(size_t carat_pos)
00237 {
00238
00239 if (carat_pos > d_text.length() - 1)
00240 {
00241 carat_pos = d_text.length() - 1;
00242 }
00243
00244
00245 if (d_caratPos != carat_pos)
00246 {
00247 d_caratPos = carat_pos;
00248 ensureCaratIsVisible();
00249
00250
00251 WindowEventArgs args(this);
00252 onCaratMoved(args);
00253 }
00254
00255 }
00256
00257
00258
00259
00260
00261 void MultiLineEditbox::setSelection(size_t start_pos, size_t end_pos)
00262 {
00263
00264 if (start_pos > d_text.length() - 1)
00265 {
00266 start_pos = d_text.length() - 1;
00267 }
00268
00269
00270 if (end_pos > d_text.length() - 1)
00271 {
00272 end_pos = d_text.length() - 1;
00273 }
00274
00275
00276 if (start_pos > end_pos)
00277 {
00278 size_t tmp = end_pos;
00279 end_pos = start_pos;
00280 start_pos = tmp;
00281 }
00282
00283
00284 if ((start_pos != d_selectionStart) || (end_pos != d_selectionEnd))
00285 {
00286
00287 d_selectionStart = start_pos;
00288 d_selectionEnd = end_pos;
00289
00290
00291 WindowEventArgs args(this);
00292 onTextSelectionChanged(args);
00293 }
00294
00295 }
00296
00297
00298
00299
00300
00301 void MultiLineEditbox::setMaxTextLength(size_t max_len)
00302 {
00303 if (d_maxTextLen != max_len)
00304 {
00305 d_maxTextLen = max_len;
00306
00307
00308 WindowEventArgs args(this);
00309 onMaximumTextLengthChanged(args);
00310
00311
00312 if (d_text.length() > d_maxTextLen)
00313 {
00314 d_text.resize(d_maxTextLen);
00315 onTextChanged(args);
00316 }
00317
00318 }
00319
00320 }
00321
00322
00323
00324
00325
00326
00327 void MultiLineEditbox::setNormalTextColour(const colour& col)
00328 {
00329 d_normalTextColour = col;
00330 requestRedraw();
00331 }
00332
00333
00334
00335
00336
00337
00338 void MultiLineEditbox::setSelectedTextColour(const colour& col)
00339 {
00340 d_selectTextColour = col;
00341 requestRedraw();
00342 }
00343
00344
00345
00346
00347
00348
00349 void MultiLineEditbox::setNormalSelectBrushColour(const colour& col)
00350 {
00351 d_selectBrushColour = col;
00352 requestRedraw();
00353 }
00354
00355
00356
00357
00358
00359
00360 void MultiLineEditbox::setInactiveSelectBrushColour(const colour& col)
00361 {
00362 d_inactiveSelectBrushColour = col;
00363 requestRedraw();
00364 }
00365
00366
00367
00368
00369
00370 void MultiLineEditbox::ensureCaratIsVisible(void)
00371 {
00372
00373 const Font* fnt = getFont();
00374 size_t caratLine = getLineNumberFromIndex(d_caratPos);
00375
00376 if (caratLine < d_lines.size())
00377 {
00378 Rect textArea(getTextRenderArea());
00379
00380 size_t caratLineIdx = d_caratPos - d_lines[caratLine].d_startIdx;
00381
00382 float ypos = caratLine * fnt->getLineSpacing();
00383 float xpos = fnt->getTextExtent(d_text.substr(d_lines[caratLine].d_startIdx, caratLineIdx));
00384
00385
00386 xpos -= d_horzScrollbar->getScrollPosition();
00387 ypos -= d_vertScrollbar->getScrollPosition();
00388
00389
00390 if (ypos < 0)
00391 {
00392 d_vertScrollbar->setScrollPosition(d_vertScrollbar->getScrollPosition() + ypos);
00393 }
00394
00395 else if ((ypos += fnt->getLineSpacing()) > textArea.getHeight())
00396 {
00397 d_vertScrollbar->setScrollPosition(d_vertScrollbar->getScrollPosition() + (ypos - textArea.getHeight()) + fnt->getLineSpacing());
00398 }
00399
00400
00401 if (xpos < 0)
00402 {
00403 d_horzScrollbar->setScrollPosition(d_horzScrollbar->getScrollPosition() + xpos - 50);
00404 }
00405
00406 else if (xpos > textArea.getWidth())
00407 {
00408 d_horzScrollbar->setScrollPosition(d_horzScrollbar->getScrollPosition() + (xpos - textArea.getWidth()) + 50);
00409 }
00410
00411 }
00412
00413 }
00414
00415
00416
00417
00418
00419 void MultiLineEditbox::setWordWrapping(bool setting)
00420 {
00421 if (setting != d_wordWrap)
00422 {
00423 d_wordWrap = setting;
00424 formatText();
00425
00426 WindowEventArgs args(this);
00427 onWordWrapModeChanged(args);
00428 }
00429
00430 }
00431
00432
00433
00434
00435
00436
00437 void MultiLineEditbox::configureScrollbars(void)
00438 {
00439 float totalHeight = (float)d_lines.size() * getFont()->getLineSpacing();
00440 float widestItem = d_widestExtent;
00441
00442
00443
00444
00445
00446 if ((totalHeight > getTextRenderArea().getHeight()) || d_forceVertScroll)
00447 {
00448 d_vertScrollbar->show();
00449
00450
00451 if ((widestItem > getTextRenderArea().getWidth()) || d_forceHorzScroll)
00452 {
00453 d_horzScrollbar->show();
00454 }
00455 else
00456 {
00457 d_horzScrollbar->hide();
00458 }
00459
00460 }
00461 else
00462 {
00463
00464 if ((widestItem > getTextRenderArea().getWidth()) || d_forceHorzScroll)
00465 {
00466 d_horzScrollbar->show();
00467
00468
00469 if ((totalHeight > getTextRenderArea().getHeight()) || d_forceVertScroll)
00470 {
00471 d_vertScrollbar->show();
00472 }
00473 else
00474 {
00475 d_vertScrollbar->hide();
00476 }
00477
00478 }
00479 else
00480 {
00481 d_vertScrollbar->hide();
00482 d_horzScrollbar->hide();
00483 }
00484
00485 }
00486
00487
00488
00489
00490 Rect renderArea(getTextRenderArea());
00491
00492 d_vertScrollbar->setDocumentSize(totalHeight);
00493 d_vertScrollbar->setPageSize(renderArea.getHeight());
00494 d_vertScrollbar->setStepSize(ceguimax(1.0f, renderArea.getHeight() / 10.0f));
00495 d_vertScrollbar->setScrollPosition(d_vertScrollbar->getScrollPosition());
00496
00497 d_horzScrollbar->setDocumentSize(widestItem);
00498 d_horzScrollbar->setPageSize(renderArea.getWidth());
00499 d_horzScrollbar->setStepSize(ceguimax(1.0f, renderArea.getWidth() / 10.0f));
00500 d_horzScrollbar->setScrollPosition(d_horzScrollbar->getScrollPosition());
00501 }
00502
00503
00504
00505
00506
00507 void MultiLineEditbox::cacheTextLines(const Rect& dest_area)
00508 {
00509
00510 Rect drawArea(dest_area);
00511 drawArea.offset(Point(-d_horzScrollbar->getScrollPosition(), -d_vertScrollbar->getScrollPosition()));
00512
00513 Renderer* renderer = System::getSingleton().getRenderer();
00514 const Font* fnt = getFont();
00515
00516 if (fnt)
00517 {
00518
00519 float textZ = renderer->getZLayer(4) - renderer->getCurrentZ();
00520 float selZ = renderer->getZLayer(3) - renderer->getCurrentZ();
00521
00522
00523 ColourRect colours;
00524 float alpha = getEffectiveAlpha();
00525 colour normalTextCol = d_normalTextColour;
00526 normalTextCol.setAlpha(normalTextCol.getAlpha() * alpha);
00527 colour selectTextCol = d_selectTextColour;
00528 selectTextCol.setAlpha(selectTextCol.getAlpha() * alpha);
00529 colour selectBrushCol = hasInputFocus() ? d_selectBrushColour : d_inactiveSelectBrushColour;
00530 selectBrushCol.setAlpha(selectBrushCol.getAlpha() * alpha);
00531
00532
00533 for (size_t i = 0; i < d_lines.size(); ++i)
00534 {
00535 Rect lineRect(drawArea);
00536 const LineInfo& currLine = d_lines[i];
00537 String lineText(d_text.substr(currLine.d_startIdx, currLine.d_length));
00538
00539
00540 if ((currLine.d_startIdx >= d_selectionEnd) ||
00541 ((currLine.d_startIdx + currLine.d_length) <= d_selectionStart) ||
00542 (d_selectionBrush == NULL))
00543 {
00544 colours.setColours(normalTextCol);
00545
00546 d_renderCache.cacheText(lineText, fnt, LeftAligned, lineRect, textZ, colours, &dest_area);
00547 }
00548
00549 else
00550 {
00551
00552 String sect;
00553 size_t sectIdx = 0, sectLen;
00554 float selStartOffset = 0.0f, selAreaWidth = 0.0f;
00555
00556
00557 if (currLine.d_startIdx < d_selectionStart)
00558 {
00559
00560 sectLen = d_selectionStart - currLine.d_startIdx;
00561
00562
00563 sect = lineText.substr(sectIdx, sectLen);
00564 sectIdx += sectLen;
00565
00566
00567 selStartOffset = fnt->getTextExtent(sect);
00568
00569
00570 colours.setColours(normalTextCol);
00571 d_renderCache.cacheText(sect, fnt, LeftAligned, lineRect, textZ, colours, &dest_area);
00572
00573
00574 lineRect.d_left += selStartOffset;
00575 }
00576
00577
00578 sectLen = ceguimin(d_selectionEnd - currLine.d_startIdx, currLine.d_length) - sectIdx;
00579
00580
00581 sect = lineText.substr(sectIdx, sectLen);
00582 sectIdx += sectLen;
00583
00584
00585 selAreaWidth = fnt->getTextExtent(sect);
00586
00587
00588 colours.setColours(selectTextCol);
00589 d_renderCache.cacheText(sect, fnt, LeftAligned, lineRect, textZ, colours, &dest_area);
00590
00591
00592 if (sectIdx < currLine.d_length)
00593 {
00594
00595 lineRect.d_left += selAreaWidth;
00596
00597
00598 sectLen = currLine.d_length - sectIdx;
00599
00600
00601 sect = lineText.substr(sectIdx, sectLen);
00602
00603
00604 colours.setColours(normalTextCol);
00605 d_renderCache.cacheText(sect, fnt, LeftAligned, lineRect, textZ, colours, &dest_area);
00606 }
00607
00608
00609 lineRect.d_left = drawArea.d_left + selStartOffset;
00610 lineRect.d_right = lineRect.d_left + selAreaWidth;
00611 lineRect.d_bottom = lineRect.d_top + fnt->getLineSpacing();
00612
00613
00614 colours.setColours(selectBrushCol);
00615 d_renderCache.cacheImage(*d_selectionBrush, lineRect, selZ, colours, &dest_area);
00616 }
00617
00618
00619 drawArea.d_top += fnt->getLineSpacing();
00620 }
00621 }
00622 }
00623
00624
00625
00626
00627
00628 void MultiLineEditbox::formatText(void)
00629 {
00630
00631 d_lines.clear();
00632 d_widestExtent = 0.0f;
00633
00634 String paraText;
00635
00636 const Font* fnt = getFont();
00637
00638 if (fnt != NULL)
00639 {
00640 float areaWidth = getTextRenderArea().getWidth();
00641
00642 String::size_type currPos = 0;
00643 String::size_type paraLen;
00644 LineInfo line;
00645
00646 while (currPos < d_text.length())
00647 {
00648 if ((paraLen = d_text.find_first_of(d_lineBreakChars, currPos)) == String::npos)
00649 {
00650 paraLen = d_text.length() - currPos;
00651 }
00652 else
00653 {
00654 ++paraLen -= currPos;
00655 }
00656
00657 paraText = d_text.substr(currPos, paraLen);
00658
00659 if (!d_wordWrap || (areaWidth <= 0.0f))
00660 {
00661
00662 line.d_startIdx = currPos;
00663 line.d_length = paraLen;
00664 line.d_extent = fnt->getTextExtent(paraText);
00665 d_lines.push_back(line);
00666
00667
00668 if (line.d_extent > d_widestExtent)
00669 {
00670 d_widestExtent = line.d_extent;
00671 }
00672
00673 }
00674
00675 else
00676 {
00677 String::size_type lineIndex = 0;
00678
00679
00680 while (lineIndex < paraLen)
00681 {
00682 String::size_type lineLen = 0;
00683 float lineExtent = 0.0f;
00684
00685
00686 while (lineLen < (paraLen - lineIndex))
00687 {
00688
00689 size_t nextTokenSize = getNextTokenLength(paraText, lineIndex + lineLen);
00690
00691
00692 float tokenExtent = fnt->getTextExtent(paraText.substr(lineIndex + lineLen, nextTokenSize));
00693
00694
00695 if ((lineExtent + tokenExtent) > areaWidth)
00696 {
00697
00698 if (lineLen == 0)
00699 {
00700
00701 lineLen = fnt->getCharAtPixel(paraText.substr(lineIndex, nextTokenSize), areaWidth);
00702 }
00703
00704
00705 break;
00706 }
00707
00708
00709 lineLen += nextTokenSize;
00710 lineExtent += tokenExtent;
00711 }
00712
00713
00714 line.d_startIdx = currPos + lineIndex;
00715 line.d_length = lineLen;
00716 line.d_extent = lineExtent;
00717 d_lines.push_back(line);
00718
00719
00720 if (lineExtent > d_widestExtent)
00721 {
00722 d_widestExtent = lineExtent;
00723 }
00724
00725
00726 lineIndex += lineLen;
00727 }
00728
00729 }
00730
00731
00732 currPos += paraLen;
00733 }
00734
00735 }
00736
00737 configureScrollbars();
00738 requestRedraw();
00739 }
00740
00741
00742
00743
00744
00745
00746 size_t MultiLineEditbox::getNextTokenLength(const String& text, size_t start_idx) const
00747 {
00748 String::size_type pos = text.find_first_of(TextUtils::DefaultWrapDelimiters, start_idx);
00749
00750
00751 if (pos == String::npos)
00752 {
00753 return (text.length() - start_idx);
00754 }
00755
00756 else if ((pos - start_idx) == 0)
00757 {
00758 return 1;
00759 }
00760 else
00761 {
00762 return (pos - start_idx);
00763 }
00764
00765 }
00766
00767
00768
00769
00770
00771
00772 size_t MultiLineEditbox::getTextIndexFromPosition(const Point& pt) const
00773 {
00774
00775
00776
00777 Point wndPt = screenToWindow(pt);
00778
00779 if (getMetricsMode() == Relative)
00780 {
00781 wndPt = relativeToAbsolute(wndPt);
00782 }
00783
00784 Rect textArea(getTextRenderArea());
00785
00786 wndPt.d_x -= textArea.d_left;
00787 wndPt.d_y -= textArea.d_top;
00788
00789
00790 wndPt.d_x += d_horzScrollbar->getScrollPosition();
00791 wndPt.d_y += d_vertScrollbar->getScrollPosition();
00792
00793 size_t lineNumber = static_cast<size_t>(wndPt.d_y / getFont()->getLineSpacing());
00794
00795 if (lineNumber >= d_lines.size())
00796 {
00797 lineNumber = d_lines.size() - 1;
00798 }
00799
00800 String lineText(d_text.substr(d_lines[lineNumber].d_startIdx, d_lines[lineNumber].d_length));
00801
00802 size_t lineIdx = getFont()->getCharAtPixel(lineText, wndPt.d_x);
00803
00804 if (lineIdx >= lineText.length() - 1)
00805 {
00806 lineIdx = lineText.length() - 1;
00807 }
00808
00809 return d_lines[lineNumber].d_startIdx + lineIdx;
00810 }
00811
00812
00813
00814
00815
00816
00817 size_t MultiLineEditbox::getLineNumberFromIndex(size_t index) const
00818 {
00819 size_t lineCount = d_lines.size();
00820
00821 if (lineCount == 0)
00822 {
00823 return 0;
00824 }
00825 else if (index >= d_text.length() - 1)
00826 {
00827 return lineCount - 1;
00828 }
00829 else
00830 {
00831 size_t indexCount = 0;
00832 size_t caratLine = 0;
00833
00834 for (; caratLine < lineCount; ++caratLine)
00835 {
00836 indexCount += d_lines[caratLine].d_length;
00837
00838 if (index < indexCount)
00839 {
00840 return caratLine;
00841 }
00842
00843 }
00844
00845 }
00846
00847 throw InvalidRequestException((utf8*)"MultiLineEditbox::getLineNumberFromIndex - Unable to identify a line from the given, invalid, index.");
00848 }
00849
00850
00851
00852
00853
00854
00855 void MultiLineEditbox::clearSelection(void)
00856 {
00857
00858 if (getSelectionLength() != 0)
00859 {
00860 setSelection(0, 0);
00861 }
00862
00863 }
00864
00865
00866
00867
00868
00869 void MultiLineEditbox::eraseSelectedText(bool modify_text)
00870 {
00871 if (getSelectionLength() != 0)
00872 {
00873
00874 setCaratIndex(getSelectionStartIndex());
00875
00876
00877 if (modify_text)
00878 {
00879 d_text.erase(getSelectionStartIndex(), getSelectionLength());
00880
00881
00882 WindowEventArgs args(this);
00883 onTextChanged(args);
00884 }
00885
00886 clearSelection();
00887 }
00888
00889 }
00890
00891
00892
00893
00894
00895 void MultiLineEditbox::handleBackspace(void)
00896 {
00897 if (!isReadOnly())
00898 {
00899 if (getSelectionLength() != 0)
00900 {
00901 eraseSelectedText();
00902 }
00903 else if (d_caratPos > 0)
00904 {
00905 d_text.erase(d_caratPos - 1, 1);
00906 setCaratIndex(d_caratPos - 1);
00907
00908 WindowEventArgs args(this);
00909 onTextChanged(args);
00910 }
00911
00912 }
00913 }
00914
00915
00916
00917
00918
00919 void MultiLineEditbox::handleDelete(void)
00920 {
00921 if (!isReadOnly())
00922 {
00923 if (getSelectionLength() != 0)
00924 {
00925 eraseSelectedText();
00926 }
00927 else if (getCaratIndex() < d_text.length() - 1)
00928 {
00929 d_text.erase(d_caratPos, 1);
00930 ensureCaratIsVisible();
00931
00932 WindowEventArgs args(this);
00933 onTextChanged(args);
00934 }
00935
00936 }
00937
00938 }
00939
00940
00941
00942
00943
00944 void MultiLineEditbox::handleCharLeft(uint sysKeys)
00945 {
00946 if (d_caratPos > 0)
00947 {
00948 setCaratIndex(d_caratPos - 1);
00949 }
00950
00951 if (sysKeys & Shift)
00952 {
00953 setSelection(d_caratPos, d_dragAnchorIdx);
00954 }
00955 else
00956 {
00957 clearSelection();
00958 }
00959
00960 }
00961
00962
00963
00964
00965
00966 void MultiLineEditbox::handleWordLeft(uint sysKeys)
00967 {
00968 if (d_caratPos > 0)
00969 {
00970 setCaratIndex(TextUtils::getWordStartIdx(d_text, getCaratIndex()));
00971 }
00972
00973 if (sysKeys & Shift)
00974 {
00975 setSelection(d_caratPos, d_dragAnchorIdx);
00976 }
00977 else
00978 {
00979 clearSelection();
00980 }
00981
00982 }
00983
00984
00985
00986
00987
00988 void MultiLineEditbox::handleCharRight(uint sysKeys)
00989 {
00990 if (d_caratPos < d_text.length() - 1)
00991 {
00992 setCaratIndex(d_caratPos + 1);
00993 }
00994
00995 if (sysKeys & Shift)
00996 {
00997 setSelection(d_caratPos, d_dragAnchorIdx);
00998 }
00999 else
01000 {
01001 clearSelection();
01002 }
01003
01004 }
01005
01006
01007
01008
01009
01010 void MultiLineEditbox::handleWordRight(uint sysKeys)
01011 {
01012 if (d_caratPos < d_text.length() - 1)
01013 {
01014 setCaratIndex(TextUtils::getNextWordStartIdx(d_text, getCaratIndex()));
01015 }
01016
01017 if (sysKeys & Shift)
01018 {
01019 setSelection(d_caratPos, d_dragAnchorIdx);
01020 }
01021 else
01022 {
01023 clearSelection();
01024 }
01025
01026 }
01027
01028
01029
01030
01031
01032 void MultiLineEditbox::handleDocHome(uint sysKeys)
01033 {
01034 if (d_caratPos > 0)
01035 {
01036 setCaratIndex(0);
01037 }
01038
01039 if (sysKeys & Shift)
01040 {
01041 setSelection(d_caratPos, d_dragAnchorIdx);
01042 }
01043 else
01044 {
01045 clearSelection();
01046 }
01047
01048 }
01049
01050
01051
01052
01053
01054 void MultiLineEditbox::handleDocEnd(uint sysKeys)
01055 {
01056 if (d_caratPos < d_text.length() - 1)
01057 {
01058 setCaratIndex(d_text.length() - 1);
01059 }
01060
01061 if (sysKeys & Shift)
01062 {
01063 setSelection(d_caratPos, d_dragAnchorIdx);
01064 }
01065 else
01066 {
01067 clearSelection();
01068 }
01069
01070 }
01071
01072
01073
01074
01075
01076 void MultiLineEditbox::handleLineHome(uint sysKeys)
01077 {
01078 size_t line = getLineNumberFromIndex(d_caratPos);
01079
01080 if (line < d_lines.size())
01081 {
01082 size_t lineStartIdx = d_lines[line].d_startIdx;
01083
01084 if (d_caratPos > lineStartIdx)
01085 {
01086 setCaratIndex(lineStartIdx);
01087 }
01088
01089 if (sysKeys & Shift)
01090 {
01091 setSelection(d_caratPos, d_dragAnchorIdx);
01092 }
01093 else
01094 {
01095 clearSelection();
01096 }
01097
01098 }
01099
01100 }
01101
01102
01103
01104
01105
01106 void MultiLineEditbox::handleLineEnd(uint sysKeys)
01107 {
01108 size_t line = getLineNumberFromIndex(d_caratPos);
01109
01110 if (line < d_lines.size())
01111 {
01112 size_t lineEndIdx = d_lines[line].d_startIdx + d_lines[line].d_length - 1;
01113
01114 if (d_caratPos < lineEndIdx)
01115 {
01116 setCaratIndex(lineEndIdx);
01117 }
01118
01119 if (sysKeys & Shift)
01120 {
01121 setSelection(d_caratPos, d_dragAnchorIdx);
01122 }
01123 else
01124 {
01125 clearSelection();
01126 }
01127
01128 }
01129
01130 }
01131
01132
01133
01134
01135
01136 void MultiLineEditbox::handleLineUp(uint sysKeys)
01137 {
01138 size_t caratLine = getLineNumberFromIndex(d_caratPos);
01139
01140 if (caratLine > 0)
01141 {
01142 float caratPixelOffset = getFont()->getTextExtent(d_text.substr(d_lines[caratLine].d_startIdx, d_caratPos - d_lines[caratLine].d_startIdx));
01143
01144 --caratLine;
01145
01146 size_t newLineIndex = getFont()->getCharAtPixel(d_text.substr(d_lines[caratLine].d_startIdx, d_lines[caratLine].d_length), caratPixelOffset);
01147
01148 setCaratIndex(d_lines[caratLine].d_startIdx + newLineIndex);
01149 }
01150
01151 if (sysKeys & Shift)
01152 {
01153 setSelection(d_caratPos, d_dragAnchorIdx);
01154 }
01155 else
01156 {
01157 clearSelection();
01158 }
01159
01160 }
01161
01162
01163
01164
01165
01166 void MultiLineEditbox::handleLineDown(uint sysKeys)
01167 {
01168 size_t caratLine = getLineNumberFromIndex(d_caratPos);
01169
01170 if ((d_lines.size() > 1) && (caratLine < (d_lines.size() - 1)))
01171 {
01172 float caratPixelOffset = getFont()->getTextExtent(d_text.substr(d_lines[caratLine].d_startIdx, d_caratPos - d_lines[caratLine].d_startIdx));
01173
01174 ++caratLine;
01175
01176 size_t newLineIndex = getFont()->getCharAtPixel(d_text.substr(d_lines[caratLine].d_startIdx, d_lines[caratLine].d_length), caratPixelOffset);
01177
01178 setCaratIndex(d_lines[caratLine].d_startIdx + newLineIndex);
01179 }
01180
01181 if (sysKeys & Shift)
01182 {
01183 setSelection(d_caratPos, d_dragAnchorIdx);
01184 }
01185 else
01186 {
01187 clearSelection();
01188 }
01189
01190 }
01191
01192
01193
01194
01195
01196 void MultiLineEditbox::handleNewLine(uint sysKeys)
01197 {
01198 if (!isReadOnly())
01199 {
01200
01201 eraseSelectedText();
01202
01203
01204 if (d_text.length() - 1 < d_maxTextLen)
01205 {
01206 d_text.insert(getCaratIndex(), 1, 0x0a);
01207 d_caratPos++;
01208
01209 WindowEventArgs args(this);
01210 onTextChanged(args);
01211 }
01212
01213 }
01214
01215 }
01216
01217
01218
01219
01220
01221 void MultiLineEditbox::onMouseButtonDown(MouseEventArgs& e)
01222 {
01223
01224 Window::onMouseButtonDown(e);
01225
01226 if (e.button == LeftButton)
01227 {
01228
01229 if (captureInput())
01230 {
01231
01232 clearSelection();
01233 d_dragging = true;
01234 d_dragAnchorIdx = getTextIndexFromPosition(e.position);
01235 setCaratIndex(d_dragAnchorIdx);
01236 }
01237
01238 e.handled = true;
01239 }
01240
01241 }
01242
01243
01244
01245
01246
01247 void MultiLineEditbox::onMouseButtonUp(MouseEventArgs& e)
01248 {
01249
01250 Window::onMouseButtonUp(e);
01251
01252 if (e.button == LeftButton)
01253 {
01254 releaseInput();
01255 e.handled = true;
01256 }
01257
01258 }
01259
01260
01261
01262
01263
01264 void MultiLineEditbox::onMouseDoubleClicked(MouseEventArgs& e)
01265 {
01266
01267 Window::onMouseDoubleClicked(e);
01268
01269 if (e.button == LeftButton)
01270 {
01271 d_dragAnchorIdx = TextUtils::getWordStartIdx(d_text, (d_caratPos == d_text.length()) ? d_caratPos : d_caratPos + 1);
01272 d_caratPos = TextUtils::getNextWordStartIdx(d_text, d_caratPos);
01273
01274
01275 setSelection(d_dragAnchorIdx, d_caratPos);
01276
01277 e.handled = true;
01278 }
01279
01280 }
01281
01282
01283
01284
01285
01286 void MultiLineEditbox::onMouseTripleClicked(MouseEventArgs& e)
01287 {
01288
01289 Window::onMouseTripleClicked(e);
01290
01291 if (e.button == LeftButton)
01292 {
01293 size_t caratLine = getLineNumberFromIndex(d_caratPos);
01294 size_t lineStart = d_lines[caratLine].d_startIdx;
01295
01296
01297 String::size_type paraStart = d_text.find_last_of(d_lineBreakChars, lineStart);
01298
01299
01300 if (paraStart == String::npos)
01301 {
01302 paraStart = 0;
01303 }
01304
01305
01306 String::size_type paraEnd = d_text.find_first_of(d_lineBreakChars, lineStart);
01307
01308
01309
01310 if (paraEnd == String::npos)
01311 {
01312 d_text.append(1, '\n');
01313 paraEnd = d_text.length() - 1;
01314 }
01315
01316
01317 d_dragAnchorIdx = paraStart;
01318 setCaratIndex(paraEnd);
01319 setSelection(d_dragAnchorIdx, d_caratPos);
01320 e.handled = true;
01321 }
01322
01323 }
01324
01325
01326
01327
01328
01329 void MultiLineEditbox::onMouseMove(MouseEventArgs& e)
01330 {
01331
01332 Window::onMouseMove(e);
01333
01334 if (d_dragging)
01335 {
01336 setCaratIndex(getTextIndexFromPosition(e.position));
01337 setSelection(d_caratPos, d_dragAnchorIdx);
01338 }
01339
01340 e.handled = true;
01341 }
01342
01343
01344
01345
01346
01347 void MultiLineEditbox::onCaptureLost(WindowEventArgs& e)
01348 {
01349 d_dragging = false;
01350
01351
01352 Window::onCaptureLost(e);
01353
01354 e.handled = true;
01355 }
01356
01357
01358
01359
01360
01361 void MultiLineEditbox::onCharacter(KeyEventArgs& e)
01362 {
01363
01364 Window::onCharacter(e);
01365
01366
01367 if (hasInputFocus() && !isReadOnly() && getFont()->isCodepointAvailable(e.codepoint))
01368 {
01369
01370 eraseSelectedText();
01371
01372
01373 if (d_text.length() - 1 < d_maxTextLen)
01374 {
01375 d_text.insert(getCaratIndex(), 1, e.codepoint);
01376 d_caratPos++;
01377
01378 WindowEventArgs args(this);
01379 onTextChanged(args);
01380 }
01381 else
01382 {
01383
01384 WindowEventArgs args(this);
01385 onEditboxFullEvent(args);
01386 }
01387
01388 }
01389
01390 e.handled = true;
01391 }
01392
01393
01394
01395
01396
01397 void MultiLineEditbox::onKeyDown(KeyEventArgs& e)
01398 {
01399
01400 Window::onKeyDown(e);
01401
01402 if (hasInputFocus() && !isReadOnly())
01403 {
01404 WindowEventArgs args(this);
01405 switch (e.scancode)
01406 {
01407 case Key::LeftShift:
01408 case Key::RightShift:
01409 if (getSelectionLength() == 0)
01410 {
01411 d_dragAnchorIdx = getCaratIndex();
01412 }
01413 break;
01414
01415 case Key::Backspace:
01416 handleBackspace();
01417 break;
01418
01419 case Key::Delete:
01420 handleDelete();
01421 break;
01422
01423 case Key::Return:
01424 case Key::NumpadEnter:
01425 handleNewLine(e.sysKeys);
01426 break;
01427
01428 case Key::ArrowLeft:
01429 if (e.sysKeys & Control)
01430 {
01431 handleWordLeft(e.sysKeys);
01432 }
01433 else
01434 {
01435 handleCharLeft(e.sysKeys);
01436 }
01437 break;
01438
01439 case Key::ArrowRight:
01440 if (e.sysKeys & Control)
01441 {
01442 handleWordRight(e.sysKeys);
01443 }
01444 else
01445 {
01446 handleCharRight(e.sysKeys);
01447 }
01448 break;
01449
01450 case Key::ArrowUp:
01451 handleLineUp(e.sysKeys);
01452 break;
01453
01454 case Key::ArrowDown:
01455 handleLineDown(e.sysKeys);
01456 break;
01457
01458 case Key::Home:
01459 if (e.sysKeys & Control)
01460 {
01461 handleDocHome(e.sysKeys);
01462 }
01463 else
01464 {
01465 handleLineHome(e.sysKeys);
01466 }
01467 break;
01468
01469 case Key::End:
01470 if (e.sysKeys & Control)
01471 {
01472 handleDocEnd(e.sysKeys);
01473 }
01474 else
01475 {
01476 handleLineEnd(e.sysKeys);
01477 }
01478 break;
01479
01480
01481 default:
01482 return;
01483 }
01484
01485 e.handled = true;
01486 }
01487
01488 }
01489
01490
01491
01492
01493
01494 void MultiLineEditbox::onTextChanged(WindowEventArgs& e)
01495 {
01496
01497 if ((d_text.length() == 0) || (d_text[d_text.length() - 1] != '\n'))
01498 d_text.append(1, '\n');
01499
01500
01501 Window::onTextChanged(e);
01502
01503
01504 clearSelection();
01505
01506 formatText();
01507
01508 performChildWindowLayout();
01509
01510 setCaratIndex(getCaratIndex());
01511
01512
01513
01514 ensureCaratIsVisible();
01515
01516 e.handled = true;
01517 }
01518
01519
01520
01521
01522
01523 void MultiLineEditbox::onSized(WindowEventArgs& e)
01524 {
01525 formatText();
01526
01527
01528 Window::onSized(e);
01529
01530 e.handled = true;
01531 }
01532
01533
01534
01535
01536
01537 void MultiLineEditbox::onMouseWheel(MouseEventArgs& e)
01538 {
01539
01540 Window::onMouseWheel(e);
01541
01542 if (d_vertScrollbar->isVisible() && (d_vertScrollbar->getDocumentSize() > d_vertScrollbar->getPageSize()))
01543 {
01544 d_vertScrollbar->setScrollPosition(d_vertScrollbar->getScrollPosition() + d_vertScrollbar->getStepSize() * -e.wheelChange);
01545 }
01546 else if (d_horzScrollbar->isVisible() && (d_horzScrollbar->getDocumentSize() > d_horzScrollbar->getPageSize()))
01547 {
01548 d_horzScrollbar->setScrollPosition(d_horzScrollbar->getScrollPosition() + d_horzScrollbar->getStepSize() * -e.wheelChange);
01549 }
01550
01551 e.handled = true;
01552 }
01553
01554
01555
01556
01557
01558 void MultiLineEditbox::onReadOnlyChanged(WindowEventArgs& e)
01559 {
01560 fireEvent(EventReadOnlyModeChanged, e, EventNamespace);
01561 }
01562
01563
01564
01565
01566
01567 void MultiLineEditbox::onWordWrapModeChanged(WindowEventArgs& e)
01568 {
01569 fireEvent(EventWordWrapModeChanged, e, EventNamespace);
01570 }
01571
01572
01573
01574
01575
01576 void MultiLineEditbox::onMaximumTextLengthChanged(WindowEventArgs& e)
01577 {
01578 fireEvent(EventMaximumTextLengthChanged, e, EventNamespace);
01579 }
01580
01581
01582
01583
01584
01585 void MultiLineEditbox::onCaratMoved(WindowEventArgs& e)
01586 {
01587 requestRedraw();
01588 fireEvent(EventCaratMoved, e, EventNamespace);
01589 }
01590
01591
01592
01593
01594
01595 void MultiLineEditbox::onTextSelectionChanged(WindowEventArgs& e)
01596 {
01597 requestRedraw();
01598 fireEvent(EventTextSelectionChanged, e, EventNamespace);
01599 }
01600
01601
01602
01603
01604
01605 void MultiLineEditbox::onEditboxFullEvent(WindowEventArgs& e)
01606 {
01607 fireEvent(EventEditboxFull, e, EventNamespace);
01608 }
01609
01610
01611
01612
01613
01614
01615 void MultiLineEditbox::onVertScrollbarModeChanged(WindowEventArgs& e)
01616 {
01617 requestRedraw();
01618 fireEvent(EventVertScrollbarModeChanged, e, EventNamespace);
01619 }
01620
01621
01622
01623
01624
01625
01626 void MultiLineEditbox::onHorzScrollbarModeChanged(WindowEventArgs& e)
01627 {
01628 requestRedraw();
01629 fireEvent(EventHorzScrollbarModeChanged, e, EventNamespace);
01630 }
01631
01632
01633
01634
01635
01636 bool MultiLineEditbox::isWordWrapped(void) const
01637 {
01638 return d_wordWrap;
01639 }
01640
01641
01642
01643
01644
01645 void MultiLineEditbox::addMultiLineEditboxProperties(void)
01646 {
01647 addProperty(&d_readOnlyProperty);
01648 addProperty(&d_wordWrapProperty);
01649 addProperty(&d_caratIndexProperty);
01650 addProperty(&d_selectionStartProperty);
01651 addProperty(&d_selectionLengthProperty);
01652 addProperty(&d_maxTextLengthProperty);
01653 addProperty(&d_normalTextColourProperty);
01654 addProperty(&d_selectedTextColourProperty);
01655 addProperty(&d_activeSelectionColourProperty);
01656 addProperty(&d_inactiveSelectionColourProperty);
01657 }
01658
01659
01660
01661
01662 bool MultiLineEditbox::handle_scrollChange(const EventArgs& args)
01663 {
01664
01665 requestRedraw();
01666 return true;
01667 }
01668
01669
01670
01671 }