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 #include "wgui_include_config.h"
00026 #include "wg_editbox.h"
00027 #include "wg_message_server.h"
00028 #include "wg_application.h"
00029 #include "wg_timer.h"
00030 #include "wg_error.h"
00031 #include "wg_view.h"
00032 #include "wutil_debug.h"
00033 #include "std_ex.h"
00034 #include <string>
00035
00036 namespace wGui
00037 {
00038
00039 CEditBox::CEditBox(const CRect& WindowRect, CWindow* pParent, CFontEngine* pFontEngine) :
00040 CWindow(WindowRect, pParent),
00041 m_SelStart(0),
00042 m_SelLength(0),
00043 m_DragStart(0),
00044 m_ScrollOffset(0),
00045 m_bReadOnly(false),
00046 m_bMouseDown(false),
00047 m_bUseMask(false),
00048 m_bLastMouseMoveInside(false),
00049 m_bDrawCursor(true)
00050 {
00051 m_BackgroundColor = COLOR_WHITE;
00052 m_ClientRect.Grow(-4);
00053 if (pFontEngine)
00054 {
00055 m_pFontEngine = pFontEngine;
00056 }
00057 else
00058 {
00059 m_pFontEngine = CApplication::Instance()->GetDefaultFontEngine();
00060 }
00061 m_pDblClickTimer = new CTimer();
00062 m_pCursorTimer = new CTimer(this);
00063 std::auto_ptr<CRenderedString> pRenderedString(new CRenderedString(
00064 m_pFontEngine, L"", CRenderedString::VALIGN_NORMAL, CRenderedString::HALIGN_LEFT));
00065 m_pRenderedString = pRenderedString;
00066 CMessageServer::Instance().RegisterMessageClient(this, CMessage::KEYBOARD_KEYDOWN);
00067 CMessageServer::Instance().RegisterMessageClient(this, CMessage::MOUSE_BUTTONUP);
00068 CMessageServer::Instance().RegisterMessageClient(this, CMessage::MOUSE_MOVE);
00069 CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_DOUBLELCLICK);
00070 CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_TIMER);
00071 CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_GAININGKEYFOCUS);
00072 CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_LOSINGKEYFOCUS);
00073 Draw();
00074 }
00075
00076
00077 CEditBox::~CEditBox(void)
00078 {
00079 delete m_pCursorTimer;
00080 delete m_pDblClickTimer;
00081 }
00082
00083
00084 void CEditBox::SetReadOnly(bool bReadOnly)
00085 {
00086 m_BackgroundColor = bReadOnly ? COLOR_LIGHTGRAY : COLOR_WHITE;
00087 m_bReadOnly = bReadOnly;
00088 Draw();
00089 }
00090
00091
00092 std::wstring CEditBox::GetSelText(void) const
00093 {
00094 std::wstring sSelText = L"";
00095 if (m_SelLength != 0 && !m_bUseMask)
00096 {
00097 std::string::size_type SelStartNorm = 0;
00098 std::string::size_type SelLenNorm = 0;
00099 if (m_SelLength < 0)
00100 {
00101 SelStartNorm = m_SelLength + m_SelStart;
00102 SelLenNorm = abs(m_SelLength);
00103 }
00104 else
00105 {
00106 SelStartNorm = m_SelStart;
00107 SelLenNorm = m_SelLength;
00108 }
00109 sSelText = m_sWindowText.substr(SelStartNorm, SelLenNorm);
00110 }
00111 return sSelText;
00112 }
00113
00114
00115 void CEditBox::SetSelection(std::wstring::size_type iSelStart, int iSelLength)
00116 {
00117 if (iSelStart < m_sWindowText.length())
00118 {
00119 m_SelStart = iSelStart;
00120 if (iSelStart + iSelLength <= m_sWindowText.length())
00121 m_SelLength = iSelLength;
00122 else
00123 m_SelLength = stdex::safe_static_cast<int>(m_sWindowText.length() - iSelStart);
00124 }
00125 else
00126 {
00127 m_SelStart = 0;
00128 m_SelLength = 0;
00129 }
00130 }
00131
00132
00133 std::wstring::size_type CEditBox::GetIndexFromPoint(const CPoint& Point) const
00134 {
00135 CPoint Offset;
00136 std::vector<CRect> CharRects;
00137 m_pRenderedString->GetMetrics(0, &Offset, &CharRects);
00138 CRect SubRect(m_WindowRect.SizeRect());
00139 SubRect.Grow(-3);
00140 std::wstring::size_type index = 0;
00141 CPoint BoundedPoint = Point;
00142 if (BoundedPoint.XPos() < SubRect.Left()) {
00143 BoundedPoint.SetX(SubRect.Left());
00144 }
00145 if (BoundedPoint.XPos() > SubRect.Right()) {
00146 BoundedPoint.SetX(SubRect.Right());
00147 }
00148 if (!CharRects.empty())
00149 {
00150 int xDelta = abs(BoundedPoint.XPos() - (CharRects.front().Left() + Offset.XPos() + SubRect.Left()));
00151 for (unsigned int i = 0; i < m_pRenderedString->GetLength(); ++i)
00152 {
00153 if (abs(BoundedPoint.XPos() - (CharRects.at(i).Right() + Offset.XPos() + SubRect.Left() + m_ScrollOffset)) < xDelta)
00154 {
00155 xDelta = abs(BoundedPoint.XPos() - (CharRects.at(i).Right() + Offset.XPos() + SubRect.Left() + m_ScrollOffset));
00156 index = i + 1;
00157 }
00158 }
00159 }
00160
00161 return index;
00162 }
00163
00164
00165 void CEditBox::Draw(void) const
00166 {
00167 CWindow::Draw();
00168
00169 if (m_pSDLSurface)
00170 {
00171 CPainter Painter(m_pSDLSurface, CPainter::PAINT_REPLACE);
00172 CRect SubRect(m_WindowRect.SizeRect());
00173 SubRect.Grow(-3);
00174 Painter.DrawRect(m_WindowRect.SizeRect(), false, COLOR_BLACK);
00175 CPoint FontCenterPoint = m_WindowRect.SizeRect().Center();
00176
00177 if (m_bUseMask)
00178 {
00179 m_pRenderedString->SetMaskChar(L'*');
00180 }
00181 else
00182 {
00183 m_pRenderedString->SetMaskChar(L' ');
00184 }
00185
00186 CRGBColor FontColor = m_bReadOnly ? DEFAULT_DISABLED_LINE_COLOR : DEFAULT_LINE_COLOR;
00187 if (CApplication::Instance()->GetKeyFocus() == dynamic_cast<const CWindow*>(this) && !m_bReadOnly)
00188 {
00189 CPoint BoundedDims;
00190 CPoint Offset;
00191 std::vector<CRect> CharRects;
00192 m_pRenderedString->GetMetrics(&BoundedDims, &Offset, &CharRects);
00193
00194 std::wstring::size_type SelStartNorm = 0;
00195 std::wstring::size_type SelLenNorm = abs(m_SelLength);
00196 if (m_SelLength < 0)
00197 {
00198 SelStartNorm = m_SelStart + m_SelLength;
00199 }
00200 else
00201 {
00202 SelStartNorm = m_SelStart;
00203 }
00204
00205
00206
00207 if (! m_bMouseDown)
00208 {
00209 if (CharRects.empty() || BoundedDims.XPos() < SubRect.Width())
00210 {
00211 m_ScrollOffset = 0;
00212 }
00213 else
00214 {
00215 int iCursorPos = 0;
00216 if (m_SelStart + m_SelLength >= CharRects.size())
00217 {
00218 iCursorPos = CharRects.back().Right() + Offset.XPos() + SubRect.Left() + m_ScrollOffset;
00219 }
00220 else
00221 {
00222 iCursorPos = CharRects.at(m_SelStart + m_SelLength).Left() + Offset.XPos() + SubRect.Left() + m_ScrollOffset;
00223 }
00224 if (iCursorPos < SubRect.Left())
00225 {
00226 m_ScrollOffset = -(iCursorPos - m_ScrollOffset - SubRect.Left() - Offset.XPos());
00227 }
00228 else if (iCursorPos > SubRect.Right())
00229 {
00230 m_ScrollOffset = -(iCursorPos - m_ScrollOffset - SubRect.Left() - Offset.XPos() - SubRect.Width() + 1);
00231 }
00232
00233 if (m_ScrollOffset < 0 && (CharRects.back().Right() + Offset.XPos() + SubRect.Left() + m_ScrollOffset < SubRect.Right()))
00234 {
00235 m_ScrollOffset = SubRect.Right() - CharRects.back().Right() - 1;
00236 if (m_ScrollOffset > 0)
00237 {
00238 m_ScrollOffset = 0;
00239 }
00240 }
00241 }
00242 }
00243
00244
00245 if (m_SelLength != 0)
00246 {
00247 CRect SelRect;
00248 SelRect.SetBottom(SubRect.Bottom());
00249 SelRect.SetTop(SubRect.Top());
00250 SelRect.SetLeft(CharRects.at(SelStartNorm).Left() + Offset.XPos() + SubRect.Left() + m_ScrollOffset);
00251 SelRect.SetRight(CharRects.at(SelLenNorm + SelStartNorm - 1).Right() + Offset.XPos() + SubRect.Left() + m_ScrollOffset);
00252 SelRect.ClipTo(SubRect);
00253 Painter.DrawRect(SelRect, true, CApplication::Instance()->GetDefaultSelectionColor(), CApplication::Instance()->GetDefaultSelectionColor());
00254 }
00255 else if (m_bDrawCursor)
00256 {
00257
00258 int CursorPos = Offset.XPos() + SubRect.Left() + m_ScrollOffset;
00259 if (m_SelStart + m_SelLength >= CharRects.size() && !CharRects.empty())
00260 {
00261 CursorPos += CharRects.back().Right();
00262 }
00263 else if (m_SelStart + m_SelLength >= 0 && m_SelStart + m_SelLength < CharRects.size())
00264 {
00265 CursorPos += CharRects.at(m_SelStart + m_SelLength).Left();
00266 }
00267 if (CursorPos >= SubRect.Left() && CursorPos <= SubRect.Right())
00268 {
00269 Painter.DrawVLine(SubRect.Top(), SubRect.Bottom(), CursorPos, COLOR_BLACK);
00270 }
00271 }
00272 }
00273
00274 if (m_pRenderedString.get())
00275 {
00276 m_pRenderedString->Draw(m_pSDLSurface, SubRect,
00277 CPoint(SubRect.Left() + m_ScrollOffset, SubRect.Bottom() - m_pRenderedString->GetMaxFontHeight() / 4), FontColor);
00278 }
00279 }
00280 }
00281
00282
00283 void CEditBox::SetWindowText(const std::wstring& sText)
00284 {
00285 m_SelStart = 0;
00286 m_SelLength = 0;
00287 std::auto_ptr<CRenderedString> pRenderedString(new CRenderedString(
00288 m_pFontEngine, sText, CRenderedString::VALIGN_NORMAL, CRenderedString::HALIGN_LEFT));
00289 m_pRenderedString = pRenderedString;
00290 CWindow::SetWindowText(sText);
00291 }
00292
00293
00294 bool CEditBox::OnMouseButtonDown(CPoint Point, unsigned int Button)
00295 {
00296 bool bResult = CWindow::OnMouseButtonDown(Point, Button);
00297
00298 CPoint WindowPoint(ViewToWindow(Point));
00299 if (!bResult && m_bVisible && (Button == CMouseMessage::LEFT) &&
00300 !m_bReadOnly && (m_ClientRect.HitTest(WindowPoint) == CRect::RELPOS_INSIDE))
00301 {
00302 bool fSkipCursorPositioning = false;
00303
00304 if (!m_pDblClickTimer->IsRunning())
00305 {
00306 m_pDblClickTimer->StartTimer(500, false);
00307 }
00308 else
00309 {
00310
00311
00312 CMessageServer::Instance().QueueMessage(new TIntMessage(CMessage::CTRL_DOUBLELCLICK, this, this, 0));
00313 m_pDblClickTimer->StopTimer();
00314 fSkipCursorPositioning = true;
00315 }
00316 if (CApplication::Instance()->GetKeyFocus() != this)
00317 {
00318 CApplication::Instance()->SetKeyFocus(this);
00319 }
00320
00321 if (!fSkipCursorPositioning)
00322 {
00323 m_SelStart = GetIndexFromPoint(WindowPoint);
00324 m_DragStart = m_SelStart;
00325 m_SelLength = 0;
00326 m_bMouseDown = true;
00327 Draw();
00328 bResult = true;
00329 }
00330 }
00331 return bResult;
00332 }
00333
00334
00335 bool CEditBox::HandleMessage(CMessage* pMessage)
00336 {
00337 bool bHandled = false;
00338 CRect SubRect(m_WindowRect);
00339 SubRect.Grow(-3);
00340
00341 if (pMessage)
00342 {
00343 switch(pMessage->MessageType())
00344 {
00345 case CMessage::CTRL_DOUBLELCLICK:
00346 if (pMessage->Destination() == this)
00347 {
00348
00349 m_SelStart = 0;
00350 m_SelLength = stdex::safe_static_cast<int>(m_sWindowText.length());
00351 Draw();
00352 bHandled = true;
00353 }
00354 break;
00355 case CMessage::MOUSE_BUTTONUP:
00356 m_bMouseDown = false;
00357 break;
00358 case CMessage::MOUSE_MOVE:
00359 {
00360 CMouseMessage* pMouseMessage = dynamic_cast<CMouseMessage*>(pMessage);
00361 if (pMouseMessage && m_bVisible && !m_bReadOnly)
00362 {
00363 CPoint WindowPoint(ViewToWindow(pMouseMessage->Point));
00364
00365
00366
00367
00368
00369 CView* pView = GetView();
00370 bool bHitFloating = pView && pView->GetFloatingWindow() && pView->GetFloatingWindow()->HitTest(pMouseMessage->Point);
00371 if (m_ClientRect.HitTest(WindowPoint) == CRect::RELPOS_INSIDE && !bHitFloating && !m_bLastMouseMoveInside)
00372 {
00373 m_bLastMouseMoveInside = true;
00374 CwgCursorResourceHandle IBeamHandle(WGRES_IBEAM_CURSOR);
00375 CApplication::Instance()->SetMouseCursor(&IBeamHandle);
00376 }
00377 else if ((m_ClientRect.HitTest(WindowPoint) != CRect::RELPOS_INSIDE || bHitFloating) && m_bLastMouseMoveInside)
00378 {
00379 m_bLastMouseMoveInside= false;
00380 CApplication::Instance()->SetMouseCursor();
00381 }
00382
00383 if (m_bMouseDown)
00384 {
00385 std::wstring::size_type CursorPos = GetIndexFromPoint(WindowPoint);
00386
00387 if (CursorPos < m_DragStart)
00388 {
00389 m_SelLength = stdex::safe_static_cast<int>(m_DragStart) - stdex::safe_static_cast<int>(CursorPos);
00390 m_SelStart = CursorPos;
00391 }
00392 else
00393 {
00394 m_SelStart = m_DragStart;
00395 m_SelLength = stdex::safe_static_cast<int>(CursorPos) - stdex::safe_static_cast<int>(m_SelStart);
00396 }
00397 bHandled = true;
00398 Draw();
00399 }
00400 }
00401 break;
00402 }
00403 case CMessage::CTRL_TIMER:
00404 if (pMessage->Destination() == this && pMessage->Source() == m_pCursorTimer)
00405 {
00406
00407 if (m_SelLength == 0)
00408 {
00409 m_bDrawCursor = !m_bDrawCursor;
00410 Draw();
00411 }
00412 bHandled = true;
00413 }
00414 break;
00415 case CMessage::CTRL_GAININGKEYFOCUS:
00416 if (pMessage->Destination() == this)
00417 {
00418 m_pCursorTimer->StartTimer(750, true);
00419 m_bDrawCursor = true;
00420 Draw();
00421 bHandled = true;
00422 }
00423 break;
00424 case CMessage::CTRL_LOSINGKEYFOCUS:
00425 if (pMessage->Destination() == this)
00426 {
00427 m_pCursorTimer->StopTimer();
00428 Draw();
00429 bHandled = true;
00430 }
00431 break;
00432 case CMessage::KEYBOARD_KEYDOWN:
00433 if (m_bVisible)
00434 {
00435 CKeyboardMessage* pKeyboardMessage = dynamic_cast<CKeyboardMessage*>(pMessage);
00436 if (pKeyboardMessage && pMessage->Destination() == this && !m_bReadOnly)
00437 {
00438 std::wstring sBuffer = m_sWindowText;
00439
00440 switch(pKeyboardMessage->Key)
00441 {
00442 case SDLK_BACKSPACE:
00443 if (m_SelLength > 0)
00444 {
00445 SelDelete(&sBuffer);
00446 }
00447 else
00448 {
00449 if (m_SelStart > 0)
00450 {
00451 sBuffer.erase(--m_SelStart, 1);
00452 }
00453 }
00454 break;
00455
00456 case SDLK_DELETE:
00457 if (m_SelStart < sBuffer.length())
00458 {
00459 if (m_SelLength > 0)
00460 {
00461 SelDelete(&sBuffer);
00462 }
00463 else
00464 {
00465 sBuffer.erase(m_SelStart, 1);
00466 }
00467 }
00468 break;
00469
00470 case SDLK_LEFT:
00471 if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00472 {
00473 if (m_SelStart > 0)
00474 {
00475 if ((m_SelLength > 0) || ((m_SelStart - abs(m_SelLength)) > 0))
00476 {
00477 if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00478 {
00479 std::wstring::size_type pos = sBuffer.rfind(L" ", (m_SelStart + m_SelLength) - 1);
00480 if (pos != std::string::npos)
00481 {
00482 m_SelLength = stdex::safe_static_cast<int>(pos) - stdex::safe_static_cast<int>(m_SelStart);
00483 }
00484 else
00485 {
00486 m_SelLength = stdex::safe_static_cast<int>(m_SelStart) * -1;
00487 }
00488 }
00489 else
00490 {
00491 m_SelLength--;
00492 }
00493 }
00494 }
00495 }
00496 else if (m_SelLength != 0)
00497 {
00498 if (m_SelLength < 0)
00499 {
00500 m_SelStart = m_SelStart + m_SelLength;
00501 }
00502 m_SelLength = 0;
00503
00504 }
00505 else if (m_SelStart > 0)
00506 {
00507 if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00508 {
00509 std::wstring::size_type pos = sBuffer.rfind(L" ", m_SelStart - 1);
00510 if (pos != std::string::npos)
00511 {
00512 m_SelStart = pos;
00513 }
00514 else
00515 {
00516 m_SelStart = 0;
00517 }
00518 }
00519 else
00520 {
00521 --m_SelStart;
00522 }
00523 m_SelLength = 0;
00524 }
00525 break;
00526 case SDLK_RIGHT:
00527 if (m_SelStart <= sBuffer.length())
00528 {
00529 if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00530 {
00531 if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00532 {
00533 std::wstring::size_type pos = sBuffer.find(L" ", m_SelStart + m_SelLength);
00534 if (pos != std::string::npos)
00535 {
00536 m_SelLength = stdex::safe_static_cast<int>(pos) - stdex::safe_static_cast<int>(m_SelStart) + 1;
00537 }
00538 else
00539 {
00540 m_SelLength = stdex::safe_static_cast<int>(sBuffer.length()) - stdex::safe_static_cast<int>(m_SelStart);
00541 }
00542 }
00543 else if (m_SelStart + m_SelLength < sBuffer.length())
00544 {
00545 m_SelLength++;
00546 }
00547 }
00548 else if(m_SelLength == 0 && m_SelStart < sBuffer.length())
00549 {
00550
00551
00552
00553
00554 if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00555 {
00556 std::wstring::size_type pos = sBuffer.find(L" ", m_SelStart + 1);
00557 if (pos != std::string::npos)
00558 {
00559 m_SelStart = pos + 1;
00560 }
00561 else
00562 {
00563 m_SelStart = sBuffer.length();
00564 }
00565 }
00566 else
00567 {
00568 ++m_SelStart;
00569 }
00570 }
00571 else
00572 {
00573 if (m_SelLength > 0)
00574 {
00575 m_SelStart = m_SelStart + m_SelLength;
00576 }
00577 m_SelLength = 0;
00578 }
00579 }
00580 break;
00581 case SDLK_END:
00582 if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00583 {
00584 m_SelLength = stdex::safe_static_cast<int>(sBuffer.length()) - stdex::safe_static_cast<int>(m_SelStart);
00585 }
00586 else
00587 {
00588 m_SelLength = 0;
00589 m_SelStart = sBuffer.length();
00590 }
00591 break;
00592 case SDLK_HOME:
00593 if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00594 {
00595 m_SelLength = stdex::safe_static_cast<int>(m_SelStart);
00596 m_SelStart = 0;
00597 }
00598 else
00599 {
00600 m_SelLength = 0;
00601 m_SelStart = 0;
00602 }
00603 break;
00604 default:
00605 if (pKeyboardMessage->Unicode)
00606 {
00607 SelDelete(&sBuffer);
00608 sBuffer.insert(m_SelStart++, 1, stdex::safe_static_cast<wchar_t>(pKeyboardMessage->Unicode));
00609 }
00610 break;
00611 }
00612
00613 if (m_sWindowText != sBuffer)
00614 {
00615 CMessageServer::Instance().QueueMessage(new TStringMessage(CMessage::CTRL_VALUECHANGE, m_pParentWindow, this, sBuffer));
00616 }
00617 m_sWindowText = sBuffer;
00618 CWindow::SetWindowText(sBuffer);
00619
00620 std::auto_ptr<CRenderedString> pRenderedString(new CRenderedString(
00621 m_pFontEngine, sBuffer, CRenderedString::VALIGN_NORMAL, CRenderedString::HALIGN_LEFT));
00622
00623 m_pRenderedString = pRenderedString;
00624 m_bDrawCursor = true;
00625 Draw();
00626 }
00627 break;
00628 }
00629 default :
00630 bHandled = CWindow::HandleMessage(pMessage);
00631 break;
00632 }
00633 }
00634
00635 return bHandled;
00636 }
00637
00638
00639 void CEditBox::SelDelete(std::wstring* psString)
00640 {
00641
00642 if (m_SelLength != 0)
00643 {
00644 std::wstring::size_type SelStartNorm = 0;
00645 std::wstring::size_type SelLenNorm = 0;
00646 if (m_SelLength < 0)
00647 {
00648 SelStartNorm = m_SelLength + m_SelStart;
00649 SelLenNorm = abs(m_SelLength);
00650 }
00651 else
00652 {
00653 SelStartNorm = m_SelStart;
00654 SelLenNorm = m_SelLength;
00655 }
00656
00657 psString->erase(SelStartNorm, SelLenNorm);
00658
00659 m_SelStart = SelStartNorm;
00660 m_SelLength = 0;
00661 }
00662 }
00663
00664 }