1 /*      _______   __   __   __   ______   __   __   _______   __   __
2  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
3  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
4  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
5  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
6  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
7  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
8  *
9  * Copyright (c) 2004 - 2008 Olof Naess�n and Per Larsson
10  *
11  *
12  * Per Larsson a.k.a finalman
13  * Olof Naess�n a.k.a jansem/yakslem
14  *
15  * Visit: http://guichan.sourceforge.net
16  *
17  * License: (BSD)
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in
25  *    the documentation and/or other materials provided with the
26  *    distribution.
27  * 3. Neither the name of Guichan nor the names of its contributors may
28  *    be used to endorse or promote products derived from this software
29  *    without specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
37  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
38  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42  */
43 
44 /*
45  * For comments regarding functions please see the header file.
46  */
47 
48 #include "guichan/widgets/textfield.hpp"
49 
50 #include "guichan/font.hpp"
51 #include "guichan/graphics.hpp"
52 #include "guichan/key.hpp"
53 #include "guichan/mouseinput.hpp"
54 
55 namespace gcn
56 {
TextField()57     TextField::TextField()
58     {
59         mCaretPosition = 0;
60         mXScroll = 0;
61 
62         setFocusable(true);
63 
64         addMouseListener(this);
65         addKeyListener(this);
66     }
67 
TextField(const std::string & text)68     TextField::TextField(const std::string& text)
69     {
70         mCaretPosition = 0;
71         mXScroll = 0;
72 
73         mText = text;
74         adjustSize();
75 
76         setFocusable(true);
77 
78         addMouseListener(this);
79         addKeyListener(this);
80     }
81 
setText(const std::string & text)82     void TextField::setText(const std::string& text)
83     {
84         if(text.size() < mCaretPosition )
85         {
86             mCaretPosition = text.size();
87         }
88 
89         mText = text;
90     }
91 
draw(Graphics * graphics)92     void TextField::draw(Graphics* graphics)
93     {
94         Color faceColor = getBaseColor();
95         Color highlightColor, shadowColor;
96         int alpha = getBaseColor().a;
97         highlightColor = faceColor + 0x303030;
98         highlightColor.a = alpha;
99         shadowColor = faceColor - 0x303030;
100         shadowColor.a = alpha;
101 
102         // Draw a border.
103         graphics->setColor(shadowColor);
104         graphics->drawLine(0, 0, getWidth() - 1, 0);
105         graphics->drawLine(0, 1, 0, getHeight() - 2);
106         graphics->setColor(highlightColor);
107         graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, getHeight() - 1);
108         graphics->drawLine(0, getHeight() - 1, getWidth() - 1, getHeight() - 1);
109 
110         // Push a clip area so the other drawings don't need to worry
111         // about the border.
112         graphics->pushClipArea(Rectangle(1, 1, getWidth() - 2, getHeight() - 2));
113 
114         graphics->setColor(getBackgroundColor());
115         graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
116 
117         if (isFocused())
118         {
119             drawCaret(graphics, getFont()->getWidth(mText.substr(0, mCaretPosition)) - mXScroll);
120         }
121 
122         graphics->setColor(getForegroundColor());
123         graphics->setFont(getFont());
124         graphics->drawText(mText, 1 - mXScroll, 1);
125 
126         graphics->popClipArea();
127     }
128 
drawCaret(Graphics * graphics,int x)129     void TextField::drawCaret(Graphics* graphics, int x)
130     {
131         // Check the current clip area as a clip area with a different
132         // size than the widget might have been pushed (which is the
133         // case in the draw method when we push a clip area after we have
134         // drawn a border).
135         const Rectangle clipArea = graphics->getCurrentClipArea();
136 
137         graphics->setColor(getForegroundColor());
138         graphics->drawLine(x, clipArea.height - 2, x, 1);
139     }
140 
mousePressed(MouseEvent & mouseEvent)141     void TextField::mousePressed(MouseEvent& mouseEvent)
142     {
143         if (mouseEvent.getButton() == MouseEvent::LEFT)
144         {
145             mCaretPosition = getFont()->getStringIndexAt(mText, mouseEvent.getX() + mXScroll);
146             fixScroll();
147         }
148     }
149 
mouseDragged(MouseEvent & mouseEvent)150     void TextField::mouseDragged(MouseEvent& mouseEvent)
151     {
152         mouseEvent.consume();
153     }
154 
keyPressed(KeyEvent & keyEvent)155     void TextField::keyPressed(KeyEvent& keyEvent)
156     {
157         Key key = keyEvent.getKey();
158 
159         if (key.getValue() == Key::LEFT && mCaretPosition > 0)
160         {
161             --mCaretPosition;
162         }
163 
164         else if (key.getValue() == Key::RIGHT && mCaretPosition < mText.size())
165         {
166             ++mCaretPosition;
167         }
168 
169         else if (key.getValue() == Key::DELETE && mCaretPosition < mText.size())
170         {
171             mText.erase(mCaretPosition, 1);
172         }
173 
174         else if (key.getValue() == Key::BACKSPACE && mCaretPosition > 0)
175         {
176             mText.erase(mCaretPosition - 1, 1);
177             --mCaretPosition;
178         }
179 
180         else if (key.getValue() == Key::ENTER)
181         {
182             distributeActionEvent();
183         }
184 
185         else if (key.getValue() == Key::HOME)
186         {
187             mCaretPosition = 0;
188         }
189 
190         else if (key.getValue() == Key::END)
191         {
192             mCaretPosition = mText.size();
193         }
194 
195         else if (key.isCharacter()
196                  && key.getValue() != Key::TAB)
197         {
198             mText.insert(mCaretPosition, std::string(1,(char)key.getValue()));
199             ++mCaretPosition;
200         }
201 
202         if (key.getValue() != Key::TAB)
203         {
204             keyEvent.consume();
205         }
206 
207         fixScroll();
208     }
209 
adjustSize()210     void TextField::adjustSize()
211     {
212         setWidth(getFont()->getWidth(mText) + 6);
213         adjustHeight();
214 
215         fixScroll();
216     }
217 
adjustHeight()218     void TextField::adjustHeight()
219     {
220         setHeight(getFont()->getHeight() + 4);
221     }
222 
fixScroll()223     void TextField::fixScroll()
224     {
225         if (isFocused())
226         {
227             int caretX = getFont()->getWidth(mText.substr(0, mCaretPosition));
228 
229             if (caretX - mXScroll >= getWidth() - 4)
230             {
231                 mXScroll = caretX - getWidth() + 4;
232             }
233             else if (caretX - mXScroll <= 0)
234             {
235                 mXScroll = caretX - getWidth() / 2;
236 
237                 if (mXScroll < 0)
238                 {
239                     mXScroll = 0;
240                 }
241             }
242         }
243     }
244 
setCaretPosition(unsigned int position)245     void TextField::setCaretPosition(unsigned int position)
246     {
247         if (position > mText.size())
248         {
249             mCaretPosition = mText.size();
250         }
251         else
252         {
253             mCaretPosition = position;
254         }
255 
256         fixScroll();
257     }
258 
getCaretPosition() const259     unsigned int TextField::getCaretPosition() const
260     {
261         return mCaretPosition;
262     }
263 
getText() const264     const std::string& TextField::getText() const
265     {
266         return mText;
267     }
268 
fontChanged()269     void TextField::fontChanged()
270     {
271         fixScroll();
272     }
273 }
274