00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "gui/widgets/browserbox.h"
00023
00024 #include "gui/linkhandler.h"
00025 #include "gui/palette.h"
00026 #include "gui/truetypefont.h"
00027
00028 #include <guichan/graphics.hpp>
00029
00030 #include <algorithm>
00031
00032 BrowserBox::BrowserBox(unsigned int mode, bool opaque):
00033 gcn::Widget(),
00034 mMode(mode), mHighMode(UNDERLINE | BACKGROUND),
00035 mOpaque(opaque),
00036 mUseLinksAndUserColors(true),
00037 mSelectedLink(-1),
00038 mMaxRows(0)
00039 {
00040 setFocusable(true);
00041 addMouseListener(this);
00042 }
00043
00044 BrowserBox::~BrowserBox()
00045 {
00046 }
00047
00048 void BrowserBox::setLinkHandler(LinkHandler* linkHandler)
00049 {
00050 mLinkHandler = linkHandler;
00051 }
00052
00053 void BrowserBox::setOpaque(bool opaque)
00054 {
00055 mOpaque = opaque;
00056 }
00057
00058 void BrowserBox::setHighlightMode(unsigned int highMode)
00059 {
00060 mHighMode = highMode;
00061 }
00062
00063 void BrowserBox::disableLinksAndUserColors()
00064 {
00065 mUseLinksAndUserColors = false;
00066 }
00067
00068 void BrowserBox::addRow(const std::string &row)
00069 {
00070 std::string tmp = row;
00071 std::string newRow;
00072 BROWSER_LINK bLink;
00073 std::string::size_type idx1, idx2, idx3;
00074 TrueTypeFont *font = static_cast<TrueTypeFont*>(getFont());
00075
00076
00077 if (mUseLinksAndUserColors)
00078 {
00079
00080 idx1 = tmp.find("@@");
00081 while (idx1 != std::string::npos)
00082 {
00083 idx2 = tmp.find("|", idx1);
00084 idx3 = tmp.find("@@", idx2);
00085
00086 if (idx2 == std::string::npos || idx3 == std::string::npos)
00087 break;
00088 bLink.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2));
00089 bLink.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1));
00090 bLink.y1 = mTextRows.size() * font->getHeight();
00091 bLink.y2 = bLink.y1 + font->getHeight();
00092
00093 newRow += tmp.substr(0, idx1);
00094
00095 std::string tmp2 = newRow;
00096 idx1 = tmp2.find("##");
00097 while (idx1 != std::string::npos)
00098 {
00099 tmp2.erase(idx1, 3);
00100 idx1 = tmp2.find("##");
00101 }
00102 bLink.x1 = font->getWidth(tmp2) - 1;
00103 bLink.x2 = bLink.x1 + font->getWidth(bLink.caption) + 1;
00104
00105 mLinks.push_back(bLink);
00106
00107 newRow += "##<" + bLink.caption;
00108
00109 tmp.erase(0, idx3 + 2);
00110 if (!tmp.empty())
00111 {
00112 newRow += "##>";
00113 }
00114 idx1 = tmp.find("@@");
00115 }
00116
00117 newRow += tmp;
00118 }
00119
00120
00121 else
00122 {
00123 newRow = row;
00124 }
00125
00126 mTextRows.push_back(newRow);
00127
00128
00129 if (mMaxRows > 0)
00130 {
00131 while (mTextRows.size() > mMaxRows)
00132 {
00133 mTextRows.pop_front();
00134 for (unsigned int i = 0; i < mLinks.size(); i++)
00135 {
00136 mLinks[i].y1 -= font->getHeight();
00137 mLinks[i].y2 -= font->getHeight();
00138
00139 if (mLinks[i].y1 < 0)
00140 mLinks.erase(mLinks.begin() + i);
00141 }
00142 }
00143 }
00144
00145
00146 if (mMode == AUTO_SIZE)
00147 {
00148 std::string plain = newRow;
00149 for (idx1 = plain.find("##"); idx1 != std::string::npos; idx1 = plain.find("##"))
00150 plain.erase(idx1, 3);
00151
00152
00153 int w = font->getWidth(plain);
00154 if (w > getWidth())
00155 setWidth(w);
00156 }
00157
00158 if (mMode == AUTO_WRAP)
00159 {
00160 unsigned int y = 0;
00161 unsigned int nextChar;
00162 const char *hyphen = "~";
00163 int hyphenWidth = font->getWidth(hyphen);
00164 int x = 0;
00165
00166 for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++)
00167 {
00168 std::string row = *i;
00169 for (unsigned int j = 0; j < row.size(); j++)
00170 {
00171 std::string character = row.substr(j, 1);
00172 x += font->getWidth(character);
00173 nextChar = j + 1;
00174
00175
00176 if ((nextChar < row.size()) && (row.at(nextChar) == ' '))
00177 {
00178 int nextSpacePos = row.find(" ", (nextChar + 1));
00179 if (nextSpacePos <= 0)
00180 {
00181 nextSpacePos = row.size() - 1;
00182 }
00183 int nextWordWidth = font->getWidth(
00184 row.substr(nextChar,
00185 (nextSpacePos - nextChar)));
00186
00187 if ((x + nextWordWidth + 10) > getWidth())
00188 {
00189 x = 15;
00190 y += 1;
00191 j++;
00192 }
00193 }
00194
00195 else if ((x + 2 * hyphenWidth) > getWidth())
00196 {
00197 x = 15;
00198 y += 1;
00199 }
00200 }
00201 }
00202
00203 setHeight(font->getHeight() * (mTextRows.size() + y));
00204 }
00205 else
00206 {
00207 setHeight(font->getHeight() * mTextRows.size());
00208 }
00209 }
00210
00211 void BrowserBox::clearRows()
00212 {
00213 mTextRows.clear();
00214 mLinks.clear();
00215 setWidth(0);
00216 mSelectedLink = -1;
00217 }
00218
00219 struct MouseOverLink
00220 {
00221 MouseOverLink(int x, int y) : mX(x),mY(y) { }
00222 bool operator() (BROWSER_LINK &link)
00223 {
00224 return (mX >= link.x1 && mX < link.x2 &&
00225 mY >= link.y1 && mY < link.y2);
00226 }
00227 int mX, mY;
00228 };
00229
00230 void BrowserBox::mousePressed(gcn::MouseEvent &event)
00231 {
00232 if (!mLinkHandler) return;
00233 LinkIterator i = find_if(mLinks.begin(), mLinks.end(),
00234 MouseOverLink(event.getX(), event.getY()));
00235
00236 if (i != mLinks.end()) {
00237 mLinkHandler->handleLink(i->link);
00238 }
00239 }
00240
00241 void BrowserBox::mouseMoved(gcn::MouseEvent &event)
00242 {
00243 LinkIterator i = find_if(mLinks.begin(), mLinks.end(),
00244 MouseOverLink(event.getX(), event.getY()));
00245
00246 mSelectedLink = (i != mLinks.end()) ? (i - mLinks.begin()) : -1;
00247 }
00248
00249 void BrowserBox::draw(gcn::Graphics *graphics)
00250 {
00251 if (mOpaque)
00252 {
00253 graphics->setColor(guiPalette->getColor(Palette::BACKGROUND));
00254 graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
00255 }
00256
00257 if (mSelectedLink >= 0)
00258 {
00259 if ((mHighMode & BACKGROUND))
00260 {
00261 graphics->setColor(guiPalette->getColor(Palette::HIGHLIGHT));
00262 graphics->fillRectangle(gcn::Rectangle(
00263 mLinks[mSelectedLink].x1,
00264 mLinks[mSelectedLink].y1,
00265 mLinks[mSelectedLink].x2 - mLinks[mSelectedLink].x1,
00266 mLinks[mSelectedLink].y2 - mLinks[mSelectedLink].y1
00267 ));
00268 }
00269
00270 if ((mHighMode & UNDERLINE))
00271 {
00272 graphics->setColor(guiPalette->getColor(Palette::HYPERLINK));
00273 graphics->drawLine(
00274 mLinks[mSelectedLink].x1,
00275 mLinks[mSelectedLink].y2,
00276 mLinks[mSelectedLink].x2,
00277 mLinks[mSelectedLink].y2);
00278 }
00279 }
00280
00281 int x = 0, y = 0;
00282 int wrappedLines = 0;
00283 int link = 0;
00284 TrueTypeFont *font = static_cast<TrueTypeFont*>(getFont());
00285
00286 graphics->setColor(guiPalette->getColor(Palette::TEXT));
00287 for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++)
00288 {
00289 const gcn::Color textColor = guiPalette->getColor(Palette::TEXT);
00290 gcn::Color selColor = textColor;
00291 gcn::Color prevColor = selColor;
00292 const std::string row = *(i);
00293 bool wrapped = false;
00294 x = 0;
00295
00296
00297 if (row.find("---", 0) == 0)
00298 {
00299 for (x = 0; x < getWidth(); x++)
00300 {
00301 font->drawString(graphics, "-", x, y);
00302 x += font->getWidth("-") - 2;
00303 }
00304 y += font->getHeight();
00305 continue;
00306 }
00307
00308
00309
00310 for (std::string::size_type start = 0, end = std::string::npos;
00311 start != std::string::npos;
00312 start = end, end = std::string::npos)
00313 {
00314
00315 if (wrapped)
00316 {
00317 y += font->getHeight();
00318 x = 15;
00319 }
00320
00321
00322 if (mUseLinksAndUserColors)
00323 end = row.find("##", start + 1);
00324
00325 if (mUseLinksAndUserColors ||
00326 (!mUseLinksAndUserColors && (start == 0)))
00327 {
00328
00329 if (row.find("##", start) == start && row.size() > start + 2)
00330 {
00331 const char c = row.at(start + 2);
00332 bool valid;
00333 const gcn::Color col = guiPalette->getColor(c, valid);
00334
00335 if (c == '>')
00336 {
00337 selColor = prevColor;
00338 }
00339 else if (c == '<')
00340 {
00341 const int size = mLinks[link].x2 - mLinks[link].x1;
00342 mLinks[link].x1 = x;
00343 mLinks[link].y1 = y;
00344 mLinks[link].x2 = mLinks[link].x1 + size;
00345 mLinks[link].y2 = y + font->getHeight();
00346 link++;
00347 selColor = col;
00348 prevColor = selColor;
00349 }
00350 else if (valid)
00351 {
00352 selColor = col;
00353 }
00354 else
00355 {
00356 switch (c)
00357 {
00358 case '1': selColor = RED; break;
00359 case '2': selColor = GREEN; break;
00360 case '3': selColor = BLUE; break;
00361 case '4': selColor = ORANGE; break;
00362 case '5': selColor = YELLOW; break;
00363 case '6': selColor = PINK; break;
00364 case '7': selColor = PURPLE; break;
00365 case '8': selColor = GRAY; break;
00366 case '9': selColor = BROWN; break;
00367 case '0':
00368 default:
00369 selColor = textColor;
00370 }
00371 }
00372 start += 3;
00373
00374 if (start == row.size())
00375 {
00376 break;
00377 }
00378 }
00379 graphics->setColor(selColor);
00380 }
00381
00382 std::string::size_type len =
00383 end == std::string::npos ? end : end - start;
00384 std::string part = row.substr(start, len);
00385
00386
00387 if (mMode == AUTO_WRAP &&
00388 (x + font->getWidth(part.c_str()) + 10) > getWidth())
00389 {
00390 bool forced = false;
00391 char const *hyphen = "~";
00392 int hyphenWidth = font->getWidth(hyphen);
00393
00394
00395
00396
00397
00398 do
00399 {
00400 if (!forced)
00401 end = row.rfind(" ", end);
00402
00403
00404 if (end == std::string::npos || end <= start)
00405 {
00406 forced = true;
00407 end = row.size();
00408 x += hyphenWidth * 2;
00409 continue;
00410 }
00411
00412
00413 while ((row[end] & 192) == 128)
00414 end--;
00415 end--;
00416
00417 part = row.substr(start, end - start + 1);
00418 } while ((x + font->getWidth(part.c_str()) + 10) > getWidth());
00419
00420 if (forced)
00421 {
00422 x -= hyphenWidth;
00423 font->drawString(graphics, hyphen,
00424 getWidth() - hyphenWidth, y);
00425 end++;
00426 }
00427 else
00428 end += 2;
00429
00430 wrapped = true;
00431 wrappedLines++;
00432 }
00433 font->drawString(graphics, part, x, y);
00434 x += font->getWidth(part.c_str());
00435 }
00436 y += font->getHeight();
00437 setHeight((mTextRows.size() + wrappedLines) * font->getHeight());
00438 }
00439 }