00001
00002
00003
00004 #ifndef OglGui_TextEdit_h
00005 #define OglGui_TextEdit_h
00006
00007 #include <deque>
00008
00009 #ifndef OglGui_DocDimensions_h
00010 #include "OglGui/DocDimensions.h"
00011 #endif
00012
00013 #ifndef OglGui_WindowScrollBar_h
00014 #include "OglGui/WindowScrollBar.h"
00015 #endif
00016
00017 #ifndef OglGui_TextEditListener_h
00018 #include "OglGui/TextEditListener.h"
00019 #endif
00020
00021
00022 namespace OglGui
00023 {
00024
00025 class TextEdit : public Window, public DocDimensions, ScrollBarListener, RepeatTimer
00026 {
00027 typedef std::string string;
00028
00029 public:
00030
00031 TextEdit(int x, int y, int w, int h, strconst text, int sB=3) :
00032 Window(x, y, w, h)
00033 {
00034 Init(text, w, h, sB);
00035 }
00036
00037 TextEdit(Window* parent, int w, int h, strconst text, int sB=3) :
00038 Window(parent, w, h)
00039 {
00040 Init(text, w, h, sB);
00041 }
00042
00043 TextEdit(Window* parent, int x,int y, int w,int h, strconst text, int sB=3):
00044 Window(parent, x, y, w, h)
00045 {
00046 Init(text, w, h, sB);
00047 }
00048
00050
00051 class ChangeInfo
00052 {
00053 public:
00054 ChangeInfo(int ind1, int ind2, std::string* str, int caret, int mark)
00055 {
00056 mStart = ind1;
00057 mEnd = ind2;
00058 mCaret = caret;
00059 mMark = mark;
00060 mStr = *str;
00061 }
00062
00063 int Start() { return mStart; }
00064 int End() { return mEnd; }
00065 int Caret() { return mCaret; }
00066 int Mark() { return mMark; }
00067 std::string Str() { return mStr; }
00068
00069 private:
00070 std::string mStr;
00071 int mStart, mEnd;
00072 int mCaret, mMark;
00073 };
00074
00076
00078
00080
00081
00082 void EmptyChangeInfoDeque(std::deque<ChangeInfo*>& deQue)
00083 {
00084 ChangeInfo *changeInfo;
00085 while (!deQue.empty())
00086 {
00087 changeInfo = deQue.back();
00088 deQue.pop_back();
00089 delete changeInfo;
00090 }
00091 }
00092
00093 void UndoAction()
00094 {
00095 if (undoDeque.empty())
00096 return;
00097
00098 ChangeInfo* undo = undoDeque.back();
00099 undoDeque.pop_back();
00100
00101 ChangeInfo* redo = undoDeque.back();
00102 undoDeque.pop_back();
00103
00104 mText.replace(undo->Start(), undo->End() - undo->Start(), undo->Str());
00105 mCaret = undo->Caret();
00106 mMark = undo->Mark();
00107
00108 redoDeque.push_back(redo);
00109 redoDeque.push_back(undo);
00110
00111 RecomputeDocument();
00112 PublishChange();
00113 }
00114
00115 void RedoAction()
00116 {
00117 if (redoDeque.empty())
00118 return;
00119
00120 ChangeInfo* undo = redoDeque.back();
00121 redoDeque.pop_back();
00122
00123 ChangeInfo* redo = redoDeque.back();
00124 redoDeque.pop_back();
00125
00126 mText.replace(redo->Start(), redo->End() - redo->Start(), redo->Str());
00127 mCaret = redo->Caret();
00128 mMark = redo->Mark();
00129
00130 undoDeque.push_back(redo);
00131 undoDeque.push_back(undo);
00132
00133 RecomputeDocument();
00134 PublishChange();
00135 }
00136
00137 void UndoableInsertKey(int c)
00138 {
00139 string str = "";
00140 string redoStr = "";
00141 char buf[2];
00142 int left, right;
00143
00144 if (!redoDeque.empty())
00145 EmptyChangeInfoDeque(redoDeque);
00146
00147 buf[0] = c; buf[1] = '\0';
00148 redoStr += buf;
00149
00150 GetSelectionLeftRight(left, right);
00151
00152 ChangeInfo* redo = new ChangeInfo(left, right, &redoStr,
00153 left+(c?1:0), left+(c?1:0));
00154 if (left != right)
00155 str = mText.substr(left, right-left);
00156 ChangeInfo* undo = new ChangeInfo(left, left+(c?1:0), &str,
00157 mCaret, mMark);
00158
00159 undoDeque.push_back(redo);
00160 undoDeque.push_back(undo);
00161 }
00162
00163 void UndoableInsertString(string str)
00164 {
00165 string undoStr = "";
00166 int left, right;
00167
00168 if (!redoDeque.empty())
00169 EmptyChangeInfoDeque(redoDeque);
00170
00171 GetSelectionLeftRight(left, right);
00172
00173 if (left != right)
00174 undoStr = mText.substr(left, right-left);
00175
00176 ChangeInfo* redo = new ChangeInfo(left, left+undoStr.length(), &str,
00177 left+str.length(), left+str.length());
00178
00179 ChangeInfo* undo = new ChangeInfo(left, left + str.length(), &undoStr,
00180 mCaret, mMark);
00181
00182 undoDeque.push_back(redo);
00183 undoDeque.push_back(undo);
00184 }
00185
00186 void UndoableDeleteKey(int dir)
00187 {
00188 string str = "";
00189 string undoStr = "";
00190 char buf[2];
00191
00192 if (!redoDeque.empty())
00193 EmptyChangeInfoDeque(redoDeque);
00194
00195 int chInd = mCaret + (dir == -1 ? -1 : 0);
00196 buf[0] = mText[chInd];
00197 buf[1] = '\0';
00198 undoStr += buf;
00199
00200 ChangeInfo* redo = new ChangeInfo(mCaret + (dir==-1 ? -1:0),
00201 mCaret + (dir==-1 ? 0:1), &str,
00202 mCaret, mCaret);
00203
00204 ChangeInfo* undo = new ChangeInfo(mCaret + (dir==-1 ? -1:0),
00205 mCaret + (dir==-1 ? -1:0), &undoStr,
00206 mCaret, mMark);
00207 undoDeque.push_back(redo);
00208 undoDeque.push_back(undo);
00209 }
00211
00213
00215
00216 bool ReadFile(strconst fileName)
00217 {
00218 if (mSingleLine)
00219 return false;
00220
00221 int nBytes = -1;
00222 FILE *fp = fopen(fileName.c_str(), "rb");
00223 if (fp)
00224 {
00225 fseek(fp, 0, SEEK_END);
00226 nBytes = ftell(fp);
00227 fseek(fp, 0, SEEK_SET);
00228 char* buf = (char *) malloc(nBytes+1);
00229 if (buf)
00230 {
00231 nBytes = fread(buf, sizeof(char), nBytes, fp);
00232 buf[nBytes] = '\0';
00233
00234 char *tmpBuf = (char*) malloc(nBytes+1);
00235 if (tmpBuf)
00236 {
00237 int ind=0;
00238 for (int i=0; i<=nBytes; i++)
00239 if (buf[i] != 13)
00240 tmpBuf[ind++] = buf[i];
00241 SetText(tmpBuf);
00242 free(tmpBuf);
00243 }
00244 else
00245 SetText(buf);
00246 free(buf);
00247 }
00248 else
00249 nBytes = -1;
00250 fclose(fp);
00251 }
00252 return nBytes != -1;
00253 }
00254
00255 void PublishChange()
00256 {
00257 if (mListener)
00258 mListener->TextEditChangedEvent(this, mListenerData);
00259 }
00260
00262
00263 void SetTextEditListener(TextEditListener* listener, void* userData=0)
00264 {
00265 mListener = listener;
00266 mListenerData = userData;
00267 }
00268
00269 void SetText(strconst txt)
00270 {
00271 EmptyChangeInfoDeque(undoDeque);
00272 EmptyChangeInfoDeque(redoDeque);
00273
00274 if (mSingleLine)
00275 {
00276 string str;
00277 int pos = txt.find_first_of("\n", 0);
00278 if (pos == string::npos)
00279 pos = txt.length();
00280 str = txt.substr(0, pos);
00281 mText = str;
00282 }
00283 else
00284 mText = txt;
00285
00286 mDocX = mDocY = 0;
00287 mCaret = mMark = 0;
00288 RecomputeDocument();
00289 PublishChange();
00290 mDocX = 0;
00291 }
00292
00293 string GetText() { return mText; }
00294
00295 ScrollBar* HorizontalScrollBar() { return mHorizontalScrollBar; }
00296 ScrollBar* VerticalScrollBar() { return mVerticalScrollBar; }
00297
00298 void Editable(bool mode) { mEditable = mode; }
00299 bool Editable() { return mEditable; }
00300
00301 void SingleLine(bool mode) { mSingleLine = mode; }
00302 bool SingleLine() { return mSingleLine; }
00303
00304 void ClipBackground(bool mode) { mClipBackground = mode; }
00305 bool ClipBackground() { return mClipBackground; }
00306
00307 void CaretColor(ULONG col) { mCaretColor = col; }
00308 ULONG CaretColor() { return mCaretColor; }
00309
00310 void TextShadowed(bool mode) { mTextShadowed = mode; }
00311 bool TextShadowed() { return mTextShadowed; }
00312
00313 void TextShadowColor(ULONG shadCol) { mShadowColor = shadCol; };
00314 ULONG TextShadowColor() { return mShadowColor; };
00315
00316 void LineHeight(int h) { mLineH = h; }
00317 int LineHeight() { return mLineH; }
00318
00319 int Length() { return mText.length(); }
00320
00321 string GetSelection()
00322 {
00323 if (mMark == mCaret)
00324 return "";
00325
00326 int left, right;
00327 GetSelectionLeftRight(left, right);
00328 return mText.substr(left, right-left);
00329 }
00330
00331 void SetCaretMark(int caret, int mark)
00332 {
00333 Caret(caret);
00334 Mark(mark);
00335 }
00336
00337 void Caret(int ind)
00338 {
00339 if ((mCaret = ind) > mText.length())
00340 mCaret = mText.length();
00341 if (mCaret < 0)
00342 mCaret = 0;
00343 }
00344
00345 void Mark(int ind)
00346 {
00347 if ((mMark = ind) > mText.length())
00348 mMark = mText.length();
00349 if (mMark < 0)
00350 mMark = 0;
00351 }
00352
00353 int Caret() { return mCaret; }
00354 int Mark() { return mMark; }
00355 int CaretX() { return mCaretX; }
00356 int CaretY() { return mCaretY; }
00358
00360 void RecomputeDocument()
00361 {
00362 ComputeDocHeight();
00363 ClampDocY();
00364 ComputeCaretPosition();
00365 MoveToCaret();
00366 }
00367
00368 int ComputeDocHeight()
00369 {
00370 int h = 0;
00371 int nChar;
00372 int ind = 0;
00373
00374 while ((nChar = GetLineAtIndex(0, ind))!=-1)
00375 {
00376 ind += nChar+1;
00377 h += mLineH;
00378 }
00379 return mDocH = h;
00380 }
00381
00382 int ComputeMaxWidth()
00383 {
00384 string str;
00385 int nChar;
00386 int lineInd = 0;
00387 int x, maxX = 0;
00388
00389 while ((nChar = GetLineAtIndex(&str, lineInd) + 1))
00390 {
00391 x = FindWidthAtIndex(&str, str.length());
00392 if (x > maxX)
00393 maxX = x;
00394 lineInd += nChar;
00395 }
00396 return maxX;
00397 }
00398
00399 void ComputeCaretPosition()
00400 {
00401 int y = mDocH + mDocY - mLineH;
00402 int lineInd = 0;
00403 int nChar;
00404
00405 while ((nChar = GetLineAtIndex(0, lineInd) + 1) &&
00406 lineInd+nChar <= mCaret)
00407 {
00408 lineInd += nChar;
00409 y -= mLineH;
00410 }
00411 mCaretY = y;
00412
00413 int x = mLeftMargin;
00414 if (nChar)
00415 {
00416 string str;
00417 GetLineAtIndex(&str, lineInd);
00418 x = FindWidthAtIndex(&str, mCaret - lineInd) + mDocX;
00419 }
00420 mCaretX = x;
00421 }
00422
00423 void ClampDocX()
00424 {
00425 if (mDocX > 0)
00426 mDocX = 0;
00427 if (mDocX + mDocW < W())
00428 mDocX = W() - mDocW;
00429 }
00430
00431 void ClampDocY()
00432 {
00433 if (mDocY < H() - mDocH)
00434 mDocY = H() - mDocH;
00435 if (mDocY > H() - mLineH)
00436 mDocY = H() - mLineH;
00437 }
00438
00439 void GetSelectionLeftRight(int& left, int& right)
00440 {
00441 if (mMark < mCaret)
00442 {
00443 left = mMark;
00444 right = mCaret;
00445 }
00446 else
00447 {
00448 left = mCaret;
00449 right = mMark;
00450 }
00451 }
00452
00453 int FindIndexAtWidth(string* str, int w)
00454 {
00455 int len = str->length();
00456 int ind = 0, indW;
00457 while (ind<len && (indW=FindWidthAtIndex(str, ind)) < w)
00458 ind++;
00459 return ind;
00460 }
00461
00462 int FindInsertionIndexAtWidth(string* str, int w)
00463 {
00464 int len = str->length();
00465 int ind = 0, indW;
00466 while (ind<len && (indW=FindWidthAtIndex(str, ind)) < w)
00467 ind++;
00468
00469
00470
00471 if (ind && (indW - (indW - FindWidthAtIndex(str, ind-1))/2) > w)
00472 ind--;
00473 return ind;
00474 }
00475
00476 int FindWidthAtIndex(string* str, int ind)
00477 {
00478 char buf[2048];
00479 int w, h;
00480 void* font;
00481
00482 int tPos, tInd = 0, tCnt = 0, tabW;
00483 int totWidth = 0;
00484 string tabStr;
00485
00486 if (ind > str->length())
00487 ind = str->length();
00488 if (ind > 2047)
00489 ind = 2047;
00490
00491 FindOglFont(mOglWnd, &font, NULL);
00492
00493 if ((tPos = str->find_first_of("\t")) == string::npos)
00494 {
00495 buf[0] = '\0';
00496 strncpy(buf, str->c_str(), ind);
00497 buf[ind] = '\0';
00498 oglSys.AbstFontTextSize(mOglWnd, font, buf, &w, &h);
00499 return mLeftMargin + w;
00500 }
00501
00502 oglSys.AbstFontTextSize(mOglWnd, font, " ", &w, &h);
00503 tabW = w;
00504
00505 bool done = false;
00506 while (!done && ((tPos = str->find_first_of("\t",tInd)) != string::npos))
00507 {
00508 tabStr = str->substr(tInd, tPos-tInd);
00509 if (ind > tInd + tabStr.length())
00510 {
00511 oglSys.AbstFontTextSize(mOglWnd, font, tabStr.c_str(), &w, &h);
00512 tCnt = (totWidth + w) / tabW + 1;
00513 totWidth = tCnt * tabW;
00514 }
00515 else
00516 {
00517 tabStr = tabStr.substr(0, ind - tInd);
00518 oglSys.AbstFontTextSize(mOglWnd, font, tabStr.c_str(), &w, &h);
00519 totWidth += w;
00520 done = true;
00521 }
00522 tInd = tPos+1;
00523 }
00524 if (!done)
00525 {
00526 tabStr = str->substr(tInd, ind - tInd);
00527 oglSys.AbstFontTextSize(mOglWnd, font, tabStr.c_str(), &w, &h);
00528 totWidth += w;
00529 }
00530 return mLeftMargin + totWidth;
00531 }
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550 int FindLineAtY(int wndY, int &lineY)
00551 {
00552 int ind = 0, nChar = 0;
00553 int y = mDocH + mDocY - mLineH;
00554
00555 while (y > wndY && (nChar = GetLineAtIndex(0, ind))!=-1)
00556 {
00557 ind += nChar+1;
00558 y -= mLineH;
00559 }
00560 if (ind > mText.length())
00561 ind = mText.length();
00562 lineY = y;
00563 return ind;
00564 }
00565
00566 int FindFirstVisibleLine(int &lineY)
00567 {
00568 return FindLineAtY( H(), lineY);
00569 }
00570
00571 int GetLineAtIndex(string* str, int ind)
00572 {
00573 if (ind >= mText.length())
00574 return -1;
00575 int pos = mText.find_first_of("\n", (size_t) ind);
00576 if (pos == string::npos)
00577 pos = mText.length();
00578 if (str!=0)
00579 *str = (pos-ind) ? mText.substr(ind, pos-ind) : "";
00580 return pos-ind;
00581 }
00582
00583 void InvertBlock(int x1, int y1, int x2, int y2)
00584 {
00585 glColor3d(1,1,1);
00586 glEnable(GL_BLEND);
00587 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
00588 glBegin(GL_QUADS);
00589 glVertex2d(x1, y1);
00590 glVertex2d(x1, y2);
00591 glVertex2d(x2, y2);
00592 glVertex2d(x2, y1);
00593 glEnd();
00594 glDisable(GL_BLEND);
00595 }
00596
00597 void HandleInvertLineSelection(string *str, int ind, int indEnd, int y)
00598 {
00599 if (mMark == mCaret)
00600 return;
00601
00602 int left, right;
00603 int xEnd, xStart, yEnd = y + mLineH;
00604
00605 GetSelectionLeftRight(left, right);
00606
00607 if (ind >= left && indEnd <= right)
00608 {
00609 if (*str == "")
00610 xEnd = mLeftMargin+4;
00611 else
00612 xEnd = FindWidthAtIndex(str, indEnd-ind);
00613 InvertBlock(mLeftMargin+mDocX, y, xEnd+mDocX, yEnd);
00614 }
00615 else
00616 {
00617 if (ind >= left && ind <= right)
00618 {
00619 xEnd = FindWidthAtIndex(str, right-ind);
00620 InvertBlock(mLeftMargin+mDocX, y, xEnd+mDocX, yEnd);
00621 }
00622 if (indEnd > left && indEnd <= right)
00623 {
00624 xStart = FindWidthAtIndex(str, left-ind)+mDocX;
00625 xEnd = FindWidthAtIndex(str, indEnd-ind)+mDocX;
00626 InvertBlock(xStart, y, xEnd, yEnd);
00627 }
00628 if (ind < left && indEnd > right)
00629 {
00630 xStart = FindWidthAtIndex(str, left-ind)+mDocX;
00631 xEnd = FindWidthAtIndex(str, right-ind)+mDocX;
00632 InvertBlock(xStart, y, xEnd, yEnd);
00633 }
00634 }
00635 }
00636
00637 void PrintBuf(int x, int y, const char* buf)
00638 {
00639 int col = GetForeground();
00640 if (!mTextShadowed)
00641 oglSys.PosColPrintf(mOglWnd, x, y, col, "%s", buf);
00642 else
00643 oglSys.ShadowPrintf(mOglWnd, x, y, mShadowColor, col, "%s", buf);
00644 }
00645
00646 void DrawTabbedLine(int x, int y, const char* buf)
00647 {
00648 string str = buf;
00649
00650 if (str.find_first_of("\t") == string::npos)
00651 return PrintBuf(x, y, buf);
00652
00653 string tabStr;
00654 int tabPos, tabInd = 0;
00655 int totWidth = 0, tabCnt = 0;
00656 int w, h;
00657 int tabWidth;
00658 void* font;
00659
00660 FindOglFont(mOglWnd, &font, NULL);
00661 oglSys.AbstFontTextSize(mOglWnd, font, " ", &tabWidth, &h);
00662
00663 while ((tabPos = str.find_first_of("\t", tabInd)) != string::npos)
00664 {
00665 tabStr = str.substr(tabInd, tabPos-tabInd);
00666 PrintBuf(x + totWidth, y, tabStr.c_str());
00667
00668 oglSys.AbstFontTextSize(mOglWnd, font, tabStr.c_str(), &w, &h);
00669 tabCnt = (totWidth + w) / tabWidth + 1;
00670 totWidth = tabCnt * tabWidth;
00671 tabInd = tabPos+1;
00672 }
00673 if (tabInd < str.length())
00674 {
00675 tabStr = str.substr(tabInd, str.length()-tabInd);
00676 PrintBuf(x + totWidth, y, tabStr.c_str());
00677 }
00678 }
00679
00680 void DrawText()
00681 {
00682 int yPos = H();
00683 int ind = 0;
00684
00685 if ((ind = FindFirstVisibleLine(yPos)) > mText.length())
00686 return;
00687
00688 string line;
00689 char buf[2048];
00690 int nChar;
00691
00692 while (yPos >= -mLineH && (nChar = GetLineAtIndex(&line, ind))!=-1)
00693 {
00694 int bufInd = 0;
00695 int xOff = mLeftMargin + mDocX;
00696 bool doShow = true;
00697
00698 strncpy(buf, line.c_str(), 2047);
00699 buf[2047] ='\0';
00700
00701 if (mDocX < 0)
00702 {
00703 bufInd = FindIndexAtWidth(&line, -mDocX);
00704 if ((xOff = FindWidthAtIndex(&line, bufInd) + mDocX) < 0)
00705 doShow = false;
00706 }
00707 if (doShow)
00708 DrawTabbedLine(xOff, yPos+2, buf + bufInd);
00709
00710 if (mMark != mCaret)
00711 HandleInvertLineSelection(&line, ind, ind+nChar+1, yPos-2);
00712 ind += nChar+1;
00713 yPos -= mLineH;
00714 }
00715 if (GetOGLWND() == oglFocusWnd) {
00716 SetSolidLineColor(mCaretColor);
00717 } else {
00718 SetSolidLineColor(mShadowColor);
00719 }
00720
00721 DrawLine(mCaretX, mCaretY-2, mCaretX, mCaretY-2+mLineH);
00722 }
00723
00724 void MoveToCaret()
00725 {
00726 int minY = mHorizontalScrollBar ? mHorizontalScrollBar->H() : 0;
00727 int maxW = W() - (mVerticalScrollBar ? mVerticalScrollBar->W() : 0);
00728
00729 if (mCaretY < minY)
00730 {
00731 mDocY -= (mCaretY-minY)-6;
00732 mCaretY = minY + 6;
00733 }
00734 if (mCaretY > H() - mLineH)
00735 {
00736 mDocY -= mCaretY - (H() - mLineH);
00737 mCaretY = H() - mLineH;
00738 }
00739 if (mCaretX < 0)
00740 {
00741 mDocX -= mCaretX-mLeftMargin;;
00742 mCaretX = mLeftMargin;
00743 }
00744 if (mCaretX > maxW - 8)
00745 {
00746 mDocX -= mCaretX - (maxW-8);
00747 }
00748 }
00749
00750 void HandleMoveKey(int c, int state)
00751 {
00752 bool shift = (state & oglShift) ? true : false;
00753 bool control = (state & oglControl) ? true : false;
00754 string str;
00755
00756 if (c==oglUP || c==oglDOWN || c==oglPAGEUP || c==oglPAGEDOWN)
00757 {
00758 int lineInd = FindLineAtY(mCaretY, mCaretY);
00759 int dY;
00760
00761 if (mUpDownPos == -1)
00762 mUpDownPos = mCaret - lineInd;
00763
00764 if (c==oglUP || c==oglDOWN)
00765 dY = (c==oglUP ? mLineH : -mLineH);
00766 else
00767 dY = (c==oglPAGEUP ? H()-2*mLineH : -H()+2*mLineH);
00768
00769 lineInd = FindLineAtY(mCaretY + dY, mCaretY);
00770 if (c==oglPAGEUP || c==oglPAGEDOWN)
00771 {
00772 int oldCaretY = mCaretY;
00773 ComputeCaretPosition();
00774 mDocY += mCaretY - oldCaretY;
00775 ClampDocY();
00776 }
00777 GetLineAtIndex(&str, lineInd);
00778 int sLen = str.length();
00779 mCaret = lineInd + ((sLen > mUpDownPos) ? mUpDownPos : sLen);
00780 }
00781 else
00782 {
00783 mUpDownPos = -1;
00784 if (c==oglLEFT || c==oglRIGHT)
00785 if ((mCaret += (c==oglLEFT) ? -1 : 1) < 0)
00786 mCaret = 0;
00787
00788 if (c==oglHOME && control)
00789 mCaret = 0;
00790 else if(c == oglHOME)
00791 mCaret = FindLineAtY(mCaretY, mCaretY);
00792
00793 if (c==oglEND && control)
00794 mCaret = mText.size();
00795 else if (c==oglEND)
00796 {
00797 int lineInd = FindLineAtY(mCaretY, mCaretY);
00798 GetLineAtIndex(&str, lineInd);
00799 mCaret = lineInd + str.length();
00800 }
00801 }
00802
00803 if (mCaret > mText.length())
00804 mCaret = mText.length();
00805
00806 if (!shift)
00807 mMark = mCaret;
00808
00809 ComputeCaretPosition();
00810 }
00811
00812 void DoDeleteSelection()
00813 {
00814 int left,right;
00815 GetSelectionLeftRight(left, right);
00816
00817 mText.erase(left, right - left);
00818 mCaret = mMark = left;
00819 }
00820
00821 void InsertKey(int c)
00822 {
00823 UndoableInsertKey(c);
00824 if (mCaret != mMark)
00825 DoDeleteSelection();
00826 mText.insert(mCaret, 1, c);
00827 mCaret++;
00828 mMark = mCaret;
00829 ComputeCaretPosition();
00830 PublishChange();
00831 }
00832
00833 void InsertString(string str)
00834 {
00835 UndoableInsertString(str);
00836 if (mCaret != mMark)
00837 DoDeleteSelection();
00838 mText.insert(mCaret, str);
00839 mMark = mCaret += str.length();
00840 ComputeCaretPosition();
00841 PublishChange();
00842 }
00843
00844 void DeleteSelection()
00845 {
00846 bool changed = mCaret != mMark;
00847 UndoableInsertKey(0);
00848 DoDeleteSelection();
00849 if (changed)
00850 PublishChange();
00851 }
00852
00853 void BackSpace()
00854 {
00855 bool changed = mCaret != mMark;
00856
00857 if (mCaret != mMark)
00858 DeleteSelection();
00859 else if (mCaret > 0)
00860 {
00861 changed = true;
00862 UndoableDeleteKey(-1);
00863 mMark = --mCaret;
00864
00865 mText.erase(mCaret, 1);
00866 }
00867 ComputeCaretPosition();
00868 if (changed)
00869 PublishChange();
00870 }
00871
00872 void DeleteKey()
00873 {
00874 bool changed = mCaret != mMark;
00875 if (mCaret != mMark)
00876 DeleteSelection();
00877 else if (mCaret < mText.size())
00878 {
00879 changed = true;
00880 UndoableDeleteKey(1);
00881
00882 mText.erase(mCaret, 1);
00883 }
00884 ComputeCaretPosition();
00885 if (changed)
00886 PublishChange();
00887 }
00888
00889 void Paste()
00890 {
00891 if (mClipboardString.empty())
00892 return;
00893 if (mSingleLine)
00894 {
00895 string str;
00896 int pos = mClipboardString.find_first_of("\n", 0);
00897 if (pos == string::npos)
00898 pos = mClipboardString.length();
00899 str = mClipboardString.substr(0, pos);
00900 InsertString(str);
00901 }
00902 else
00903 InsertString(mClipboardString);
00904 PublishChange();
00905 }
00906
00907 void Copy()
00908 {
00909 if (mCaret == mMark)
00910 return;
00911
00912 int left,right;
00913 GetSelectionLeftRight(left, right);
00914 mClipboardString = mText.substr(left, right - left);
00915 }
00916
00917 void Cut()
00918 {
00919 bool changed = mCaret != mMark;
00920 Copy();
00921 DeleteSelection();
00922 if (changed)
00923 PublishChange();
00924 }
00925
00926 void StartScrollClipping()
00927 {
00928 int x = 0, y = 0, w = W(), h = H();
00929 if (mHorizontalScrollBar)
00930 {
00931 y = mHorizontalScrollBar->H();
00932 h -= mHorizontalScrollBar->H();
00933 }
00934 if (mVerticalScrollBar)
00935 w -= mVerticalScrollBar->W();
00936 oglSys.StartScissor(mOglWnd, x, y, w, h);
00937
00938 }
00939
00940 void EndScrollClipping()
00941 {
00942 oglSys.EndScissor();
00943 }
00944
00945 virtual void OnScroll(ScrollBar *src, int position, void* userData)
00946 {
00947 if (src == mHorizontalScrollBar)
00948 mDocX = -position;
00949 if (src == mVerticalScrollBar)
00950 mDocY = -mDocH + H() + position;
00951 ComputeCaretPosition();
00952 }
00953
00954 void HandleScrollBarsOnDisplay()
00955 {
00956 if (mVerticalScrollBar)
00957 {
00958 int extra = mHorizontalScrollBar ? mHorizontalScrollBar->H() : 0;
00959 mVerticalScrollBar->SetRange(mDocH+4 + extra, H() - extra - mLineH);
00960 int nPos = mDocH - H() + mDocY;
00961 mVerticalScrollBar->SetNewPos(nPos);
00962 }
00963
00964 if (mHorizontalScrollBar)
00965 {
00966 mHorizontalScrollBar->SetRange(mDocW, W()-20);
00967 mHorizontalScrollBar->SetNewPos(-mDocX);
00968 }
00969
00970 if (mPropagateScrollingY && RepeatTime())
00971 {
00972 mDocY += mPropagateScrollingY;
00973 ClampDocY();
00974 MouseFunc(oglMouseMove, 0, oglLeftButton, mLastMouseX, mLastMouseY);
00975 }
00976
00977 if (mPropagateScrollingX && RepeatTime())
00978 {
00979 mDocX += mPropagateScrollingX;
00980 ClampDocX();
00981 MouseFunc(oglMouseMove, 0, oglLeftButton, mLastMouseX, mLastMouseY);
00982 }
00983 }
00984
00985 virtual void InitFunc()
00986 {
00987 Window::InitFunc();
00988 mInitialized = true;
00989 }
00990
00991 virtual void ExitFunc()
00992 {
00993 EmptyChangeInfoDeque(undoDeque);
00994 EmptyChangeInfoDeque(redoDeque);
00995 Window::ExitFunc();
00996 }
00997
00998 virtual void InitDisplayFunc()
00999 {
01000 if (mClipBackground && (mHorizontalScrollBar || mVerticalScrollBar))
01001 StartScrollClipping();
01002 Window::InitDisplayFunc();
01003 }
01004
01005 virtual void DisplayFunc()
01006 {
01007 Window::DisplayFunc();
01008
01009 HandleScrollBarsOnDisplay();
01010
01011 if (!mClipBackground && (mHorizontalScrollBar || mVerticalScrollBar))
01012 StartScrollClipping();
01013
01014 OGC oldOGC;
01015 OGCSave(&oldOGC);
01016 DrawText();
01017 OGCRestore(&oldOGC);
01018 ComputeCaretPosition();
01019
01020 if (mHorizontalScrollBar || mVerticalScrollBar)
01021 EndScrollClipping();
01022 }
01023
01024 virtual void MouseFunc(int msg, int btn, int state, int x, int y)
01025 {
01026 if (msg == oglMouseDown && btn == oglLeftButton && !(state&oglControl))
01027 {
01028 string str;
01029 int lineInd = FindLineAtY(y, mCaretY);
01030
01031 GetLineAtIndex(&str, lineInd);
01032 mCaret = lineInd + FindInsertionIndexAtWidth(&str, -mDocX+x);
01033 if (!(state & oglShift))
01034 mMark = mCaret;
01035
01036 StartRepeatTime();
01037 oglSys.SetAlwaysDraw(mOglWnd, 1);
01038 mLastMouseX = x;
01039 mLastMouseY = y;
01040 mUpDownPos = mCaret - lineInd;
01041 }
01042 if (msg == oglMouseMove && (state & oglLeftButton))
01043 {
01044 string str;
01045 int lineInd = FindLineAtY(y, mCaretY);
01046
01047 GetLineAtIndex(&str, lineInd);
01048 mCaret = lineInd + FindInsertionIndexAtWidth(&str, -mDocX+x);
01049
01050 mPropagateScrollingX = mPropagateScrollingY = 0;
01051
01052 int minY = mHorizontalScrollBar ? mHorizontalScrollBar->H() : 0;
01053 if (y < minY)
01054 mPropagateScrollingY = 4 - (y-minY)/2;
01055 else if(y > H())
01056 mPropagateScrollingY = -4 - (y - H())/2;
01057
01058 int maxW = W() - (mVerticalScrollBar ? mVerticalScrollBar->W() : 0);
01059 if (x < 0)
01060 mPropagateScrollingX = 4 - x/2;
01061 else if(x > maxW)
01062 mPropagateScrollingX = -4 - (x - maxW)/2;
01063
01064 mLastMouseX = x;
01065 mLastMouseY = y;
01066 mUpDownPos = mCaret - lineInd;
01067 }
01068
01069 if (msg == oglMouseUp && btn == oglLeftButton)
01070 {
01071 mPropagateScrollingY = mPropagateScrollingX = 0;
01072 oglSys.SetAlwaysDraw(mOglWnd, 0);
01073 }
01074
01075 if (msg == oglMouseWheelUp || msg == oglMouseWheelDown)
01076 {
01077 mDocY += (msg == oglMouseWheelUp ? -mLineH : mLineH);
01078 ClampDocY();
01079 }
01080
01081 ComputeCaretPosition();
01082 Window::MouseFunc(msg, btn, state, x, y);
01083 }
01084
01085 virtual void KeyboardFunc(int c, int state)
01086 {
01087 bool shift = (state & oglShift) ? true : false;
01088 int oldDocH = mDocH;
01089
01090 Window::KeyboardFunc(c, state);
01091
01092 if (c == oglUP || c == oglDOWN ||
01093 c == oglLEFT || c == oglRIGHT ||
01094 c == oglPAGEUP || c == oglPAGEDOWN ||
01095 c == oglHOME || c == oglEND)
01096 {
01097 HandleMoveKey(c, state);
01098 }
01099 if (c == oglCTRL('c'))
01100 Copy();
01101
01102 if (mEditable)
01103 {
01104 if (c == oglCTRL('z'))
01105 UndoAction();
01106 if (c == oglCTRL('y'))
01107 RedoAction();
01108
01109 if (c == oglCTRL('x'))
01110 Cut();
01111 if (c == oglCTRL('v'))
01112 Paste();
01113
01114 if( c == '\r' )
01115 c = '\n';
01116
01117 if( c == 8 )
01118 BackSpace();
01119 if (c == oglDELETE)
01120 DeleteKey();
01121 if (c == '\n' || c == '\t' || c == ' ' || isalnum(c) || ispunct(c))
01122 if (!(mSingleLine && c == '\n'))
01123 InsertKey(c);
01124
01125 mDocY -= ComputeDocHeight() - oldDocH;
01126 }
01127
01128 MoveToCaret();
01129 }
01130
01131
01132 virtual void ReshapeFunc(int w, int h)
01133 {
01134 Window::ReshapeFunc(w,h);
01135
01136 if (!mInitialized)
01137 return;
01138
01139 ComputeDocHeight();
01140
01141
01142 mDocY += (h - mOldH);
01143 mOldH = h;
01144
01145 ClampDocY();
01146 ComputeCaretPosition();
01147 }
01148
01149 protected:
01150
01151 void Init(strconst text, int w, int h, int sB)
01152 {
01153 SetBorderType( BEV_RIDGE );
01154 mListener = 0;
01155 mHorizontalScrollBar = mVerticalScrollBar = 0;
01156
01157
01158
01159 mClipBackground = false;
01160
01161 mInitialized = false;
01162 mEditable = true;
01163 mSingleLine = false;
01164
01165
01166 mDocW = 800;
01167 mOldH = h;
01168 mLeftMargin = 8;
01169
01170 mMark = 0;
01171 mCaret = 0;
01172 mCaretY = h - mLineH;
01173 mCaretX = mLeftMargin;
01174 mCaretColor = oglLIGHTRED;
01175
01176 mTextShadowed = false;
01177 mShadowColor = oglLIGHTGREY;
01178 mLineH = 16;
01179
01180 SetText(text);
01181
01182
01183 if (sB & 1)
01184 {
01185 mHorizontalScrollBar = new WindowScrollBar(this, true, 1, 16,
01186 (sB&2)?16:0);
01187 mHorizontalScrollBar->SetLineIncrement(mLineH);
01188 mHorizontalScrollBar->SetScrollBarListener(this);
01189 mHorizontalScrollBar->MapKeysTo(this);
01190 }
01191 if (sB & 2)
01192 {
01193 mVerticalScrollBar = new WindowScrollBar(this, false, 1, 16,
01194 (sB&1)?16:0);
01195 mVerticalScrollBar->SetLineIncrement(mLineH);
01196 mVerticalScrollBar->SetScrollBarListener(this);
01197 mVerticalScrollBar->MapKeysTo(this);
01198 }
01199
01200 mUpDownPos = -1;
01201
01202 mPropagateScrollingY = 0;
01203 mPropagateScrollingX = 0;
01204 mLastMouseX = 0;
01205
01206 SetDisableGlobalKeyListener( true );
01207 SetDisableOGLViewKeys( true );
01208 SetDisableOGLViewMouse( true );
01209
01210 SetBackground(oglWHITE);
01211 }
01212
01213 std::deque<ChangeInfo*> undoDeque;
01214 std::deque<ChangeInfo*> redoDeque;
01215
01216 WindowScrollBar* mHorizontalScrollBar;
01217 WindowScrollBar* mVerticalScrollBar;
01218 bool mClipBackground;
01219
01220 string mText;
01221 int mLineH;
01222
01223 bool mInitialized;
01224 bool mEditable;
01225 bool mSingleLine;
01226
01227 int mMark;
01228 int mCaret;
01229 int mCaretX;
01230 int mCaretY;
01231 ULONG mCaretColor;
01232
01233 int mLeftMargin;
01234
01235 int mOldH;
01236 int mUpDownPos;
01237
01238 bool mTextShadowed;
01239 ULONG mShadowColor;
01240
01241 int mPropagateScrollingY;
01242 int mPropagateScrollingX;
01243
01244 int mLastMouseX;
01245 int mLastMouseY;
01246
01247
01248 TextEditListener* mListener;
01249 void* mListenerData;
01250 };
01251
01252 }
01253 #endif