wg_textbox.cpp

00001 // wg_textbox.cpp
00002 //
00003 // CTextBox class implementation
00004 //
00005 //
00006 // Copyright (c) 2002-2004 Rob Wiskow
00007 // rob-dev@boxedchaos.com
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00022 //
00023 
00024 
00025 #include "wgui_include_config.h"
00026 #include "wg_textbox.h"
00027 #include "wg_message_server.h"
00028 #include "wg_application.h"
00029 #include "wg_view.h"
00030 #include "wg_error.h"
00031 #include "wutil_debug.h"
00032 #include "std_ex.h"
00033 #include <string>
00034 
00035 namespace wGui
00036 {
00037 
00038 CTextBox::CTextBox(const CRect& WindowRect, CWindow* pParent, CFontEngine* pFontEngine) :
00039      CWindow(WindowRect, pParent),
00040      m_SelStart(0),
00041      m_SelLength(0),
00042      m_DragStart(0),
00043      m_bReadOnly(false),
00044      m_bMouseDown(false),
00045      m_bLastMouseMoveInside(false),
00046      m_pVerticalScrollBar(0),
00047      m_pHorizontalScrollBar(0),
00048      m_iLineCount(0),
00049      m_iRowHeight(0),
00050      m_iMaxWidth(0),
00051      m_bDrawCursor(true),
00052      m_bScrollToCursor(false)
00053  {
00054      m_BackgroundColor = COLOR_WHITE;
00055      m_ClientRect = CRect(3, 3, m_WindowRect.Width() - 17, m_WindowRect.Height() - 17);
00056      if (pFontEngine)
00057      {
00058           m_pFontEngine = pFontEngine;
00059      }
00060      else
00061      {
00062           m_pFontEngine = CApplication::Instance()->GetDefaultFontEngine();
00063      }
00064 
00065      m_pDblClickTimer = new CTimer();
00066      m_pCursorTimer = new CTimer(this);
00067      m_ClientRect.Grow(-3);
00068      m_pVerticalScrollBar = new CScrollBar(
00069           CRect(m_WindowRect.Width() - 12, 1, m_WindowRect.Width(), m_WindowRect.Height() - 12)  - m_ClientRect.TopLeft(), this, CScrollBar::VERTICAL);
00070      m_pHorizontalScrollBar = new CScrollBar(
00071           CRect(1, m_WindowRect.Height() - 12, m_WindowRect.Width() - 12, m_WindowRect.Height())  - m_ClientRect.TopLeft(), this, CScrollBar::HORIZONTAL);
00072      m_ScrollBarVisibilityMap[CScrollBar::VERTICAL] = SCROLLBAR_VIS_AUTO;
00073      m_ScrollBarVisibilityMap[CScrollBar::HORIZONTAL] = SCROLLBAR_VIS_AUTO;
00074      PrepareWindowText(L"");
00075 
00076      CMessageServer::Instance().RegisterMessageClient(this, CMessage::KEYBOARD_KEYDOWN);
00077      CMessageServer::Instance().RegisterMessageClient(this, CMessage::MOUSE_BUTTONUP);
00078      CMessageServer::Instance().RegisterMessageClient(this, CMessage::MOUSE_MOVE);
00079      CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_VALUECHANGE);
00080      CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_VALUECHANGING);
00081      CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_DOUBLELCLICK);
00082      CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_TIMER);
00083      CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_GAININGKEYFOCUS);
00084      CMessageServer::Instance().RegisterMessageClient(this, CMessage::CTRL_LOSINGKEYFOCUS);
00085      Draw();
00086 }
00087 
00088 
00089 CTextBox::~CTextBox(void)  // virtual
00090 {
00091      for(std::vector<CRenderedString*>::iterator iter = m_vpRenderedString.begin(); iter !=  m_vpRenderedString.end(); ++iter)
00092           delete *iter;
00093      m_vpRenderedString.clear();
00094 
00095      delete m_pCursorTimer;
00096      delete m_pDblClickTimer;
00097 }
00098 
00099 
00100 void CTextBox::SetReadOnly(bool bReadOnly)  // virtual
00101 {
00102      m_BackgroundColor = bReadOnly ? COLOR_LIGHTGRAY : COLOR_WHITE;
00103      m_bReadOnly = bReadOnly;
00104      Draw();
00105 }
00106 
00107 
00108 std::wstring CTextBox::GetSelText(void) const  // virtual
00109 {
00110      std::wstring sSelText = L"";
00111      if (m_SelLength != 0)
00112      {
00113                std::wstring::size_type SelStartNorm = 0;
00114                std::wstring::size_type SelLenNorm = abs(m_SelLength);
00115                if (m_SelLength < 0)
00116                {
00117                     SelStartNorm   = m_SelStart + m_SelLength;
00118                }
00119                else
00120                {
00121                     SelStartNorm   = m_SelStart;
00122                }
00123                sSelText = m_sWindowText.substr(SelStartNorm, SelLenNorm);
00124      }
00125 
00126      return sSelText;
00127 }
00128 
00129 
00130 void CTextBox::SetSelection(std::wstring::size_type iSelStart, int iSelLength)  // virtual
00131 {
00132      if (iSelStart < m_sWindowText.length())
00133      {
00134           m_SelStart = iSelStart;
00135           if (iSelStart + iSelLength <= m_sWindowText.length())
00136                m_SelLength = iSelLength;
00137           else
00138                m_SelLength = stdex::safe_static_cast<int>(m_sWindowText.length() - iSelStart);
00139      }
00140      else
00141      {
00142           m_SelStart = 0;
00143           m_SelLength = 0;
00144      }
00145 }
00146 
00147 
00148 void CTextBox::SetScrollBarVisibility(CScrollBar::EScrollBarType ScrollBarType, EScrollBarVisibility Visibility)  // virtual
00149 {
00150      m_ScrollBarVisibilityMap[ScrollBarType] = Visibility;
00151      UpdateScrollBars();
00152 }
00153 
00154 
00155 void CTextBox::Draw(void) const  // virtual
00156 {
00157      CWindow::Draw();
00158 
00159      if (m_pSDLSurface)
00160      {
00161           CPainter Painter(m_pSDLSurface, CPainter::PAINT_REPLACE);
00162           Painter.DrawRect(m_WindowRect.SizeRect(), false, COLOR_BLACK);
00163           CPoint FontCenterPoint = m_WindowRect.Center();
00164 
00165           CRGBColor FontColor = m_bReadOnly ? DEFAULT_DISABLED_LINE_COLOR : DEFAULT_LINE_COLOR;
00166           if (CApplication::Instance()->GetKeyFocus() == dynamic_cast<const CWindow*>(this) && !m_bReadOnly)
00167           {
00168                // first normalize the selection
00169                std::wstring::size_type SelStartNorm = 0;
00170                std::wstring::size_type SelLenNorm = abs(m_SelLength);
00171                if (m_SelLength < 0)
00172                {
00173                     SelStartNorm   = m_SelStart + m_SelLength;
00174                }
00175                else
00176                {
00177                     SelStartNorm   = m_SelStart;
00178                }
00179                CPoint SelStartPoint(RowColFromIndex(SelStartNorm));
00180                CPoint SelEndPoint(RowColFromIndex(SelStartNorm + SelLenNorm));
00181 
00182                // now get the dimensions for each character
00183                std::vector<CPoint> vOffsets;
00184                std::vector<CPoint> vBoundingDimensions;
00185                std::vector<std::vector<CRect> > vCharRects;
00186                int iIndex = 0;
00187                for (std::vector<CRenderedString*>::const_iterator iter = m_vpRenderedString.begin(); iter != m_vpRenderedString.end(); ++iter, ++iIndex)
00188                {
00189                     CPoint BoundingDimensions;
00190                     CPoint Offset;
00191                     std::vector<CRect> CharRects;
00192                     (*iter)->GetMetrics(&BoundingDimensions, &Offset, &CharRects);
00193                     vBoundingDimensions.push_back(BoundingDimensions);
00194                     vOffsets.push_back(Offset);
00195                     vCharRects.push_back(CharRects);
00196                }
00197 
00198                // move the cursor into view by scrolling if necessary
00199                int CursorPos = vCharRects.at(SelStartPoint.YPos()).at(SelStartPoint.XPos()).Left() +
00200                     vOffsets.at(SelStartPoint.YPos()).XPos() + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10;
00201                if (m_bScrollToCursor)
00202                {
00203                     if (CursorPos < m_ClientRect.Left())
00204                     {
00205                          m_pHorizontalScrollBar->SetValue(m_pHorizontalScrollBar->GetValue() - (m_ClientRect.Left() - CursorPos) / 10 - 1);
00206                          CursorPos = vCharRects.at(SelStartPoint.YPos()).at(SelStartPoint.XPos()).Left() +
00207                               vOffsets.at(SelStartPoint.YPos()).XPos() + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10;
00208                     }
00209                     if (CursorPos > m_ClientRect.Right())
00210                     {
00211                          m_pHorizontalScrollBar->SetValue(m_pHorizontalScrollBar->GetValue() + (CursorPos - m_ClientRect.Right()) / 10 + 1);
00212                          CursorPos = vCharRects.at(SelStartPoint.YPos()).at(SelStartPoint.XPos()).Left() +
00213                               vOffsets.at(SelStartPoint.YPos()).XPos() + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10;
00214                     }
00215                     if (SelStartPoint.YPos() < m_pVerticalScrollBar->GetValue())
00216                     {
00217                          m_pVerticalScrollBar->SetValue(SelStartPoint.YPos());
00218                     }
00219                     if (SelStartPoint.YPos() > stdex::safe_static_cast<int>(m_pVerticalScrollBar->GetValue() + m_ClientRect.Height() / m_iRowHeight))
00220                     {
00221                          m_pVerticalScrollBar->SetValue(SelStartPoint.YPos() - m_ClientRect.Height() / m_iRowHeight);
00222                     }
00223                     m_bScrollToCursor = false;
00224                }
00225 
00226                // draw the selection
00227                if (m_SelLength != 0 && SelEndPoint.YPos() >= m_pVerticalScrollBar->GetValue())
00228                {
00229                     for (int CurLine = SelStartPoint.YPos(); CurLine <= SelEndPoint.YPos(); ++CurLine)
00230                     {
00231                          CPoint TopLeft = m_ClientRect.TopLeft() + CPoint(0, m_iRowHeight * (CurLine - m_pVerticalScrollBar->GetValue()));
00232                          CRect SelRect(TopLeft, TopLeft + CPoint(vBoundingDimensions.at(CurLine).XPos(), m_iRowHeight - 2));
00233                          if (CurLine == SelStartPoint.YPos())
00234                          {
00235                               SelRect.SetLeft(vCharRects.at(CurLine).at(SelStartPoint.XPos()).Left() +
00236                                    vOffsets.at(CurLine).XPos() + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10);
00237                          }
00238                          if (CurLine == SelEndPoint.YPos())
00239                          {
00240                               SelRect.SetRight(vCharRects.at(CurLine).at(SelEndPoint.XPos()).Left() +
00241                                    vOffsets.at(CurLine).XPos() + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10);
00242                          }
00243                          SelRect.ClipTo(m_ClientRect);
00244                          Painter.DrawRect(SelRect, true, CApplication::Instance()->GetDefaultSelectionColor(), CApplication::Instance()->GetDefaultSelectionColor());
00245                     }
00246                }
00247 
00248                // draw the cursor
00249                if (m_SelLength == 0 && SelStartPoint.YPos() >= m_pVerticalScrollBar->GetValue() && m_bDrawCursor)
00250                {
00251                     if (CursorPos >= m_ClientRect.Left() && CursorPos <= m_ClientRect.Right())
00252                     {
00253                          Painter.DrawVLine(m_ClientRect.Top() + m_iRowHeight * (SelStartPoint.YPos() - m_pVerticalScrollBar->GetValue()) - 1,
00254                               m_ClientRect.Top() + m_iRowHeight * (SelStartPoint.YPos() - m_pVerticalScrollBar->GetValue() + 1) - 1, CursorPos, COLOR_BLACK);
00255                     }
00256                }
00257           }
00258 
00259           // finally, draw the text
00260           CPoint LineOrigin = m_ClientRect.TopLeft() - CPoint(m_pHorizontalScrollBar->GetValue() * 10, 0);
00261           for(std::vector<CRenderedString*>::const_iterator iter = m_vpRenderedString.begin() + m_pVerticalScrollBar->GetValue();
00262                     iter != m_vpRenderedString.end() && LineOrigin.YPos() < m_ClientRect.Bottom(); ++iter) {
00263                (*iter)->Draw(m_pSDLSurface, m_ClientRect, LineOrigin, FontColor);
00264                LineOrigin.SetY(LineOrigin.YPos() + m_iRowHeight);
00265           }
00266      }
00267 }
00268 
00269 
00270 void CTextBox::SetWindowText(const std::wstring& sText)  // virtual
00271 {
00272      m_SelStart = 0;
00273      m_SelLength = 0;
00274      PrepareWindowText(sText);
00275      CWindow::SetWindowText(sText);
00276 }
00277 
00278 
00279 void CTextBox::SetWindowRect(const CRect& WindowRect)
00280 {
00281      CWindow::SetWindowRect(WindowRect);
00282      m_ClientRect = m_WindowRect.SizeRect();
00283      m_ClientRect.Grow(-3);
00284      UpdateScrollBars();
00285 }
00286 
00287 
00288 bool CTextBox::OnMouseButtonDown(CPoint Point, unsigned int Button)  // virtual
00289 {
00290      bool bResult = CWindow::OnMouseButtonDown(Point, Button);
00291 
00292      CPoint WindowPoint(ViewToWindow(Point));
00293      if (!bResult && m_bVisible && (m_ClientRect.HitTest(WindowPoint) == CRect::RELPOS_INSIDE))
00294      {
00295           if (Button == CMouseMessage::LEFT && !m_bReadOnly)
00296           {
00297                bool fSkipCursorPositioning = false;
00298                // If we haven't initialized the double click timer, do so.
00299                if (!m_pDblClickTimer->IsRunning())
00300                {
00301                     m_pDblClickTimer->StartTimer(500, false);
00302                }
00303                else
00304                {
00305                     // Raise double click event
00306                     CMessageServer::Instance().QueueMessage(new TIntMessage(CMessage::CTRL_DOUBLELCLICK, this, this, 0));
00307                     fSkipCursorPositioning = true;
00308                }
00309 
00310                if (CApplication::Instance()->GetKeyFocus() != this)
00311                {
00312                     CApplication::Instance()->SetKeyFocus(this);
00313                }
00314 
00315                if (!fSkipCursorPositioning)
00316                {
00317                     // set the cursor
00318                     std::vector<CPoint> vOffsets;
00319                     std::vector<std::vector<CRect> > vCharRects;
00320                     int iIndex = 0;
00321                     // get the dimensions of all the characters
00322                     for (std::vector<CRenderedString*>::iterator iter = m_vpRenderedString.begin(); iter != m_vpRenderedString.end(); ++iter, ++iIndex)
00323                     {
00324                          CPoint Offset;
00325                          std::vector<CRect> CharRects;
00326                          (*iter)->GetMetrics(0, &Offset, &CharRects);
00327                          vOffsets.push_back(Offset);
00328                          vCharRects.push_back(CharRects);
00329                     }
00330 
00331                     // figure out which line was clicked on
00332                     unsigned int iCurLine = (WindowPoint.YPos() - m_ClientRect.Top()) / m_iRowHeight + m_pVerticalScrollBar->GetValue();
00333                     if (iCurLine >= m_iLineCount)
00334                     {
00335                          iCurLine = m_iLineCount - 1;
00336                     }
00337                     // figure out which character was clicked on
00338                     int xDelta = abs(WindowPoint.XPos() - (vCharRects.at(iCurLine).at(0).Left() + vOffsets.at(iCurLine).XPos() + m_ClientRect.Left()));
00339                     m_SelStart = 0;
00340                     for (unsigned int i = 0; i < vCharRects.at(iCurLine).size(); ++i)
00341                     {
00342                          if (abs(WindowPoint.XPos() - (vCharRects.at(iCurLine).at(i).Right() + vOffsets.at(iCurLine).XPos()
00343                               + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10)) < xDelta)
00344                          {
00345                               xDelta = abs(WindowPoint.XPos() - (vCharRects.at(iCurLine).at(i).Right() + vOffsets.at(iCurLine).XPos()
00346                                    + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10));
00347                               m_SelStart = i + 1;
00348                          }
00349                     }
00350                     for (unsigned int iChar = 0; iChar < iCurLine; ++iChar)
00351                     {
00352                          m_SelStart += vCharRects.at(iChar).size();
00353                     }
00354                }
00355 
00356                m_DragStart = m_SelStart;
00357                m_SelLength = 0;
00358                m_bMouseDown = true;
00359                Draw();
00360                bResult = true;
00361           }
00362           if (Button == CMouseMessage::WHEELUP)
00363           {
00364                m_pVerticalScrollBar->SetValue(m_pVerticalScrollBar->GetValue() - 1);
00365                bResult = true;
00366           }
00367           else if (Button == CMouseMessage::WHEELDOWN)
00368           {
00369                m_pVerticalScrollBar->SetValue(m_pVerticalScrollBar->GetValue() + 1);
00370                bResult = true;
00371           }
00372      }
00373 
00374      return bResult;
00375 }
00376 
00377 
00378 bool CTextBox::HandleMessage(CMessage* pMessage)  // virtual
00379 {
00380      bool bHandled = false;
00381 
00382      if (pMessage)
00383      {
00384           switch(pMessage->MessageType())
00385           {
00386           case CMessage::CTRL_DOUBLELCLICK:
00387                if (pMessage->Destination() == this)
00388                {
00389                     //wUtil::Trace("Got the double click message!");
00390                     m_SelStart = 0;
00391                     m_SelLength = stdex::safe_static_cast<int>(m_sWindowText.length());
00392                     Draw();
00393                     bHandled = true;
00394                }
00395                break;
00396           case CMessage::MOUSE_BUTTONUP:
00397                m_bMouseDown = false;
00398                break;
00399           case CMessage::MOUSE_MOVE:
00400           {
00401                CMouseMessage* pMouseMessage = dynamic_cast<CMouseMessage*>(pMessage);
00402                if (pMouseMessage && m_bVisible && !m_bReadOnly)
00403                {
00404                     CPoint WindowPoint(ViewToWindow(pMouseMessage->Point));
00405                     //If the cursor is within the control then check to see if we've already
00406                     // set the cursor to the I Beam, if we have, don't do anything.  If we
00407                     // havent, set it to the ibeam.
00408                     //Else if it's outside the control and the I Beam cursor is set, set it
00409                     // back to a normal cursor.
00410                     CView* pView = GetView();
00411                     bool bHitFloating = pView && pView->GetFloatingWindow() && pView->GetFloatingWindow()->HitTest(pMouseMessage->Point);
00412                     if (m_ClientRect.HitTest(WindowPoint) == CRect::RELPOS_INSIDE && !bHitFloating && !m_bLastMouseMoveInside)
00413                     {
00414                          m_bLastMouseMoveInside = true;
00415                          CwgCursorResourceHandle IBeamHandle(WGRES_IBEAM_CURSOR);
00416                          CApplication::Instance()->SetMouseCursor(&IBeamHandle);
00417                     }
00418                     else if ((m_ClientRect.HitTest(WindowPoint) != CRect::RELPOS_INSIDE || bHitFloating) && m_bLastMouseMoveInside)
00419                     {
00420                          m_bLastMouseMoveInside= false;
00421                          CApplication::Instance()->SetMouseCursor();
00422                     }
00423 
00424                     if (m_bMouseDown)
00425                     {
00426                          // get the dimensions for all the characters
00427                          std::vector<CPoint> vOffsets;
00428                          std::vector<std::vector<CRect> > vCharRects;
00429                          int iIndex = 0;
00430                          for (std::vector<CRenderedString*>::iterator iter = m_vpRenderedString.begin(); iter != m_vpRenderedString.end(); ++iter, ++iIndex)
00431                          {
00432                               CPoint Offset;
00433                               std::vector<CRect> CharRects;
00434                               (*iter)->GetMetrics(0, &Offset, &CharRects);
00435                               vOffsets.push_back(Offset);
00436                               vCharRects.push_back(CharRects);
00437                          }
00438 
00439                          // figure out which line was clicked on
00440                          unsigned int iCurLine = 0;
00441                          if (m_ClientRect.HitTest(WindowPoint) == CRect::RELPOS_INSIDE)
00442                          {
00443                               iCurLine = (WindowPoint.YPos() - m_ClientRect.Top()) / m_iRowHeight + m_pVerticalScrollBar->GetValue();
00444                               if (iCurLine >= m_iLineCount)
00445                               {
00446                                    iCurLine = m_iLineCount - 1;
00447                               }
00448                          }
00449                          else if (m_ClientRect.HitTest(WindowPoint) == CRect::RELPOS_BELOW)
00450                          {
00451                               iCurLine = m_iLineCount - 1;
00452                          }
00453 
00454                          // figure out which character was clicked on
00455                          int xDelta = abs(WindowPoint.XPos() - (vCharRects.at(iCurLine).at(0).Left() + vOffsets.at(iCurLine).XPos() + m_ClientRect.Left()));
00456                          std::wstring::size_type CursorPos = 0;
00457                          for (unsigned int i = 0; i < vCharRects.at(iCurLine).size(); ++i)
00458                          {
00459                               if (abs(WindowPoint.XPos() - (vCharRects.at(iCurLine).at(i).Right() + vOffsets.at(iCurLine).XPos()
00460                                    + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10)) < xDelta)
00461                               {
00462                                    xDelta = abs(WindowPoint.XPos() - (vCharRects.at(iCurLine).at(i).Right() + vOffsets.at(iCurLine).XPos()
00463                                         + m_ClientRect.Left() - m_pHorizontalScrollBar->GetValue() * 10));
00464                                    CursorPos = i + 1;
00465                               }
00466                          }
00467                          for (unsigned int iChar = 0; iChar < iCurLine; ++iChar)
00468                          {
00469                               CursorPos += vCharRects.at(iChar).size();
00470                          }
00471 
00472                          // set the highlighting
00473                          if (CursorPos < m_DragStart)
00474                          {
00475                               m_SelLength = stdex::safe_static_cast<int>(m_DragStart) - stdex::safe_static_cast<int>(CursorPos);
00476                               m_SelStart = CursorPos;
00477                          }
00478                          else
00479                          {
00480                               m_SelStart = m_DragStart;
00481                               m_SelLength = stdex::safe_static_cast<int>(CursorPos) - stdex::safe_static_cast<int>(m_SelStart);
00482                          }
00483                          bHandled = true;
00484                          Draw();
00485                     }
00486                }
00487                break;
00488           }
00489           case CMessage::CTRL_TIMER:
00490                if (pMessage->Destination() == this && pMessage->Source() == m_pCursorTimer)
00491                {
00492                     //This redraws the whole control each time we want to show/not show the cursor, that's probably a bad idea.
00493                     m_bDrawCursor = !m_bDrawCursor;
00494                     Draw();
00495                     bHandled = true;
00496                }
00497                break;
00498           case CMessage::CTRL_GAININGKEYFOCUS:
00499                if (pMessage->Destination() == this)
00500                {
00501                     m_pCursorTimer->StartTimer(750, true);
00502                     m_bDrawCursor = true;
00503                     Draw();
00504                     bHandled = true;
00505                }
00506                break;
00507           case CMessage::CTRL_LOSINGKEYFOCUS:
00508                if (pMessage->Destination() == this)
00509                {
00510                     m_pCursorTimer->StopTimer();
00511                     Draw();
00512                     bHandled = true;
00513                }
00514                break;
00515           case CMessage::KEYBOARD_KEYDOWN:
00516                if (m_bVisible)
00517                {
00518                     CKeyboardMessage* pKeyboardMessage = dynamic_cast<CKeyboardMessage*>(pMessage);
00519                     if (pKeyboardMessage && pMessage->Destination() == this && !m_bReadOnly)
00520                     {
00521                          std::wstring sBuffer = m_sWindowText;
00522 
00523                          switch(pKeyboardMessage->Key)
00524                          {
00525                          case SDLK_BACKSPACE:
00526                               if (m_SelLength > 0)
00527                               {
00528                                    SelDelete(&sBuffer);
00529                               }
00530                               else
00531                               {
00532                                    if (m_SelStart > 0)
00533                                    {
00534                                         sBuffer.erase(--m_SelStart, 1);
00535                                    }
00536                               }
00537                               break;
00538 
00539                          case SDLK_DELETE:
00540                               if (m_SelStart < sBuffer.length())
00541                               {
00542                                    if (m_SelLength > 0)
00543                                    {
00544                                         SelDelete(&sBuffer);
00545                                    }
00546                                    else
00547                                    {
00548                                         sBuffer.erase(m_SelStart, 1);
00549                                    }
00550                               }
00551                               break;
00552 
00553                          case SDLK_LEFT:
00554                               if (pKeyboardMessage->Modifiers & KMOD_SHIFT) //Shift modifier
00555                               {
00556                                    if (m_SelStart > 0)
00557                                    {
00558                                         if ((m_SelLength > 0) || ((m_SelStart - abs(m_SelLength)) > 0))
00559                                         {
00560                                              if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00561                                              {
00562                                                   std::wstring::size_type pos = sBuffer.rfind(L" ", (m_SelStart + m_SelLength) - 1);
00563                                                   if (pos != std::wstring::npos)
00564                                                   {
00565                                                        m_SelLength = stdex::safe_static_cast<int>(pos) - stdex::safe_static_cast<int>(m_SelStart);
00566                                                   }
00567                                                   else
00568                                                   {
00569                                                        m_SelLength = stdex::safe_static_cast<int>(m_SelStart) * -1;
00570                                                   }
00571                                              }
00572                                              else
00573                                              {
00574                                                   m_SelLength--;
00575                                              }
00576                                         }
00577                                    }
00578                               }
00579                               else if (m_SelLength != 0)
00580                               {
00581                                    if (m_SelLength < 0)
00582                                    {
00583                                         m_SelStart = m_SelStart + m_SelLength;
00584                                    }
00585                                    m_SelLength = 0;
00586                               }
00587                               else if (m_SelStart > 0)
00588                               {
00589                                    if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00590                                    {
00591                                         std::wstring::size_type pos = sBuffer.rfind(L" ", m_SelStart - 1);
00592                                         if (pos != std::wstring::npos)
00593                                         {
00594                                              m_SelStart = pos;
00595                                         }
00596                                         else
00597                                         {
00598                                              m_SelStart = 0;
00599                                         }
00600                                    }
00601                                    else
00602                                    {
00603                                         --m_SelStart;
00604                                    }
00605                                    m_SelLength = 0;
00606                               }
00607                               break;
00608                          case SDLK_RIGHT:
00609                               if (m_SelStart <= sBuffer.length())
00610                               {
00611                                    if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00612                                    {
00613                                         if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00614                                         {
00615                                              std::wstring::size_type pos = sBuffer.find(L" ", m_SelStart + m_SelLength);
00616                                              if (pos != std::wstring::npos)
00617                                              {
00618                                                   m_SelLength = stdex::safe_static_cast<int>(pos) - stdex::safe_static_cast<int>(m_SelStart) + 1;
00619                                              }
00620                                              else
00621                                              {
00622                                                   m_SelLength = stdex::safe_static_cast<int>(sBuffer.length()) - stdex::safe_static_cast<int>(m_SelStart);
00623                                              }
00624                                         }
00625                                         else if (m_SelStart + m_SelLength < sBuffer.length())
00626                                         {
00627                                              m_SelLength++; //Selecting, one character at a time, increase the selection length by one.
00628                                         }
00629                                    }
00630                                    else if(m_SelLength == 0 && m_SelStart < sBuffer.length())
00631                                    {
00632                                         //With the ctrl modifier used, we look for the next instance of a space.
00633                                         // If we find one, we set the cursor position to that location.
00634                                         // If we can't find one, we set the cursor position to the end of the string.
00635                                         // If we don't have the ctrl modifier, then we just incriment the cursor position by one character
00636                                         if (pKeyboardMessage->Modifiers & KMOD_CTRL)
00637                                         {
00638                                              std::wstring::size_type pos = sBuffer.find(L" ", m_SelStart + 1);
00639                                              if (pos != std::wstring::npos)
00640                                              {
00641                                                   m_SelStart = pos + 1;
00642                                              }
00643                                              else
00644                                              {
00645                                                   m_SelStart = sBuffer.length();
00646                                              }
00647                                         }
00648                                         else
00649                                         {
00650                                              ++m_SelStart; //We don't have anything selected, so we'll just incriment the start position
00651                                         }
00652                                    }
00653                                    else
00654                                    {
00655                                         if (m_SelLength > 0)
00656                                         {
00657                                              m_SelStart = m_SelStart + m_SelLength; //Reset cursor position to the end of the selection
00658                                         }
00659                                         m_SelLength = 0; //Set selection length to zero
00660                                    }
00661                               }
00662                               break;
00663                          case SDLK_DOWN:
00664                               if (m_SelStart <= sBuffer.length())
00665                               {
00666                                    if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00667                                    {
00668                                         CPoint CursorPoint(RowColFromIndex(m_SelStart + m_SelLength));
00669                                         if (CursorPoint.YPos() == stdex::safe_static_cast<int>(m_iLineCount - 1))
00670                                         {
00671                                              m_SelLength = stdex::safe_static_cast<int>(m_sWindowText.size() - m_SelStart);
00672                                         }
00673                                         else
00674                                         {
00675                                              m_SelLength = stdex::safe_static_cast<int>(IndexFromRowCol(CursorPoint.YPos() + 1, CursorPoint.XPos()))
00676                                                   - stdex::safe_static_cast<int>(m_SelStart);
00677                                         }
00678                                    }
00679                                    else if(m_SelLength == 0)
00680                                    {
00681                                         CPoint CursorPoint(RowColFromIndex(m_SelStart));
00682                                         if (CursorPoint.YPos() == stdex::safe_static_cast<int>(m_iLineCount - 1))
00683                                         {
00684                                              m_SelStart = m_sWindowText.size();
00685                                         }
00686                                         else
00687                                         {
00688                                              m_SelStart = IndexFromRowCol(CursorPoint.YPos() + 1, CursorPoint.XPos());
00689                                         }
00690                                    }
00691                                    else
00692                                    {
00693                                         if (m_SelLength > 0)
00694                                         {
00695                                              m_SelStart = m_SelStart + m_SelLength;
00696                                         }
00697                                         m_SelLength = 0;
00698                                    }
00699                               }
00700                               break;
00701                          case SDLK_UP:
00702                               if (m_SelStart <= sBuffer.length())
00703                               {
00704                                    if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00705                                    {
00706                                         CPoint CursorPoint(RowColFromIndex(m_SelStart + m_SelLength));
00707                                         if (CursorPoint.YPos() == 0)
00708                                         {
00709                                              m_SelLength = -stdex::safe_static_cast<int>(m_SelStart);
00710                                         }
00711                                         else
00712                                         {
00713                                              m_SelLength = stdex::safe_static_cast<int>(IndexFromRowCol(CursorPoint.YPos() - 1, CursorPoint.XPos()))
00714                                                   - stdex::safe_static_cast<int>(m_SelStart);
00715                                         }
00716                                    }
00717                                    else if(m_SelLength == 0 )
00718                                    {
00719                                         CPoint CursorPoint(RowColFromIndex(m_SelStart));
00720                                         if (CursorPoint.YPos() == 0)
00721                                         {
00722                                              m_SelStart = 0;
00723                                         }
00724                                         else
00725                                         {
00726                                              m_SelStart = IndexFromRowCol(CursorPoint.YPos() - 1, CursorPoint.XPos());
00727                                         }
00728                                    }
00729                                    else
00730                                    {
00731                                         if (m_SelLength < 0)
00732                                         {
00733                                              m_SelStart = m_SelStart + m_SelLength;
00734                                         }
00735                                         m_SelLength = 0;
00736                                    }
00737                               }
00738                               break;
00739                          case SDLK_END:
00740                               {
00741                                    std::wstring::size_type endPos = sBuffer.length();
00742                                    
00743                                    //If they're pressing Ctrl we want to move to the end of the text.
00744                                    //If they're not pressing Ctrl we want to move to the end of the current line.
00745                                    if ((pKeyboardMessage->Modifiers & KMOD_CTRL) == 0)
00746                                    {
00747                                         std::wstring::size_type lineEndPos = sBuffer.find(L"\n", m_SelStart + m_SelLength + 1);
00748                                         if (lineEndPos != std::wstring::npos)
00749                                         {
00750                                              endPos = lineEndPos;
00751                                         }
00752                                    }
00753 
00754                                    if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00755                                    {
00756                                         m_SelLength = stdex::safe_static_cast<int>(endPos) - stdex::safe_static_cast<int>(m_SelStart);
00757                                    }
00758                                    else
00759                                    {
00760                                         m_SelStart = endPos;
00761                                    }
00762                               }
00763                               break;
00764                          case SDLK_HOME:
00765                               {
00766                                    //If they're pressing Ctrl we want to move to the beginning of the text.
00767                                    //If they're not pressing Ctrl we want to move to the beginning of the current line.
00768                                    std::wstring::size_type endPos = 0;
00769                                    if ((pKeyboardMessage->Modifiers & KMOD_CTRL) == 0)
00770                                    {
00771                                         std::wstring::size_type lineEndPos = sBuffer.rfind(L"\n", m_SelStart + m_SelLength - 1);
00772                                         if (lineEndPos != std::wstring::npos)
00773                                         {
00774                                              endPos = lineEndPos + 1;
00775                                         }
00776                                    }
00777                                    if (pKeyboardMessage->Modifiers & KMOD_SHIFT)
00778                                    {
00779                                         m_SelLength = stdex::safe_static_cast<long int>(endPos);
00780                                    }
00781                                    else
00782                                    {
00783                                         m_SelLength = 0;
00784                                         m_SelStart = endPos;
00785                                    }
00786                               }
00787                               break;
00788                          case SDLK_RETURN:
00789                               SelDelete(&sBuffer);
00790                               sBuffer.insert(m_SelStart++, 1, L'\n');
00791                               break;
00792                          case SDLK_ESCAPE:
00793                               //do nothing on escape otherwise it inserts a weird character.
00794                               break;
00795                          default:
00796                               if (pKeyboardMessage->Unicode)
00797                               {
00798                                    SelDelete(&sBuffer);
00799                                    sBuffer.insert(m_SelStart++, 1, stdex::safe_static_cast<wchar_t>(pKeyboardMessage->Unicode));
00800                               }
00801                               break;
00802                          }
00803 
00804                          if (m_sWindowText != sBuffer)
00805                          {
00806                               CMessageServer::Instance().QueueMessage(new TStringMessage(CMessage::CTRL_VALUECHANGE, m_pParentWindow, this, sBuffer));
00807                               m_sWindowText = sBuffer;
00808                               PrepareWindowText(sBuffer);
00809                          }
00810                          m_bDrawCursor = true;
00811                          m_bScrollToCursor = true;  // a key was pressed, so we need to make sure that the cursor is visible
00812                          Draw();
00813                     }
00814                     break;
00815                }
00816           case CMessage::CTRL_VALUECHANGE:
00817           case CMessage::CTRL_VALUECHANGING:
00818           {
00819                if (pMessage->Source() == m_pVerticalScrollBar || pMessage->Source() == m_pHorizontalScrollBar)
00820                {
00821                     Draw();
00822                     bHandled = true;
00823                }
00824                break;
00825           }
00826           default :
00827                bHandled = CWindow::HandleMessage(pMessage);
00828                break;
00829           }
00830      }
00831 
00832      return bHandled;
00833 }
00834 
00835 
00836 void CTextBox::SelDelete(std::wstring* psString)
00837 {
00838      //This means we've selected something, therefore, should replace it
00839      if (m_SelLength != 0)
00840      {
00841           std::wstring::size_type SelStartNorm = 0;
00842           std::wstring::size_type SelLenNorm = abs(m_SelLength);
00843           if (m_SelLength < 0)
00844           {
00845                SelStartNorm   = m_SelStart + m_SelLength;
00846           }
00847           else
00848           {
00849                SelStartNorm   = m_SelStart;
00850           }
00851           psString->erase(SelStartNorm, SelLenNorm);
00852           //Since we're deleting the stuff here and setting the selection length to zero, we also need to set the selection start properly
00853           m_SelStart = SelStartNorm;
00854           m_SelLength = 0;
00855      }
00856 }
00857 
00858 
00859 void CTextBox::PrepareWindowText(const std::wstring& sText)
00860 {
00861      for(std::vector<CRenderedString*>::iterator iter = m_vpRenderedString.begin(); iter !=  m_vpRenderedString.end(); ++iter)
00862           delete *iter;
00863      m_vpRenderedString.clear();
00864 
00865      m_iLineCount = 0;
00866      std::wstring::size_type start = 0;
00867      std::wstring::size_type loc = 0;
00868      m_iMaxWidth = 0;
00869      while (loc != std::wstring::npos)
00870      {
00871           loc = sText.find(L"\n", start);
00872           m_vpRenderedString.push_back(new CRenderedString(
00873                m_pFontEngine, sText.substr(start, loc - start), CRenderedString::VALIGN_TOP, CRenderedString::HALIGN_LEFT));
00874           CPoint BoundingDimensions;
00875           m_vpRenderedString.back()->GetMetrics(&BoundingDimensions, 0, 0);
00876           if (BoundingDimensions.XPos() > stdex::safe_static_cast<int>(m_iMaxWidth))
00877           {
00878                m_iMaxWidth = BoundingDimensions.XPos();
00879           }
00880           start = loc + 1;
00881           ++m_iLineCount;
00882      }
00883      m_iRowHeight = m_vpRenderedString.at(0)->GetMaxFontHeight() + 2;
00884 
00885      UpdateScrollBars();
00886 }
00887 
00888 
00889 void CTextBox::UpdateScrollBars(void)
00890 {
00891      m_ClientRect = CRect(3, 3, m_WindowRect.Width() - 5, m_WindowRect.Height() - 5);
00892      bool bVertVisible = m_ScrollBarVisibilityMap[CScrollBar::VERTICAL] == SCROLLBAR_VIS_ALWAYS ||
00893           (m_ScrollBarVisibilityMap[CScrollBar::VERTICAL] == SCROLLBAR_VIS_AUTO && m_iLineCount > (m_ClientRect.Height() - 12) / m_iRowHeight);
00894      bool bHorzVisible = m_ScrollBarVisibilityMap[CScrollBar::HORIZONTAL] == SCROLLBAR_VIS_ALWAYS ||
00895           (m_ScrollBarVisibilityMap[CScrollBar::HORIZONTAL] == SCROLLBAR_VIS_AUTO && stdex::safe_static_cast<int>(m_iMaxWidth) > (m_ClientRect.Width() - 12));
00896      int iMaxHorzLimit = (stdex::safe_static_cast<int>(m_iMaxWidth) - (m_ClientRect.Width() - 12)) / 10;
00897      if (iMaxHorzLimit < 0)
00898      {
00899           iMaxHorzLimit = 0;
00900      }
00901 
00902      m_pVerticalScrollBar->SetVisible(bVertVisible);
00903      m_pHorizontalScrollBar->SetVisible(bHorzVisible);
00904      int iLastLine = m_iLineCount - 1;
00905      if (iLastLine < 0)
00906      {
00907           iLastLine = 0;
00908      }
00909      if (bVertVisible)
00910      {
00911           m_ClientRect.SetRight(m_WindowRect.Width() - 17);
00912      }
00913      if (bHorzVisible)
00914      {
00915           m_ClientRect.SetBottom(m_WindowRect.Height() - 17);
00916      }
00917 
00918      m_pVerticalScrollBar->SetMaxLimit(iLastLine);
00919      if (m_pVerticalScrollBar->GetValue() > iLastLine)
00920      {
00921           m_pVerticalScrollBar->SetValue(iLastLine);
00922      }
00923      m_pVerticalScrollBar->SetWindowRect(CRect(m_ClientRect.Width() + 2, 1 - m_ClientRect.Top(), m_ClientRect.Width() + 14, m_ClientRect.Height()));
00924      m_pHorizontalScrollBar->SetMaxLimit(iMaxHorzLimit);
00925      if (m_pHorizontalScrollBar->GetValue() > iMaxHorzLimit)
00926      {
00927           m_pHorizontalScrollBar->SetValue(iMaxHorzLimit);
00928      }
00929      m_pHorizontalScrollBar->SetWindowRect(CRect(1 - m_ClientRect.Left(), m_ClientRect.Height() + 2, m_ClientRect.Width(), m_ClientRect.Height() + 14));
00930 }
00931 
00932 
00933 CPoint CTextBox::RowColFromIndex(std::wstring::size_type Index) const
00934 {
00935      int iRow = 0;
00936      int iCol = stdex::safe_static_cast<int>(Index);
00937      std::wstring::size_type loc = m_sWindowText.find(L"\n");
00938      std::wstring::size_type start = 0;
00939      while (loc != std::wstring::npos && loc < Index)
00940      {
00941           ++iRow;
00942           iCol -= (stdex::safe_static_cast<int>(loc - start) + 1);
00943           start = loc + 1;
00944           loc = m_sWindowText.find(L"\n", start);
00945      }
00946      return CPoint(iCol, iRow);
00947 }
00948 
00949 
00950 std::wstring::size_type CTextBox::IndexFromRowCol(std::wstring::size_type Row, std::wstring::size_type Col) const
00951 {
00952      std::wstring::size_type Index = 0;
00953      for (std::wstring::size_type iRow = 0; iRow < Row && iRow < m_vpRenderedString.size(); ++iRow)
00954           Index += m_vpRenderedString.at(iRow)->GetLength() + 1;
00955      if (Col > m_vpRenderedString.at(Row)->GetLength())
00956           Col = m_vpRenderedString.at(Row)->GetLength();
00957      return Index + Col;
00958 }
00959 
00960 }

Generated on Wed May 16 23:11:26 2007 for wGui by  doxygen 1.5.1