EditorWindow.cpp

Go to the documentation of this file.
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 }

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:13 2010 by Doxygen version 1.4.7 for Racer version svn335.