Fix css style order when using external css files
[ryzomcore.git] / ryzom / client / src / interfaces_manager / chat_control.cpp
blob44068c84a03f4a8acea9d48ce6317932e3c2cb75
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "stdpch.h"
21 /////////////
22 // Include //
23 /////////////
24 // Interface 3D
25 #include "nel/3d/u_driver.h"
26 #include "nel/3d/u_text_context.h"
27 // Client
28 #include "chat_control.h"
29 #include "interfaces_manager.h"
31 #include <stdlib.h> // for rand()
32 #include <time.h>
35 ///////////
36 // Using //
37 ///////////
38 using namespace NL3D;
39 using namespace NLMISC;
40 using namespace std;
43 ////////////
44 // Extern //
45 ////////////
46 extern UDriver *Driver;
47 extern UTextContext *TextContext;
51 * Constructor
53 CChatControl::CChatControl(uint id)
54 : CMultiList(id, 2)
56 init(0,0);
59 //-----------------------------------------------
60 // CChatControl :
61 // Constructor.
62 //-----------------------------------------------
63 CChatControl::CChatControl(uint id, float x, float y, float x_pixel, float y_pixel, float w, float h, float w_pixel, float h_pixel, uint leftFunc, uint rightFunc, const CPen &pen)
64 : CMultiList(id, x, y, x_pixel, y_pixel, w, h, w_pixel, h_pixel, pen, 2)
66 init(leftFunc, rightFunc);
67 }// CMultiList //
70 //-----------------------------------------------
71 // CMultiList :
72 // Constructor.
73 //-----------------------------------------------
74 CChatControl::CChatControl(uint id, float x, float y, float x_pixel, float y_pixel, float w, float h, float w_pixel, float h_pixel, uint leftFunc, uint rightFunc, uint32 fontSize, CRGBA color, bool shadow)
75 : CMultiList(id, x, y, x_pixel, y_pixel, w, h, w_pixel, h_pixel, fontSize, color, shadow, 2)
77 init(leftFunc, rightFunc);
78 }// CMultiList //
81 //-----------------------------------------------
82 // init :
83 //-----------------------------------------------
84 void CChatControl::init(uint left, uint right)
86 _LeftClickFunction = left;
87 _RightClickFunction = right;
89 _HistorySize = 200; // memorize the last 200 lines
90 _AutoScroll = true;
92 const float size = static_cast<float> (1.0/_NbCol);
93 _ColSize[0] = size/2;
94 _ColSize[1] = static_cast<float> (size * 1.5);
96 _LastStringHeight = 0;
97 _MouseX = 0;
98 _MouseY = 0;
100 _MutedColor = NLMISC::CRGBA(100,100,100);
101 _MutedHighlightColor = NLMISC::CRGBA(100,200,200);
102 _HighlightColor = NLMISC::CRGBA(255,255,255);
103 _SysTextColor = NLMISC::CRGBA(255,255,255);
105 _UsedColors.insert(_MutedColor);
106 _UsedColors.insert(_MutedHighlightColor);
107 _UsedColors.insert(_HighlightColor);
108 _UsedColors.insert(_SysTextColor);
110 _SystemDisplayText = ucstring("");
111 _CommandSid = CEntityId(); // \todo GUIGUI : REMOVE SID.
113 _SelectedPlayer = NULL;
114 _SelectedPlayerSid = NULL;
116 /* Seed the random-number generator with current time so that
117 * the numbers will be different every time we run.
119 srand( (unsigned)time( NULL ) );
121 // map the command dummy NLMISC::CEntityId with the display color
122 _PlayersColor.insert( std::make_pair( _CommandSid, &_SysTextColor ) );
123 }// init //
127 //-----------------------------------------------
128 // clear :
129 //-----------------------------------------------
130 void CChatControl::clear()
132 _ItemsList.clear();
133 _PlayersSid.clear();
134 _EndingIterator = _ItemsList.rbegin();
135 _EndingSidIterator = _PlayersSid.rbegin();
136 }// clear //
140 //-----------------------------------------------
141 // add :(private)
142 // DEPRECATED, do not use this function
143 //-----------------------------------------------
144 void CChatControl::add(const std::list<ucstring> &strList)
146 }// add //
149 //-----------------------------------------------
150 // add :
151 // add text from player
152 //-----------------------------------------------
153 void CChatControl::add( const NLMISC::CEntityId &sid, const ucstring &name, const ucstring &text)
155 /// \toto Malkav: ensure the name is different from system messages displayed text (_SystemDisplayText)
158 // if the player is muted, ignore this phrase
159 if ( _MutedPlayers.find( sid ) != _MutedPlayers.end() )
160 return;
162 // make a list with the name and text
163 static std::list<ucstring> ls;
164 ls.clear();
165 ls.push_back( name );
166 ls.push_back( text );
168 // add the NLMISC::CEntityId to the list of NLMISC::CEntityId
169 _PlayersSid.push_back( sid );
171 // if the max number of line isn't reached
172 if ( ( _HistorySize > _ItemsList.size() ) || ( _HistorySize == 0) )
174 // add the new string
175 _ItemsList.push_back( ls );
177 // if this is the first string, init the ending point
178 if ( _ItemsList.size() == 1)
180 _EndingIterator = _ItemsList.rbegin();
181 _EndingSidIterator = _PlayersSid.rbegin();
184 // max number reached, delete the oldest line and insert the new one
185 else
187 // remove the oldest string
188 _ItemsList.pop_front();
189 // remove the oldest NLMISC::CEntityId
190 _PlayersSid.pop_front();
192 // add the new one
193 _ItemsList.push_back( ls );
195 if ( (_AutoScroll) || (_ItemsList.size() == 1) )
197 _EndingIterator = _ItemsList.rbegin();
198 _EndingSidIterator = _PlayersSid.rbegin();
202 // if this a an unknown player:
203 if ( _PlayersColor.find( sid ) == _PlayersColor.end() )
205 // get a new display color for that player
206 static CRGBA color;
207 color = getNewColor();
209 // add the color to the set of existing color
210 std::pair<TSetColors::iterator, bool> ret = _UsedColors.insert( color );
211 //nlassert(ret.second == true);
212 if (ret.second != true)
214 nlwarning("<CChatControl::add> failed to insert color in _UsedColors !");
217 // map the NLMISC::CEntityId with the display color
218 _PlayersColor.insert( std::make_pair( sid, &(*(ret.first)) ) );
223 //-----------------------------------------------
224 // add :
225 // add system text
226 //-----------------------------------------------
227 void CChatControl::add(const ucstring &text)
230 // make a list with the name and text
231 static std::list<ucstring> ls;
232 ls.clear();
233 ls.push_back( _SystemDisplayText );
234 ls.push_back( text );
236 // add the dummy NLMISC::CEntityId to the list of NLMISC::CEntityId
237 _PlayersSid.push_back( _CommandSid );
239 // if the max number of line isn't reached
240 if ( ( _HistorySize > _ItemsList.size() ) || ( _HistorySize == 0) )
242 // add the new string
243 _ItemsList.push_back( ls );
245 // if this is the first string, init the ending point
246 if ( _ItemsList.size() == 1)
248 _EndingIterator = _ItemsList.rbegin();
249 _EndingSidIterator = _PlayersSid.rbegin();
252 // max number reached, delete the oldest line and insert the new one
253 else
255 // remove the oldest string
256 _ItemsList.pop_front();
257 // remove the oldest NLMISC::CEntityId
258 _PlayersSid.pop_front();
260 // add the new one
261 _ItemsList.push_back( ls );
263 if ( (_AutoScroll) || (_ItemsList.size() == 1) )
265 _EndingIterator = _ItemsList.rbegin();
266 _EndingSidIterator = _PlayersSid.rbegin();
269 }// add //
271 //-----------------------------------------------
272 // display :
273 // Display the control.
274 //-----------------------------------------------
275 void CChatControl::display()
277 // \todo GUIGUI : remove this damn thing after the UBI demo.
278 const CRGBA playerColors[10] =
280 CRGBA(250,250, 10), // 0 : Jaune
281 CRGBA( 50,200, 50), // 1 : Vert
282 CRGBA( 0, 0,255), // 2 : Bleu
283 CRGBA(255,100, 0), // 3 : Orange
284 CRGBA( 64, 0,128), // 4 : Violet
285 CRGBA(128,128, 0), // 5 : Verdatre
286 CRGBA(128, 0, 64), // 6 : Pourpre
287 CRGBA(255,128,192), // 7 : Rose
288 CRGBA( 0,255,255), // 8 : Bleu ciel
289 CRGBA(100,100,100) // 9 : Gris
292 // clear _NamesYPos
293 _NamesYPos.clear();
295 // If the control is hide -> return
296 if(!_Show)
297 return;
299 /// \todo GUIGUI : if the scissor is corrected -> draw the bitmap after the scissor.
300 // calculate the real display size ( clip if scroll bar is visible )
301 float wDisplay, yDisplay, hDisplay;
303 if (_VScroll->show() )
304 wDisplay = _W_Display - _VScroll->size();
305 else
306 wDisplay = _W_Display;
308 if (_HScroll->show() )
310 hDisplay = _H_Display - _HScroll->size();
311 yDisplay = _Y_Display + _HScroll->size();
313 else
315 hDisplay = _H_Display;
316 yDisplay = _Y_Display;
319 // Draw each Background
320 /* float w = 0 , x_display = _X_Display;
322 for (uint j = 0 ; j < _NbCol ; ++j)
324 w = _ColSize[j] * wDisplay;
325 Driver->drawBitmap(x_display , yDisplay, w, hDisplay, *CInterfMngr::getTexture(_BackgroundTexture), true, _BackgroundColor);
326 x_display += w;
330 /// \todo GUIGUI : initialize the scissor with oldScissor and remove tmp variables.
331 // Backup scissor and create the new scissor to clip the list correctly.
332 CScissor oldScissor = Driver->getScissor();
333 CScissor scissor;
335 float scisX, scisY, scisWidth, scisHeight;
336 scisX = oldScissor.X;
337 scisY = oldScissor.Y;
338 scisWidth = oldScissor.Width;
339 scisHeight = oldScissor.Height;
341 float xtmp = _X_Display + _ColSize[0] * wDisplay;
342 float ytmp = _Y_Display + _H_Display;
343 float xscistmp = scisX + scisWidth;
344 float yscistmp = scisY + scisHeight;
345 if(_X_Display>scisX)
346 scisX = _X_Display;
347 if(_Y_Display>scisY)
348 scisY = _Y_Display;
349 if(xtmp<xscistmp)
350 scisWidth = xtmp-scisX;
351 else
352 scisWidth = xscistmp-scisX;
353 if(ytmp<yscistmp)
354 scisHeight = ytmp-scisY;
355 else
356 scisHeight = yscistmp-scisY;
357 scissor.init(scisX, scisY, scisWidth, scisHeight);
358 Driver->setScissor(scissor);
361 // Display the text of the list.
362 TextContext->setShaded(_Shadow);
363 TextContext->setFontSize(_FontSize);
364 TextContext->setHotSpot(UTextContext::BottomLeft);
366 uint32 index;
367 UTextContext::CStringInfo info;
369 // if the ending item is the last item (so the first item in reverse order), hide/disable scrollbar
370 // if (_EndingIterator == _ItemsList.rbegin() )
371 // vScroll( false );
373 static std::list<ucstring>::const_iterator itText;
374 static NLMISC::CEntityId * sid = NULL;
376 TItemList::reverse_iterator ritList;
377 TSidList::reverse_iterator ritSidList = _EndingSidIterator;
379 const TItemList::reverse_iterator ritEndList = _ItemsList.rend();
380 const TMapSidToColor::iterator itColorEnd = _PlayersColor.end();
382 float maxH = 0;
383 float y = _Y_Display + _LastStringHeight/3.0f; // start a bit upper to have room to print a line
384 float x = _X_Display;
385 const float yLimit = yDisplay + hDisplay;
386 const float xLimit = _X_Display + wDisplay;
388 for( ritList = _EndingIterator ; ritList != ritEndList ; ++ritList)
390 maxH = 0;
391 x = _X_Display;
393 itText = (*ritList).begin();
394 sid = const_cast<NLMISC::CEntityId*> (&(*ritSidList));
396 // Test mouse position.
397 if ( (_MouseY >= y) && (_MouseY <= y + _LastStringHeight) && (_MouseX >= x) && (_MouseX <= xLimit))
399 // if player is muted
400 if ( (!_MutedPlayers.empty() ) && ( _MutedPlayers.find( *sid ) != _MutedPlayers.end() ) )
402 TextContext->setColor( _MutedHighlightColor );
404 else
405 TextContext->setColor( _HighlightColor );
407 else
409 // if player is muted
410 if ( (!_MutedPlayers.empty() ) && ( _MutedPlayers.find( *sid ) != _MutedPlayers.end() ) )
412 TextContext->setColor( _MutedColor );
414 else
416 // \todo GUIGUI : remove those thing after UBI demo.
417 // Get display color for that player.
418 TMapSidToColor::iterator itColor = _PlayersColor.find( *sid );
419 if(itColor != itColorEnd)
420 TextContext->setColor(playerColors[((*sid).Id / 10)%10]);
421 else
422 nlwarning("CChatControl::display : Player does not exist.");
423 // TextContext->setColor( *(*itColor).second );
427 // display player name
428 // if this is a command line, then, use SysColor;
429 if ( (*sid) == _CommandSid )
430 TextContext->setColor( _SysTextColor );
432 index = TextContext->textPush( *itText );
433 info = TextContext->getStringInfo(index);
434 info.convertTo01Size(Driver);
435 if (maxH < info.StringHeight)
436 maxH = info.StringHeight;
438 TextContext->printAt(x, y, index);
439 TextContext->erase(index);
440 x += _ColSize[0] * wDisplay;
442 // insert player names and NLMISC::CEntityId coordinates in the _NamesYPos map (only if it's not a system message)
443 if ( (*sid) != _CommandSid)
444 _NamesYPos.push_back( std::make_pair( std::make_pair(y, y+info.StringHeight), std::make_pair( &(*itText), sid) ) );
446 ++ itText;
448 // set the new scissor
449 scisX = oldScissor.X;
450 scisWidth = oldScissor.Width;
452 xtmp = _X_Display + _W_Display;
453 xscistmp = scisX + scisWidth;
455 if(xtmp<xscistmp)
456 scisWidth = xtmp-scisX;
457 else
458 scisWidth = xscistmp-scisX;
460 scissor.init(scisX, scisY, scisWidth, scisHeight);
461 Driver->setScissor(scissor);
463 // print text
464 index = TextContext->textPush( *itText );
465 info = TextContext->getStringInfo(index);
466 info.convertTo01Size(Driver);
467 if (maxH < info.StringHeight)
468 maxH = info.StringHeight;
470 TextContext->printAt(x, y, index);
471 TextContext->erase(index);
472 x += _ColSize[1] * wDisplay;
474 _LastStringHeight = info.StringHeight;
476 // if display boundaries are reached, enable scroll bar and stop displaying
477 y += maxH;
478 if(y >= yLimit)
480 vScroll( true );
481 break;
484 ++ ritSidList;
487 // Restore Scissor.
488 Driver->setScissor(oldScissor);
490 // draw scrollBar
491 _VScroll->display();
493 }// display //
497 //-----------------------------------------------
498 // click :
499 // Manage the click of the mouse for the Button.
500 //-----------------------------------------------
501 void CChatControl::click(float x, float y, bool &taken)
503 if (_VScroll != NULL)
504 _VScroll->click(x,y,taken);
506 // if click still not caught by a control
507 if ( !taken)
509 // if user has clicked on a player ID, mute/unmute that player
510 // test x coordinate
511 if ( (x >= _X_Display) && ( x <= (_X_Display + _W_Display * _ColSize[0]) ) )
513 // find the name with the y coordinate
514 bool found = searchPlayerAtPos( y ,_SelectedPlayerSid, _SelectedPlayer ) ;
516 // if a player was found
517 if ( found )
519 CInterfMngr::runFuncCtrl(_LeftClickFunction, id());
520 taken = true;
525 _SelectedPlayer = NULL;
526 _SelectedPlayerSid = NULL;
527 }// click //
531 //-----------------------------------------------
532 // clickRight :
533 // Manage the click of the mouse for the Button.
534 //-----------------------------------------------
535 void CChatControl::clickRight(float x, float y, bool &taken)
538 if ( !taken )
540 // if user has right clicked on a player, open the good pop-up menu
541 // if user has clicked on a player ID, mute/unmute that player
542 // test x coordinate
543 if ( (x >= _X_Display) && ( x <= (_X_Display + _W_Display * _ColSize[0]) ) )
545 // find the name with the y coordinate
546 bool found = searchPlayerAtPos( y ,_SelectedPlayerSid, _SelectedPlayer ) ;
548 // if a player was found
549 if ( found )
551 CInterfMngr::runFuncCtrl(_RightClickFunction, id());
552 taken = true;
557 _SelectedPlayer = NULL;
558 _SelectedPlayerSid = NULL;
559 }// clickRight //
563 //-----------------------------------------------
564 // mouseMove :
565 //-----------------------------------------------
566 void CChatControl::mouseMove( float x, float y)
568 _MouseX = x;
569 _MouseY = y;
570 }// mouseMove //
574 //-----------------------------------------------
575 // searchNameAtPos :
576 //-----------------------------------------------
577 bool CChatControl::searchPlayerAtPos(float y, NLMISC::CEntityId *&sid, ucstring *&name) const
579 // allready test if y is within display limits
580 if ( (y < _Y_Display) || (y>_Y_Display + _H_Display) )
581 return NULL;
584 const TPairPFloatPStrSid::const_iterator itB = _NamesYPos.begin();
585 const TPairPFloatPStrSid::const_iterator itEnd = _NamesYPos.end();
586 TPairPFloatPStrSid::const_iterator it = itB;
588 const uint32 size = _NamesYPos.size();
590 if (size == 0) return NULL;
592 // we know the first name is at _Y_Display and the last one at _Y_Display+_H_Display (approx.)
593 // so we make an interpolation of name position in the list according to the y coordinate and the limits coordinates
594 for ( uint32 start = static_cast<uint32> ( (y - _Y_Display)/(_H_Display) * size) ; start > 0 ; --start)
596 ++it;
597 if (it == itEnd)
599 --it;
600 break;
605 float yMin = (*it).first.first, yMax = (*it).first.second;
607 while ( (y < yMin) )
609 if (it == itB)
611 return NULL;
613 --it;
614 yMin = (*it).first.first;
616 while ( (y > yMax) )
618 ++it;
619 if (it == itEnd)
621 return NULL;
623 yMax = (*it).first.second;
626 if ( y <= yMax && y >= yMin)
628 name = const_cast<ucstring*> ( (*it).second.first );
629 sid = const_cast<NLMISC::CEntityId*> ( (*it).second.second );
630 return true;
633 return false;
635 }// searchNameAtPos //
638 //-----------------------------------------------
639 // getNewColor :
640 //-----------------------------------------------
641 CRGBA CChatControl::getNewColor() const
643 static CRGBA color(255,0,0);
645 const TSetColors::const_iterator itColorsEnd = _UsedColors.end();
647 uint16 max = 0;
649 while( (_UsedColors.find( color ) != itColorsEnd )&& (max <180) )
651 // generate a new color
652 // randomly add a number beetween 10 and 200 (in absolute) to each composant (rgb)
654 sint8 val = static_cast<sint8> ( (rand() - RAND_MAX/2) % 190 );
655 if (val>=0) val+=10;
656 else val -= 10;
658 color.R += val;
659 max = color.R;
661 val = static_cast<sint8> ( (rand() - RAND_MAX/2) % 190 );
662 if (val>=0) val+=10;
663 else val -= 10;
665 color.G += val;
666 if( color.G > max)
667 max = color.G;
669 val = static_cast<sint8> ( (rand() - RAND_MAX/2) % 190 );
670 if (val>=0) val+=10;
671 else val -= 10;
673 color.B += val;
674 if( color.B > max)
675 max = color.B;
678 return color;
679 }// getNewColor //
683 //-----------------------------------------------
684 // scrollV :
685 //-----------------------------------------------
686 void CChatControl::scrollV(sint32 scroll)
688 // scrolling downward
689 if (scroll < 0)
691 const TItemList::reverse_iterator itB = _ItemsList.rbegin();
693 while ( ( _EndingIterator != itB) && (scroll != 0) )
695 --_EndingIterator;
696 --_EndingSidIterator;
697 ++scroll;
700 // scrolling upward
701 else
703 const TItemList::reverse_iterator itE = --_ItemsList.rend();
705 while ( ( _EndingIterator != itE) && (scroll != 0) )
707 ++_EndingIterator;
708 ++_EndingSidIterator;
709 --scroll;
712 }// scrollV //