00001 #include "pgapplication.h"
00002 #include "pgmultilineedit.h"
00003 #include "pglog.h"
00004
00005 using namespace std;
00006
00007 PG_MultiLineEdit::PG_MultiLineEdit(PG_Widget* parent, const PG_Rect& r, const std::string& style, int maximumLength)
00008 : PG_LineEdit(parent, r, style, maximumLength) {
00009 my_vscroll = new PG_ScrollBar(this, PG_Rect(r.w-16,0,16,r.h));
00010 my_vscroll->MoveWidget(PG_Rect(r.w-my_vscroll->w, 0, my_vscroll->w, r.h));
00011 my_isCursorAtEOL = false;
00012 my_allowHiddenCursor = false;
00013 my_firstLine = 0;
00014 my_vscroll->sigScrollPos.connect(slot(*this, &PG_MultiLineEdit::handleScroll));
00015 my_vscroll->sigScrollTrack.connect(slot(*this, &PG_MultiLineEdit::handleScroll));
00016 my_vscroll->Hide();
00017 my_mark = -1;
00018 }
00019
00020 bool PG_MultiLineEdit::handleScroll(PG_ScrollBar* widget, long data) {
00021 SetVPosition(my_vscroll->GetPosition());
00022 my_allowHiddenCursor = true;
00023 return true;
00024 }
00025
00026 void PG_MultiLineEdit::SetVPosition(int line) {
00027 if (line < 0) {
00028 line = 0;
00029 }
00030
00031 if (line > my_vscroll->GetMaxRange()) {
00032 line = my_vscroll->GetMaxRange();
00033 }
00034
00035 my_firstLine = line;
00036
00037 if (my_vscroll->GetPosition() != line) {
00038 my_vscroll->SetPosition(line);
00039 }
00040
00041 Update();
00042 }
00043
00044 void PG_MultiLineEdit::eventBlit(SDL_Surface* surface, const PG_Rect& src, const PG_Rect& dst) {
00045 PG_ThemeWidget::eventBlit(surface, src, dst);
00046 DrawText(dst);
00047 }
00048
00049 void PG_MultiLineEdit::DrawText(const PG_Rect& dst) {
00050 int _x = 3;
00051 int _y = 3;
00052
00053
00054 int pos = 0;
00055 for (unsigned int i = 0; i < (unsigned int)my_firstLine; ++i) {
00056 pos += my_textdata[i].size();
00057 }
00058
00059
00060 int maxLines = my_height/GetFontSize() + 1;
00061 int endpos, start, end;
00062
00063 int x1 = 0;
00064 Uint16 w = 0;
00065 int offset = 0;
00066 PG_Color color(GetFontColor());
00067 PG_Color inv_color(255 - color.r, 255 - color.g, 255 - color.b);
00068 SDL_Surface* screen = PG_Application::GetScreen();
00069 Uint32 highlightColor = SDL_MapRGB(screen->format, color.r, color.g, color.b);
00070 PG_String s, middlepart;
00071
00072 for (unsigned int i = my_firstLine; i < (unsigned int)my_firstLine + maxLines && i < my_textdata.size(); ++i) {
00073 endpos = pos + my_textdata[i].size();
00074 start = (my_cursorPosition < my_mark ? my_cursorPosition : my_mark);
00075 end = (my_cursorPosition >= my_mark ? my_cursorPosition : my_mark);
00076
00077
00078 if (my_mark != -1 && my_mark != my_cursorPosition && pos <= end && endpos >= start) {
00079 x1 = _x;
00080 offset = 0;
00081
00082
00083 if (pos < start) {
00084 s = my_textdata[i].substr(0, start-pos);
00085 PG_Widget::DrawText(x1, _y, s);
00086 PG_FontEngine::GetTextSize(s, GetFont(), &w);
00087 x1 += w;
00088 offset = start-pos;
00089 }
00090
00091 middlepart = my_textdata[i].substr(offset);
00092
00093
00094 if (endpos > end) {
00095 middlepart = middlepart.substr(0, middlepart.size() - (endpos-end));
00096 s = my_textdata[i].substr(end - pos, my_textdata[i].size() - (end - pos));
00097 PG_FontEngine::GetTextSize(middlepart, GetFont(), &w);
00098 PG_Widget::DrawText(x1+w, _y, s);
00099 }
00100
00101 SetFontColor(inv_color);
00102 PG_FontEngine::GetTextSize(middlepart, GetFont(), &w);
00103 if(_y < my_height) {
00104 SDL_Rect rect = {x + x1, y + _y, w, (_y + GetFontHeight() > my_height) ? my_height - _y : GetFontHeight()};
00105 SDL_FillRect(screen, &rect, highlightColor);
00106 }
00107 PG_Widget::DrawText(x1, _y, middlepart);
00108 SetFontColor(color);
00109 } else {
00110 PG_Widget::DrawText(_x, _y, my_textdata[i]);
00111 }
00112 _y += GetFontHeight();
00113 pos += my_textdata[i].size();
00114 }
00115
00116
00117 if(IsCursorVisible()) {
00118 DrawTextCursor();
00119 }
00120 }
00121
00122 void PG_MultiLineEdit::DrawTextCursor() {
00123
00124
00125 int x = my_xpos + 1;
00126 int y = my_ypos + 1;
00127 int xpos, ypos;
00128 GetCursorPos(xpos, ypos);
00129
00130
00131 if(!my_allowHiddenCursor) {
00132
00133 while (ypos < 0 && my_firstLine > 0) {
00134 SetVPosition(--my_firstLine);
00135 GetCursorPos(xpos, ypos);
00136 }
00137
00138
00139 while (ypos + GetFontHeight() > my_height && my_firstLine < my_vscroll->GetMaxRange()) {
00140 SetVPosition(++my_firstLine);
00141 GetCursorPos(xpos, ypos);
00142 }
00143 }
00144
00145
00146 if(my_srfTextCursor == NULL) {
00147 DrawVLine(xpos + 2, ypos + 2, GetFontHeight()-4, PG_Color());
00148 }
00149
00150 else {
00151 PG_Rect src, dst;
00152 PG_Rect rect(x + xpos, y + ypos + GetFontHeight()/2 - my_srfTextCursor->h/2,
00153 my_srfTextCursor->w, my_srfTextCursor->h);
00154 GetClipRects(src, dst, rect);
00155 PG_Widget::eventBlit(my_srfTextCursor, src, dst);
00156 }
00157 }
00158
00159 void PG_MultiLineEdit::FindWordRight() {
00160 unsigned int currentPos = my_cursorPosition;
00161
00162
00163 ++currentPos;
00164
00165
00166 while (currentPos-1 <= my_text.size() && my_text[currentPos-1] != ' ' && my_text[currentPos-1] != '\n') {
00167 ++currentPos;
00168 }
00169
00170
00171 while (currentPos <= my_text.size() && (my_text[currentPos] == ' ' || my_text[currentPos] == '\n')) {
00172 ++currentPos;
00173 }
00174
00175 SetCursorPos(currentPos);
00176 }
00177
00178 void PG_MultiLineEdit::FindWordLeft() {
00179 unsigned int currentPos = my_cursorPosition;
00180
00181
00182 while (currentPos-1 >= 0 && (my_text[currentPos-1] == ' ' || my_text[currentPos-1] == '\n')) {
00183 --currentPos;
00184 }
00185
00186
00187 while (currentPos-1 >= 0 && my_text[currentPos-1] != ' ' && my_text[currentPos-1] != '\n') {
00188 --currentPos;
00189 }
00190
00191 SetCursorPos(currentPos);
00192 }
00193
00194 void PG_MultiLineEdit::GetCursorTextPosFromScreen(int x, int y, unsigned int& horzOffset, unsigned int& lineOffset) {
00195
00196 if (my_textdata.size() == 0) {
00197 horzOffset = 0;
00198 lineOffset = 0;
00199 return;
00200 }
00201
00202
00203 int ypos = (y - my_ypos - 3) / GetFontHeight() + my_firstLine;
00204
00205
00206 if (ypos < 0) {
00207 ypos = 0;
00208 }
00209
00210 if ((unsigned int)ypos >= my_textdata.size()) {
00211 ypos = my_textdata.size()-1;
00212 }
00213
00214 unsigned int min = static_cast<unsigned int>(-1);
00215 unsigned int min_xpos = 0;
00216
00217
00218 PG_String temp;
00219
00220 for (Uint16 i = 0; i <= my_textdata[ypos].size(); ++i) {
00221
00222 temp = my_textdata[ypos].substr(0, i);
00223
00224
00225 Uint16 w;
00226 PG_FontEngine::GetTextSize(temp, GetFont(), &w);
00227 unsigned int dist = abs(x - (my_xpos + 3 + w));
00228
00229
00230 if (dist < min) {
00231 min = dist;
00232 min_xpos = i;
00233 }
00234 }
00235
00236
00237 horzOffset = min_xpos;
00238 lineOffset = static_cast<unsigned int>(ypos);
00239 }
00240
00241 void PG_MultiLineEdit::GetCursorTextPos(unsigned int& horzOffset, unsigned int& lineOffset) {
00242
00243 if (my_textdata.size() == 0) {
00244 horzOffset = 0;
00245 lineOffset = 0;
00246 return;
00247 }
00248
00249 unsigned int currentPos = my_cursorPosition;
00250 unsigned int line = 0;
00251
00252
00253 for (vector<PG_String>::iterator i = my_textdata.begin(); i != my_textdata.end(); ++i) {
00254 if(currentPos < i->size() )
00255 break;
00256
00257 if ( currentPos <= i->size() && currentPos > 0 && (*i)[currentPos-1] != '\n')
00258 break;
00259
00260 if ( !i->size() )
00261 break;
00262
00263 currentPos -= i->size();
00264 line++;
00265 }
00266
00267
00268 if (line >= my_textdata.size()) {
00269 line = my_textdata.size()-1;
00270 currentPos = my_textdata[line].size();
00271 }
00272
00273
00274 if (currentPos > my_textdata[line].size()) {
00275 currentPos = my_textdata[line].size();
00276 }
00277
00278 horzOffset = currentPos;
00279 lineOffset = line;
00280 }
00281
00282 void PG_MultiLineEdit::GetCursorPos(int& x, int& y) {
00283
00284 if (my_textdata.size() == 0) {
00285 x = 0;
00286 y = 0;
00287 return;
00288 }
00289
00290
00291 unsigned int currentPos, line;
00292 GetCursorTextPos(currentPos, line);
00293
00294
00295 PG_String temp = my_textdata[line].substr(0, currentPos);
00296
00297 Uint16 w;
00298 PG_FontEngine::GetTextSize(temp, GetFont(), &w);
00299
00300 x = w;
00301 y = (line - my_firstLine)*GetFontHeight();
00302 }
00303
00304 void PG_MultiLineEdit::CreateTextVector(bool bSetupVScroll) {
00305 int w = my_width - 6 - ((my_vscroll->IsVisible() || !my_vscroll->IsHidden()) ? my_vscroll->w : 0);
00306
00307
00308 my_textdata.clear();
00309 unsigned int start = 0, end = 0, last = 0;
00310
00311 PG_String temp;
00312
00313 do {
00314 Uint16 lineWidth = 0;
00315 temp = my_text.substr(start, end-start);
00316 PG_FontEngine::GetTextSize(temp, GetFont(), &lineWidth);
00317
00318 if (lineWidth > w) {
00319 if (last == start) {
00320 PG_String s = my_text.substr(start, end-start-1);
00321 my_textdata.push_back(s);
00322 start = --end;
00323 } else {
00324 temp = my_text.substr(start, last-start);
00325 my_textdata.push_back(temp);
00326 start = last;
00327 end = last-1;
00328 }
00329 last = start;
00330 } else if (my_text[end] == ' ') {
00331 last = end+1;
00332 } else if (my_text[end] == '\n' || my_text[end] == '\0' || end == my_text.size()-1) {
00333 temp = my_text.substr(start, end-start+1);
00334 my_textdata.push_back(temp);
00335 start = end+1;
00336 last = start;
00337 }
00338 } while (end++ < my_text.size());
00339
00340
00341 if(bSetupVScroll) {
00342 SetupVScroll();
00343 }
00344 }
00345
00346 void PG_MultiLineEdit::SetupVScroll() {
00347 if (my_textdata.size()*GetFontHeight() < my_height) {
00348 my_vscroll->SetRange(0, 0);
00349 my_vscroll->Hide();
00350 SetVPosition(0);
00351 CreateTextVector(false);
00352 } else {
00353 my_vscroll->SetRange(0, my_textdata.size() - my_height/GetFontHeight() + 1);
00354 my_vscroll->SetPageSize( my_height/GetFontHeight() );
00355 if (my_firstLine > my_vscroll->GetMaxRange()) {
00356 SetVPosition(my_vscroll->GetMaxRange());
00357 }
00358
00359 if (!my_vscroll->IsVisible() || my_vscroll->IsHidden()) {
00360
00361
00362 my_vscroll->Show();
00363 CreateTextVector(false);
00364 }
00365 }
00366 }
00367
00368 bool PG_MultiLineEdit::eventKeyDown(const SDL_KeyboardEvent* key) {
00369 PG_Char c;
00370
00371 if(!IsCursorVisible()) {
00372 return false;
00373 }
00374
00375 SDL_KeyboardEvent key_copy = *key;
00376 PG_Application::TranslateNumpadKeys(&key_copy);
00377
00378 if ((key_copy.keysym.mod & KMOD_SHIFT) && my_mark == -1) {
00379 my_mark = my_cursorPosition;
00380 }
00381
00382 if(key_copy.keysym.mod & KMOD_CTRL) {
00383 switch(key_copy.keysym.sym) {
00384 case SDLK_HOME:
00385 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00386 my_mark = -1;
00387 }
00388 SetCursorPos(0);
00389 return true;
00390
00391 case SDLK_END:
00392 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00393 my_mark = -1;
00394 }
00395 SetCursorPos(my_text.length());
00396 return true;
00397
00398 case SDLK_RIGHT:
00399 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00400 my_mark = -1;
00401 }
00402 FindWordRight();
00403 return true;
00404
00405 case SDLK_LEFT:
00406 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00407 my_mark = -1;
00408 }
00409 FindWordLeft();
00410 return true;
00411
00412 case SDLK_UP:
00413 my_allowHiddenCursor = true;
00414 SetVPosition(--my_firstLine);
00415 return true;
00416
00417 case SDLK_DOWN:
00418 my_allowHiddenCursor = true;
00419 SetVPosition(++my_firstLine);
00420 return true;
00421
00422 default:
00423 break;
00424 }
00425 } else if(key_copy.keysym.mod & (KMOD_ALT | KMOD_META)) {}
00426 else {
00427 unsigned int currentPos, line;
00428 int x, y;
00429
00430 switch(key_copy.keysym.sym) {
00431 case SDLK_LEFT:
00432 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00433 my_mark = -1;
00434 }
00435 PG_LineEdit::SetCursorPos(--my_cursorPosition);
00436 return true;
00437
00438 case SDLK_RIGHT:
00439 if(!(key_copy.keysym.mod & KMOD_SHIFT)) {
00440 my_mark = -1;
00441 }
00442 PG_LineEdit::SetCursorPos(++my_cursorPosition);
00443 return true;
00444
00445 case SDLK_UP:
00446 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00447 my_mark = -1;
00448 }
00449 GetCursorPos(x, y);
00450 GetCursorTextPosFromScreen(my_xpos + x + 3, my_ypos + y + 3 - GetFontHeight(), currentPos, line);
00451 SetCursorTextPos(currentPos, line);
00452 return true;
00453
00454 case SDLK_DOWN:
00455 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00456 my_mark = -1;
00457 }
00458 GetCursorPos(x, y);
00459 GetCursorTextPosFromScreen(my_xpos + x + 3, my_ypos + y + 3 + GetFontHeight(), currentPos, line);
00460 SetCursorTextPos(currentPos, line);
00461 return true;
00462
00463 case SDLK_PAGEUP:
00464 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00465 my_mark = -1;
00466 }
00467 GetCursorPos(x, y);
00468 GetCursorTextPosFromScreen(my_xpos + x + 3, my_ypos + y + 3 - (my_height - GetFontHeight()), currentPos, line);
00469 SetCursorTextPos(currentPos, line);
00470 return true;
00471
00472 case SDLK_PAGEDOWN:
00473 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00474 my_mark = -1;
00475 }
00476
00477 GetCursorPos(x, y);
00478 GetCursorTextPosFromScreen(my_xpos + x + 3, my_ypos + y + 3 + (my_height - GetFontHeight()), currentPos, line);
00479 SetCursorTextPos(currentPos, line);
00480 return true;
00481
00482 case SDLK_HOME:
00483 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00484 my_mark = -1;
00485 }
00486 GetCursorTextPos(currentPos, line);
00487 SetCursorTextPos(0, line);
00488 return true;
00489
00490 case SDLK_END: {
00491 if (!(key_copy.keysym.mod & KMOD_SHIFT)) {
00492 my_mark = -1;
00493 }
00494 GetCursorTextPos(currentPos, line);
00495 int cursorPos = my_textdata[line].size() - (my_textdata[line][my_textdata[line].size()-1] == '\n' ? 1 : 0);
00496 SetCursorTextPos(cursorPos, line);
00497 }
00498 return true;
00499
00500 case SDLK_KP_ENTER:
00501 case SDLK_RETURN:
00502 c = '\n';
00503 InsertChar(c);
00504 SetCursorPos(my_cursorPosition);
00505 return true;
00506
00507 default:
00508 break;
00509 }
00510 }
00511
00512 return PG_LineEdit::eventKeyDown(key);
00513 }
00514
00515 bool PG_MultiLineEdit::eventMouseButtonDown(const SDL_MouseButtonEvent* button) {
00516
00517 if ((button->button == 4 || button->button == 5) && my_vscroll->IsVisible()) {
00518 if (button->button == 4) {
00519 SetVPosition(--my_firstLine);
00520 } else {
00521 SetVPosition(++my_firstLine);
00522 }
00523 return true;
00524 }
00525
00526 if (!GetEditable()) {
00527 return false;
00528 }
00529
00530 if (!IsCursorVisible()) {
00531 EditBegin();
00532 }
00533
00534
00535 if (my_vscroll->IsVisible() && button->x > my_xpos + my_width - my_vscroll->w) {
00536 return false;
00537 }
00538
00539 if (button->button == 1) {
00540 Uint8* keys = SDL_GetKeyState(NULL);
00541
00542 if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) {
00543 my_mark = -1;
00544 }
00545
00546 unsigned int currentPos, line;
00547 GetCursorTextPosFromScreen(button->x, button->y, currentPos, line);
00548 SetCursorTextPos(currentPos, line);
00549 if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) {
00550 my_mark = my_cursorPosition;
00551 }
00552 }
00553
00554 return true;
00555 }
00556
00557 bool PG_MultiLineEdit::eventMouseMotion(const SDL_MouseMotionEvent* motion) {
00558 if (motion->state & SDL_BUTTON(1)) {
00559 unsigned int currentPos, line;
00560 GetCursorTextPosFromScreen(motion->x, motion->y, currentPos, line);
00561 SetCursorTextPos(currentPos, line);
00562 }
00563
00564 return PG_LineEdit::eventMouseMotion(motion);
00565 }
00566
00567 bool PG_MultiLineEdit::eventMouseButtonUp(const SDL_MouseButtonEvent* button) {
00568 return true;
00569 }
00570
00571 void PG_MultiLineEdit::SetCursorTextPos(unsigned int offset, unsigned int line) {
00572 my_allowHiddenCursor = false;
00573 if (line < 0) {
00574 SetCursorPos(0);
00575 } else if (line >= my_textdata.size()) {
00576 SetCursorPos(my_text.size());
00577 my_isCursorAtEOL = false;
00578 } else {
00579 PG_LineEdit::SetCursorPos(ConvertCursorPos(offset, line));
00580 my_isCursorAtEOL = (offset == my_textdata[line].size() && my_textdata[line].size() != 0);
00581 Update();
00582 }
00583 }
00584
00585 int PG_MultiLineEdit::ConvertCursorPos(unsigned int offset, unsigned int line) {
00586 unsigned int charCount = 0;
00587 for (unsigned int i = 0; i < line; ++i) {
00588 charCount += my_textdata[i].size();
00589 }
00590
00591 return charCount+offset;
00592 }
00593
00594 void PG_MultiLineEdit::SetCursorPos(int p) {
00595 my_isCursorAtEOL = false;
00596 my_allowHiddenCursor = false;
00597 PG_LineEdit::SetCursorPos(p);
00598
00599 }
00600
00601 void PG_MultiLineEdit::InsertText(const std::string& c) {
00602 my_allowHiddenCursor = false;
00603 if (my_mark != -1 && my_mark != my_cursorPosition) {
00604 DeleteSelection();
00605 }
00606
00607 PG_Application::BulkModeActivator bulk;
00608 for ( int i = 0; i < c.length(); ++i )
00609 PG_LineEdit::InsertChar(c[i]);
00610 bulk.disable();
00611 my_mark = -1;
00612 CreateTextVector();
00613 Update();
00614 }
00615
00616 void PG_MultiLineEdit::InsertChar(const PG_Char& c) {
00617 std::string s;
00618 s += c;
00619 InsertText( s );
00620 }
00621
00622
00623 void PG_MultiLineEdit::DeleteChar(Uint16 pos) {
00624 my_allowHiddenCursor = false;
00625 if (my_mark != -1 && my_mark != my_cursorPosition) {
00626 Uint16 oldpos = my_cursorPosition;
00627 DeleteSelection();
00628
00629 if (pos == oldpos-1) {
00630 my_cursorPosition++;
00631 }
00632 } else {
00633 PG_LineEdit::DeleteChar(pos);
00634 }
00635
00636 my_mark = -1;
00637 CreateTextVector();
00638
00639 }
00640
00641 void PG_MultiLineEdit::DeleteSelection() {
00642 if (my_mark != -1 && my_mark != my_cursorPosition) {
00643 int start = (my_cursorPosition < my_mark ? my_cursorPosition : my_mark);
00644 int end = (my_cursorPosition >= my_mark ? my_cursorPosition : my_mark);
00645 my_text.erase(start, end-start);
00646 if (my_mark < my_cursorPosition) {
00647 SetCursorPos(my_mark);
00648 }
00649 my_mark = -1;
00650 }
00651 }
00652
00653 void PG_MultiLineEdit::SetText(const std::string& new_text) {
00654 PG_LineEdit::SetText(new_text);
00655 CreateTextVector();
00656 my_isCursorAtEOL = false;
00657 my_allowHiddenCursor = false;
00658 my_mark = -1;
00659 SetVPosition(0);
00660 }