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