1 /*
2 * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3 *
4 * Copyright (C) 2005-2010 Jan Reucker (original author)
5 * Copyright (C) 2006, 2008 Jens Wilhelm Wulf
6 * Copyright (C) 2008, 2009, 2012 Joel Lienard
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 */
23
24
25 // implementation of class CGUILocationDialog
26 #include "../i18n.h"
27 #include "../global.h"
28 #include "../crrc_main.h"
29 #include "../global_video.h"
30 #include "../SimStateHandler.h"
31 #include "../mod_landscape/crrc_scenery.h"
32 #include "../mod_mode/F3A/handlerF3A.h"
33 #include "../mod_mode/F3F/handlerF3F.h"
34 #include "../mod_windfield/windfield.h"
35 #include "../mod_misc/filesystools.h"
36 #include "util.h"
37 #include "crrc_gui_main.h"
38 #include "crrc_location.h"
39
40 #include <iostream>
41 #include <sstream>
42 #include <sys/types.h>
43 #include <dirent.h>
44 #include <string>
45 using namespace std;
46
47
48
49 #define LIST_WIDGET_HEIGHT (220)
50 #define LIST_WIDGET_WIDTH (200)
51 #define BUTTON_BOX_HEIGHT (2*DLG_DEF_SPACE+DLG_DEF_BUTTON_HEIGHT)
52 #define CONF_SEL_WIDTH (256)
53 #define DESCRIPTION_HEIGHT (100)
54 #define PREVIEW_HEIGHT (128)
55 #define PREVIEW_WIDTH (256)
56
57
58 static void CGUILocationCallback(puObject *obj);
59 static void CGUILocationSelectCallback(puObject *obj);
60
61
CGUILocationDialog()62 CGUILocationDialog::CGUILocationDialog()
63 : CRRCDialog(), cbox(NULL), preview_texture(NULL)
64 {
65 // gather information on all installed sceneries
66 DIR *dir=NULL;
67 struct dirent *ent;
68 const char* curLocName = Global::scenery->getName();
69 int curSkyVariant = Global::scenery->getSkyVariant();
70 int curLocIndex = -1;
71 std::vector<std::string> paths;
72 std::vector<std::string> extlist;
73 extlist.push_back("xml");
74
75 T_Config::getLocationDirs(paths);
76 //std::cout << "Scanning scenery directories:" << std::endl;
77
78 for (unsigned int i = 0; i < paths.size(); i++)
79 {
80 if ((dir = opendir(paths[i].c_str())) == NULL)
81 {
82 #ifdef DEBUG_GUI
83 std::cerr << "createFileList(): unable to open directory " << paths[i];
84 std::cerr << std::endl;
85 #endif
86 }
87 else
88 {
89 while ((ent = readdir(dir)) != NULL)
90 {
91 std::string tmp;
92 bool fMatch = false;
93
94 tmp = ent->d_name;
95
96 for (unsigned int n=0; n<extlist.size() && fMatch == false; n++)
97 {
98 if (T_GUI_Util::checkExtension(tmp, extlist[n]))
99 fMatch = true;
100 }
101
102 if (fMatch)
103 {
104 std::string fullpath = paths[i] + "/" + tmp;
105 //std::cout << fullpath << std::endl;
106 SimpleXMLTransfer *loc = NULL;
107 bool ok=false;
108 std::string name;
109 try
110 {
111 loc = new SimpleXMLTransfer(fullpath);
112 name = loc->getChild("name")->getContentString();
113 ok=true;
114 }
115 catch (XMLException e)
116 {
117 std::cerr << "Caught XML exception in CGUIlocationSelectDialog:" << std::endl;
118 std::cerr << " " << e.what() << std::endl;
119 std::cerr << " File: " << fullpath << std::endl;
120 }
121 if(ok)
122 {
123 //std::cout << " " << name << "(" << fullpath << ")" << std::endl;
124 lists_insert(name, fullpath);
125 }
126 delete loc;
127 }
128 }
129 closedir(dir);
130 }
131 }
132
133 if (fileslist.size() == 0)
134 {
135 std::cerr << " No sceneries found! Something is wrong with your installation!" << std::endl;
136 std::cerr << " Search path was:" << std::endl;
137 for (unsigned int i = 0; i < paths.size(); i++)
138 {
139 std::cerr << " " << paths[i] << std::endl;
140 }
141 }
142 curLocIndex = index_in_locationslist(curLocName);
143 filesList = T_GUI_Util::loadnames(fileslist, filesListSize);
144 locationsList = T_GUI_Util::loadnames(locationslist, locationsListSize);
145 skiesList = NULL;
146 skiesListSize = 0;
147
148 // Now setup the GUI widgets
149 // height of a text label
150 int msg_height = puGetDefaultLegendFont().getStringHeight("jX")
151 + puGetDefaultLegendFont().getStringDescender()
152 + PUSTR_TGAP + PUSTR_BGAP;
153
154 // top of the list box
155 int top_of_listbox = BUTTON_BOX_HEIGHT
156 + 2*DLG_DEF_SPACE
157 + msg_height
158 + LIST_WIDGET_HEIGHT
159 + DESCRIPTION_HEIGHT;
160
161 // Scrolled ListBox for the scenery files
162 cbox = new puaScrListBox (DLG_DEF_SPACE,
163 top_of_listbox - LIST_WIDGET_HEIGHT,
164 LIST_WIDGET_WIDTH,
165 LIST_WIDGET_HEIGHT,
166 locationsList);
167
168 cbox->setLabelPlace(PUPLACE_TOP_LEFT);
169 cbox->setLabel(_("Select location:"));
170 cbox->setCallback(CGUILocationSelectCallback);
171 cbox->setUserData(this);
172 cbox->setValue(curLocIndex);
173
174 ptext = new puText(DLG_DEF_SPACE,
175 BUTTON_BOX_HEIGHT + DLG_DEF_SPACE + DESCRIPTION_HEIGHT);
176 ptext->setLabel("File: /path/to/my/scenery.xml");
177
178
179 // Combo box for sky variant selection
180 sbox = new puaComboBox(2*DLG_DEF_SPACE + LIST_WIDGET_WIDTH,
181 top_of_listbox - DLG_DEF_BUTTON_HEIGHT,
182 2*DLG_DEF_SPACE + LIST_WIDGET_WIDTH + CONF_SEL_WIDTH,
183 top_of_listbox,
184 NULL,
185 false);
186 sbox->setChildColourScheme(PUCLASS_POPUPMENU, dlgCol1[0], dlgCol1[1], dlgCol1[2]);
187 sbox->setLabelPlace(PUPLACE_TOP_LEFT);
188 sbox->setLabel(_("Select sky or resolution:"));
189 sbox->setUserData(this);
190 sbox->setCallback(CGUISkySelectCallback);
191
192 // preview widget
193 preview = new puaImageFrame (
194 2*DLG_DEF_SPACE + LIST_WIDGET_WIDTH,
195 top_of_listbox - DLG_DEF_BUTTON_HEIGHT -DLG_DEF_SPACE-PREVIEW_HEIGHT - DLG_DEF_BUTTON_HEIGHT, // lower left
196 2*DLG_DEF_SPACE + LIST_WIDGET_WIDTH + PREVIEW_WIDTH,
197 top_of_listbox - DLG_DEF_BUTTON_HEIGHT -DLG_DEF_SPACE - DLG_DEF_BUTTON_HEIGHT // upper right
198 );
199 /*preview->setLabelPlace(PUPLACE_TOP_LEFT);
200 preview->setLabel("Preview:");*/
201
202 // the description box
203 description = new puaLargeInput( DLG_DEF_SPACE,
204 BUTTON_BOX_HEIGHT + DLG_DEF_SPACE,
205 2*DLG_DEF_SPACE + LIST_WIDGET_WIDTH + CONF_SEL_WIDTH,
206 DESCRIPTION_HEIGHT,
207 1, // num of arrow pairs
208 16, // slider width
209 1); // wrap text
210 description->disableInput();
211 description->setText("This is a short description of the selected scenery.");
212 /*description->setLabelPlace(PUPLACE_TOP_LEFT);
213 description->setLabel(_("Description:"));*/
214
215 // finalize the dialog
216 setUserData(this);
217 updateLocationInfo(-1);
218 sbox->setCurrentItem(curSkyVariant);
219
220 close();
221 setSize(LIST_WIDGET_WIDTH + CONF_SEL_WIDTH + 4*DLG_DEF_SPACE,
222 BUTTON_BOX_HEIGHT + LIST_WIDGET_HEIGHT + msg_height + 5*DLG_DEF_SPACE + DESCRIPTION_HEIGHT );
223 setCallback(CGUILocationCallback);
224 centerOnScreen();
225 reveal();
226 }
227
228 /**
229 * Destroy the dialog.
230 */
~CGUILocationDialog()231 CGUILocationDialog::~CGUILocationDialog()
232 {
233 T_GUI_Util::freenames(locationsList, locationsListSize);
234 T_GUI_Util::freenames(filesList, filesListSize);
235 T_GUI_Util::freenames(skiesList, skiesListSize);
236 delete preview_texture;
237 }
238
239 /**
240 * Update all widgets that depend on the currently selected location.
241 */
updateLocationInfo(int dlg_sky_index)242 void CGUILocationDialog::updateLocationInfo(int dlg_sky_index)
243 {
244 std::vector<std::string> sky_descriptions;
245 std::vector<std::string> sky_previews;//specifics previews for sky variant
246 std::string preview_filename="";//generic preview for scenery
247 int nLocId = getLocationId();
248 std::string location_filename = "";
249 if ((nLocId >= 0) && (nLocId < filesListSize))
250 {
251 location_filename = filesList[nLocId];
252 SimpleXMLTransfer *xml = new SimpleXMLTransfer(location_filename);
253 path_string = _("File: ");
254 path_string += location_filename;
255 ptext->setLabel(path_string.c_str());
256 int children = xml->getChildCount();
257 std::vector<int> sky_indices;
258 for (int i = 0; i < children; i++)
259 {
260 SimpleXMLTransfer *child = xml->getChildAt(i);
261 if (child->getName() == "sky")
262 {
263 sky_indices.push_back(i);
264 std::string desc = child->getChild("descr_short.en", true)->getContentString();
265 if (desc == "")
266 {
267 std::ostringstream s;
268 s << sky_indices.size() << ": no description";
269 desc = s.str();
270 }
271 sky_descriptions.push_back(desc);
272 std::string prev = child->getChild("preview", true)->attribute("filename", "");
273 sky_previews.push_back(prev);
274 //std::cout << " <sky> " << sky_indices.size() << " at child idx " << i << ": " << desc<< std::endl;
275 }
276 else if(child->getName() == "preview")
277 {
278 preview_filename =child->attribute("filename", "");
279 }
280 }
281
282 // Update the description box
283 description_string = "";
284 try
285 {
286 description_string = xml->getChild("description.en")->getContentString();
287 }
288 catch (XMLException &e)
289 {
290 description_string = _("No description available.");
291 }
292 description_string = T_GUI_Util::cleanText(description_string);
293 description->setText(description_string.c_str());
294 description->setTopLineInWindow(0);
295 description->setSelectRegion (0,0) ;
296 delete xml;
297 }
298 else
299 {
300 std::cerr << "CGUILocationDialog::updateLocationInfo: index out of range:" << std::endl;
301 std::cerr << "nLocId: " << nLocId << " filesListSize: " << filesListSize << std::endl;
302 return;
303 }
304
305 // If the scenery offers more than one sky to choose from,
306 // update the combo box. Else hide it.
307 if(dlg_sky_index == -1)//if not valide index, update combo box
308 {
309 int sky_variant = 0;
310 if ( sky_descriptions.size() > 1)
311 {
312 // Is there a skyvariant registered for this location
313 SimpleXMLTransfer* ptr = cfg->getLocCfgPtr(cfgfile, location_filename);
314 try
315 {
316 sky_variant = ptr->getChild("sky",false)->attributeAsInt("nUse",0);
317 dlg_sky_index = sky_variant ;
318 }
319 catch (XMLException &e)
320 {
321 sky_variant = 0;
322 }
323 T_GUI_Util::freenames(skiesList, skiesListSize);
324 skiesList = T_GUI_Util::loadnames( sky_descriptions, skiesListSize);
325 sbox->newList(skiesList);
326 sbox->reveal();
327 }
328 else
329 {
330 sbox->hide();
331 }
332 sbox->setCurrentItem(sky_variant);
333 }
334
335 // Update preview
336 {
337 std::string filename;
338 if (dlg_sky_index < 0) dlg_sky_index = 0;
339 if ((int)sky_previews.size() >= (dlg_sky_index+1))
340 filename = sky_previews[dlg_sky_index];//specific preview of sky variant
341 if (filename == "") filename = preview_filename;//generic preview of scenery
342 if (filename == "")
343 {
344 preview->hide();
345 }
346 else
347 {
348 if (preview_texture) delete(preview_texture);
349 std::string filename1 = FileSysTools::getDataPath(filename);
350 preview_texture = new ssgTexture(filename1.c_str());
351 preview->setTexture(preview_texture);
352 preview->reveal();
353 }
354 }
355 }
356
357 /******
358 insert element in listes of location name and filename
359 with respect of alpabetic order of location name.
360 ********/
lists_insert(std::string lname,std::string fullpath)361 void CGUILocationDialog::lists_insert(std::string lname, std::string fullpath)
362 {
363 vector<std::string>::iterator itf, itl;
364 for (itf=fileslist.begin(), itl=locationslist.begin(); itl<locationslist.end(); itf++, itl++)
365 {
366 if(lname < *itl )
367 {
368 locationslist.insert(itl, lname);
369 fileslist.insert(itf, fullpath);
370 return;
371 }
372 }
373 locationslist.insert(itl, lname);
374 fileslist.insert(itf, fullpath);
375 }
376
377 /***************************/
index_in_locationslist(std::string lname)378 int CGUILocationDialog::index_in_locationslist(std::string lname)
379 {
380 vector<std::string>::iterator itl;
381 int index = 0;
382 for (itl=locationslist.begin(); itl<locationslist.end(); itl++)
383 {
384 if(lname == *itl )
385 {
386 return index;
387 }
388 index ++;
389 }
390 return -1;//not found
391 }
392
getLocation() const393 std::string CGUILocationDialog::getLocation() const
394 {
395 std::string loc = cbox->getStringValue();
396 return loc;
397 }
398
getLocationId() const399 int CGUILocationDialog::getLocationId() const
400 {
401 return cbox->getIntegerValue();
402 }
403
saveSelection() const404 bool CGUILocationDialog::saveSelection() const
405 {
406 const char* curLocName = Global::scenery->getName();
407 int curSkyVariant = Global::scenery->getSkyVariant();
408 int id = getLocationId();
409 std::string filename = filesList[id];
410 std::string newLocname = locationsList[id];
411 int newSkyVariant = sbox->getCurrentItem();
412 if (newSkyVariant < 0)
413 {
414 // If the combo box was empty, the current item was -1.
415 // In this case, correct the sky variant index to 0.
416 newSkyVariant = 0;
417 }
418
419 if ((newLocname != curLocName) || (newSkyVariant != curSkyVariant))
420 {
421 // temporarily set a null scenery to erase 3D display
422 Scenery* cur_scenery = Global::scenery;
423 Global::scenery = new SceneryNull();
424
425 Scenery* new_scenery = loadScenery(FileSysTools::getDataPath(filename).c_str(),
426 newSkyVariant);
427
428 // restore original scenery
429 delete Global::scenery;
430 Global::scenery = cur_scenery;
431
432 if (new_scenery)
433 {
434 cfg->setLocation(filename.c_str(), newSkyVariant, cfgfile);
435
436 clear_wind_field();
437 delete Global::scenery;
438 Global::scenery = new_scenery;
439
440 //reinitialise game mode
441 if (Global::gameHandler)
442 {
443 delete Global::gameHandler;
444 }
445 if (cfgfile->getInt("game.f3a.enabled",0))
446 Global::gameHandler = new HandlerF3A();
447 else if (cfgfile->getInt("game.f3f.enabled",0))
448 Global::gameHandler = new HandlerF3F();
449 else
450 Global::gameHandler= new T_GameHandler();
451 }
452 cfg->read(cfgfile);
453 Video::setWindowTitleString();
454 player_pos = Global::scenery->getPlayerPosition();
455 Global::Simulation->reset();
456 }
457 return true;
458 }
459
460 /** \brief The dialog's callback.
461 *
462 * Load the new location.
463 */
CGUILocationCallback(puObject * obj)464 void CGUILocationCallback(puObject *obj)
465 {
466 if (obj->getIntegerValue() == CRRC_DIALOG_OK)
467 {
468 CGUILocationDialog *dlg = (CGUILocationDialog*)obj->getUserData();
469 dlg->hide();
470 dlg->saveSelection();
471 }
472
473 Global::gui->hide();
474 puDeleteObject(obj);
475 }
476
477 /** \brief The location list's callback.
478 *
479 * Update scenery information whenever a new
480 * location is chosen.
481 */
CGUILocationSelectCallback(puObject * obj)482 void CGUILocationSelectCallback(puObject *obj)
483 {
484 CGUILocationDialog* dlg = static_cast<CGUILocationDialog*>(obj->getUserData());
485 dlg->updateLocationInfo(-1);
486 }
487
CGUISkySelectCallback(puObject * obj)488 void CGUILocationDialog::CGUISkySelectCallback(puObject *obj)
489 {
490 CGUILocationDialog *dlg = (CGUILocationDialog*)obj->getUserData();
491 int newSkyVariant = dlg->sbox->getCurrentItem();
492 if (newSkyVariant < 0) newSkyVariant = 0;
493 dlg->updateLocationInfo(newSkyVariant);
494 }
495