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