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