1 #include "pch.h"
2 #include "common/Def_Str.h"
3 #include "common/Gui_Def.h"
4 #include "../vdrift/pathmanager.h"
5 #include "../vdrift/settings.h"
6 #include "../vdrift/game.h"
7 #include "CGame.h"
8 #include "CGui.h"
9 #include "common/data/CData.h"
10 #include "common/data/TracksXml.h"
11 #include "common/GuiCom.h"
12 #include "common/MultiList2.h"
13 #include "common/Slider.h"
14 #include "../network/gameclient.hpp"
15 #include <boost/filesystem.hpp>
16 #include <OgreTimer.h>
17 #include <MyGUI_Gui.h>
18 #include <MyGUI_TabControl.h>
19 #include <MyGUI_TabItem.h>
20 #include <MyGUI_EditBox.h>
21 #include <MyGUI_Window.h>
22 #include <MyGUI_ImageBox.h>
23 #include <MyGUI_MultiListBox.h>
24 #include <MyGUI_PolygonalSkin.h>
25 #include "../tinyxml/tinyxml2.h"
26 using namespace std;
27 using namespace Ogre;
28 using namespace MyGUI;
29 using namespace tinyxml2;
30 
31 
32 ///  Car list
33 
34 //  sort	 . . . . . . . . . . . . . . . . . . . . . .
35 //-----------------------------------------------------------------------------------------------------------
36 /*  common sort code,  no info only by name  */
37 #define sArg  const CarL& c2, const CarL& c1
38 #define sortDef  bool t = false;  if (!c1.ci || !c2.ci)  return c1.name > c2.name || t;
39 const int allSortFunc = 5;
40 
41 //  sorting functions for columns
Sort0(sArg)42 /* 0  name    */  bool Sort0 (sArg){  sortDef  return c1.name  < c2.name   || t;  }
Sort1(sArg)43 /* 1  speed   */  bool Sort1 (sArg){  sortDef  return c1.ci->speed < c2.ci->speed || t;  }
Sort2(sArg)44 /* 2  year    */  bool Sort2 (sArg){  sortDef  return c1.ci->year < c2.ci->year  || t;  }
Sort3(sArg)45 /* 3  type    */  bool Sort3 (sArg){  sortDef  return c1.ci->type < c2.ci->type || t;  }
Sort4(sArg)46 /* 4  rate    */  bool Sort4 (sArg){  sortDef  return c1.ci->rating < c2.ci->rating || t;  }
47 
48 //  sorting functions array (to access by column index)
49 bool (*CarSort[allSortFunc])(const CarL& c1, const CarL& c2) = {
50 	Sort0, Sort1, Sort2, Sort3, Sort4 };
51 
52 
53 //  done every list sort column change or find edit text change
54 //  fills gui cars list
55 //-----------------------------------------------------------------------------------------------------------
CarListUpd(bool resetNotFound)56 void CGui::CarListUpd(bool resetNotFound)
57 {
58 	bool filter = isChallGui();
59 
60 	if (carList)
61 	{	carList->removeAllItems();
62 		int ii = 0, si = -1;  bool bFound = false;
63 
64 		//  sort
65 		int numFunc = min(allSortFunc-1, (int)carList->mSortColumnIndex);
66 		std::list<CarL> liCar2 = liCar;  // copy
67 		liCar2.sort(CarSort[numFunc]);
68 		if (carList->mSortUp)  liCar2.reverse();
69 
70 		//  original
71 		for (std::list<CarL>::iterator i = liCar2.begin(); i != liCar2.end(); ++i)
72 		{
73 			String name = (*i).name;  //, nlow = name;  StringUtil::toLowerCase(nlow);
74 			//if (sTrkFind == "" || strstr(nlow.c_str(), sTrkFind.c_str()) != 0)
75 
76 			///  filter for challenge
77 			if (!filter || IsChallCar(name))
78 			{
79 				AddCarL(name, (*i).ci);
80 				if (name == pSet->gui.car[0])  {  si = ii;
81 					carList->setIndexSelected(si);
82 					bFound = true;  }
83 				++ii;
84 		}	}
85 
86 		//  not found last car, set last
87 		if (resetNotFound && !bFound)
88 			pSet->gui.car[0] = carList->getItemNameAt(carList->getItemCount()-1).substr(7);
89 
90 		if (si > -1)  // center
91 			carList->beginToItemAt(max(0, si-5));
92 	}
93 }
94 
AddCarL(string name,const CarInfo * ci)95 void CGui::AddCarL(string name, const CarInfo* ci)
96 {
97 	MultiList2* li = carList;
98 	CarInfo cci;
99 	if (!ci)  ci = &cci;  //  details
100 	String clr = data->cars->colormap[ci->type];  if (clr.length() != 7)  clr = "#C0D0E0";
101 
102 	li->addItem(clr+ name);  int l = li->getItemCount()-1; //, y = ci->year%100;
103 	li->setSubItemNameAt(1,l, gcom->clrsDiff[std::min(8, (int)(ci->speed*0.9f))]+ fToStr(ci->speed,1,3));
104 	//li->setSubItemNameAt(2,l, clr+ "\'"+toStr(y/10)+toStr(y%10));
105 	li->setSubItemNameAt(2,l, clr+ toStr(ci->year));
106 	li->setSubItemNameAt(3,l, clr+ TR("#{CarType_"+ci->type+"}"));
107 }
108 
FillCarList()109 void CGui::FillCarList()
110 {
111 	liCar.clear();
112 	strlist li;
113 	PATHMANAGER::DirList(PATHMANAGER::Cars(), li);
114 	for (strlist::iterator i = li.begin(); i != li.end(); ++i)
115 	{
116 		if (boost::filesystem::exists(PATHMANAGER::Cars() + "/" + *i + "/about.txt"))
117 		{	String s = *i;
118 			CarL c;  c.name = *i;  //c.pA = this;
119 			int id = data->cars->carmap[*i];
120 			if (id)
121 			{	c.ci = &data->cars->cars[id-1];
122 				liCar.push_back(c);
123 	}	}	}
124 }
125 //-----------------------------------------------------------------------------------------------------------
126 
127 
128 //  ghost filename
129 //
ghostFile(SETTINGS * pSet,string sim_mode,string car)130 string ghostFile(SETTINGS* pSet, string sim_mode, string car)
131 {
132 	return PATHMANAGER::Ghosts()+"/" +sim_mode+"/"
133 		+ pSet->game.track + (pSet->game.track_user ? "_u" : "") + (pSet->game.trackreverse ? "_r" : "")
134 		+ "_" + car + ".rpl";
135 }
136 
GetGhostFile(string * ghCar)137 const String& CGui::GetGhostFile(string* ghCar)
138 {
139 	static String file;
140 	string sim_mode = pSet->game.sim_mode, car = pSet->game.car[0];
141 	file = ghostFile(pSet, sim_mode, car);
142 	if (PATHMANAGER::FileExists(file))
143 		return file;
144 
145 	if (!ghCar)
146 		return file;
147 
148 	///--  if doesnt exist look for other cars, then other sim modes
149 
150 	//  cars list sorted by car speed
151 	std::list<CarL> liCar2 = liCar;
152 	liCar2.sort(CarSort[1]);
153 
154 	std::vector<string> cars;
155 	for (std::list<CarL>::iterator i = liCar2.begin(); i != liCar2.end(); ++i)
156 	{
157 		String name = (*i).name;
158 		cars.push_back(name);
159 		//LogO(name);
160 	}
161 
162 	//  find current
163 	int i = 0, si = cars.size(), ci = 0;
164 	while (i < si)
165 	{	if (cars[i] == car)
166 		{	ci = i;  break;  }
167 		++i;
168 	}
169 	//LogO(toStr(ci)+" ci "+cars[ci]+" all "+toStr(si));
170 
171 	std::vector<string> cars2;
172 	int a = ci, b = ci;  i = 0;
173 	cars2.push_back(cars[ci]);  // 1st cur
174 	while (cars2.size() < si)  // same size
175 	{	// +1, -1, +2, -2 ..
176 		if (i % 2 == 0)
177 		{	++a;  // next faster car
178 			if (a < si)  cars2.push_back(cars[a]);
179 		}else
180 		{	--b;  // next slower car
181 			if (b >= 0)  cars2.push_back(cars[b]);
182 		}	++i;
183 	}
184 	//for (i=0; i < cars2.size(); ++i)
185 	//	LogO(toStr(i)+"> "+cars2[i]);
186 
187 	bool srch = true;
188 	i = 0;  a = 0;
189 	while (srch)
190 	{
191 		const string& car = cars2[i];
192 		file = ghostFile(pSet, sim_mode, car);
193 
194 		if (PATHMANAGER::FileExists(file))
195 		{	srch = false;  *ghCar = car;  }
196 		++i;
197 		if (i >= si)
198 		{	i = 0;
199 			if (sim_mode == "easy")  sim_mode = "normal";
200 			else  sim_mode = "easy";
201 			++a;  if (a==2)  srch = false;  // only those 2
202 		}
203 	}
204 	return file;
205 }
206 
GetRplListDir()207 string CGui::GetRplListDir()
208 {
209 	return (pSet->rpl_listghosts
210 		? (PATHMANAGER::Ghosts() + "/" + pSet->gui.sim_mode)
211 		: PATHMANAGER::Replays() );
212 }
213 
214 
215 //  [Game] 	. . . . . . . . . . . . . . . . . . . .    --- lists ----    . . . . . . . . . . . . . . . . . .
216 
217 //  car
listCarChng(MultiList2 * li,size_t)218 void CGui::listCarChng(MultiList2* li, size_t)
219 {
220 	size_t i = li->getIndexSelected();  if (i==ITEM_NONE)  return;
221 	Ogre::Timer ti;
222 	sListCar = li->getItemNameAt(i).substr(7);
223 
224 	if (imgCar && !pSet->dev_no_prvs)  imgCar->setImageTexture(sListCar+".jpg");
225 	if (app->mClient)  app->mClient->updatePlayerInfo(pSet->nickname, sListCar);
226 
227 	//  car desc txt
228 	String sd = String("#BFD3E5")+TR("#{CarDesc_"+sListCar+"}");
229 
230 	//  car info
231 	bool car = true;
232 	int id = data->cars->carmap[sListCar];
233 	if (id > 0 && txCarSpeed && barCarSpeed)
234 	{	const CarInfo& ci = data->cars->cars[id-1];
235 		car = ci.car;
236 
237 		txCarAuthor->setCaption(ci.author);
238 		txCarSpeed->setCaption(gcom->clrsDiff[std::min(8, (int)(ci.speed*0.9f))]+ fToStr(ci.speed,1,3));
239 		txCarType->setCaption(data->cars->colormap[ci.type]+ TR("#{CarType_"+ci.type+"}"));
240 		txCarYear->setCaption(toStr(ci.year));
241 
242 		if (ci.type == "Spaceship" || ci.type == "Other")
243 			sd += TR("#E0E060 #{CarDesc_Pipes}");
244 
245 		float v = std::max(0.f, 1.f - ci.speed/13.f);
246 		barCarSpeed->setImageCoord(IntCoord(v*128.f,0,128,16));
247 		barCarSpeed->setColour(Colour(1.f, 0.2f + 0.8f * v, v * 0.3f));
248 	}
249 	carDesc->setCaption(sd);
250 
251 	changeCar();
252 	UpdCarStats(car);
253 	//LogO(String(":::: Time car tab upd: ") + fToStr(ti.getMilliseconds(),0,3) + " ms");
254 }
changeCar()255 void CGui::changeCar()
256 {
257 	if (iCurCar < 4)
258 		pSet->gui.car[iCurCar] = sListCar;
259 }
260 
261 
262 ///  load car stats xml
263 //-----------------------------------------------------------------------------------------------------------
UpdCarStats(bool car)264 void CGui::UpdCarStats(bool car)
265 {
266 	string path = PATHMANAGER::CarSim() + "/" + pSet->gui.sim_mode + "/cars/" + sListCar + "_stats.xml";
267 	float f;
268 	#define  vis(i,v)  \
269 		{  txCarStTxt[i]->setVisible(v);  txCarStVals[i]->setVisible(v);  barCarSt[i]->setVisible(v);  }
270 
271 	XMLDocument doc;  int i;
272 	XMLError er = doc.LoadFile(path.c_str());
273 	if (er != XML_SUCCESS)
274 	{
275 		for (i=0; i < iCarSt; ++i)	vis(i,false);
276 		return;
277 	}
278 	XMLElement* root = doc.RootElement();
279 	if (!root)  return;
280 
281 	for (i=0; i < iCarSt; ++i)	vis(i,true);
282 
283 	//  read xml  --------
284 	XMLElement* e;  const char* a;
285 	float mass=0.f, comFront=0.f,  maxTrq=0.f, rpmMaxTq=0.f, maxPwr=0.f, rpmMaxPwr=0.f, bhpPerTon=0.f,
286 		maxVel=0.f, tiMaxVel=0.f,  t0to60=0.f, t0to100=0.f, t0to160=0.f, t0to200=0.f,
287 		stop60=0.f, stop100=0.f, stop160=0.f,  down100=0.f, down160=0.f, down200=0.f;
288 
289 	e = root->FirstChildElement("car");
290 	if (e)
291 	{	a = e->Attribute("mass");	if (a)  mass = s2r(a);
292 		//a = e->Attribute("inertia");	if (a)  inert = s2v(a);
293 	}
294 	e = root->FirstChildElement("com");
295 	if (e)
296 	{	a = e->Attribute("frontPercent");	if (a)  comFront = s2r(a);
297 		//a = e->Attribute("pos");	if (a)  com = s2v(a);
298 		//a = e->Attribute("whf");	if (a)  whf = s2r(a);
299 		//a = e->Attribute("whr");	if (a)  whr = s2r(a);
300 	}
301 	e = root->FirstChildElement("torque");
302 	if (e)
303 	{	a = e->Attribute("max");	if (a)  maxTrq = s2r(a);
304 		//a = e->Attribute("rpm");	if (a)  rpmMaxTq = s2r(a);
305 		//a = e->Attribute("mul");	if (a)  mul = s2r(a);
306 	}
307 	e = root->FirstChildElement("power");
308 	if (e)
309 	{	a = e->Attribute("max");	if (a)  maxPwr = s2r(a);
310 		//a = e->Attribute("rpm");	if (a)  rpmMaxPwr = s2r(a);
311 	}
312 	e = root->FirstChildElement("bhpPerTon");
313 	if (e)
314 	{	a = e->Attribute("val");	if (a)  bhpPerTon = s2r(a);
315 	}
316 	e = root->FirstChildElement("top");
317 	if (e)
318 	{	a = e->Attribute("speed");	if (a)  maxVel = s2r(a);
319 		//a = e->Attribute("time");	if (a)  tiMaxVel = s2r(a);
320 	}
321 	/*e = root->FirstChildElement("quarterMile");
322 	if (e)
323 	{	a = e->Attribute("time");	if (a)  timeQM = s2r(a);
324 		a = e->Attribute("vel");	if (a)  velAtQM = s2r(a);
325 	}*/
326 	e = root->FirstChildElement("accel");
327 	if (e)
328 	{	a = e->Attribute("t60");	if (a)  t0to60 = s2r(a);
329 		a = e->Attribute("t100");	if (a)  t0to100 = s2r(a);
330 		a = e->Attribute("t160");	if (a)  t0to160 = s2r(a);
331 		a = e->Attribute("t200");	if (a)  t0to200 = s2r(a);
332 	}
333 	/*e = root->FirstChildElement("downForce");
334 	if (e)
335 	{	a = e->Attribute("d100");	if (a)  down100 = s2r(a);
336 		a = e->Attribute("d160");	if (a)  down160 = s2r(a);
337 		a = e->Attribute("d200");	if (a)  down200 = s2r(a);
338 	}*/
339 	e = root->FirstChildElement("stop");
340 	if (e)
341 	{	//a = e->Attribute("s160");	if (a)  stop160 = s2r(a);
342 		a = e->Attribute("s100");	if (a)  stop100 = s2r(a);
343 		//a = e->Attribute("s60");	if (a)  stop60 = s2r(a);
344 	}
345 
346 	//  speed graph points
347 	e = root->FirstChildElement("velGraph");
348 	std::vector<float> ttim,tkmh;
349 	if (e)
350 	{	float t,v;
351 		XMLElement* p = e->FirstChildElement("p");
352 		while (p)
353 		{	a = p->Attribute("t");  if (a) {  t = s2r(a);
354 			a = p->Attribute("v");	if (a) {  v = s2r(a);
355 				ttim.push_back(t);  tkmh.push_back(v);
356 			}	}
357 			p = p->NextSiblingElement("p");
358 	}	}
359 
360 
361 	///  upd vel graph  ~~~
362 	float xs = 10.f, ys = 0.4f, yo = 166.f, x2 = 500.f;
363 	const IntSize& wi = app->mWndOpts->getSize();
364 	const float sx = wi.width/1248.f, sy = wi.height/935.f;
365 	xs *= sx;  ys *= sy;  yo *= sy;  x2 *= sx;
366 
367 	std::vector<FloatPoint> points,grid;
368 	points.push_back(FloatPoint(0.f, yo));
369 	for (i = 0; i < (int)ttim.size(); ++i)
370 		points.push_back(FloatPoint(ttim[i] * xs, yo - ys * tkmh[i]));
371 	graphVel->setPoints(points);
372 
373 	//  grid lines
374 	const int y1 = yo +10, y2 = -10, x1 = -10;  //outside
375 	for (i = 0; i < 6; ++i)  // ||
376 	{	float fi = i > 2 ? 10.f*(i-1) : 5.f*i;
377 		grid.push_back(FloatPoint(fi * xs,  i%2==0 ? y1 : y2));
378 		grid.push_back(FloatPoint(fi * xs,  i%2==0 ? y2 : y1));
379 	}
380 	grid.push_back(FloatPoint(x2, y1));
381 	grid.push_back(FloatPoint(x1, y1));
382 	for (i = 0; i < 4; ++i)  // ==
383 	{	grid.push_back(FloatPoint(i%2==0 ? x1 : x2,  yo - ys * i * 100.f));
384 		grid.push_back(FloatPoint(i%2==0 ? x2 : x1,  yo - ys * i * 100.f));
385 	}
386 	graphVGrid->setPoints(grid);
387 
388 
389 	//  upd text  --------
390 	bool kmh = !pSet->show_mph;  float k2m = 0.621371f;
391 	String s[iCarSt], v[iCarSt];
392 	float sm = pSet->gui.sim_mode == "easy" ? 0.75f : 1.f;
393 
394 	#define bar(n,sc, r,g,b)  \
395 		f = std::max(0.f, (1.f - sc)) * 128.f;  barCarSt[n]->setImageCoord(IntCoord(f,0,128,16));  \
396 		barCarSt[n]->setColour(Colour(r,g,b));
397 
398 	s[0]= "#80E080"+ TR("#{Car_Mass}");
399 	v[0]= "#90FF90"+ fToStr(mass,0,3) +TR(" #{UnitKg}");
400 	bar(0, mass / 3000.f, 0.6,1.0,0.6);
401 	s[1]= "#B0E0B0"+ TR("#{Car_MassFront}");
402 	v[1]= "#C0FFC0"+ fToStr(comFront,0,3) +"%";
403 	bar(1, comFront / 100.f, 0.8,1.0,0.8);
404 
405 	s[2]= "#E0C0A0"+ TR("#{Car_MaxTorque}");
406 	v[2]= "#F0D0B0"+ fToStr(maxTrq,0,3) +TR(" #{UnitNm}");  //  #{at} ")+ fToStr(rpmMaxTq,0,3) +TR(" #{UnitRpm} ");
407 	bar(2, maxTrq / 970.f, 0.9,0.8,0.6);  vis(2,car);
408 	s[3]= "#E0B090"+ TR("#{Car_MaxPower}");
409 	v[3]= "#F0C0A0"+ fToStr(maxPwr,0,3) +TR(" #{UnitBhp}");  //  #{at} ")+ fToStr(rpmMaxPwr,0,3) +TR(" #{UnitRpm} ");
410 	bar(3, maxPwr / 700.f, 0.9,0.7,0.5);  vis(3,car);
411 	s[4]= "#E0E0A0"+ TR("#{Car_BhpPerTon}");
412 	v[4]= "#F0F0B0"+ fToStr(bhpPerTon,0,3);
413 	bar(4, bhpPerTon / 450.f, 1.0,1.0,0.6);  vis(4,car);
414 
415 	#define sVel(s,v)  \
416 		if (kmh)  s += fToStr(v, 0,3) +TR(" #{UnitKmh}");  \
417 		    else  s += fToStr(v*k2m, 0,3) +TR(" #{UnitMph}");
418 
419 	s[5]= "#80C0FF"+ TR("#{Car_TopSpeed}");
420 	v[5]= "#90D0FF";  sVel(v[5], maxVel);  //v[5]+= TR("  #{at} ")+ fToStr(tiMaxVel,1,4) +TR(" #{UnitS} ");
421 	bar(5, maxVel / 300.f, 0.6,0.9,1.0);
422 
423 	s[6]= "#8ECEFE"+ TR("#{Car_TimeTo} ");  sVel(s[6], 100.f);
424 	v[6]= "#9EDEFF"+ fToStr(t0to100,1,4) +TR(" #{UnitS} ");
425 	bar(6, t0to100 / 8.f * sm, 0.8,1.0,1.0);
426 
427 	s[7]= "#88C8F8"+ TR("#{Car_TimeTo} ");  sVel(s[7], 160.f);
428 	v[7]= "#98D8FF"+ fToStr(t0to160,1,4) +TR(" #{UnitS} ");
429 	bar(7, t0to160 / 17.f * sm, 0.75,1.0,1.0);  vis(7, t0to160 > 0.f);
430 
431 	s[8]= "#80C0F0"+ TR("#{Car_TimeTo} ");  sVel(s[8], 200.f);
432 	v[8]= "#90D0FF"+ fToStr(t0to200,1,4) +TR(" #{UnitS} ");
433 	bar(8, t0to200 / 27.f * sm, 0.7,1.0,1.0);  vis(8, t0to200 > 0.f);
434 
435 	s[9]= "#70E0E0"+ TR("#{Car_StopTimeFrom} ");  sVel(s[9], 100.f);
436 	v[9]= "#80F0F0"+ fToStr(stop100,1,4) +TR(" #{UnitS} ");
437 	bar(9, stop100 / 5.f * sm, 0.5,1.0,1.0);
438 
439 	for (i=0; i < iCarSt; ++i)
440 	{	txCarStTxt[i]->setCaption(s[i]);  txCarStVals[i]->setCaption(v[i]);  }
441 }
442 
443 
444 //  track
changeTrack()445 void CGui::changeTrack()
446 {
447 	pSet->gui.track = gcom->sListTrack;
448 	pSet->gui.track_user = gcom->bListTrackU;
449 							//_ only for host..
450 	if (app->mMasterClient && valNetPassword->getVisible())
451 	{	uploadGameInfo();
452 		updateGameInfoGUI();  }
453 }
454 
455 //  new game
btnNewGame(WP wp)456 void CGui::btnNewGame(WP wp)
457 {
458 	if (app->mWndGame->getVisible() && app->mWndTabsGame->getIndexSelected() < TAB_Champs  || app->mClient)
459 		BackFromChs();  /// champ, back to single race
460 
461 	bool force = false;
462 	if (wp)
463 	{	string s = wp->getName();
464 		s = s.substr(s.length()-1,1);
465 		bool force = s=="3" || s=="4";
466 	}
467 	app->NewGame(force);  app->isFocGui = false;  // off gui
468 	app->mWndOpts->setVisible(app->isFocGui);
469 	app->mWndRpl->setVisible(false);//
470 	gcom->bnQuit->setVisible(app->isFocGui);
471 
472 	app->updMouse();
473 
474 	gcom->mToolTip->setVisible(false);
475 }
btnNewGameStart(WP wp)476 void CGui::btnNewGameStart(WP wp)
477 {
478 	changeTrack();
479 	btnNewGame(wp);
480 }
481 
482 
483 //  Menu
484 //-----------------------------------------------------------------------------------------------------------
485 
toggleGui(bool toggle)486 void CGui::toggleGui(bool toggle)
487 {
488 	if (toggle)
489 		app->isFocGui = !app->isFocGui;
490 
491 	bool notMain = app->isFocGui && !pSet->isMain;
492 	app->mWndMain->setVisible(app->isFocGui && pSet->isMain);
493 	app->mWndReplays->setVisible(notMain && pSet->inMenu == MNU_Replays);
494 	app->mWndHelp->setVisible(notMain && pSet->inMenu == MNU_Help);
495 	app->mWndOpts->setVisible(notMain && pSet->inMenu == MNU_Options);
496 	if (!app->isFocGui)  app->mWndTrkFilt->setVisible(false);
497 
498 	//  load Readme editbox from file
499 	if (app->mWndHelp->getVisible() && loadReadme)
500 	{
501 		loadReadme = false;
502 		Ed ed = fEd("Readme");
503 		if (ed)
504 		{	string path = PATHMANAGER::Data()+"/../Readme.txt";
505 			std::ifstream fi(path.c_str());
506 			if (fi.good())
507 			{	String text = "", s;
508 				while (getline(fi,s))
509 					text += s + "\n";
510 
511 				text = StringUtil::replaceAll(text, "#", "##");
512 				ed->setCaption(UString(text));
513 				ed->setVScrollPosition(0);
514 	}	}	}
515 
516 	///  update track tab, for champs wnd
517 	bool game = pSet->inMenu == MNU_Single, champ = pSet->inMenu == MNU_Champ,
518 		tutor = pSet->inMenu == MNU_Tutorial, chall = pSet->inMenu == MNU_Challenge,
519 		chAny = champ || tutor || chall, gc = game || chAny;
520 	UString sCh = chall ? TR("#90FFD0#{Challenge}") : (tutor ? TR("#FFC020#{Tutorial}") : TR("#80C0FF#{Championship}"));
521 
522 	UpdChampTabVis();
523 
524 	bool vis = notMain  && gc;
525 	app->mWndGame->setVisible(vis);
526 	if (vis)
527 	{
528 		app->mWndGame->setCaption(chAny ? sCh : TR("#{SingleRace}"));
529 		TabItem* t = app->mWndTabsGame->getItemAt(TAB_Champs);
530 		t->setCaption(sCh);
531 	}
532 	if (notMain && gc)  // show hide champs,stages
533 	{
534 		Tab t = app->mWndTabsGame;
535 		size_t id = t->getIndexSelected();
536 		t->setButtonWidthAt(TAB_Track, chAny ? 1 :-1);  if (id == TAB_Track && chAny)  t->setIndexSelected(TAB_Champs);
537 		t->setButtonWidthAt(TAB_Multi, chAny ? 1 :-1);  if (id == TAB_Multi && chAny)  t->setIndexSelected(TAB_Champs);
538 		t->setButtonWidthAt(TAB_Champs,chAny ?-1 : 1);  if (id == TAB_Champs && !chAny)  t->setIndexSelected(TAB_Track);
539 		t->setButtonWidthAt(TAB_Stages,chAny ?-1 : 1);  if (id == TAB_Stages && !chAny)  t->setIndexSelected(TAB_Track);
540 		t->setButtonWidthAt(TAB_Stage, chAny ?-1 : 1);  if (id == TAB_Stage  && !chAny)  t->setIndexSelected(TAB_Track);
541 	}
542 
543 	gcom->bnQuit->setVisible(app->isFocGui);
544 	app->updMouse();
545 	if (!app->isFocGui)  gcom->mToolTip->setVisible(false);
546 
547 	for (int i=0; i < ciMainBtns; ++i)
548 		app->mWndMainPanels[i]->setVisible(pSet->inMenu == i);
549 
550 	//  1st center mouse
551 	static bool first = true;
552 	if (app->isFocGui && first)
553 	{	first = false;
554 		gcom->GuiCenterMouse();
555 	}
556 }
557 
558 
559 //  Gui Shortcut  alt-letters
560 //.......................................................................................
GuiShortcut(MNU_Btns mnu,int tab,int subtab)561 void CGui::GuiShortcut(MNU_Btns mnu, int tab, int subtab)
562 {
563 	if (subtab == -1 && (!app->isFocGui || pSet->inMenu != mnu))
564 		subtab = -2;  // cancel subtab cycling
565 
566 	app->isFocGui = true;
567 	pSet->isMain = false;
568 	if (!(tab == TAB_Car && pSet->inMenu >= MNU_Tutorial && pSet->inMenu <= MNU_Challenge))  //
569 		pSet->inMenu = mnu;
570 
571 	TabPtr mWndTabs = 0;
572 	std::vector<TabControl*>* subt = 0;
573 
574 	switch (mnu)
575 	{	case MNU_Replays:	mWndTabs = app->mWndTabsRpl;  break;
576 		case MNU_Help:		mWndTabs = app->mWndTabsHelp;  break;
577 		case MNU_Options:	mWndTabs = app->mWndTabsOpts;  subt = &vSubTabsOpts;  break;
578 		default:			mWndTabs = app->mWndTabsGame;  subt = &vSubTabsGame;  break;
579 	}
580 	toggleGui(false);
581 
582 
583 	size_t t = mWndTabs->getIndexSelected();
584 	mWndTabs->setIndexSelected(tab);
585 
586 	if (!subt)  return;
587 	TabControl* tc = (*subt)[tab];  if (!tc)  return;
588 	int  cnt = tc->getItemCount();
589 
590 	if (t == tab && subtab == -1)  // cycle subpages if same tab
591 	{	if (app->shift)
592 			tc->setIndexSelected( (tc->getIndexSelected()-1+cnt) % cnt );
593 		else
594 			tc->setIndexSelected( (tc->getIndexSelected()+1) % cnt );
595 	}
596 	if (subtab > -1)
597 		tc->setIndexSelected( std::min(cnt-1, subtab) );
598 
599 	if (!tc->eventTabChangeSelect.empty())
600 		tc->eventTabChangeSelect(tc, tc->getIndexSelected());
601 }
602 
603 //  close netw end
btnNetEndClose(WP)604 void CGui::btnNetEndClose(WP)
605 {
606 	app->mWndNetEnd->setVisible(false);
607 	app->isFocGui = true;  // show back gui
608 	toggleGui(false);
609 }
610 
611 
612 //  utility
613 //---------------------------------------------------------------------------------------------------------------------
614 
615 //  next/prev in list by key
LNext(Mli2 lp,int rel,int ofs)616 int CGui::LNext(Mli2 lp, int rel, int ofs)
617 {
618 	size_t cnt = lp->getItemCount();
619 	if (cnt==0)  return 0;
620 	int i = std::max(0, std::min((int)cnt-1, (int)lp->getIndexSelected()+rel ));
621 	lp->setIndexSelected(i);
622 	lp->beginToItemAt(std::max(0, i-ofs));  // center
623 	return i;
624 }
LNext(Mli lp,int rel)625 int CGui::LNext(Mli lp, int rel)
626 {
627 	size_t cnt = lp->getItemCount();
628 	if (cnt==0)  return 0;
629 	int i = std::max(0, std::min((int)cnt-1, (int)lp->getIndexSelected()+rel ));
630 	lp->setIndexSelected(i);
631 	return i;
632 }
LNext(Li lp,int rel,int ofs)633 int CGui::LNext(Li lp, int rel, int ofs)
634 {
635 	size_t cnt = lp->getItemCount();
636 	if (cnt==0)  return 0;
637 	int i = std::max(0, std::min((int)cnt-1, (int)lp->getIndexSelected()+rel ));
638 	lp->setIndexSelected(i);
639 	lp->beginToItemAt(std::max(0, i-ofs));  // center
640 	return i;
641 }
642 
LNext(int rel)643 void CGui::LNext(int rel)
644 {
645 	//if (!ap->isFocGui || pSet->isMain)  return;
646 	if (pSet->inMenu == MNU_Replays)
647 		listRplChng(rplList,  LNext(rplList, rel, 11));
648 	else
649 		if (app->mWndGame->getVisible())
650 		switch (app->mWndTabsGame->getIndexSelected())
651 		{	case TAB_Track:  gcom->listTrackChng(gcom->trkList,  LNext(gcom->trkList, rel, 11));  return;
652 			case TAB_Car:	 listCarChng(carList,    LNext(carList, rel, 5));  return;
653 			case TAB_Game:	 if (rel > 0)  radSimNorm(0);  else  radSimEasy(0);  return;
654 			case TAB_Champs:
655 				if (isChallGui())
656 				      listChallChng(liChalls, LNext(liChalls, rel, 8));
657 				else  listChampChng(liChamps, LNext(liChamps, rel, 8));
658 				return;
659 			case TAB_Stages: listStageChng(liStages, LNext(liStages, rel, 8));  return;
660 			case TAB_Stage:	 if (rel > 0)  btnStageNext(0);  else  btnStagePrev(0);  return;
661 		}
662 }
663 
664 
665 ///  Update (frame start)  .,.,.,.,..,.,.,.,..,.,.,.,..,.,.,.,.
GuiUpdate()666 void CGui::GuiUpdate()
667 {
668 	gcom->UnfocusLists();
669 
670 	if (gcom->bGuiReinit)  // after language change from combo
671 	{	gcom->bGuiReinit = false;
672 
673 		mGui->destroyWidgets(app->vwGui);
674 		gcom->bnQuit=0; app->mWndOpts=0;  //todo: rest too..  delete, new gui; ?
675 
676 		bGI = false;
677 		InitGui();
678 
679 		app->bWindowResized = true;
680 		app->mWndTabsOpts->setIndexSelected(3);  // switch back to view tab
681 	}
682 
683 
684 	//  sort trk list
685 	gcom->SortTrkList();
686 
687 	//  sort car list
688 	if (gcom->needSort(carList))
689 	{
690 		pSet->cars_sort = carList->mSortColumnIndex;
691 		pSet->cars_sortup = carList->mSortUp;
692 		CarListUpd(false);
693 	}
694 
695 	//  upd tweak tire save
696 	if (app->pGame->reloadSimDone)
697 	{	app->pGame->reloadSimDone = false;
698 
699 		FillTweakLists();
700 		btnTweakTireLoad(0);  // load back
701 	}
702 
703 
704 	///  rpl convert tool
705 	if (bConvertRpl)
706 	{	boost::this_thread::sleep(boost::posix_time::milliseconds(50));
707 
708 		txtConvert->setCaption(
709 		"#C0C0FF""Path: "+ iToStr(iConvPathCur+1,1) +" / "+ iToStr(iConvPathAll,1) +"\n"+
710 		"#A0D0FF""Files: "+ iToStr(iConvCur+1,4) +" / "+ iToStr(iConvAll,4) +"  : "+ iToStr(iConvFiles,4) +"\n"+
711 		(totalConv == 0 ? "" :
712 		"#A0F0F0""Progress: "+ fToStr(100.f* float(totalConvCur)/float(totalConv), 2,5) +" %\n")+
713 		"#A0C0E0""Sizes\n"+
714 		"#F0A0A0""old:    "+ fToStr( float(totalConv)/1000000.f, 2,5) +" MiB\n"+
715 		"#A0F0A0""new:  "+ fToStr( float(totalConvNew)/1000000.f, 2,5) +" MiB\n"+
716 		(totalConvCur!=totalConv || totalConv==0 ? "" :
717 		"#F0F0A0""ratio:  "+ fToStr(100.f* float(totalConvNew)/float(totalConv), 1,4) +" %") );
718 	}
719 }
720