1 /***************************************************************************
2                           tegui.cpp  -  The Track Editor GUI
3                              -------------------
4     begin                : su jul 23 2006
5     copyright            : (C) 2006 by CJP
6     email                : cornware-cjp@users.sourceforge.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cmath>
21 #include <unistd.h>
22 
23 #include <libintl.h>
24 #define _(String) gettext (String)
25 #define N_(String1, String2, n) ngettext ((String1), (String2), (n))
26 
27 #include "pi.h"
28 #include "lconfig.h"
29 #include "usmacros.h"
30 #include "datafile.h"
31 #include "trackdocument.h"
32 #include "edittrack.h"
33 #include "environmentaction.h"
34 #include "resizeaction.h"
35 
36 #include "longmenu.h"
37 #include "tileselect.h"
38 #include "objectviewwidget.h"
39 #include "scenerypreview.h"
40 
41 #include "tegui.h"
42 
CTEGUI(CWinSystem * winsys)43 CTEGUI::CTEGUI(CWinSystem *winsys) : CGUI(winsys)
44 {
45 	{
46 		vector<CString> conffiles = getDataDirContents("tiles", ".conf");
47 		for(unsigned int i=0; i < conffiles.size(); i++)
48 		{
49 			CDataFile dfile("tiles/" + conffiles[i]);
50 			CLConfig conf(dfile.useExtern());
51 
52 			STileDescr tile;
53 			tile.conffile = "tiles/" + conffiles[i];
54 			tile.description = conf.getValue("description", "text");
55 			tile.glbfile     = conf.getValue("model"      , "glbfile");
56 			tile.textures    = conf.getValue("model"      , "textures");
57 			tile.flags       = conf.getValue("model"      , "flags");
58 
59 			m_TileFiles.push_back(tile);
60 		}
61 	}
62 
63 	m_CurrentTile = -1;
64 
65 	m_LoadPage.m_Title = _("Select a track");
66 	m_LoadPage.m_DrawBackground = true;
67 	CLongMenu *lmenu = new CLongMenu;
68 	lmenu->m_Xrel = 0.1;
69 	lmenu->m_Yrel = 0.2;
70 	lmenu->m_Wrel = 0.8;
71 	lmenu->m_Hrel = 0.6;
72 	lmenu->m_Selected = 0;
73 	lmenu->m_AlignLeft = false;
74 	m_LoadPage.m_Widgets.push_back(lmenu);
75 
76 
77 	m_TilesPage.m_Title = _("Change tiles collection");
78 	m_TilesPage.m_OnlyTopGetsMouseEvents = false;
79 	CTileSelect *tselect = new CTileSelect;
80 	tselect->m_Xrel = 0.1;
81 	tselect->m_Yrel = 0.2;
82 	tselect->m_Wrel = 0.8;
83 	tselect->m_Hrel = 0.6;
84 	m_TilesPage.m_Widgets.push_back(tselect);
85 	CMenu *menu = new CMenu;
86 	menu->m_Xrel = 0.1;
87 	menu->m_Yrel = 0.1;
88 	menu->m_Wrel = 0.8;
89 	menu->m_Hrel = 0.1;
90 	menu->m_Selected = 0;
91 	menu->m_AlignLeft = true;
92 	m_TilesPage.m_Widgets.push_back(menu);
93 
94 
95 	m_TilePage.m_Title = _("Edit this tile");
96 	CObjectViewWidget *tileview = new CObjectViewWidget(theTrackDocument->m_DataManager);
97 	tileview->m_Xrel = 0.1;
98 	tileview->m_Yrel = 0.2;
99 	tileview->m_Wrel = 0.8;
100 	tileview->m_Hrel = 0.6;
101 	m_TilePage.m_Widgets.push_back(tileview);
102 	menu = new CMenu;
103 	menu->m_Xrel = 0.1;
104 	menu->m_Yrel = 0.1;
105 	menu->m_Wrel = 0.8;
106 	menu->m_Hrel = 0.1;
107 	menu->m_Selected = 0;
108 	menu->m_AlignLeft = true;
109 	m_TilePage.m_Widgets.push_back(menu);
110 
111 	m_SceneryPage.m_Title = _("Change scenery settings");
112 	CSceneryPreview *preview = new CSceneryPreview;
113 	preview->m_Xrel = 0.3;
114 	preview->m_Yrel = 0.5;
115 	preview->m_Wrel = 0.4;
116 	preview->m_Hrel = 0.3;
117 	m_SceneryPage.m_Widgets.push_back(preview);
118 	menu = new CMenu;
119 	menu->m_Xrel = 0.1;
120 	menu->m_Yrel = 0.1;
121 	menu->m_Wrel = 0.8;
122 	menu->m_Hrel = 0.4;
123 	menu->m_Selected = 0;
124 	menu->m_AlignLeft = true;
125 	m_SceneryPage.m_Widgets.push_back(menu);
126 
127 	m_ChildWidget = &m_MainPage;
128 }
129 
~CTEGUI()130 CTEGUI::~CTEGUI()
131 {
132 }
133 
updateMenuTexts()134 void CTEGUI::updateMenuTexts()
135 {
136 	m_MainPage.updateDocInfo();
137 
138 	CMenu *menu = (CMenu *)(m_LoadPage.m_Widgets[0]);
139 	menu->m_Lines = getDataDirContents("tracks", ".track");
140 	menu->m_Selected = 0;
141 
142 	menu = (CMenu *)(m_TilesPage.m_Widgets[1]);
143 	menu->m_Lines.clear();
144 	menu->m_Lines.push_back(_("Add a new tile"));
145 	menu->m_Lines.push_back(_("Remove unused tiles"));
146 	menu->m_Lines.push_back(_("Return to main menu"));
147 
148 	menu = (CMenu *)(m_TilePage.m_Widgets[1]);
149 	menu->m_Lines.clear();
150 	menu->m_Lines.push_back(_("Change the tile model"));
151 	menu->m_Lines.push_back(_("Delete this tile"));
152 	menu->m_Lines.push_back(_("Ready"));
153 
154 	menu = (CMenu *)(m_SceneryPage.m_Widgets[1]);
155 	menu->m_Lines.clear();
156 	menu->m_Lines.push_back(_("Change the cloud image"));
157 	menu->m_Lines.push_back(_("Change the background image"));
158 	menu->m_Lines.push_back(_("Change the sky color"));
159 	menu->m_Lines.push_back(_("Change the horizon sky color"));
160 	menu->m_Lines.push_back(_("Change the cloud and background color"));
161 	menu->m_Lines.push_back(_("Change the fog color"));
162 	menu->m_Lines.push_back(_("Change the light color"));
163 	menu->m_Lines.push_back(_("Change the light direction"));
164 	menu->m_Lines.push_back(_("Return to main menu"));
165 }
166 
start()167 void CTEGUI::start()
168 {
169 	enter2DMode();
170 
171 	CString section = "mainmenu";
172 	while(true)
173 	{
174 		updateMenuTexts();
175 
176 		if(section=="mainmenu")
177 			section = viewMainMenu();
178 		else if(section=="loadmenu")
179 			section = viewLoadMenu();
180 		else if(section=="saveasmenu")
181 			section = viewSaveAsMenu();
182 		else if(section=="importmenu")
183 			section = viewImportMenu();
184 		else if(section=="tilesmenu")
185 			section = viewTilesMenu();
186 		else if(section=="tilemenu")
187 			section = viewTileMenu();
188 		else if(section=="loadtilemenu")
189 			section = viewLoadTileMenu();
190 		else if(section=="scenerymenu")
191 			section = viewSceneryMenu();
192 		else
193 			{printf("Error: unknown menu\n");}
194 
195 		if(section == "exit" || section == "")
196 		{
197 			if(showYNMessageBox(_("Do you really want to quit?")) )
198 				{break;}
199 			else
200 				{section = "mainmenu";}
201 		}
202 	}
203 
204 	leave2DMode();
205 }
206 
viewMainMenu()207 CString CTEGUI::viewMainMenu()
208 {
209 	m_ChildWidget = &m_MainPage;
210 	if(!m_WinSys->runLoop(this))
211 		return "exit";
212 
213 	switch(m_MainPage.getSelection())
214 	{
215 		case CIconList::eLoad:
216 			return "loadmenu";
217 		case CIconList::eSave:
218 			return "saveasmenu";
219 		case CIconList::eImport:
220 			return "importmenu";
221 		case CIconList::eQuit:
222 			return "exit";
223 		case CIconList::eUndo:
224 			theTrackDocument->undo();
225 			return "mainmenu";
226 		case CIconList::eRedo:
227 			theTrackDocument->redo();
228 			return "mainmenu";
229 		case CIconList::eTiles:
230 			return "tilesmenu";
231 		case CIconList::eScenery:
232 			return "scenerymenu";
233 		case CIconList::eResizeTrack:
234 			resizeTrack();
235 			return "mainmenu";
236 		default:
237 			printf("Error: received Iconbar event with unknown ID\n");
238 			return "mainmenu"; //unknown: do nothing
239 	}
240 
241 	return "";
242 }
243 
viewLoadMenu()244 CString CTEGUI::viewLoadMenu()
245 {
246 	m_LoadPage.m_Title = _("Select a track");
247 	m_ChildWidget = &m_LoadPage;
248 	if(!m_WinSys->runLoop(this))
249 		return "mainmenu";
250 
251 	CMenu *menu = (CMenu *)(m_LoadPage.m_Widgets[0]);
252 
253 	theTrackDocument->m_Trackname = CString("tracks/") + menu->m_Lines[menu->m_Selected];
254 	if(!theTrackDocument->load())
255 	{
256 		CString msg;
257 		msg.format(_("Error: %s could not be loaded"), 256, theTrackDocument->m_Trackname.c_str());
258 		showMessageBox(msg);
259 		exit(1);
260 	}
261 
262 	m_MainPage.resetCameraPosition();
263 
264 	return "mainmenu";
265 }
266 
viewImportMenu()267 CString CTEGUI::viewImportMenu()
268 {
269 	//Let user select a file
270 	bool cancelled = false;
271 	CString importedfile = showFileSelect(_("Select the Stunts track"), ".trk", &cancelled);
272 	if(cancelled) return "mainmenu";
273 
274 	//Set trackname to filename without directory
275 	int lastSlash = -1;
276 	for(unsigned int i=0; i < importedfile.length(); i++)
277 		if(importedfile[i] == '/') lastSlash = i;
278 	if(lastSlash < 0)
279 		{theTrackDocument->m_Trackname = importedfile;}
280 	else
281 		{theTrackDocument->m_Trackname = importedfile.mid(lastSlash+1);}
282 
283 	//Load the file
284 	if(!theTrackDocument->import(importedfile))
285 	{
286 		CString msg;
287 		msg.format(_("Error: %s could not be loaded"), 256, theTrackDocument->m_Trackname.c_str());
288 		showMessageBox(msg);
289 		exit(1);
290 	}
291 
292 	m_MainPage.resetCameraPosition();
293 
294 	return "mainmenu";
295 }
296 
viewSaveAsMenu()297 CString CTEGUI::viewSaveAsMenu()
298 {
299 	m_LoadPage.m_Title = _("Tracks:");
300 	m_ChildWidget = &m_LoadPage;
301 
302 	CString name = theTrackDocument->m_Trackname;
303 
304 	//Remove directory
305 	int pos = name.inStr('/');
306 	while(pos >= 0)
307 	{
308 		name = name.mid(pos+1);
309 		pos = name.inStr('/');
310 	}
311 
312 	//Add .track extension
313 	if(name.mid(name.length()-6) != ".track")
314 			name += ".track";
315 
316 	//Allow user to modify name
317 	bool cancelled = false;
318 	name = showInputBox(_("Enter the filename:"), name, &cancelled);
319 
320 	//Add .track extension again if user deleted it
321 	if(name.mid(name.length()-6) != ".track")
322 			name += ".track";
323 
324 	if(!cancelled)
325 	{
326 		CString oldname = theTrackDocument->m_Trackname;
327 
328 		theTrackDocument->m_Trackname = CString("tracks/") + name;
329 
330 		if(dataFileExists(theTrackDocument->m_Trackname, true)) //search only locally
331 		{
332 			CString q;
333 			q.format(_("File %s already exists. Overwrite? The hiscore of the track will be reset."), 256, theTrackDocument->m_Trackname.c_str());
334 			if(!showYNMessageBox(q)) return "mainmenu"; //and don't save
335 		}
336 
337 		if(theTrackDocument->save())
338 		{
339 			//Remove the hiscore file
340 			CString hiscoreFile = theTrackDocument->m_Trackname;
341 			hiscoreFile =
342 				hiscoreFile.mid(0, hiscoreFile.length()-6) + ".high";
343 			deleteDataFile(hiscoreFile);
344 		}
345 		else
346 		{
347 			CString msg;
348 			msg.format(_("Saving of %s failed"), 256, theTrackDocument->m_Trackname.c_str());
349 			showMessageBox(msg);
350 			theTrackDocument->m_Trackname = oldname;
351 		}
352 	}
353 
354 	return "mainmenu";
355 }
356 
viewTilesMenu()357 CString CTEGUI::viewTilesMenu()
358 {
359 	m_ChildWidget = &m_TilesPage;
360 	if(!m_WinSys->runLoop(this))
361 		return "mainmenu";
362 
363 	if(m_TilesPage.m_EventWidget == 0) //tile-triggered
364 	{
365 		CTileSelect *tselect = (CTileSelect *)(m_TilesPage.m_Widgets[0]);
366 		printf("Edit tile %d\n", tselect->getSelection());
367 		m_CurrentTile = tselect->getSelection();
368 
369 		if(theTrackDocument->getCurrentTrack()->tileIsUsed(m_CurrentTile))
370 		{
371 			printf("Tile is used\n");
372 
373 			bool cancelled = false;
374 			if(showYNMessageBox(
375 				_("This tile is in use in the track. Do you really want to change it?")
376 				, &cancelled) && !cancelled)
377 			{
378 				return "tilemenu"; //change tile
379 			}
380 
381 			return "tilesmenu"; //return to this menu
382 		}
383 
384 		return "tilemenu"; //change tile
385 	}
386 	else if(m_TilesPage.m_EventWidget == 1) //menu-triggered
387 	{
388 		CMenu *menu = (CMenu *)(m_TilesPage.m_Widgets[1]);
389 		switch(menu->m_Selected)
390 		{
391 		case 0: //New tile
392 			printf("New tile\n");
393 			m_CurrentTile = -1;
394 			return "loadtilemenu";
395 		case 1: //Delete unused tiles
396 			theTrackDocument->deleteUnusedTiles();
397 			return "tilesmenu";
398 		case 2: //Return to main menu
399 		default:
400 			break;
401 		}
402 	}
403 
404 	return "mainmenu";
405 }
406 
viewTileMenu()407 CString CTEGUI::viewTileMenu()
408 {
409 	if(m_CurrentTile < 0)
410 		return "tilesmenu";
411 
412 	const CTEManager *manager = theTrackDocument->m_DataManager;
413 	const CDataObject *tile = manager->getTile(m_CurrentTile);
414 
415 	CObjectViewWidget *tileview = (CObjectViewWidget *)(m_TilePage.m_Widgets[0]);
416 	tileview->m_ObjectViewer.m_Objects.clear();
417 	tileview->m_ObjectViewer.addObject(tile->getFilename(), tile->getParamList(),
418 			CVector(0,0,0), CMatrix(), false, CDataObject::eTileModel);
419 
420 	m_ChildWidget = &m_TilePage;
421 	if(!m_WinSys->runLoop(this))
422 		return "tilesmenu";
423 
424 	CMenu *menu = (CMenu *)(m_TilePage.m_Widgets[1]);
425 	switch(menu->m_Selected)
426 	{
427 	case 0: //Change model
428 		return "loadtilemenu";
429 	case 1: //Delete
430 		if(theTrackDocument->getCurrentTrack()->tileIsUsed(m_CurrentTile))
431 		{
432 
433 			bool cancelled = false;
434 			if(showYNMessageBox(
435 				_("This tile is in use in the track. Do you really want to delete it?")
436 				, &cancelled) && !cancelled)
437 			{
438 				theTrackDocument->deleteTile(m_CurrentTile);
439 				return "tilesmenu";
440 			}
441 
442 			//Don't remove it
443 			return "tilemenu";
444 		}
445 
446 		theTrackDocument->deleteTile(m_CurrentTile);
447 		return "tilesmenu";
448 	case 2: //Ready
449 	default:
450 		return "tilesmenu";
451 	}
452 
453 	return "tilesmenu";
454 }
455 
viewLoadTileMenu()456 CString CTEGUI::viewLoadTileMenu()
457 {
458 	m_LoadPage.m_Title = _("Tiles:");
459 	CMenu *menu = (CMenu *)(m_LoadPage.m_Widgets[0]);
460 
461 	menu->m_Lines.clear();
462 	for(unsigned int i=0; i < m_TileFiles.size(); i++)
463 	{
464 		menu->m_Lines.push_back(m_TileFiles[i].description);
465 	}
466 
467 	m_ChildWidget = &m_LoadPage;
468 	if(!m_WinSys->runLoop(this))
469 	{
470 		if(m_CurrentTile >= 0)
471 			return "tilemenu";
472 
473 		return "tilesmenu";
474 	}
475 
476 	CTEManager *manager = theTrackDocument->m_DataManager;
477 
478 	const STileDescr &td = m_TileFiles[menu->m_Selected];
479 	CString newfile = td.conffile;
480 	/*
481 	CString texSubset = manager->getTextureSubset(td.textures);
482 
483 	printf("Textures: \"%s\"\n", td.textures.c_str());
484 	printf("Subset: \"%s\"\n", texSubset.c_str());
485 
486 	CParamList plist;
487 	SParameter p;
488 	p.name = "subset";
489 	p.value = texSubset;
490 	plist.push_back(p);
491 	p.name = "flags";
492 	p.value = td.flags;
493 	plist.push_back(p);
494 	*/
495 
496 	if(m_CurrentTile >= 0)
497 	{
498 		CDataObject *tile = manager->getTile(m_CurrentTile);
499 		tile->load(newfile, CParamList());
500 	}
501 	else
502 	{
503 		m_CurrentTile = manager->loadObject(newfile, CParamList(), CDataObject::eTileModel);
504 		if(m_CurrentTile < 0) return "tilesmenu"; //TODO: error message
505 	}
506 
507 	return "tilemenu";
508 }
509 
viewSceneryMenu()510 CString CTEGUI::viewSceneryMenu()
511 {
512 	CSceneryPreview *preview =
513 		(CSceneryPreview *)(m_SceneryPage.m_Widgets[0]);
514 
515 	static bool enteredFromOutside = true;
516 
517 	if(enteredFromOutside)
518 		preview->m_Environment =
519 			theTrackDocument->getCurrentTrack()->m_Environment;
520 
521 	bool toMainMenu = false;
522 
523 	m_ChildWidget = &m_SceneryPage;
524 	if(!m_WinSys->runLoop(this))
525 		toMainMenu = true;
526 
527 	if(!toMainMenu)
528 	{
529 		CMenu *menu = (CMenu *)(m_SceneryPage.m_Widgets[1]);
530 		switch(menu->m_Selected)
531 		{
532 		case 0: //cloud image
533 			preview->m_Environment.m_SkyFilename =
534 				showInputBox(_("Cloud image file:"),
535 					preview->m_Environment.m_SkyFilename);
536 			break;
537 		case 1: //background image
538 			preview->m_Environment.m_HorizonFilename =
539 				showInputBox(_("Background image file:"),
540 					preview->m_Environment.m_HorizonFilename);
541 			break;
542 		case 2: //sky color
543 			preview->m_Environment.m_SkyColor =
544 				showColorSelect(_("Sky color:"),
545 					preview->m_Environment.m_SkyColor);
546 			break;
547 		case 3: //horizon sky color
548 			preview->m_Environment.m_HorizonSkyColor =
549 				showColorSelect(_("Horizon sky color:"),
550 					preview->m_Environment.m_HorizonSkyColor);
551 			break;
552 		case 4: //cloud and background color
553 			preview->m_Environment.m_EnvironmentColor =
554 				showColorSelect(_("Cloud and background color:"),
555 					preview->m_Environment.m_EnvironmentColor);
556 			break;
557 		case 5: //fog color
558 			preview->m_Environment.m_FogColor =
559 				showColorSelect(_("Fog color:"),
560 					preview->m_Environment.m_FogColor);
561 			break;
562 		case 6: //light color
563 			preview->m_Environment.m_LightColor =
564 				showColorSelect(_("Light color:"),
565 					preview->m_Environment.m_LightColor);
566 			break;
567 		case 7: //light direction
568 			{
569 				float lightY = asin(
570 					-preview->m_Environment.m_LightDirection.normal().y
571 					);
572 				float lightX = atan2f(
573 					-preview->m_Environment.m_LightDirection.x,
574 					 preview->m_Environment.m_LightDirection.z);
575 				lightX *= 180.0 / M_PI;
576 				lightY *= 180.0 / M_PI;
577 				if(lightX < 0.0) lightX += 360.0;
578 
579 				//printf("%s\n", CString(
580 				//	preview->m_Environment.m_LightDirection).c_str()
581 				//	);
582 				//printf("%f, %f\n", lightX, lightY);
583 
584 				lightX =
585 					showInputBox(_("Light compass direction (degrees):"),
586 						CString(lightX)).toFloat();
587 
588 				lightY =
589 					showInputBox(_("Light angle above the horizon (degrees):"),
590 						CString(lightY)).toFloat();
591 
592 				//printf("%f, %f\n", lightX, lightY);
593 
594 				lightX *= M_PI/180.0;
595 				lightY *= M_PI/180.0;
596 
597 				preview->m_Environment.m_LightDirection =
598 					CVector(
599 						-cos(lightY)*sin(lightX),
600 						-sin(lightY),
601 						cos(lightY)*cos(lightX)
602 					);
603 
604 				//printf("%s\n", CString(
605 				//	preview->m_Environment.m_LightDirection).c_str()
606 				//	);
607 			}
608 			break;
609 		case 8: //main menu
610 			toMainMenu = true;
611 			break;
612 		}
613 	}
614 
615 	if(toMainMenu)
616 	{
617 		//Apply the changes to the track:
618 		CEnvironmentAction a(preview->m_Environment);
619 		theTrackDocument->setNewAction(&a);
620 		theTrackDocument->commitAction();
621 
622 		m_MainPage.enableActionWidgetAction();
623 
624 		enteredFromOutside = true;
625 		return "mainmenu";
626 	}
627 
628 	enteredFromOutside = false;
629 	return "scenerymenu";
630 }
631 
resizeTrack()632 void CTEGUI::resizeTrack()
633 {
634 	bool cancelled = false;
635 	int dx_min = showInputBox(
636 		_("Number of tiles to add on the west side (negative = remove tiles): "),
637 		"0", &cancelled).toInt();
638 	if(cancelled) return;
639 	int dx_max = showInputBox(
640 		_("Number of tiles to add on the east side (negative = remove tiles): "),
641 		"0", &cancelled).toInt();
642 	if(cancelled) return;
643 	int dz_min = showInputBox(
644 		_("Number of tiles to add on the north side (negative = remove tiles): "),
645 		"0", &cancelled).toInt();
646 	if(cancelled) return;
647 	int dz_max = showInputBox(
648 		_("Number of tiles to add on the south side (negative = remove tiles): "),
649 		"0", &cancelled).toInt();
650 	if(cancelled) return;
651 
652 	//Apply the changes to the track:
653 	CResizeAction a(dx_min, dx_max, dz_min, dz_max);
654 	theTrackDocument->setNewAction(&a);
655 	theTrackDocument->commitAction();
656 
657 	//Move cursor inside the new track
658 	theTrackDocument->moveCursor(
659 		theTrackDocument->getCursorX() + dx_min,
660 		theTrackDocument->getCursorY(),
661 		theTrackDocument->getCursorZ() + dz_min
662 		);
663 
664 	//TODO: find some way to update the camera
665 
666 	//Enable the original action
667 	m_MainPage.enableActionWidgetAction();
668 }
669 
670 
671