/*************************************************************************** * Copyright (C) 2003 by Rick L. Vinyard, Jr. * * rvinyard@cs.nmsu.edu * * * * This file is part of the osgGtk library. * * * * The osgGtk library is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * version 3 as published by the Free Software Foundation. * * * * The osgGtk library is distributed in the hope that it will be * * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this software. If not see <http://www.gnu.org/licenses/>. * ***************************************************************************/ // Of course, we need the gtkmm headers #include <gtkmm.h> // We'll need this to load the command line argument // as well as the handling the file load button #include <osgDB/ReadFile> #include <osgGA/TrackballManipulator> // And we'll need this for the Gtkmm OSG viewer #include <osgGtkmm/ViewerGtkmm.h> const char* HELP_TEXT = "<b><i>Right-click</i></b> while holding <b><i>CTRL</i></b> <u>or</u> <b><i>SHIFT</i></b> to pop up a fake Gtkmm menu.\n" "\n" "Use the standard OSG TrackballManipulator keys and mouse actions to\n" "rotate, zoom and move the loaded model.\n" "\n" "<i>Note:\n" "<small>\tIf the viewer is stopped, user actions won't be processed until the viewer is restarted.\n" "\tIn this example events are only processed when the viewer is running.</small></i>\n" ; // This is global so that the callbacks can see it as well // Alternatively, we could use sigc::bind, but making this // global simplifies the example. osgViewer::ViewerGtkmm* viewer = NULL; // This is also global so the callback function can check to see the // state of the toggle button Gtk::ToggleButton* fps_button; // Callback to handle mouse button events // This is what will look for right-clicks and pop up the menu bool on_button_event(GdkEventButton* event); // Callback that loads a selected scene as the root node of the scenegraph void on_open_file_clicked(); // Callback that runs the viewer a specific number of frames void on_run_frames_button_clicked(); // Callback that starts/stops the viewer void on_fps_toggled(); int main( int argc, char** argv ) { // We'll set a basic size of 800x600, but the user can resize larger if desired (but not smaller) int width=800, height=600; // This is some gtkmm housekeeping... we need the main loop and we also need to initialize the OpenGL system Gtk::Main gtkmm_main( argc, argv ); Gtk::GL::init( argc, argv ); // Now, let's create our viewer using ViewerGtkmm so we can use the gtkmm convenience method later // // We'll also set desired frame rate of 60 fps and load the first command line // argument as the scene viewer = new osgViewer::ViewerGtkmm(); viewer->setCameraManipulator(new osgGA::TrackballManipulator); viewer->set_fps(60); if ( argc >= 2 ) { osg::ref_ptr<osg::Node> model = osgDB::readNodeFile(argv[1]); if ( model.valid() ) viewer->setSceneData(model.get()); } // Now we'll setup the viewer in a gtkmm window using the setup_viewer_in_gtkmm_window() // convenience method // // The return type is a Gtkmm widget (subclassed from Gtk::DrawingArea) that we can // use anywhere we want osgViewer::GraphicsWindowGtkmm* gw = viewer->setup_viewer_in_gtkmm_window(width, height); // All Gtk apps need a toplevel window // // We'll also set the title, add our widget created by the viewer and make sure it's visible Gtk::Window window(Gtk::WINDOW_TOPLEVEL); window.set_title("osgviewerGtkmm"); // Almost all of this is just gtkmm code... creating the various // layouts and other UI widgets Gtk::VBox main_vbox; Gtk::HBox bottom_hbox; Gtk::VBox button_vbox; Gtk::Label help_text(HELP_TEXT); Gtk::Button file_button("Open File"); Gtk::Button run_frames_button("Run 120 frames"); Gtk::Button close_button("Close"); fps_button = Gtk::manage(new Gtk::ToggleButton()); window.add(main_vbox); main_vbox.pack_start(*gw); main_vbox.pack_start(bottom_hbox); bottom_hbox.pack_start(button_vbox); help_text.set_use_markup(); bottom_hbox.pack_start(help_text); button_vbox.pack_start(file_button); button_vbox.pack_start(run_frames_button); button_vbox.pack_start(*fps_button); button_vbox.pack_start(close_button); window.show_all(); // Even though we won't do anything except popup the menu on button release // we still need to handle the case where the button is pressed with the // shift or control key. Otherwise the button press will be passed on to // the gtk handler. gw->signal_button_press_event().connect(&on_button_event); gw->signal_button_release_event().connect(&on_button_event); // Let's connect the other Gtk buttons to their callbacks file_button.signal_clicked().connect(&on_open_file_clicked); run_frames_button.signal_clicked().connect(&on_run_frames_button_clicked); fps_button->signal_toggled().connect(&on_fps_toggled); close_button.signal_clicked().connect(&Gtk::Main::quit); // And, just before we kick things off, let's start the viewer running // by setting the fps button to active, allowing the connected callback // to start things off. fps_button->set_active(true); // This is boilerplate for all gtkmm apps... start the gtkmm main loop Gtk::Main::run(window); // If we got here, gtkmm exited it's main loop, so we'll exit cleanly return 0; } // This function is called every time there is a button press or button // release event that occurs in the OSG graphics window // // Even though it is only the button release that we're concerned with, // we still handle the button press event since anything that we don't // handle is passed along to OSG. We don't want button press events // to be passed into OSG when the user is holding down the shift // key or the control key, so we stop that by returning true // when either key is held down and the right mouse button is clicked. bool on_button_event(GdkEventButton* event) { if ( event->button == 3 and ( event->state & GDK_SHIFT_MASK or event->state & GDK_CONTROL_MASK ) ) { // We get here if it's either a button press or release, but we'll // only pop up the menu on a release if ( event->type == GDK_BUTTON_RELEASE ) { Gtk::Menu* menu = Gtk::manage(new Gtk::Menu()); menu->append(*Gtk::manage(new Gtk::MenuItem("Option"))); menu->append(*Gtk::manage(new Gtk::MenuItem("Another Option"))); menu->append(*Gtk::manage(new Gtk::MenuItem("Still More Options"))); menu->show_all(); menu->popup(event->button, event->time); } // Whether it's press or release, if it's a right mouse event with // control or shift held down we return true, stopping it from // going on to OSG // // If we wanted to handled it _and_ let it pass into OSG we // could simply return false here. return true; } // But, since this was something other than a right mouse event with // a ctrl/shift key pressed, we'll just let it pass into OSG return false; } void on_open_file_clicked() { Gtk::FileChooserDialog fc("Please select an OSG file to load...", Gtk::FILE_CHOOSER_ACTION_OPEN); fc.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); fc.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); if ( fc.run() == Gtk::RESPONSE_ACCEPT ) { Glib::ustring file = fc.get_filename(); osg::ref_ptr<osg::Node> model = osgDB::readNodeFile(file); if ( model.valid() ) viewer->setSceneData(model.get()); } } void on_run_frames_button_clicked() { fps_button->set_active(false); if ( viewer ) viewer->run_frames(120); } void on_fps_toggled() { if ( fps_button->get_active() ) { if ( viewer ) viewer->run(); fps_button->set_label("Click to stop"); } else { if ( viewer ) viewer->stop(); fps_button->set_label("Click to run at 60fps"); } }