textbox.cpp

00001 /*      _______   __   __   __   ______   __   __   _______   __   __
00002  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
00003  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
00004  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
00005  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
00006  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
00007  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
00008  *
00009  * Copyright (c) 2004, 2005, 2006, 2007 Olof Naessén and Per Larsson
00010  *
00011  *                                                         Js_./
00012  * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
00013  * Olof Naessén a.k.a jansem/yakslem                _asww7!uY`>  )\a//
00014  *                                                 _Qhm`] _f "'c  1!5m
00015  * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
00016  *                                               .)j(] .d_/ '-(  P .   S
00017  * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
00018  * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
00019  * binary forms, with or without                 )4d[#7r, .   '     )d`)[
00020  * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
00021  * that the following conditions are met:       j<<WP+k/);.        _W=j f
00022  * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
00023  *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
00024  *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
00025  *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
00026  * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
00027  *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
00028  *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
00029  *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
00030  *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
00031  *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
00032  * 3. Neither the name of Guichan nor the      :$we` _! + _/ .        j?
00033  *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
00034  *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
00035  *    from this software without specific        (js, \[QQW$QWW#?!V"".
00036  *    prior written permission.                    ]y:.<\..          .
00037  *                                                 -]n w/ '         [.
00038  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
00039  * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
00040  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
00041  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
00042  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
00043  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
00044  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
00045  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
00046  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
00047  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
00048  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
00049  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                     ^
00050  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00051  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00052  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00053  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00054  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00055  */
00056 
00057 /*
00058  * For comments regarding functions please see the header file.
00059  */
00060 
00061 #include "guichan/widgets/textbox.hpp"
00062 
00063 #include "guichan/basiccontainer.hpp"
00064 #include "guichan/font.hpp"
00065 #include "guichan/graphics.hpp"
00066 #include "guichan/key.hpp"
00067 #include "guichan/mouseinput.hpp"
00068 
00069 namespace gcn
00070 {
00071     TextBox::TextBox()
00072     {
00073         mCaretColumn = 0;
00074         mCaretRow = 0;
00075         mEditable = true;
00076         mOpaque = true;
00077 
00078         setFocusable(true);
00079 
00080         addMouseListener(this);
00081         addKeyListener(this);
00082         adjustSize();
00083         setText("");
00084     }
00085 
00086     TextBox::TextBox(const std::string& text)
00087     {
00088         mCaretColumn = 0;
00089         mCaretRow = 0;
00090         mEditable = true;
00091         mOpaque = true;
00092 
00093         setText(text);
00094 
00095         setFocusable(true);
00096 
00097         addMouseListener(this);
00098         addKeyListener(this);
00099         adjustSize();
00100     }
00101 
00102     void TextBox::setText(const std::string& text)
00103     {
00104         mCaretColumn = 0;
00105         mCaretRow = 0;
00106 
00107         mTextRows.clear();
00108 
00109         std::string::size_type pos, lastPos = 0;
00110         int length;
00111         do
00112         {
00113             pos = text.find("\n", lastPos);
00114 
00115             if (pos != std::string::npos)
00116             {
00117                 length = pos - lastPos;
00118             }
00119             else
00120             {
00121                 length = text.size() - lastPos;
00122             }
00123             std::string sub = text.substr(lastPos, length);
00124             mTextRows.push_back(sub);
00125             lastPos = pos + 1;
00126 
00127         } while (pos != std::string::npos);
00128 
00129         adjustSize();
00130     }
00131 
00132     void TextBox::draw(Graphics* graphics)
00133     {
00134         /*
00135         int width = getWidth() + getBorderSize() * 2 - 1;
00136         int height = getHeight() + getBorderSize() * 2 - 1;
00137 
00138         graphics->setColor(getBackgroundColor());
00139 
00140         unsigned int i;
00141         for (i = 0; i < getBorderSize(); ++i)
00142         {
00143             graphics->drawLine(i,i, width - i, i);
00144             graphics->drawLine(i,i + 1, i, height - i - 1);
00145             graphics->drawLine(width - i,i + 1, width - i, height - i);
00146             graphics->drawLine(i,height - i, width - i - 1, height - i);
00147         }
00148         */
00149 
00150         unsigned int i;
00151 
00152         if (mOpaque)
00153         {
00154             graphics->setColor(getBackgroundColor());
00155             graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
00156         }
00157 
00158         if (isFocused() && isEditable())
00159         {
00160             drawCaret(graphics, getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn)), mCaretRow * getFont()->getHeight());
00161         }
00162 
00163         graphics->setColor(getForegroundColor());
00164         graphics->setFont(getFont());
00165 
00166         for (i = 0; i < mTextRows.size(); i++)
00167         {
00168             // Move the text one pixel so we can have a caret before a letter.
00169             graphics->drawText(mTextRows[i], 1, i * getFont()->getHeight());
00170         }
00171     }
00172 
00173     void TextBox::drawCaret(Graphics* graphics, int x, int y)
00174     {
00175         graphics->setColor(getForegroundColor());
00176         graphics->drawLine(x, getFont()->getHeight() + y, x, y);
00177     }
00178 
00179     void TextBox::mousePressed(MouseEvent& mouseEvent)
00180     {
00181         if (mouseEvent.getButton() == MouseEvent::LEFT)
00182         {
00183             mCaretRow = mouseEvent.getY() / getFont()->getHeight();
00184 
00185             if (mCaretRow >= (int)mTextRows.size())
00186             {
00187                 mCaretRow = mTextRows.size() - 1;
00188             }
00189 
00190             mCaretColumn = getFont()->getStringIndexAt(mTextRows[mCaretRow], mouseEvent.getX());
00191         }
00192     }
00193 
00194     void TextBox::mouseDragged(MouseEvent& mouseEvent)
00195     {
00196         mouseEvent.consume();
00197     }
00198 
00199     void TextBox::keyPressed(KeyEvent& keyEvent)
00200     {
00201         Key key = keyEvent.getKey();
00202 
00203         if (key.getValue() == Key::LEFT)
00204         {
00205             --mCaretColumn;
00206             if (mCaretColumn < 0)
00207             {
00208                 --mCaretRow;
00209 
00210                 if (mCaretRow < 0)
00211                 {
00212                     mCaretRow = 0;
00213                     mCaretColumn = 0;
00214                 }
00215                 else
00216                 {
00217                     mCaretColumn = mTextRows[mCaretRow].size();
00218                 }
00219             }
00220         }
00221 
00222         else if (key.getValue() == Key::RIGHT)
00223         {
00224             ++mCaretColumn;
00225             if (mCaretColumn > (int)mTextRows[mCaretRow].size())
00226             {
00227                 ++mCaretRow;
00228 
00229                 if (mCaretRow >= (int)mTextRows.size())
00230                 {
00231                     mCaretRow = mTextRows.size() - 1;
00232                     if (mCaretRow < 0)
00233                     {
00234                         mCaretRow = 0;
00235                     }
00236 
00237                     mCaretColumn = mTextRows[mCaretRow].size();
00238                 }
00239                 else
00240                 {
00241                     mCaretColumn = 0;
00242                 }
00243             }
00244         }
00245 
00246         else if (key.getValue() == Key::DOWN)
00247         {
00248             setCaretRow(mCaretRow + 1);
00249         }
00250 
00251         else if (key.getValue() == Key::UP)
00252         {
00253             setCaretRow(mCaretRow - 1);
00254         }
00255 
00256         else if (key.getValue() == Key::HOME)
00257         {
00258             mCaretColumn = 0;
00259         }
00260 
00261         else if (key.getValue() == Key::END)
00262         {
00263             mCaretColumn = mTextRows[mCaretRow].size();
00264         }
00265 
00266         else if (key.getValue() == Key::ENTER && mEditable)
00267         {
00268             mTextRows.insert(mTextRows.begin() + mCaretRow + 1,
00269                              mTextRows[mCaretRow].substr(mCaretColumn, mTextRows[mCaretRow].size() - mCaretColumn));
00270             mTextRows[mCaretRow].resize(mCaretColumn);
00271             ++mCaretRow;
00272             mCaretColumn = 0;
00273         }
00274 
00275         else if (key.getValue() == Key::BACKSPACE
00276                  && mCaretColumn != 0
00277                  && mEditable)
00278         {
00279             mTextRows[mCaretRow].erase(mCaretColumn - 1, 1);
00280             --mCaretColumn;
00281         }
00282 
00283         else if (key.getValue() == Key::BACKSPACE
00284                  && mCaretColumn == 0
00285                  && mCaretRow != 0
00286                  && mEditable)
00287         {
00288             mCaretColumn = mTextRows[mCaretRow - 1].size();
00289             mTextRows[mCaretRow - 1] += mTextRows[mCaretRow];
00290             mTextRows.erase(mTextRows.begin() + mCaretRow);
00291             --mCaretRow;
00292         }
00293 
00294         else if (key.getValue() == Key::DELETE
00295                  && mCaretColumn < (int)mTextRows[mCaretRow].size()
00296                  && mEditable)
00297         {
00298             mTextRows[mCaretRow].erase(mCaretColumn, 1);
00299         }
00300 
00301         else if (key.getValue() == Key::DELETE
00302                  && mCaretColumn == (int)mTextRows[mCaretRow].size()
00303                  && mCaretRow < ((int)mTextRows.size() - 1)
00304                  && mEditable)
00305         {
00306             mTextRows[mCaretRow] += mTextRows[mCaretRow + 1];
00307             mTextRows.erase(mTextRows.begin() + mCaretRow + 1);
00308         }
00309 
00310         else if(key.getValue() == Key::PAGE_UP)
00311         {
00312             Widget* par = getParent();
00313 
00314             if (par != NULL)
00315             {
00316                 int rowsPerPage = par->getChildrenArea().height / getFont()->getHeight();
00317                 mCaretRow -= rowsPerPage;
00318 
00319                 if (mCaretRow < 0)
00320                 {
00321                     mCaretRow = 0;
00322                 }
00323             }
00324         }
00325 
00326         else if(key.getValue() == Key::PAGE_DOWN)
00327         {
00328             Widget* par = getParent();
00329 
00330             if (par != NULL)
00331             {
00332                 int rowsPerPage = par->getChildrenArea().height / getFont()->getHeight();
00333                 mCaretRow += rowsPerPage;
00334 
00335                 if (mCaretRow >= (int)mTextRows.size())
00336                 {
00337                     mCaretRow = mTextRows.size() - 1;
00338                 }
00339             }
00340         }
00341 
00342         else if(key.getValue() == Key::TAB
00343                 && mEditable)
00344         {
00345             mTextRows[mCaretRow].insert(mCaretColumn,std::string("    "));
00346             mCaretColumn += 4;
00347         }
00348 
00349         else if (key.isCharacter()
00350                  && mEditable)
00351         {
00352             mTextRows[mCaretRow].insert(mCaretColumn,std::string(1,(char)key.getValue()));
00353             ++mCaretColumn;
00354         }
00355 
00356         adjustSize();
00357         scrollToCaret();
00358 
00359         keyEvent.consume();
00360     }
00361 
00362     void TextBox::adjustSize()
00363     {
00364         unsigned int i;
00365         int width = 0;
00366         for (i = 0; i < mTextRows.size(); ++i)
00367         {
00368             int w = getFont()->getWidth(mTextRows[i]);
00369             if (width < w)
00370             {
00371                 width = w;
00372             }
00373         }
00374 
00375         setWidth(width + 1);
00376         setHeight(getFont()->getHeight() * mTextRows.size());
00377     }
00378 
00379     void TextBox::setCaretPosition(unsigned int position)
00380     {
00381         int row;
00382 
00383         for (row = 0; row < (int)mTextRows.size(); row++)
00384         {
00385             if (position <= mTextRows[row].size())
00386             {
00387                 mCaretRow = row;
00388                 mCaretColumn = position;
00389                 return; // we are done
00390             }
00391             else
00392             {
00393                 position--;
00394             }
00395         }
00396 
00397         // position beyond end of text
00398         mCaretRow = mTextRows.size() - 1;
00399         mCaretColumn = mTextRows[mCaretRow].size();
00400     }
00401 
00402     unsigned int TextBox::getCaretPosition() const
00403     {
00404         int pos = 0, row;
00405 
00406         for (row = 0; row < mCaretRow; row++)
00407         {
00408             pos += mTextRows[row].size();
00409         }
00410 
00411         return pos + mCaretColumn;
00412     }
00413 
00414     void TextBox::setCaretRowColumn(int row, int column)
00415     {
00416         setCaretRow(row);
00417         setCaretColumn(column);
00418     }
00419 
00420     void TextBox::setCaretRow(int row)
00421     {
00422         mCaretRow = row;
00423 
00424         if (mCaretRow >= (int)mTextRows.size())
00425         {
00426             mCaretRow = mTextRows.size() - 1;
00427         }
00428 
00429         if (mCaretRow < 0)
00430         {
00431             mCaretRow = 0;
00432         }
00433 
00434         setCaretColumn(mCaretColumn);
00435     }
00436 
00437     unsigned int TextBox::getCaretRow() const
00438     {
00439         return mCaretRow;
00440     }
00441 
00442     void TextBox::setCaretColumn(int column)
00443     {
00444         mCaretColumn = column;
00445 
00446         if (mCaretColumn > (int)mTextRows[mCaretRow].size())
00447         {
00448             mCaretColumn = mTextRows[mCaretRow].size();
00449         }
00450 
00451         if (mCaretColumn < 0)
00452         {
00453             mCaretColumn = 0;
00454         }
00455     }
00456 
00457     unsigned int TextBox::getCaretColumn() const
00458     {
00459         return mCaretColumn;
00460     }
00461 
00462     const std::string& TextBox::getTextRow(int row) const
00463     {
00464         return mTextRows[row];
00465     }
00466 
00467     void TextBox::setTextRow(int row, const std::string& text)
00468     {
00469         mTextRows[row] = text;
00470 
00471         if (mCaretRow == row)
00472         {
00473             setCaretColumn(mCaretColumn);
00474         }
00475 
00476         adjustSize();
00477     }
00478 
00479     unsigned int TextBox::getNumberOfRows() const
00480     {
00481         return mTextRows.size();
00482     }
00483 
00484     std::string TextBox::getText() const
00485     {
00486         if (mTextRows.size() == 0)
00487         {
00488             return std::string("");
00489         }
00490 
00491         int i;
00492         std::string text;
00493 
00494         for (i = 0; i < (int)mTextRows.size() - 1; ++i)
00495         {
00496             text = text + mTextRows[i] + "\n";
00497         }
00498 
00499         text = text + mTextRows[i];
00500 
00501         return text;
00502     }
00503 
00504     void TextBox::fontChanged()
00505     {
00506         adjustSize();
00507     }
00508 
00509     void TextBox::scrollToCaret()
00510     {
00511         Rectangle scroll;
00512         scroll.x = getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn));
00513         scroll.y = getFont()->getHeight() * mCaretRow;
00514         scroll.width = getFont()->getWidth(" ");
00515         scroll.height = getFont()->getHeight() + 2; // add 2 for some extra space
00516 
00517         showPart(scroll);
00518     }
00519 
00520     void TextBox::setEditable(bool editable)
00521     {
00522         mEditable = editable;
00523     }
00524 
00525     bool TextBox::isEditable() const
00526     {
00527         return mEditable;
00528     }
00529 
00530     void TextBox::addRow(const std::string row)
00531     {
00532         mTextRows.push_back(row);
00533         adjustSize();
00534     }
00535 
00536     bool TextBox::isOpaque()
00537     {
00538         return mOpaque;
00539     }
00540 
00541     void TextBox::setOpaque(bool opaque)
00542     {
00543         mOpaque = opaque;
00544     }
00545 }

Generated on Sun Jan 20 21:48:14 2008 for Guichan by  doxygen 1.5.2