comparison ext/guichan-0.8.2/src/widgets/textbox.cpp @ 378:64738befdf3b

bringing in the changes from the build_system_rework branch in preparation for the 0.3.0 release. This commit will require the Jan2010 devkit. Clients will also need to be modified to the new way to import fife.
author vtchill@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 11 Jan 2010 23:34:52 +0000
parents
children
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
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/textbox.hpp"
49
50 #include "guichan/basiccontainer.hpp"
51 #include "guichan/font.hpp"
52 #include "guichan/graphics.hpp"
53 #include "guichan/key.hpp"
54 #include "guichan/mouseinput.hpp"
55
56 namespace gcn
57 {
58 TextBox::TextBox()
59 {
60 mCaretColumn = 0;
61 mCaretRow = 0;
62 mEditable = true;
63 mOpaque = true;
64
65 setText("");
66
67 setFocusable(true);
68
69 addMouseListener(this);
70 addKeyListener(this);
71 adjustSize();
72 }
73
74 TextBox::TextBox(const std::string& text)
75 {
76 mCaretColumn = 0;
77 mCaretRow = 0;
78 mEditable = true;
79 mOpaque = true;
80
81 setText(text);
82
83 setFocusable(true);
84
85 addMouseListener(this);
86 addKeyListener(this);
87 adjustSize();
88 }
89
90 void TextBox::setText(const std::string& text)
91 {
92 mCaretColumn = 0;
93 mCaretRow = 0;
94
95 mTextRows.clear();
96
97 std::string::size_type pos, lastPos = 0;
98 int length;
99 do
100 {
101 pos = text.find("\n", lastPos);
102
103 if (pos != std::string::npos)
104 {
105 length = pos - lastPos;
106 }
107 else
108 {
109 length = text.size() - lastPos;
110 }
111 std::string sub = text.substr(lastPos, length);
112 mTextRows.push_back(sub);
113 lastPos = pos + 1;
114
115 } while (pos != std::string::npos);
116
117 adjustSize();
118 }
119
120 void TextBox::draw(Graphics* graphics)
121 {
122 unsigned int i;
123
124 if (mOpaque)
125 {
126 graphics->setColor(getBackgroundColor());
127 graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
128 }
129
130 if (isFocused() && isEditable())
131 {
132 drawCaret(graphics, getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn)), mCaretRow * getFont()->getHeight());
133 }
134
135 graphics->setColor(getForegroundColor());
136 graphics->setFont(getFont());
137
138 for (i = 0; i < mTextRows.size(); i++)
139 {
140 // Move the text one pixel so we can have a caret before a letter.
141 graphics->drawText(mTextRows[i], 1, i * getFont()->getHeight());
142 }
143 }
144
145 void TextBox::drawCaret(Graphics* graphics, int x, int y)
146 {
147 graphics->setColor(getForegroundColor());
148 graphics->drawLine(x, getFont()->getHeight() + y, x, y);
149 }
150
151 void TextBox::mousePressed(MouseEvent& mouseEvent)
152 {
153 if (mouseEvent.getButton() == MouseEvent::LEFT)
154 {
155 mCaretRow = mouseEvent.getY() / getFont()->getHeight();
156
157 if (mCaretRow >= (int)mTextRows.size())
158 {
159 mCaretRow = mTextRows.size() - 1;
160 }
161
162 mCaretColumn = getFont()->getStringIndexAt(mTextRows[mCaretRow], mouseEvent.getX());
163 }
164 }
165
166 void TextBox::mouseDragged(MouseEvent& mouseEvent)
167 {
168 mouseEvent.consume();
169 }
170
171 void TextBox::keyPressed(KeyEvent& keyEvent)
172 {
173 Key key = keyEvent.getKey();
174
175 if (key.getValue() == Key::LEFT)
176 {
177 --mCaretColumn;
178 if (mCaretColumn < 0)
179 {
180 --mCaretRow;
181
182 if (mCaretRow < 0)
183 {
184 mCaretRow = 0;
185 mCaretColumn = 0;
186 }
187 else
188 {
189 mCaretColumn = mTextRows[mCaretRow].size();
190 }
191 }
192 }
193
194 else if (key.getValue() == Key::RIGHT)
195 {
196 ++mCaretColumn;
197 if (mCaretColumn > (int)mTextRows[mCaretRow].size())
198 {
199 ++mCaretRow;
200
201 if (mCaretRow >= (int)mTextRows.size())
202 {
203 mCaretRow = mTextRows.size() - 1;
204 if (mCaretRow < 0)
205 {
206 mCaretRow = 0;
207 }
208
209 mCaretColumn = mTextRows[mCaretRow].size();
210 }
211 else
212 {
213 mCaretColumn = 0;
214 }
215 }
216 }
217
218 else if (key.getValue() == Key::DOWN)
219 {
220 setCaretRow(mCaretRow + 1);
221 }
222
223 else if (key.getValue() == Key::UP)
224 {
225 setCaretRow(mCaretRow - 1);
226 }
227
228 else if (key.getValue() == Key::HOME)
229 {
230 mCaretColumn = 0;
231 }
232
233 else if (key.getValue() == Key::END)
234 {
235 mCaretColumn = mTextRows[mCaretRow].size();
236 }
237
238 else if (key.getValue() == Key::ENTER && mEditable)
239 {
240 mTextRows.insert(mTextRows.begin() + mCaretRow + 1,
241 mTextRows[mCaretRow].substr(mCaretColumn, mTextRows[mCaretRow].size() - mCaretColumn));
242 mTextRows[mCaretRow].resize(mCaretColumn);
243 ++mCaretRow;
244 mCaretColumn = 0;
245 }
246
247 else if (key.getValue() == Key::BACKSPACE
248 && mCaretColumn != 0
249 && mEditable)
250 {
251 mTextRows[mCaretRow].erase(mCaretColumn - 1, 1);
252 --mCaretColumn;
253 }
254
255 else if (key.getValue() == Key::BACKSPACE
256 && mCaretColumn == 0
257 && mCaretRow != 0
258 && mEditable)
259 {
260 mCaretColumn = mTextRows[mCaretRow - 1].size();
261 mTextRows[mCaretRow - 1] += mTextRows[mCaretRow];
262 mTextRows.erase(mTextRows.begin() + mCaretRow);
263 --mCaretRow;
264 }
265
266 else if (key.getValue() == Key::DELETE
267 && mCaretColumn < (int)mTextRows[mCaretRow].size()
268 && mEditable)
269 {
270 mTextRows[mCaretRow].erase(mCaretColumn, 1);
271 }
272
273 else if (key.getValue() == Key::DELETE
274 && mCaretColumn == (int)mTextRows[mCaretRow].size()
275 && mCaretRow < ((int)mTextRows.size() - 1)
276 && mEditable)
277 {
278 mTextRows[mCaretRow] += mTextRows[mCaretRow + 1];
279 mTextRows.erase(mTextRows.begin() + mCaretRow + 1);
280 }
281
282 else if(key.getValue() == Key::PAGE_UP)
283 {
284 Widget* par = getParent();
285
286 if (par != NULL)
287 {
288 int rowsPerPage = par->getChildrenArea().height / getFont()->getHeight();
289 mCaretRow -= rowsPerPage;
290
291 if (mCaretRow < 0)
292 {
293 mCaretRow = 0;
294 }
295 }
296 }
297
298 else if(key.getValue() == Key::PAGE_DOWN)
299 {
300 Widget* par = getParent();
301
302 if (par != NULL)
303 {
304 int rowsPerPage = par->getChildrenArea().height / getFont()->getHeight();
305 mCaretRow += rowsPerPage;
306
307 if (mCaretRow >= (int)mTextRows.size())
308 {
309 mCaretRow = mTextRows.size() - 1;
310 }
311 }
312 }
313
314 else if(key.getValue() == Key::TAB
315 && mEditable)
316 {
317 mTextRows[mCaretRow].insert(mCaretColumn,std::string(" "));
318 mCaretColumn += 4;
319 }
320
321 else if (key.isCharacter()
322 && mEditable)
323 {
324 mTextRows[mCaretRow].insert(mCaretColumn,std::string(1,(char)key.getValue()));
325 ++mCaretColumn;
326 }
327
328 adjustSize();
329 scrollToCaret();
330
331 keyEvent.consume();
332 }
333
334 void TextBox::adjustSize()
335 {
336 unsigned int i;
337 int width = 0;
338 for (i = 0; i < mTextRows.size(); ++i)
339 {
340 int w = getFont()->getWidth(mTextRows[i]);
341 if (width < w)
342 {
343 width = w;
344 }
345 }
346
347 setWidth(width + 1);
348 setHeight(getFont()->getHeight() * mTextRows.size());
349 }
350
351 void TextBox::setCaretPosition(unsigned int position)
352 {
353 int row;
354
355 for (row = 0; row < (int)mTextRows.size(); row++)
356 {
357 if (position <= mTextRows[row].size())
358 {
359 mCaretRow = row;
360 mCaretColumn = position;
361 return; // we are done
362 }
363 else
364 {
365 position--;
366 }
367 }
368
369 // position beyond end of text
370 mCaretRow = mTextRows.size() - 1;
371 mCaretColumn = mTextRows[mCaretRow].size();
372 }
373
374 unsigned int TextBox::getCaretPosition() const
375 {
376 int pos = 0, row;
377
378 for (row = 0; row < mCaretRow; row++)
379 {
380 pos += mTextRows[row].size();
381 }
382
383 return pos + mCaretColumn;
384 }
385
386 void TextBox::setCaretRowColumn(int row, int column)
387 {
388 setCaretRow(row);
389 setCaretColumn(column);
390 }
391
392 void TextBox::setCaretRow(int row)
393 {
394 mCaretRow = row;
395
396 if (mCaretRow >= (int)mTextRows.size())
397 {
398 mCaretRow = mTextRows.size() - 1;
399 }
400
401 if (mCaretRow < 0)
402 {
403 mCaretRow = 0;
404 }
405
406 setCaretColumn(mCaretColumn);
407 }
408
409 unsigned int TextBox::getCaretRow() const
410 {
411 return mCaretRow;
412 }
413
414 void TextBox::setCaretColumn(int column)
415 {
416 mCaretColumn = column;
417
418 if (mCaretColumn > (int)mTextRows[mCaretRow].size())
419 {
420 mCaretColumn = mTextRows[mCaretRow].size();
421 }
422
423 if (mCaretColumn < 0)
424 {
425 mCaretColumn = 0;
426 }
427 }
428
429 unsigned int TextBox::getCaretColumn() const
430 {
431 return mCaretColumn;
432 }
433
434 const std::string& TextBox::getTextRow(int row) const
435 {
436 return mTextRows[row];
437 }
438
439 void TextBox::setTextRow(int row, const std::string& text)
440 {
441 mTextRows[row] = text;
442
443 if (mCaretRow == row)
444 {
445 setCaretColumn(mCaretColumn);
446 }
447
448 adjustSize();
449 }
450
451 unsigned int TextBox::getNumberOfRows() const
452 {
453 return mTextRows.size();
454 }
455
456 std::string TextBox::getText() const
457 {
458 if (mTextRows.size() == 0)
459 {
460 return std::string("");
461 }
462
463 int i;
464 std::string text;
465
466 for (i = 0; i < (int)mTextRows.size() - 1; ++i)
467 {
468 text = text + mTextRows[i] + "\n";
469 }
470
471 text = text + mTextRows[i];
472
473 return text;
474 }
475
476 void TextBox::fontChanged()
477 {
478 adjustSize();
479 }
480
481 void TextBox::scrollToCaret()
482 {
483 Rectangle scroll;
484 scroll.x = getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn));
485 scroll.y = getFont()->getHeight() * mCaretRow;
486 scroll.width = getFont()->getWidth(" ");
487 scroll.height = getFont()->getHeight() + 2; // add 2 for some extra space
488
489 showPart(scroll);
490 }
491
492 void TextBox::setEditable(bool editable)
493 {
494 mEditable = editable;
495 }
496
497 bool TextBox::isEditable() const
498 {
499 return mEditable;
500 }
501
502 void TextBox::addRow(const std::string row)
503 {
504 mTextRows.push_back(row);
505 adjustSize();
506 }
507
508 bool TextBox::isOpaque()
509 {
510 return mOpaque;
511 }
512
513 void TextBox::setOpaque(bool opaque)
514 {
515 mOpaque = opaque;
516 }
517 }