1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2011
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20
21 #include <GLW/GLWChatView.h>
22 #include <GLW/GLWFont.h>
23 #include <GLW/GLWPanel.h>
24 #include <GLW/GLWColors.h>
25 #include <GLEXT/GLState.h>
26 #include <graph/OptionsDisplay.h>
27 #include <image/ImageFactory.h>
28 #include <XML/XMLParser.h>
29 #include <common/Keyboard.h>
30 #include <common/ToolTipResource.h>
31 #include <client/ClientChannelManager.h>
32 #include <client/ScorchedClient.h>
33 #include <target/TargetContainer.h>
34 #include <lang/LangResource.h>
35 #include <lang/LangParam.h>
36
37 REGISTER_CLASS_SOURCE(GLWChatView);
38
GLWChatView(float x,float y,float w,float h)39 GLWChatView::GLWChatView(float x, float y, float w, float h) :
40 GLWidget(x, y, w, h),
41 init_(false),
42 visibleLines_(5), totalLines_(50),
43 displayTime_(10.0f),
44 fontSize_(12.0f), outlineFontSize_(14.0f), lineDepth_(18),
45 currentVisible_(0),
46 alignTop_(false), parentSized_(false), splitLargeLines_(false),
47 scrollPosition_(-1), allowScroll_(false),
48 upButton_(x_ + 2.0f, y_ + 1.0f, 12.0f, 12.0f),
49 downButton_(x_ + 2.0f, y_ + 1.0f, 12.0f, 12.0f),
50 resetButton_(x_ + 2.0f, y_ + 1.0f, 14.0f, 14.0f),
51 scrollUpKey_(0), scrollDownKey_(0), scrollResetKey_(0)
52 {
53 setX(x_);
54 setY(y_);
55
56 upButton_.setHandler(this);
57 downButton_.setHandler(this);
58 resetButton_.setHandler(this);
59
60 upButton_.setToolTip(new ToolTipResource(
61 ToolTip::ToolTipAlignLeft | ToolTip::ToolTipHelp,
62 "CHAT_PREVIOUS", "Chat Previous",
63 "CHAT_PREVIOUS_TOOLTIP", "Show previous chat entry"));
64 downButton_.setToolTip(new ToolTipResource(
65 ToolTip::ToolTipAlignLeft | ToolTip::ToolTipHelp,
66 "CHAT_NEXT", "Chat Next",
67 "CHAT_NEXT_TOOLTIP", "Show next chat entry"));
68 resetButton_.setToolTip(new ToolTipResource(
69 ToolTip::ToolTipAlignLeft | ToolTip::ToolTipHelp,
70 "CHAT_LAST", "Chat Last",
71 "CHAT_LAST_TOOLTIP", "View end of the chat log, \n"
72 "hide all elapsed entries"));
73
74 upButton_.setTextureImage(ImageID(S3D::eModLocation,"", "data/windows/arrow_u.png"));
75 downButton_.setTextureImage(ImageID(S3D::eModLocation,"", "data/windows/arrow_d.png"));
76 resetButton_.setTextureImage(ImageID(S3D::eModLocation,"", "data/windows/arrow_s.png"));
77 }
78
~GLWChatView()79 GLWChatView::~GLWChatView()
80 {
81 clearChat();
82 }
83
clearChat()84 void GLWChatView::clearChat()
85 {
86 scrollPosition_ = -1;
87 while (!textLines_.empty())
88 {
89 delete textLines_.back();
90 textLines_.pop_back();
91 }
92 }
93
buttonDown(unsigned int id)94 void GLWChatView::buttonDown(unsigned int id)
95 {
96 if (!allowScroll_) return;
97
98 if (id == upButton_.getId())
99 {
100 scrollPosition_ ++;
101 if (scrollPosition_ > (int) textLines_.size() - 1)
102 scrollPosition_ = (int) textLines_.size() - 1;
103 }
104 else if (id == downButton_.getId())
105 {
106 scrollPosition_ --;
107 if (scrollPosition_ < 0) scrollPosition_ = 0;
108 }
109 else
110 {
111 scrollPosition_ = -1;
112 }
113 }
114
splitLine(const LangString & message)115 int GLWChatView::splitLine(const LangString &message)
116 {
117 int lastSpace = 0;
118 int totalLen = (int) message.size();
119 for (int len=1; len<totalLen; len++)
120 {
121 float width = 0.0f;
122 if (splitLargeLines_)
123 {
124 width = GLWFont::instance()->getGameFont()->
125 getWidth(outlineFontSize_, message, len);
126 }
127
128 if (width > w_)
129 {
130 // If there is a space within the last 15 characters split to it
131 if (lastSpace && (len - lastSpace < 15))
132 {
133 return lastSpace;
134 }
135 else
136 {
137 // Else just split where we are now
138 return len - 1;
139 }
140 }
141
142 if (message[len] == '\n')
143 {
144 return len + 1;
145 }
146 if (message[len] == ' ')
147 {
148 lastSpace = len + 1;
149 }
150 }
151
152 // The whole message fits, split to the end of the message
153 return totalLen;
154 }
155
addLargeChat(const Vector & color,const LangString & text,GLFont2dI * renderer)156 void GLWChatView::addLargeChat(const Vector &color, const LangString &text, GLFont2dI *renderer)
157 {
158 int currentLen = 0;
159 int totalLen = (int) text.size();
160 while (currentLen < totalLen)
161 {
162 // Get the next split position
163 int partLen = splitLine(&text[currentLen]);
164 bool nl=(text[currentLen + partLen - 1] == '\n');
165 LangString subset(text, currentLen, (nl?partLen-1:partLen));
166
167 // Create the new text and add it
168 addChat(color, subset, currentLen==0?renderer:0);
169
170 // Increment the current position
171 currentLen += partLen;
172 }
173 }
174
addChat(const Vector & color,const LangString & text,GLFont2dI * renderer)175 void GLWChatView::addChat(const Vector &color, const LangString &text, GLFont2dI *renderer)
176 {
177 GLWChatViewEntry *entry = new GLWChatViewEntry(color, text, displayTime_, renderer);
178 textLines_.push_front(entry);
179
180 if (scrollPosition_ > 0)
181 {
182 scrollPosition_ ++;
183 if (scrollPosition_ > (int) textLines_.size() - 1)
184 scrollPosition_ = (int) textLines_.size() - 1;
185 }
186
187 if (textLines_.size() > (unsigned int) totalLines_)
188 {
189 delete textLines_.back();
190 textLines_.pop_back();
191 }
192 }
193
simulate(float frameTime)194 void GLWChatView::simulate(float frameTime)
195 {
196 currentVisible_ = 0;
197 int count = 0;
198 std::list<GLWChatViewEntry*>::iterator itor;
199 for (itor = textLines_.begin();
200 itor != textLines_.end() && count < visibleLines_;
201 ++itor, count++)
202 {
203 GLWChatViewEntry &entry = *(*itor);
204 entry.timeRemaining -= frameTime;
205 if (entry.timeRemaining > 0.0f) currentVisible_ ++;
206 }
207 }
208
setX(float x)209 void GLWChatView::setX(float x)
210 {
211 GLWidget::setX(x);
212 upButton_.setX(x_ + 1.0f);
213 downButton_.setX(x_ + 1.0f);
214 resetButton_.setX(x_ + 1.0f);
215 }
216
setY(float y)217 void GLWChatView::setY(float y)
218 {
219 GLWidget::setY(y);
220 upButton_.setY(y_ + 36.0f);
221 downButton_.setY(y_ + 24.0f);
222 resetButton_.setY(y_ + 6.0f);
223 }
224
draw()225 void GLWChatView::draw()
226 {
227 GLWidget::draw();
228
229 if (allowScroll_)
230 {
231 upButton_.draw();
232 downButton_.draw();
233 resetButton_.draw();
234 }
235
236 if (!init_)
237 {
238 init_ = true;
239 if (parent_ && parentSized_)
240 {
241 w_ = parent_->getW();
242 h_ = parent_->getH();
243 }
244 }
245
246 // Check if there is anything to draw
247 if (textLines_.empty()) return;
248
249 // Find the start of the area to draw to
250 GLState currentStateBlend(GLState::TEXTURE_ON |
251 GLState::BLEND_ON | GLState::DEPTH_OFF);
252 float start = y_ + 8.0f; //lineDepth_;
253 if (alignTop_)
254 {
255 start = y_ + h_ - float(currentVisible_) * lineDepth_;
256 }
257
258 // Skip the lines we are not viewing
259 // (Should make this more efficient)
260 std::list<GLWChatViewEntry*>::iterator startingPos = textLines_.begin();
261 {
262 int count = 0;
263 for (;
264 startingPos != textLines_.end() && count < scrollPosition_;
265 ++startingPos, count++)
266 {
267 }
268 }
269
270 // Draw all the black outlines first
271 // Do this in a block incase any of the outlines would over write any
272 // of the other lines
273 {
274 int count = 0;
275 std::list<GLWChatViewEntry*>::iterator itor;
276 for (itor = startingPos;
277 itor != textLines_.end() && count < visibleLines_;
278 ++itor, count++)
279 {
280 GLWChatViewEntry &entry = *(*itor);
281
282 float alpha = 1.0f;
283 if (scrollPosition_ < 0)
284 {
285 if (entry.timeRemaining <= 0.0f) continue;
286 if (entry.timeRemaining < 1.0f) alpha = entry.timeRemaining;
287 }
288
289 float x = x_ + 20.0f;
290 float y = start + count * lineDepth_;
291
292 GLWFont::instance()->getGameShadowFont()->
293 drawA(GLWColors::black, alpha, fontSize_,
294 x - 1.0f, y + 1.0f, 0.0f,
295 entry.text);
296 }
297 }
298 // Draw the actual text
299 {
300 int count = 0;
301 std::list<GLWChatViewEntry*>::iterator itor;
302 for (itor = startingPos;
303 itor != textLines_.end() && count < visibleLines_;
304 ++itor, count++)
305 {
306 GLWChatViewEntry &entry = *(*itor);
307
308 float alpha = 1.0f;
309 if (scrollPosition_ < 0)
310 {
311 if (entry.timeRemaining <= 0.0f) continue;
312 if (entry.timeRemaining < 1.0f) alpha = entry.timeRemaining;
313 }
314
315 float x = x_ + 20.0f;
316 float y = start + count * lineDepth_;
317
318 if (entry.renderer)
319 {
320 GLWFont::instance()->getGameFont()->
321 drawA(entry.renderer, entry.color, alpha, fontSize_,
322 x, y, 0.0f,
323 entry.text);
324 }
325 else
326 {
327 GLWFont::instance()->getGameFont()->
328 drawA(entry.color, alpha, fontSize_,
329 x, y, 0.0f,
330 entry.text);
331 }
332 }
333 }
334 }
335
mouseDown(int button,float x,float y,bool & skipRest)336 void GLWChatView::mouseDown(int button, float x, float y, bool &skipRest)
337 {
338 skipRest = false;
339 upButton_.mouseDown(button, x, y, skipRest);
340 downButton_.mouseDown(button, x, y, skipRest);
341 resetButton_.mouseDown(button, x, y, skipRest);
342 }
343
mouseUp(int button,float x,float y,bool & skipRest)344 void GLWChatView::mouseUp(int button, float x, float y, bool &skipRest)
345 {
346 skipRest = false;
347 upButton_.mouseUp(button, x, y, skipRest);
348 downButton_.mouseUp(button, x, y, skipRest);
349 resetButton_.mouseUp(button, x, y, skipRest);
350 }
351
mouseDrag(int button,float mx,float my,float x,float y,bool & skipRest)352 void GLWChatView::mouseDrag(int button, float mx, float my, float x, float y, bool &skipRest)
353 {
354 skipRest = false;
355 upButton_.mouseDrag(button, mx, my, x, y, skipRest);
356 downButton_.mouseDrag(button, mx, my, x, y, skipRest);
357 resetButton_.mouseDrag(button, mx, my, x, y, skipRest);
358 }
359
keyDown(char * buffer,unsigned int keyState,KeyboardHistory::HistoryElement * history,int hisCount,bool & skipRest)360 void GLWChatView::keyDown(char *buffer, unsigned int keyState,
361 KeyboardHistory::HistoryElement *history, int hisCount,
362 bool &skipRest)
363 {
364 if (scrollUpKey_ && scrollUpKey_->keyDown(buffer, keyState, false))
365 {
366 buttonDown(upButton_.getId());
367 skipRest = true;
368 }
369 if (scrollDownKey_ && scrollDownKey_->keyDown(buffer, keyState, false))
370 {
371 buttonDown(downButton_.getId());
372 skipRest = true;
373 }
374 if (scrollResetKey_ && scrollResetKey_->keyDown(buffer, keyState, false))
375 {
376 buttonDown(resetButton_.getId());
377 skipRest = true;
378 }
379 }
380
initFromXML(XMLNode * node)381 bool GLWChatView::initFromXML(XMLNode *node)
382 {
383 if (!GLWidget::initFromXML(node)) return false;
384
385 if (!initFromXMLInternal(node)) return false;
386
387 return true;
388 }
389
initFromXMLInternal(XMLNode * node)390 bool GLWChatView::initFromXMLInternal(XMLNode *node)
391 {
392 if (!node->getNamedChild("displaytime", displayTime_)) return false;
393 if (!node->getNamedChild("totallines", totalLines_)) return false;
394 if (!node->getNamedChild("visiblelines", visibleLines_)) return false;
395 if (!node->getNamedChild("linedepth", lineDepth_)) return false;
396 if (!node->getNamedChild("fontsize", fontSize_)) return false;
397 if (!node->getNamedChild("outlinefontsize", outlineFontSize_)) return false;
398 if (!node->getNamedChild("textaligntop", alignTop_)) return false;
399 if (!node->getNamedChild("parentsized", parentSized_)) return false;
400 if (!node->getNamedChild("splitlargelines", splitLargeLines_)) return false;
401 if (!node->getNamedChild("allowscroll", allowScroll_)) return false;
402
403 std::string scrollUpKey, scrollDownKey, scrollResetKey;
404 if (node->getNamedChild("scrollupkey", scrollUpKey, false))
405 {
406 scrollUpKey_ = Keyboard::instance()->getKey(scrollUpKey.c_str());
407 }
408 if (node->getNamedChild("scrolldownkey", scrollDownKey, false))
409 {
410 scrollDownKey_ = Keyboard::instance()->getKey(scrollDownKey.c_str());
411 }
412 if (node->getNamedChild("scrollresetkey", scrollResetKey, false))
413 {
414 scrollResetKey_ = Keyboard::instance()->getKey(scrollResetKey.c_str());
415 }
416 return true;
417 }
418