00001 00005 /* Copyright © 2009, 2010 James Legg. 00006 This program is free software: you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation, either version 3 of the License, or 00009 (at your option) any later version. 00010 */ 00011 #include "EditorWindow.h" 00012 00013 #include <gtkmm/stock.h> 00014 #include <gtkmm/filechooserdialog.h> 00015 #include <gtkmm/aboutdialog.h> 00016 #include <gtkmm/messagedialog.h> 00017 #include <gtkmm/main.h> 00018 #include <gtkmm/stock.h> 00019 #include <gtkmm/accelmap.h> 00020 00021 #include <giomm/appinfo.h> 00022 00023 #include <Debug.h> 00024 #include <config.h> 00025 #include <fstream> 00026 #include <sstream> 00027 00028 #include <libtrack/FormErrors.h> 00029 #include <libtrack/document/ChangeLightingDelta.h> 00030 00031 EditorWindow::EditorWindow(bool show_guide, std::string filename) 00032 : m_ref_action_group(Gtk::ActionGroup::create()) 00033 , m_ref_ui_manager(Gtk::UIManager::create()) 00034 , m_new_form(*this) 00035 , m_viewport_top() 00036 , m_guide_preference(show_guide) 00037 { 00038 set_title("Racer Editor"); 00039 assemble_ui(); 00040 m_viewport_top.set_angle(View::VIEW_TOP); 00041 m_new_form.show(); 00042 on_action_file_new(); 00043 show(); 00044 // Find the themes after the window is shown to make starting the program 00045 // more responsive. 00046 00047 while (Gtk::Main::instance()->events_pending()) 00048 { 00049 Gtk::Main::instance()->iteration(); 00050 } 00051 m_new_form.find_themes(); 00052 if (filename != "") 00053 { 00054 open_file(filename); 00055 } else { 00056 // give the theme list focus. 00057 m_new_form.grab_focus(); 00058 } 00059 } 00060 00061 void EditorWindow::assemble_ui() 00062 { 00063 add(m_box); 00064 00065 00066 // file menu stuff 00067 m_ref_action_group->add( Gtk::Action::create("MenuFile", "_File") ); 00068 m_ref_action_group->add( Gtk::Action::create("New", Gtk::Stock::NEW), 00069 sigc::mem_fun(*this, &EditorWindow::on_action_file_new) ); 00070 m_ref_action_group->add( Gtk::Action::create("Open", Gtk::Stock::OPEN), 00071 sigc::mem_fun(*this, &EditorWindow::on_action_file_open) ); 00072 m_ref_action_group->add( Gtk::Action::create("Save", Gtk::Stock::SAVE), 00073 sigc::mem_fun(*this, &EditorWindow::on_action_file_save) ); 00074 m_ref_action_group->add( Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS), 00075 sigc::mem_fun(*this, &EditorWindow::on_action_file_save_as) ); 00076 00077 m_ref_action_group->add( Gtk::Action::create("Test", Gtk::Stock::MEDIA_PLAY, "Test", "Try a race"), 00078 sigc::mem_fun(*this, &EditorWindow::on_action_file_test) ); 00079 00080 m_ref_action_group->add( Gtk::Action::create("Quit", Gtk::Stock::QUIT), 00081 sigc::mem_fun(*this, &EditorWindow::on_action_file_quit) ); 00082 00083 // edit menu stuff 00084 m_ref_action_group->add( Gtk::Action::create("MenuEdit", "_Edit") ); 00085 00086 m_ref_action_group->add( Gtk::Action::create("Undo", Gtk::Stock::UNDO), 00087 Gtk::AccelKey('z', Gdk::CONTROL_MASK, "<racer-editor-track>/Edit/Undo"), 00088 sigc::mem_fun(*this, &EditorWindow::on_action_edit_undo) ); 00089 m_ref_action_group->add( Gtk::Action::create("Redo", Gtk::Stock::REDO), 00090 Gtk::AccelKey('z', Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, "<racer-editor-track>/Edit/Redo"), 00091 sigc::mem_fun(*this, &EditorWindow::on_action_edit_redo) ); 00092 00093 m_ref_action_group->add( Gtk::Action::create("Cut", Gtk::Stock::CUT), 00094 sigc::mem_fun(*this, &EditorWindow::on_action_edit_cut) ); 00095 m_ref_action_group->add( Gtk::Action::create("Copy", Gtk::Stock::COPY), 00096 sigc::mem_fun(*this, &EditorWindow::on_action_edit_copy) ); 00097 m_ref_action_group->add( Gtk::Action::create("Paste", Gtk::Stock::PASTE), 00098 sigc::mem_fun(*this, &EditorWindow::on_action_edit_paste) ); 00099 00100 m_ref_action_group->add( Gtk::Action::create("Delete", Gtk::Stock::DELETE), 00101 sigc::mem_fun(*this, &EditorWindow::on_action_edit_delete) ); 00102 m_ref_action_group->add( Gtk::Action::create("SelectAll", Gtk::Stock::SELECT_ALL), 00103 sigc::mem_fun(*this, &EditorWindow::on_action_edit_select_all) ); 00104 m_ref_action_group->add( Gtk::Action::create("Lighting", Gtk::Stock::COLOR_PICKER, "Lighting..."), 00105 Gtk::AccelKey("F2", "<racer-editor-track>/Edit/Lighting"), 00106 sigc::mem_fun(*this, &EditorWindow::on_action_edit_lighting) ); 00107 00108 // view menu stuff 00109 m_ref_action_group->add(Gtk::Action::create("MenuView", "_View")); 00110 m_ref_action_group->add( Gtk::Action::create("ZoomIn", Gtk::Stock::ZOOM_IN), 00111 Gtk::AccelKey('+', Gdk::CONTROL_MASK, "<racer-editor-track>/View/ZoomIn"), 00112 sigc::mem_fun(*this, &EditorWindow::on_action_view_zoom_in) ); 00113 m_ref_action_group->add( Gtk::Action::create("ZoomOut", Gtk::Stock::ZOOM_OUT), 00114 Gtk::AccelKey('-', Gdk::CONTROL_MASK, "<racer-editor-track>/View/ZoomOut"), 00115 sigc::mem_fun(*this, &EditorWindow::on_action_view_zoom_out) ); 00116 m_ref_action_group->add( Gtk::Action::create("ZoomToFit", Gtk::Stock::ZOOM_FIT), 00117 Gtk::AccelKey('0', Gdk::CONTROL_MASK, "<racer-editor-track>/View/ZoomToFit"), 00118 sigc::mem_fun(*this, &EditorWindow::on_action_view_zoom_to_fit) ); 00120 m_ref_action_group->add( Gtk::Action::create("ShowTop", Gtk::Stock::GO_DOWN, "Show top view"), 00121 Gtk::AccelKey('1', Gdk::CONTROL_MASK, "<racer-editor-track>/View/ShowTop"), 00122 sigc::mem_fun(*this, &EditorWindow::on_action_view_top) ); 00123 m_ref_action_group->add( Gtk::Action::create("ShowFront", Gtk::Stock::GO_FORWARD, "Show front view"), 00124 Gtk::AccelKey('2', Gdk::CONTROL_MASK, "<racer-editor-track>/View/ShowFront"), 00125 sigc::mem_fun(*this, &EditorWindow::on_action_view_front) ); 00126 m_ref_action_group->add( Gtk::Action::create("ShowSide", Gtk::Stock::GO_BACK, "Show side view"), 00127 Gtk::AccelKey('3', Gdk::CONTROL_MASK, "<racer-editor-track>/View/ShowSide"), 00128 sigc::mem_fun(*this, &EditorWindow::on_action_view_side) ); 00129 m_ref_action_group->add( Gtk::Action::create("NewWindow", Gtk::Stock::LEAVE_FULLSCREEN, "New window"), 00130 sigc::mem_fun(*this, &EditorWindow::on_action_view_new_window) ); 00131 00132 // help menu stuff 00133 m_ref_action_group->add(Gtk::Action::create("MenuHelp", "_Help")); 00134 m_ref_action_guide = Gtk::ToggleAction::create("HelpGuide", Gtk::Stock::HELP, "Show Guide", "Show tips under the toolbar.", true); 00135 m_ref_action_group->add(m_ref_action_guide, 00136 Gtk::AccelKey("F1", "<racer-editor-track>/Help/ShowGuide"), 00137 sigc::mem_fun(*this, &EditorWindow::on_action_help_guide) ); 00138 m_ref_action_group->add( Gtk::Action::create("HelpWiki", Gtk::Stock::HELP, "Open website"), 00139 sigc::mem_fun(*this, &EditorWindow::on_action_help_wiki) ); 00140 m_ref_action_group->add( Gtk::Action::create("HelpAbout", Gtk::Stock::ABOUT), 00141 sigc::mem_fun(*this, &EditorWindow::on_action_help_about) ); 00142 00143 // load any customised keyboard accelerators. 00144 accel_filename = Glib::get_user_config_dir() + "/racer-editor_accel_map"; 00145 Gtk::AccelMap::load(accel_filename); 00146 DEBUG_MESSAGE("Loaded accelerators from " << accel_filename); 00147 00148 // use all these actions 00149 m_ref_ui_manager->insert_action_group(m_ref_action_group); 00150 add_accel_group(m_ref_ui_manager->get_accel_group()); 00151 00152 Glib::ustring ui_info = 00153 "<ui>" 00154 " <menubar name='MenuBar'>" 00155 " <menu action='MenuFile'>" 00156 " <menuitem action='New'/>" 00157 " <menuitem action='Open'/>" 00158 " <menuitem action='Save'/>" 00159 " <menuitem action='SaveAs'/>" 00160 " <separator/>" 00161 " <menuitem action='Test'/>" 00162 " <separator/>" 00163 " <menuitem action='Quit'/>" 00164 " </menu>" 00165 " <menu action='MenuEdit'>" 00166 " <menuitem action='Undo'/>" 00167 " <menuitem action='Redo'/>" 00168 " <separator/>" 00169 " <menuitem action='Lighting'/>" 00170 // " <separator/>" 00171 // " <menuitem action='Cut'/>" 00172 // " <menuitem action='Copy'/>" 00173 // " <menuitem action='Paste'/>" 00174 // " <separator/>" 00175 // " <menuitem action='Delete'/>" 00176 // " <menuitem action='SelectAll'/>" 00177 " </menu>" 00178 " <menu action='MenuView'>" 00179 " <menuitem action='ZoomIn'/>" 00180 " <menuitem action='ZoomOut'/>" 00181 " <menuitem action='ZoomToFit'/>" 00182 " <separator/>" 00183 " <menuitem action='ShowTop'/>" 00184 " <menuitem action='ShowFront'/>" 00185 " <menuitem action='ShowSide'/>" 00186 " <separator/>" 00187 " <menuitem action='NewWindow'/>" 00188 " </menu>" 00189 " <menu action='MenuHelp'>" 00190 " <menuitem action='HelpGuide'/>" 00191 " <menuitem action='HelpWiki'/>" 00192 " <menuitem action='HelpAbout'/>" 00193 " </menu>" 00194 " </menubar>" 00195 " <toolbar name='ToolBar'>" 00196 " <toolitem action='Quit'/>" 00197 " <separator/>" 00198 " <toolitem action='New'/>" 00199 " <toolitem action='Open'/>" 00200 " <toolitem action='Save'/>" 00201 " <separator/>" 00202 " <toolitem action='Test'/>" 00203 " <separator/>" 00204 " <toolitem action='Undo'/>" 00205 " <toolitem action='Redo'/>" 00206 " <separator/>" 00207 // " <toolitem action='Cut'/>" 00208 // " <toolitem action='Copy'/>" 00209 // " <toolitem action='Paste'/>" 00210 // " <separator/>" 00211 // " <toolitem action='Delete'/>" 00212 // " <separator/>" 00213 " <toolitem action='ZoomIn'/>" 00214 " <toolitem action='ZoomOut'/>" 00215 " <toolitem action='ZoomToFit'/>" 00216 " <separator/>" 00217 " <toolitem action='ShowTop'/>" 00218 " <toolitem action='ShowFront'/>" 00219 " <toolitem action='ShowSide'/>" 00220 " <separator/>" 00221 " <toolitem action='NewWindow'/>" 00222 " </toolbar>" 00223 "</ui>"; 00224 00225 m_ref_ui_manager->add_ui_from_string(ui_info); 00226 Gtk::Widget* menu_bar = m_ref_ui_manager->get_widget("/MenuBar"); 00227 m_box.pack_start(*menu_bar, Gtk::PACK_SHRINK); 00228 Gtk::Widget* tool_bar = m_ref_ui_manager->get_widget("/ToolBar"); 00229 m_box.pack_start(*tool_bar, Gtk::PACK_SHRINK); 00230 tool_bar->show(); 00231 00232 m_box.pack_end(m_status_bar, Gtk::PACK_SHRINK); 00233 m_status_bar.show(); 00234 00235 // The usage guide bar. 00236 m_box.pack_start(m_guide, Gtk::PACK_SHRINK); 00237 m_guide.set_visible(m_guide_preference); 00238 // Set the menu item's active state to reflect the visiblity preference. 00239 Glib::RefPtr<Gtk::Action> guide= m_ref_action_group->get_action("HelpGuide"); 00240 Glib::RefPtr<Gtk::ToggleAction> guide_t= Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic(guide); 00241 assert(guide_t); 00242 guide_t->set_active(m_guide_preference); 00243 00244 // the scrollable top down view. 00245 m_box.pack_start(m_viewport_top); 00246 m_viewport_top.signal_command().connect( 00247 sigc::mem_fun(*this, &EditorWindow::on_view_command)); 00248 m_viewport_top.signal_preview_command().connect( 00249 sigc::mem_fun(*this, &EditorWindow::on_view_preview_command)); 00250 m_viewport_top.signal_preview_cancel().connect( 00251 sigc::mem_fun(*this, &EditorWindow::on_view_preview_cancel)); 00252 00253 m_box.pack_start(m_new_form); 00254 m_new_form.signal_theme_picked().connect( 00255 sigc::mem_fun(*this, &EditorWindow::on_theme_picked)); 00256 00257 m_lighting_window.signal_changed().connect( 00258 sigc::mem_fun(*this, &EditorWindow::on_lighting_changed)); 00259 00260 m_box.show(); 00261 00262 set_default_size(640, 480); 00263 } 00264 00265 EditorWindow::EditorWindow (EditorWindow & source) 00266 : m_ref_action_group(Gtk::ActionGroup::create()) 00267 , m_ref_ui_manager(Gtk::UIManager::create()) 00268 , m_new_form(*this) 00269 , m_viewport_top(source.m_viewport_top) 00270 , m_theme(source.m_theme) 00271 , m_document(source.m_document) 00272 , filename_set(source.filename_set) 00273 , filename(source.filename) 00274 , theme_filename(source.theme_filename) 00275 { 00276 set_title("Racer Editor (copy)"); 00277 assemble_ui(); 00278 show(); 00279 if (m_document) 00280 { 00281 m_document->signal_command_run().connect( 00282 sigc::mem_fun(*this, &EditorWindow::on_command_run)); 00283 // show tools. 00284 show_editor(); 00285 // make sure undo / redo actions' availability is set. 00286 on_command_run(); 00287 } 00288 else 00289 { 00290 m_new_form.show(); 00291 } 00292 } 00293 00294 EditorWindow::~EditorWindow() 00295 { 00296 // save settings. 00297 std::string config_filename = Glib::get_user_config_dir() + "/racer-editor.config"; 00298 DEBUG_MESSAGE("Saving configuration to " << config_filename); 00299 std::ofstream config_file(config_filename.c_str()); 00300 00301 config_file << "#Set to true to show the usage guide." << std::endl; 00302 config_file << "show-guide=" << (m_guide_preference ? "true" : "false") << std::endl; 00303 00304 } 00305 00306 void EditorWindow::on_action_file_new() 00307 { 00308 // check if it is allowed first 00309 if (!check_clear_document()) return; 00310 // allowed. Begin by forgeting the current file if applicable. 00311 filename_set = false; 00312 m_document = boost::shared_ptr<Document::Document>(); 00313 m_theme = boost::shared_ptr<Track::Theme>(); 00314 00315 m_new_form.show(); 00316 m_viewport_top.hide(); 00317 00318 m_guide.hide(); 00319 00320 // disable actions that make no sense with no file open. 00321 m_ref_action_group->get_action("New")->set_sensitive(false); 00322 m_ref_action_group->get_action("Save")->set_sensitive(false); 00323 m_ref_action_group->get_action("SaveAs")->set_sensitive(false); 00324 m_ref_action_group->get_action("Undo")->set_sensitive(false); 00325 m_ref_action_group->get_action("Redo")->set_sensitive(false); 00326 m_ref_action_group->get_action("Test")->set_sensitive(false); 00327 m_ref_action_group->get_action("Cut")->set_sensitive(false); 00328 m_ref_action_group->get_action("Copy")->set_sensitive(false); 00329 m_ref_action_group->get_action("Paste")->set_sensitive(false); 00330 m_ref_action_group->get_action("Delete")->set_sensitive(false); 00331 m_ref_action_group->get_action("SelectAll")->set_sensitive(false); 00332 m_ref_action_group->get_action("Lighting")->set_sensitive(false); 00333 m_ref_action_group->get_action("ZoomIn")->set_sensitive(false); 00334 m_ref_action_group->get_action("ZoomOut")->set_sensitive(false); 00335 m_ref_action_group->get_action("ZoomToFit")->set_sensitive(false); 00336 m_ref_action_group->get_action("ShowTop")->set_sensitive(false); 00337 m_ref_action_group->get_action("ShowFront")->set_sensitive(false); 00338 m_ref_action_group->get_action("ShowSide")->set_sensitive(false); 00339 m_ref_action_group->get_action("NewWindow")->set_sensitive(false); 00340 m_ref_action_group->get_action("HelpGuide")->set_sensitive(false); 00341 } 00342 00343 void EditorWindow::on_action_file_open() 00344 { 00345 // check for unsaved changes... 00346 if (!check_clear_document()) return; 00347 // open the file. 00348 Gtk::FileChooserDialog open_dialog(*this, "Choose file to open"); 00349 open_dialog.add_shortcut_folder("data/tracks"); 00350 open_dialog.set_current_folder("data/tracks"); 00351 open_dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_REJECT); 00352 open_dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); 00353 int result = open_dialog.run(); 00354 if (result == Gtk::RESPONSE_ACCEPT) 00355 { 00356 open_dialog.hide(); 00357 // open the file 00358 open_file(open_dialog.get_filename()); 00359 } 00360 } 00361 00362 void EditorWindow::open_file(std::string filename_in) 00363 { 00364 filename_set = true; 00365 filename = filename_in; 00366 std::ifstream file; 00367 file.exceptions(std::ifstream::eofbit | 00368 std::ifstream::failbit | 00369 std::ifstream::badbit); 00370 try 00371 { 00372 m_status_bar.push("Loading track"); 00373 while (Gtk::Main::events_pending()) Gtk::Main::iteration(); 00374 file.open(filename.c_str()); 00375 std::getline(file, theme_filename); 00376 m_status_bar.push("Loading theme"); 00377 while (Gtk::Main::events_pending()) Gtk::Main::iteration(); 00378 load_theme(theme_filename); 00379 m_status_bar.pop(); 00380 while (Gtk::Main::events_pending()) Gtk::Main::iteration(); 00381 m_document = boost::shared_ptr<Document::Document>(new Document::Document(file, *m_theme)); 00382 // monitor document for changes 00383 m_document->signal_command_run().connect( 00384 sigc::mem_fun(*this, &EditorWindow::on_command_run)); 00385 // show tools. 00386 show_editor(); 00387 // update display. 00388 on_command_run(); 00389 m_status_bar.pop(); 00390 } 00391 catch (std::ifstream::failure error) 00392 { 00393 Gtk::MessageDialog dialog(*this, 00394 "Error, cannot load file. " 00395 "It may be invalid or unreadable.", 00396 false, 00397 Gtk::MESSAGE_ERROR); 00398 dialog.run(); 00399 on_action_file_new(); 00400 m_status_bar.pop(); m_status_bar.pop(); // clear loading messages. 00401 } 00402 catch (ThemeChangedError) 00403 { 00404 Gtk::MessageDialog dialog(*this, 00405 "Error, cannot load file. " 00406 "The track's theme had data removed which the track depended on.", 00407 false, 00408 Gtk::MESSAGE_ERROR); 00409 dialog.run(); 00410 on_action_file_new(); 00411 m_status_bar.pop(); m_status_bar.pop(); // clear loading messages. 00412 } 00413 catch (CorruptedError) 00414 { 00415 Gtk::MessageDialog dialog(*this, 00416 "Error, cannot load file. " 00417 "The file has been corrupted.", 00418 false, 00419 Gtk::MESSAGE_ERROR); 00420 dialog.run(); 00421 m_status_bar.pop(); m_status_bar.pop(); // clear loading messages. 00422 } 00423 catch (NewVersionError) 00424 { 00425 Gtk::MessageDialog dialog(*this, 00426 "Error, cannot load file. " 00427 "The file has been written in a version newer than this editor understands. Please upgrade.", 00428 false, 00429 Gtk::MESSAGE_ERROR); 00430 dialog.run(); 00431 on_action_file_new(); 00432 m_status_bar.pop(); m_status_bar.pop(); // clear loading messages. 00433 } 00434 catch (DepreciatedVersionError) 00435 { 00436 Gtk::MessageDialog dialog(*this, 00437 "Error, cannot load file. " 00438 "The file uses data in a format no longer supported. You may be able to open the file using an older version of the editor.", 00439 false, 00440 Gtk::MESSAGE_ERROR); 00441 dialog.run(); 00442 on_action_file_new(); 00443 m_status_bar.pop(); m_status_bar.pop(); // clear loading messages. 00444 } 00445 } 00446 00447 void EditorWindow::on_action_file_save() 00448 { 00449 if (filename_set) 00450 { 00451 std::ofstream file; 00452 file.exceptions(std::ofstream::eofbit | 00453 std::ofstream::failbit | 00454 std::ofstream::badbit); 00455 try 00456 { 00457 m_status_bar.push("Saving track"); 00458 while (Gtk::Main::events_pending()) Gtk::Main::iteration(); 00459 file.open(filename.c_str()); 00460 file << theme_filename << "\n"; 00461 m_document->save(file); 00462 m_status_bar.pop(); 00463 } 00464 catch (std::ofstream::failure error) 00465 { 00466 Gtk::MessageDialog dialog(*this, "Error, cannot save file.", 00467 false, Gtk::MESSAGE_ERROR); 00468 dialog.run(); 00469 } 00470 } 00471 else 00472 { 00473 // Ask for a filename since it wasn't set. If the user doesn't press 00474 // cancel, this function will be called again but filename_set will be 00475 // true, so the file will be written. 00476 on_action_file_save_as(); 00477 } 00478 } 00479 00480 void EditorWindow::on_action_file_save_as() 00481 { 00482 Gtk::FileChooserDialog dialog (*this, "Choose where to save", 00483 Gtk::FILE_CHOOSER_ACTION_SAVE); 00484 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_REJECT); 00485 dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); 00486 dialog.add_shortcut_folder("data/tracks"); 00487 dialog.set_current_folder("data/tracks"); 00488 dialog.set_do_overwrite_confirmation(); 00489 if (dialog.run() == Gtk::RESPONSE_ACCEPT) 00490 { 00491 // user accepts, save 00492 filename_set = true; 00493 filename = dialog.get_filename(); 00494 on_action_file_save(); 00495 } 00496 } 00497 00498 void EditorWindow::on_action_file_test() 00499 { 00500 if (!m_document->is_saved()) 00501 { 00502 // not saved, ask for confirmation to save it first. 00503 Gtk::MessageDialog dialog(*this, 00504 Glib::ustring("Would you like to save?"), 00505 false, 00506 Gtk::MESSAGE_QUESTION, 00507 Gtk::BUTTONS_OK_CANCEL); 00508 dialog.set_secondary_text("You must save your track before you can play on it."); 00509 int result = dialog.run(); 00510 switch (result) 00511 { 00512 case Gtk::RESPONSE_OK: 00513 on_action_file_save(); 00514 break; 00515 default: 00516 return; 00517 break; 00518 } 00519 } 00520 std::string command("./racer " + filename); 00521 DEBUG_MESSAGE("Starting racer with command " << command); 00522 try 00523 { 00524 Glib::spawn_command_line_async(command); 00525 } 00526 catch (Glib::Error e) 00527 { 00531 Gtk::MessageDialog dialog(*this, 00532 "Cannot launch racer, please start it manually.", 00533 false, 00534 Gtk::MESSAGE_ERROR); 00535 dialog.set_secondary_text("The command " + command + " failed, with error " + e.what() + "."); 00536 } 00537 } 00538 00539 void EditorWindow::on_action_file_quit() 00540 { 00541 GdkEventAny event; 00542 if (!on_delete_event(&event)) hide(); 00543 } 00544 00545 void EditorWindow::on_action_edit_undo() 00546 { 00547 m_document->undo_command(); 00548 } 00549 00550 void EditorWindow::on_action_edit_redo() 00551 { 00552 m_document->redo_command(); 00553 } 00554 00555 void EditorWindow::on_action_edit_cut() 00556 { 00557 PRINT_STUB_MESSAGE; 00558 } 00559 00560 void EditorWindow::on_action_edit_copy() 00561 { 00562 PRINT_STUB_MESSAGE; 00563 } 00564 00565 void EditorWindow::on_action_edit_paste() 00566 { 00567 PRINT_STUB_MESSAGE; 00568 } 00569 00570 void EditorWindow::on_action_edit_select_all() 00571 { 00572 PRINT_STUB_MESSAGE; 00573 } 00574 00575 void EditorWindow::on_action_edit_delete() 00576 { 00577 PRINT_STUB_MESSAGE; 00578 } 00579 00580 void EditorWindow::on_action_edit_lighting() 00581 { 00583 m_lighting_window.present(); 00584 m_lighting_window.raise(); 00585 } 00586 00587 void EditorWindow::on_action_view_zoom_in() 00588 { 00589 m_viewport_top.zoom_in(); 00590 } 00591 00592 void EditorWindow::on_action_view_zoom_out() 00593 { 00594 m_viewport_top.zoom_out(); 00595 } 00596 00597 void EditorWindow::on_action_view_zoom_to_fit() 00598 { 00599 m_viewport_top.scale_to_fit(); 00600 } 00601 00602 void EditorWindow::on_action_view_top() 00603 { 00604 m_viewport_top.set_angle(View::VIEW_TOP); 00605 } 00606 00607 void EditorWindow::on_action_view_front() 00608 { 00609 m_viewport_top.set_angle(View::VIEW_FRONT); 00610 } 00611 00612 void EditorWindow::on_action_view_side() 00613 { 00614 m_viewport_top.set_angle(View::VIEW_SIDE); 00615 } 00616 00617 void EditorWindow::on_action_view_new_window() 00618 { 00619 EditorWindow new_window(*this); 00620 new_window.show(); 00621 } 00622 00623 void EditorWindow::on_action_help_guide() 00624 { 00625 m_guide.set_visible(m_ref_action_guide->get_active()); 00626 m_guide_preference = m_guide.is_visible(); 00627 } 00628 00629 void EditorWindow::on_action_help_wiki() 00630 { 00631 std::string url("http://sourceforge.net/apps/mediawiki/racer/index.php?title=Editor"); 00632 std::string command("xdg-open " + url); 00633 try 00634 { 00635 Glib::spawn_command_line_async(command); 00636 } 00637 catch (Glib::Error e) 00638 { 00642 Gtk::MessageDialog dialog(*this, 00643 "Cannot open web browser, please open " + url + " manually.", 00644 false, 00645 Gtk::MESSAGE_ERROR); 00646 dialog.set_secondary_text("The command " + command + " failed, with error " + e.what() + "."); 00647 } 00648 } 00649 00650 void EditorWindow::on_action_help_about() 00651 { 00652 Gtk::AboutDialog about; 00653 about.set_comments("A track editor for the game Racer.\n" 00654 "Built on " BUILD_TIME "."); 00655 about.set_copyright("Copyright © 2009 James Legg."); 00656 about.set_version(VERSION); 00657 about.set_license( 00658 "Racer Editor is free software: you can redistribute it and/or modify " 00659 "it under the terms of the GNU General Public License as published by " 00660 "the Free Software Foundation, either version 3 of the License, or " 00661 "(at your option) any later version.\n\n" 00662 00663 "Racer Editor is distributed in the hope that it will be useful, " 00664 "but without any warranty; without even the implied warranty of " 00665 "merchantability or fitness for a particular purpose. See the " 00666 "GNU General Public License for more details. \n\n" 00667 00668 "You should have received a copy of the GNU General Public License " 00669 "along with Racer Editor. If not, see <http://www.gnu.org/licenses/>." 00670 ); 00671 about.set_wrap_license(true); 00672 about.set_website("http://racer.sourceforge.net/"); 00673 about.run(); 00674 } 00675 00676 void EditorWindow::on_theme_picked(std::string filename) 00677 { 00678 m_status_bar.push("Loading theme."); 00679 while (Gtk::Main::events_pending()) Gtk::Main::iteration(); 00680 // load the selected theme 00681 Glib::ustring host_name; 00682 load_theme(filename.c_str()); 00683 00684 // create a new document 00685 m_document = boost::shared_ptr<Document::Document>(new Document::Document(*m_theme)); 00686 // monitor the document for changes. 00687 m_document->signal_command_run().connect( 00688 sigc::mem_fun(*this, &EditorWindow::on_command_run)); 00689 00690 // show tools that now make sense 00691 show_editor(); 00692 00693 // show the new document 00694 on_command_run(); 00695 m_status_bar.pop(); 00696 } 00697 00698 void EditorWindow::on_lighting_changed() 00699 { 00700 if (m_lighting_window.get_lighting() != m_document->get_track().get_lighting()) 00701 { 00702 boost::shared_ptr<Document::DocumentDelta> delta( 00703 new Document::ChangeLightingDelta( 00704 m_lighting_window.get_lighting())); 00705 m_document->do_command(delta); 00706 } 00707 } 00708 00709 void EditorWindow::on_command_run() 00710 { 00711 // check undo/redo state 00712 m_ref_action_group->get_action("Undo")->set_sensitive( 00713 m_document->get_undo_avaliable()); 00714 m_ref_action_group->get_action("Redo")->set_sensitive( 00715 m_document->get_redo_avaliable()); 00716 // update displayed data 00717 m_viewport_top.update(); 00718 m_lighting_window.set_lighting(m_document->get_track().get_lighting()); 00719 } 00720 00721 void EditorWindow::on_view_command(boost::shared_ptr<Document::DocumentDelta> delta) 00722 { 00723 m_document->do_command(delta); 00724 } 00725 00726 void EditorWindow::on_view_preview_command(boost::shared_ptr<Document::DocumentDelta> delta) 00727 { 00728 m_document->preview_command(delta); 00729 } 00730 00731 void EditorWindow::on_view_preview_cancel() 00732 { 00733 m_document->cancel_preview(); 00734 } 00735 00736 bool EditorWindow::on_delete_event(GdkEventAny* event) 00737 { 00738 // save keyboard shortcuts. 00739 DEBUG_MESSAGE("Saving accelerators to " << accel_filename); 00740 Gtk::AccelMap::save(accel_filename); 00741 if (check_clear_document()) 00742 { 00743 return Gtk::Window::on_delete_event(event); 00744 } 00745 return true; 00746 } 00747 00748 bool EditorWindow::check_clear_document() 00749 { 00750 // it doesn't matter if the document is already clear. 00751 if (!m_document) 00752 { 00753 return true; 00754 } 00755 // we already have a document open, is it good to replace it? 00756 if (m_document->is_saved()) 00757 { 00758 // saved, so allow it. 00759 return true; 00760 } 00761 // not saved, ask for confirmation 00762 std::stringstream ss; 00763 ss << m_document->get_changes_since_save(); 00764 Glib::ustring changes_since_last_save_string(ss.str()); 00765 Gtk::MessageDialog dialog(*this, 00766 filename_set ? Glib::ustring("Save the changes to track \"") + Glib::ustring(filename) + Glib::ustring("\" before closing?") :"Save changes to unsaved track before closing?", 00767 false, 00768 Gtk::MESSAGE_QUESTION, 00769 Gtk::BUTTONS_NONE, 00770 true); 00771 dialog.set_secondary_text 00772 ( 00773 Glib::ustring("If you don't save, your ") 00774 + changes_since_last_save_string 00775 + Glib::ustring(" changes since the last save will be permanetly lost.") 00776 ); 00777 Gtk::Button * close_button = dialog.add_button("Close _without Saving", Gtk::RESPONSE_NO); 00778 Gtk::Image close_icon(Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON); 00779 close_button->set_image(close_icon); 00780 close_icon.show(); 00781 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL)->get_image()->show(); 00782 dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT)->get_image()->show(); 00783 dialog.set_default_response(Gtk::RESPONSE_ACCEPT); 00784 switch (dialog.run()) 00785 { 00786 case Gtk::RESPONSE_CANCEL: 00787 case Gtk::RESPONSE_DELETE_EVENT: // I'll take that as a cancel. 00788 return false; 00789 break; 00790 case Gtk::RESPONSE_ACCEPT: 00791 // save before continuing 00792 on_action_file_save(); 00793 break; 00794 default: 00795 // don't save and lose the changes. 00796 break; 00797 } 00798 // We've got confirmation now. 00799 return true; 00800 } 00801 00802 void EditorWindow::load_theme(std::string filename) 00803 { 00804 DEBUG_MESSAGE("Loading theme " << filename); 00805 theme_filename = filename; 00806 std::ifstream theme_file(theme_filename.c_str()); 00807 m_theme = boost::shared_ptr<Track::Theme>(new Track::Theme(theme_file)); 00808 } 00809 00810 void EditorWindow::show_editor() 00811 { 00812 m_ref_action_group->get_action("New")->set_sensitive(true); 00813 m_ref_action_group->get_action("Save")->set_sensitive(true); 00814 m_ref_action_group->get_action("SaveAs")->set_sensitive(true); 00815 m_ref_action_group->get_action("Test")->set_sensitive(true); 00816 m_ref_action_group->get_action("SelectAll")->set_sensitive(true); 00817 m_ref_action_group->get_action("Lighting")->set_sensitive(true); 00818 m_ref_action_group->get_action("ZoomIn")->set_sensitive(true); 00819 m_ref_action_group->get_action("ZoomOut")->set_sensitive(true); 00820 m_ref_action_group->get_action("ZoomToFit")->set_sensitive(true); 00821 m_ref_action_group->get_action("ShowTop")->set_sensitive(true); 00822 m_ref_action_group->get_action("ShowFront")->set_sensitive(true); 00823 m_ref_action_group->get_action("ShowSide")->set_sensitive(true); 00824 m_ref_action_group->get_action("NewWindow")->set_sensitive(true); 00825 m_ref_action_group->get_action("HelpGuide")->set_sensitive(true); 00826 00827 m_new_form.hide(); 00828 m_viewport_top.set_document(*m_document); 00829 m_viewport_top.show(); 00830 m_guide.set_visible(m_guide_preference); 00831 00832 // Move keyboard focus to the view controls on the toolbar. 00833 m_ref_ui_manager->get_widget("/ToolBar/ShowTop")->grab_focus(); 00834 }
Generated at Mon Sep 6 00:41:13 2010 by Doxygen version 1.4.7 for Racer version svn335.