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