00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "framework/application.hpp"
00035 #include "framework/widget.hpp"
00036
00037 #include "data/exception.hpp"
00038 #include "data/list.hpp"
00039 #include "data/file.hpp"
00040 #include "data/directory.hpp"
00041 #include "data/broker.hpp"
00042
00043 #include "platform/thread.hpp"
00044 #include "platform/budget.hpp"
00045 #include "platform/display.hpp"
00046 #include "platform/texture.hpp"
00047
00048 #include "scenegraph/event_map.hpp"
00049 #include "scenegraph/simulation.hpp"
00050 #include "scenegraph/model.hpp"
00051
00052 #include "platform/lowlevel.hpp"
00053 #include "platform/extensions.hpp"
00054
00055
00056 #ifndef WIN32
00057 #include <dlfcn.h>
00058 #endif
00059
00060 #if defined (__OpenBSD__)
00061 #define DLOPEN_FLAG DL_LAZY
00062 #elif defined (__linux__)
00063 #define DLOPEN_FLAG RTLD_NOW
00064 #elif defined (WIN32)
00065 #elif defined (__APPLE__)
00066 #define DLOPEN_FLAG RTLD_NOW
00067 #else
00068 #error You must specify the correct flag to dlopen() for your OS.
00069 #endif
00070
00071
00072 namespace gsgl
00073 {
00074
00075 using namespace data;
00076 using namespace io;
00077 using namespace platform;
00078 using namespace scenegraph;
00079
00080
00081
00082 framework::application *framework::application::instance = 0;
00083
00084
00085 namespace framework
00086 {
00087
00088 config_variable<string> application::PROGRAM_PATH(L"paths/program", L".");
00089 config_variable<string> application::SYS_DATA_PATH(L"paths/sys_data", L"data");
00090 config_variable<string> application::USER_DATA_PATH(L"paths/user_data", L"user");
00091 config_variable<string> application::EVENT_MAP_PATH(L"paths/event_map", L"EventMap.cfg");
00092 config_variable<string> application::USER_CONFIG_PATH(L"paths/user_config", L"UserConfig.cfg");
00093
00094
00095
00096
00097
00098 application::application(const string & title, const int & argc, const char **argv)
00099 : framework_object(), singleton<application>(),
00100 state(APP_NO_STATE), title(title),
00101 splash_screen(0), focus_widget(0),
00102 global_context(0), global_scenery(0), global_simulation(0),
00103 global_console(0), global_mapper(0),
00104 global_budget(0)
00105 {
00106
00107 get_config_overrides(argc, argv);
00108
00109
00110 global_budget = new budget();
00111
00112
00113 global_mapper = new event_map(EVENT_MAP_PATH);
00114
00115
00116 if (TTF_Init() == -1)
00117 throw runtime_exception(L"SDL Font System Error: %hs.", TTF_GetError());
00118
00119 if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == 0)
00120 {
00121 try
00122 {
00123 SDL_WM_SetCaption(title.c_string(), title.c_string());
00124 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
00125 SDL_EnableUNICODE(1);
00126
00127 global_console = new display(platform::display::DISPLAY_WIDTH, platform::display::DISPLAY_HEIGHT, true);
00128 }
00129 catch (exception & e)
00130 {
00131 throw runtime_exception(L"Error: %s.", e.get_message());
00132 }
00133 }
00134 else
00135 {
00136 throw runtime_exception(L"Unable to initialize SDL: %hs", SDL_GetError());
00137 }
00138
00139
00140 global_caches.append(model::create_global_model_cache());
00141 global_caches.append(material::create_global_material_cache());
00142 global_caches.append(font::create_global_font_cache());
00143 global_caches.append(texture::create_global_texture_cache());
00144
00145
00146 string splashscreen_path = SYS_DATA_PATH + L"splashscreen.png";
00147 if (io::file::exists(splashscreen_path))
00148 splash_screen = new texture(splashscreen_path, TEXTURE_ENV_REPLACE);
00149
00150
00151 string pkg_path = SYS_DATA_PATH + L"Default.package";
00152 if (io::file::exists(pkg_path))
00153 load_package(pkg_path);
00154
00155
00156 const config_record & packages_config = const_cast<config_record &>(global_config::get_config()).get_child(L"packages");
00157 for (list<config_record>::const_iterator pkg = packages_config.get_children().iter(); pkg.is_valid(); ++pkg)
00158 {
00159 if (pkg->get_name() == L"package")
00160 {
00161 const string & filename = pkg->get_attribute(L"filename");
00162 if (!filename.is_empty())
00163 {
00164 load_package(SYS_DATA_PATH + filename);
00165 }
00166 }
00167 }
00168 }
00169
00170
00171 application::~application()
00172 {
00173 delete global_budget;
00174
00175 delete global_simulation; global_simulation = 0;
00176 delete global_context; global_context = 0;
00177 delete global_scenery; global_scenery = 0;
00178
00179 delete splash_screen; splash_screen = 0;
00180
00181
00182 while (widgets.size())
00183 {
00184 delete widgets.top();
00185 widgets.pop();
00186 }
00187
00188 focus_widget = 0;
00189
00190 delete global_console; global_console = 0;
00191 delete global_mapper; global_mapper = 0;
00192
00193
00194 for (list<gsgl::data_object *>::iterator i = global_caches.iter(); i.is_valid(); ++i)
00195 delete *i;
00196 global_caches.clear();
00197
00198
00199 TTF_Quit();
00200 SDL_Quit();
00201 }
00202
00203
00204 void application::load_package(const string & fname)
00205 {
00206 for (list<package *>::iterator i = loaded_packages.iter(); i.is_valid(); ++i)
00207 {
00208 if ((*i)->get_fname() == fname)
00209 throw runtime_exception(L"Attempted to load duplicate package %ls", fname.w_string());
00210 }
00211
00212 loaded_packages.append(new package(fname));
00213 }
00214
00215
00216 void application::load_and_run_simulation(const string & fname, gsgl::scenegraph::context *c)
00217 {
00218 assert(global_scenery);
00219 assert(c);
00220
00221 if (global_simulation)
00222 unload_and_quit_simulation();
00223
00224 gsgl::log(string(L"application: loading simulation ") + fname);
00225
00226 state = APP_SIM_LOADING;
00227 config_record sim_config(fname);
00228 global_context = c;
00229 global_simulation = new simulation(sim_config, global_console, global_context, global_scenery);
00230 global_simulation->init();
00231
00232 if (widgets.size())
00233 {
00234 widgets.top()->set_flags(widget::WIDGET_INVISIBLE, true);
00235 }
00236
00237 state = APP_SIM_RUNNING;
00238 }
00239
00240
00241 void application::unload_and_quit_simulation()
00242 {
00243 gsgl::log(L"application: unloading simulation");
00244
00245
00246 if (global_simulation)
00247 {
00248 global_simulation->cleanup();
00249 delete global_simulation;
00250 global_simulation = 0;
00251 }
00252
00253
00254 if (global_context)
00255 {
00256 delete global_context;
00257 global_context = 0;
00258 }
00259
00260
00261 if (global_scenery)
00262 {
00263 remove_viewpoint_nodes(global_scenery);
00264 }
00265
00266
00267 if (widgets.size())
00268 {
00269 widgets.top()->set_flags(widget::WIDGET_INVISIBLE, false);
00270 }
00271
00272 state = APP_UI_RUNNING;
00273 }
00274
00275
00276 void application::quit_application()
00277 {
00278 if (global_simulation)
00279 unload_and_quit_simulation();
00280
00281 state = APP_QUITTING;
00282 cleanup();
00283 }
00284
00285
00286 void application::run()
00287 {
00288 if (state != APP_NO_STATE)
00289 throw runtime_exception(L"You cannot call application::run() twice!");
00290
00291
00292 state = APP_INITIALIZING;
00293 this->init();
00294
00295
00296 state = APP_UI_RUNNING;
00297 bool should_draw_budget = false;
00298 unsigned int start_tick, end_tick;
00299 global_budget->reset();
00300 start_tick = SDL_GetTicks();
00301
00302 while (state != APP_QUITTING)
00303 {
00304
00305 glClearDepth(1); CHECK_GL_ERRORS();
00306 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); CHECK_GL_ERRORS();
00307 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); CHECK_GL_ERRORS();
00308
00309 glViewport(0, 0, global_console->get_width(), global_console->get_height()); CHECK_GL_ERRORS();
00310
00311
00312 if (!this->draw())
00313 {
00314 if (state == APP_SIM_RUNNING && global_simulation)
00315 {
00316 global_simulation->pre_draw();
00317 global_simulation->draw();
00318 }
00319 else if (splash_screen)
00320 {
00321 budget_record br(L"application: splash screen");
00322
00323 draw_splash_screen();
00324 }
00325 }
00326
00327
00328 if ((state == APP_UI_RUNNING || state == APP_SIM_RUNNING))
00329 {
00330 budget_record br(L"application: user interface");
00331
00332
00333 int i, num = widgets.size();
00334 for (i = 0; i < num; ++i)
00335 {
00336 if ((widgets[i]->get_flags() & widget::WIDGET_INVISIBLE) == 0)
00337 {
00338 glPushAttrib(GL_ALL_ATTRIB_BITS); CHECK_GL_ERRORS();
00339 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); CHECK_GL_ERRORS();
00340
00341 glMatrixMode(GL_PROJECTION); CHECK_GL_ERRORS();
00342 glLoadIdentity(); CHECK_GL_ERRORS();
00343
00344 glOrtho(0, global_console->get_width(), 0, global_console->get_height(), -1, 1);
00345
00346 glMatrixMode(GL_MODELVIEW); CHECK_GL_ERRORS();
00347 glPushMatrix(); CHECK_GL_ERRORS();
00348 glLoadIdentity(); CHECK_GL_ERRORS();
00349 glTranslatef(0.375f, 0.375f, 0.0f); CHECK_GL_ERRORS();
00350
00351 glEnable(GL_BLEND); CHECK_GL_ERRORS();
00352 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); CHECK_GL_ERRORS();
00353
00354 glEnable(GL_LINE_SMOOTH); CHECK_GL_ERRORS();
00355 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); CHECK_GL_ERRORS();
00356
00357 draw_ui(widgets[i]);
00358
00359 glMatrixMode(GL_MODELVIEW); CHECK_GL_ERRORS();
00360 glPopMatrix(); CHECK_GL_ERRORS();
00361
00362 glPopClientAttrib(); CHECK_GL_ERRORS();
00363 glPopAttrib(); CHECK_GL_ERRORS();
00364 }
00365 }
00366 }
00367
00368
00369 end_tick = SDL_GetTicks();
00370
00371 if (should_draw_budget)
00372 draw_budget(end_tick - start_tick);
00373
00374 global_budget->reset();
00375 start_tick = SDL_GetTicks();
00376
00377
00378 {
00379 budget_record br(L"application: buffer swap");
00380 SDL_GL_SwapBuffers();
00381 }
00382
00383
00384 if (state != APP_QUITTING && global_simulation)
00385 {
00386 if (global_simulation->is_running())
00387 global_simulation->update();
00388 else
00389 unload_and_quit_simulation();
00390 }
00391
00392
00393 {
00394 budget_record br(L"application: event handling");
00395
00396 SDL_Event e;
00397 while (SDL_PollEvent(&e))
00398 {
00399 switch (e.type)
00400 {
00401 case SDL_QUIT:
00402 quit_application();
00403 break;
00404 case SDL_KEYDOWN:
00405 if (e.key.keysym.sym == SDLK_b && (e.key.keysym.mod & (KMOD_CTRL | KMOD_ALT)))
00406 {
00407 should_draw_budget = !should_draw_budget;
00408 break;
00409 }
00410 else if (e.key.keysym.sym == SDLK_w && (e.key.keysym.mod & (KMOD_CTRL | KMOD_ALT)))
00411 {
00412 if (global_simulation)
00413 global_simulation->get_context()->render_flags ^= scenegraph::context::RENDER_WIREFRAME;
00414 }
00415 else if (e.key.keysym.sym == SDLK_t && (e.key.keysym.mod & (KMOD_CTRL | KMOD_ALT)))
00416 {
00417 if (global_simulation)
00418 global_simulation->get_context()->render_flags ^= scenegraph::context::RENDER_UNTEXTURED;
00419 }
00420 else if (e.key.keysym.sym == SDLK_l && (e.key.keysym.mod & (KMOD_CTRL | KMOD_ALT)))
00421 {
00422 if (global_simulation)
00423 global_simulation->get_context()->render_flags ^= scenegraph::context::RENDER_UNLIT;
00424 }
00425
00426
00427 default:
00428 if (widgets.size() && handle_ui_event(e, widgets.top()))
00429 continue;
00430
00431 if (this->handle_event(e))
00432 continue;
00433
00434 if (global_simulation)
00435 global_mapper->handle_event(e, global_simulation);
00436
00437 break;
00438 }
00439 }
00440 }
00441
00442
00443 if (state != APP_QUITTING)
00444 this->update();
00445 }
00446
00447
00448 state = APP_DEAD;
00449 }
00450
00451
00452
00453
00454 void application::draw_splash_screen()
00455 {
00456 float width = static_cast<float>(global_console->get_width());
00457 float height = static_cast<float>(global_console->get_height());
00458
00459 glPushAttrib(GL_ALL_ATTRIB_BITS); CHECK_GL_ERRORS();
00460 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); CHECK_GL_ERRORS();
00461
00462 glMatrixMode(GL_PROJECTION); CHECK_GL_ERRORS();
00463 glLoadIdentity(); CHECK_GL_ERRORS();
00464
00465 glOrtho(0, width, 0, height, -1, 1);
00466
00467 glMatrixMode(GL_MODELVIEW); CHECK_GL_ERRORS();
00468 glLoadIdentity(); CHECK_GL_ERRORS();
00469
00470 glEnable(GL_TEXTURE_2D); CHECK_GL_ERRORS();
00471 splash_screen->bind();
00472 display::draw_rectangle(0, 0, width, height);
00473
00474 glPopClientAttrib(); CHECK_GL_ERRORS();
00475 glPopAttrib(); CHECK_GL_ERRORS();
00476 }
00477
00478
00479 static const int BUDGET_FONT_SIZE = 12;
00480 static const color BUDGET_COLOR(1.0f, 0, 0, 1.0f);
00481 static const int BUDGET_BAR_WIDTH = 256;
00482 static const wchar_t *BUDGET_FORMAT = L"%6u";
00483 static font *budget_font = 0;
00484
00485 void application::draw_budget(unsigned int ticks)
00486 {
00487 if (!budget_font)
00488 budget_font = new font(L"Sans", BUDGET_FONT_SIZE, BUDGET_COLOR);
00489
00490
00491 int step = BUDGET_FONT_SIZE * 4 / 3;
00492 int num = global_budget->get_data().size();
00493 int widest = 0;
00494 unsigned int highest = 0;
00495 int sum = 0;
00496
00497 for (dictionary<unsigned int, string>::iterator i = global_budget->get_data().iter(); i.is_valid(); ++i)
00498 {
00499 int width = static_cast<int>(budget_font->calc_width(i.get_index()));
00500 if (width > widest)
00501 widest = width;
00502
00503 if (*i > highest)
00504 highest = *i;
00505
00506 sum += *i;
00507 }
00508
00509
00510 global_console->draw_text_start();
00511
00512 global_console->draw_2d_text(0, static_cast<float>((num+1)*step), budget_font, L"TOTAL");
00513 global_console->draw_2d_text(static_cast<float>(widest), static_cast<float>((num+1)*step), budget_font, string::format(BUDGET_FORMAT, ticks));
00514
00515 int n = 0;
00516 for (dictionary<unsigned int, string>::iterator i = global_budget->get_data().iter(); i.is_valid(); ++i)
00517 {
00518 int y = (num-n)*step;
00519
00520 global_console->draw_2d_text(0, static_cast<float>(y), budget_font, i.get_index());
00521 global_console->draw_2d_text(static_cast<float>(widest), static_cast<float>(y), budget_font, string::format(BUDGET_FORMAT, *i));
00522
00523 int w = static_cast<int>(BUDGET_BAR_WIDTH * static_cast<double>(*i)/static_cast<double>(highest));
00524 BUDGET_COLOR.set();
00525 glMatrixMode(GL_MODELVIEW);
00526 glLoadIdentity();
00527
00528 glBegin(GL_TRIANGLE_STRIP);
00529 glVertex2i(widest+64, y+8);
00530 glVertex2i(widest+64, y+2);
00531 glVertex2i(widest+64+w, y+8);
00532 glVertex2i(widest+64+w, y+2);
00533 glEnd();
00534
00535 ++n;
00536 }
00537
00538 global_console->draw_2d_text(0, 0, budget_font, L"other");
00539 global_console->draw_2d_text(static_cast<float>(widest), 0, budget_font, string::format(BUDGET_FORMAT, ticks - sum));
00540
00541 int w = static_cast<int>(BUDGET_BAR_WIDTH * static_cast<double>(ticks - sum)/static_cast<double>(highest));
00542 BUDGET_COLOR.set();
00543 glMatrixMode(GL_MODELVIEW);
00544 glLoadIdentity();
00545
00546 glBegin(GL_TRIANGLE_STRIP);
00547 glVertex2i(widest+64, 10);
00548 glVertex2i(widest+64, 2);
00549 glVertex2i(widest+64+w, 10);
00550 glVertex2i(widest+64+w, 2);
00551 glEnd();
00552
00553 global_console->draw_text_stop();
00554 }
00555
00556
00557 void application::draw_ui(widget *w)
00558 {
00559 assert(w);
00560
00561 glMatrixMode(GL_MODELVIEW);
00562 glPushMatrix();
00563
00564 glTranslatef(static_cast<GLfloat>(w->get_x()), static_cast<GLfloat>(w->get_y()), 0);
00565
00566 if ((w->get_flags() & widget::WIDGET_INVISIBLE) == 0)
00567 {
00568 w->draw();
00569
00570
00571 const int num = w->get_children().size();
00572 for (int i = 0; i < num; ++i)
00573 {
00574 draw_ui(w->get_children()[i]);
00575 }
00576 }
00577
00578 glMatrixMode(GL_MODELVIEW);
00579 glPopMatrix();
00580 }
00581
00582
00583
00584
00585
00586
00587 bool application::handle_event(const SDL_Event &)
00588 {
00589 return false;
00590 }
00591
00592
00593
00594 void application::init()
00595 {
00596 }
00597
00598
00599
00600
00601 bool application::draw()
00602 {
00603 return false;
00604 }
00605
00606
00607 void application::update()
00608 {
00609 }
00610
00611
00612
00613 void application::cleanup()
00614 {
00615 }
00616
00617
00618
00619
00620 void application::remove_viewpoint_nodes(node *n)
00621 {
00622 list<node *> children_to_remove;
00623
00624
00625 for (simple_array<node *>::iterator i = n->get_children().iter(); i.is_valid(); ++i)
00626 {
00627 node *child = *i;
00628
00629 if (dynamic_cast<freeview *>(child))
00630 children_to_remove.append(child);
00631 else
00632 remove_viewpoint_nodes(child);
00633 }
00634
00635
00636 for (list<node *>::iterator i = children_to_remove.iter(); i.is_valid(); ++i)
00637 {
00638 for (simple_array<node *>::iterator j = n->get_children().iter(); j.is_valid(); ++j)
00639 {
00640 if (*i == *j)
00641 {
00642 n->get_children().remove(j);
00643 break;
00644 }
00645 }
00646 }
00647 }
00648
00649
00650
00651
00652 bool application::get_cmdline_dir(const int & argc, const char **argv, const int & pos, const string & arg_key, config_record & conf, const string & conf_key)
00653 {
00654 string arg(argv[pos]);
00655
00656 if (arg == arg_key)
00657 {
00658 if (pos+1 < argc)
00659 {
00660 string val(argv[pos+1]);
00661 io::directory dir(val);
00662 conf.get_child(conf_key).get_text() = dir.get_full_path();
00663 return true;
00664 }
00665 else
00666 {
00667 throw runtime_exception(L"You must specify a directory after %ls on the command line.", arg_key.w_string());
00668 }
00669 }
00670
00671 return false;
00672 }
00673
00674
00675 void application::get_config_overrides(const int & argc, const char **argv)
00676 {
00677
00678 const_cast<config_record &>(global_config::get_config()).get_name() = title;
00679
00680
00681 config_record cmd_line_config;
00682 cmd_line_config.get_name() = title;
00683
00684 config_record & program = cmd_line_config.get_child(L"paths/program");
00685 program.get_text() = get_program_dir(string(argv[0]));
00686
00687 for (int i = 1; i < argc; ++i)
00688 {
00689 string arg(argv[i]);
00690
00691
00692 if (get_cmdline_dir(argc, argv, i, L"-sys_data", cmd_line_config, L"paths/sys_data"))
00693 {
00694 ++i;
00695 continue;
00696 }
00697
00698
00699 if (get_cmdline_dir(argc, argv, i, L"-user_data", cmd_line_config, L"paths/user_data"))
00700 {
00701 ++i;
00702 continue;
00703 }
00704 }
00705
00706 config_record & sys_data = cmd_line_config.get_child(L"paths/sys_data");
00707 if (sys_data.get_text().is_empty())
00708 sys_data.get_text() = program.get_text() + SYS_DATA_PATH;
00709
00710 config_record & user_data = cmd_line_config.get_child(L"paths/user_data");
00711 if (user_data.get_text().is_empty())
00712 user_data.get_text() = get_user_dir();
00713
00714 config_record & event_map = cmd_line_config.get_child(L"paths/event_map");
00715 if (event_map.get_text().is_empty())
00716 event_map.get_text() = user_data.get_text() + EVENT_MAP_PATH;
00717
00718 config_record & user_config = cmd_line_config.get_child(L"paths/user_config");
00719 if (user_config.get_text().is_empty())
00720 user_config.get_text() = user_data.get_text() + title + L".cfg";
00721
00722
00723 global_config::override_with(cmd_line_config);
00724
00725
00726 if (!io::directory::exists(SYS_DATA_PATH))
00727 throw runtime_exception(L"Invalid system data directory: %ls", SYS_DATA_PATH.get_value().w_string());
00728
00729 if (!io::directory::exists(USER_DATA_PATH))
00730 io::directory::create(USER_DATA_PATH);
00731
00732
00733 if (!io::file::exists(USER_CONFIG_PATH))
00734 io::file::copy(SYS_DATA_PATH + L"Sample " + title + L".cfg", USER_CONFIG_PATH);
00735
00736 if (!io::file::exists(EVENT_MAP_PATH))
00737 io::file::copy(SYS_DATA_PATH + L"Sample " + title + L" EventMap.cfg", EVENT_MAP_PATH);
00738
00739
00740 config_record user_record(USER_CONFIG_PATH);
00741 global_config::override_with(user_record);
00742 global_config::override_with(cmd_line_config);
00743
00744
00745 global_config::save(USER_CONFIG_PATH);
00746 }
00747
00748
00749
00750
00751 node *application::load_objects(const config_record & obj_config, synchronized<string> & status_string)
00752 {
00753 node *result = 0;
00754 data::broker *b = data::broker::global_instance();
00755 assert(b);
00756
00757 const string & type_name = obj_config.get_name();
00758
00759 status_string = string::format(L"Loading %ls...", type_name.w_string());
00760
00761 if (b->has_object(type_name))
00762 {
00763 result = dynamic_cast<node *>(b->create_object(type_name, obj_config));
00764
00765 if (!result)
00766 throw runtime_exception(L"Unable to create object of type %ls referenced in %ls.", obj_config.get_name(), obj_config.get_file().get_full_path().w_string());
00767
00768
00769 for (list<config_record>::const_iterator child = obj_config.get_children().iter(); child.is_valid(); ++child)
00770 {
00771 node *child_node = load_objects(*child, status_string);
00772 if (child_node)
00773 result->add_child(child_node);
00774 }
00775 }
00776 else
00777 {
00778 throw runtime_exception(L"Unknown object type %ls in %ls.", obj_config.get_name().w_string(), obj_config.get_file().get_full_path().w_string());
00779 }
00780
00781 return result;
00782 }
00783
00784
00785 void application::unload_scenery()
00786 {
00787 delete global_scenery;
00788 global_scenery = 0;
00789 }
00790
00791
00792 void application::load_scenery(synchronized<string> & status_string)
00793 {
00794
00795 list<node *> all_nodes;
00796
00797 for (list<package *>::iterator package = loaded_packages.iter(); package.is_valid(); ++package)
00798 {
00799 for (list<pkg_scenery *>::const_iterator scenery = (*package)->get_loaded_sceneries().iter(); scenery.is_valid(); ++scenery)
00800 {
00801 const string & fname = (*scenery)->get_fname();
00802 if (!fname.is_empty())
00803 {
00804 config_record scn_config(fname);
00805 if (scn_config.get_name() == L"scenery")
00806 {
00807 for (list<config_record>::iterator i = scn_config.get_children().iter(); i.is_valid(); ++i)
00808 {
00809 if (i->get_name() != L"description")
00810 {
00811 node *obj = load_objects(*i, status_string);
00812 if (obj)
00813 all_nodes.append(obj);
00814 }
00815 }
00816 }
00817 else
00818 {
00819 throw runtime_exception(L"Invalid scenery specification in %ls.", fname.w_string());
00820 }
00821 }
00822 }
00823 }
00824
00825
00826 node *scenery = new node(L"scenery", 0);
00827
00828
00829 list<node *> non_roots;
00830
00831 for (list<node *>::iterator n = all_nodes.iter(); n.is_valid(); ++n)
00832 {
00833 if ((*n)->get_parent_name().is_empty())
00834 {
00835 scenery->add_child(*n);
00836 }
00837 else
00838 {
00839 non_roots.append(*n);
00840 }
00841 }
00842
00843 if (scenery->get_children().size() == 0)
00844 {
00845 throw runtime_exception(L"There must be at least one scenery node without a parent.");
00846 }
00847
00848
00849 list<node *> nodes_to_check, nodes_not_added;
00850
00851 nodes_to_check = non_roots;
00852
00853 while (true)
00854 {
00855
00856 for (list<node *>::iterator child = non_roots.iter(); child.is_valid(); ++child)
00857 {
00858 bool found_root = false;
00859
00860
00861 for (list<node *>::iterator parent = all_nodes.iter(); parent.is_valid(); ++parent)
00862 {
00863 if (*child != *parent && (*child)->get_parent_name() == (*parent)->get_name())
00864 {
00865 (*parent)->add_child(*child);
00866 found_root = true;
00867 break;
00868 }
00869 }
00870
00871 if (found_root)
00872 break;
00873 else
00874 nodes_not_added.append(*child);
00875 }
00876
00877 if (nodes_not_added.size() == 0)
00878 {
00879 break;
00880 }
00881 else if (nodes_not_added.size() == nodes_to_check.size())
00882 {
00883
00884 throw runtime_exception(L"Found a parentless node: %ls", nodes_not_added[0]->get_name().w_string());
00885 }
00886 else
00887 {
00888 nodes_to_check = nodes_not_added;
00889 nodes_not_added.clear();
00890 }
00891 }
00892
00893 global_scenery = scenery;
00894 }
00895
00896
00897
00898
00899 bool application::handle_ui_event(const SDL_Event & e, widget *w)
00900 {
00901 assert(w);
00902
00903 if (e.type == SDL_MOUSEBUTTONDOWN)
00904 {
00905 mouse_button_pressed = e.button.button;
00906 mouse_button_x = e.button.x;
00907 mouse_button_y = global_console->get_height() - e.button.y;
00908 }
00909
00910 return handle_ui_event(e, w, e.button.x, global_console->get_height() - e.button.y);
00911 }
00912
00913
00914 bool application::handle_ui_event(const SDL_Event & e, widget *w, int mouse_x, int mouse_y)
00915 {
00916 assert(w);
00917
00918 if (!(w->get_flags() & (widget::WIDGET_INVISIBLE | widget::WIDGET_INACTIVE)))
00919 {
00920 int widget_x = w->get_x();
00921 int widget_w = w->get_w();
00922 int widget_y = w->get_y();
00923 int widget_h = w->get_h();
00924
00925 if ((widget_x <= mouse_x && mouse_x <= (widget_x + widget_w))
00926 && (widget_y <= mouse_y && mouse_y <= (widget_y + widget_h)))
00927 {
00928 if (w->get_event_handler() && w->get_event_handler()(w, e))
00929 return true;
00930 if (w->handle_event(e))
00931 return true;
00932
00933 for (int i = 0; i < w->get_children().size(); ++i)
00934 {
00935 if (handle_ui_event(e, w->get_children()[i], mouse_x - widget_x, mouse_y - widget_y))
00936 return true;
00937 }
00938 }
00939 }
00940
00941 return false;
00942 }
00943
00944
00945 void application::get_mousedown_info(int & button, int & x, int & y)
00946 {
00947 button = mouse_button_pressed;
00948 x = mouse_button_x;
00949 y = mouse_button_y;
00950 }
00951
00952
00953
00954
00955 #ifdef WIN32
00956
00957 #pragma warning (disable : 4996)
00958
00959 #define WIN32_LEAN_AND_MEAN
00960 #include <windows.h>
00961 #include <shlobj.h>
00962 #include <direct.h>
00963 #endif
00964
00965
00966 #ifdef WIN32
00967 static const wchar_t *SLASH = L"\\";
00968 #else
00969 #error Must define the actual directory separator!
00970 #endif
00971
00972
00973 string application::get_program_dir(const string & arg)
00974 {
00975 string result;
00976 int last_slash_pos = arg.find_reverse(SLASH);
00977
00978 if (last_slash_pos != -1)
00979 {
00980 result = arg.substring(0, last_slash_pos);
00981 }
00982 else
00983 {
00984 io::directory cwd;
00985 result = cwd.get_full_path();
00986 }
00987
00988 io::directory progdir(result);
00989 return progdir.get_full_path();
00990 }
00991
00992
00993 string application::get_user_dir()
00994 {
00995 #ifdef WIN32
00996 smart_pointer<wchar_t, true> buf(new wchar_t[MAX_PATH]);
00997
00998 if (SUCCEEDED(SHGetFolderPath(0, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, buf)))
00999 {
01000 string path(buf);
01001 io::directory userdir(path);
01002
01003 return userdir.get_full_path() + title + io::directory::SEPARATOR;
01004 }
01005 else
01006 {
01007 throw io_exception(L"Unable to find user data directory.");
01008 }
01009 #else
01010 #error get_user_dir() unimplemented!
01011 #endif
01012 }
01013
01014
01015 }
01016
01017 }