Logo Search packages:      
Sourcecode: k3d version File versions

auto_dialog.cpp

Go to the documentation of this file.
// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program 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 program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
            \author Tim Shead (tshead@k-3d.com)
*/

#include "auto_dialog.h"
#include "bitmap_preview.h"
#include "button.h"
#include "check_button.h"
#include "chooser.h"
#include "color_chooser.h"
#include "combo_box.h"
#include "edit_control.h"
#include "object_chooser.h"
#include "orientation.h"
#include "path_chooser.h"
#include "scale.h"
#include "keyboard.h"
#include "position.h"
#include "property_button.h"
#include "script_editor.h"
#include "spin_button.h"
#include "viewport_window.h"

#include <k3dsdk/application.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/frames.h>
#include <k3dsdk/ianimation_render_engine.h>
#include <k3dsdk/ienumeration_property.h>
#include <k3dsdk/ilist_property.h>
#include <k3dsdk/imeasurement_property.h>
#include <k3dsdk/imesh_sink.h>
#include <k3dsdk/imesh_source.h>
#include <k3dsdk/iobject_collection.h>
#include <k3dsdk/iproperty.h>
#include <k3dsdk/iproperty_collection.h>
#include <k3dsdk/iproperty_group_collection.h>
#include <k3dsdk/iscript_property.h>
#include <k3dsdk/istill_render_engine.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iviewport_host.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/property.h>
#include <k3dsdk/selection.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/string_cast.h>
#include <k3dsdk/time_source.h>
#include <k3dsdk/user_interface.h>
#include <k3dsdk/utility.h>
#include <k3dsdk/viewport.h>

#include <sdpgtk/sdpgtkevents.h>

#include <boost/scoped_ptr.hpp>

#include <set>

// We have an unfortunate clash with X
#ifdef RootWindow
#undef RootWindow
#endif

namespace
{

const std::string event_configure("event_configure");
const std::string event_delete("event_delete");
const std::string event_key_press("event_key_press");

std::set<std::string> skip_properties()
{
      static std::set<std::string> results;
      if(results.empty())
            {
                  results.insert("position");
                  results.insert("orientation");
                  results.insert("scale");
                  results.insert("matrix");
            }
      
      return results;
}

/////////////////////////////////////////////////////////////////////////////
// auto_controls

/// Performs the heavy-lifting of automatically generating a user-interface for a generic object by examining its interfaces & properties
class auto_controls :
      public k3dUserInterfaceElement
{
      typedef k3dUserInterfaceElement base;

public:
      typedef enum
      {
            WITH_UNDO,
            NO_UNDO
      } undo_t;

      auto_controls(k3d::icommand_node* Parent, const std::string& Name, k3d::iobject& Object, const undo_t Undo, sdpGtkContainer& Container) :
            base(Parent, Name),
            m_object(Object)
      {
            // Cache the state recorder to pass to controls, based on whether undos are enabled or not ...
            k3d::istate_recorder* const state_recorder = (WITH_UNDO == Undo) ? &m_object.document().state_recorder() : 0;

            sdpGtkVBox container;
            container.Create(false, 0);
            container.Show();
            Container.Attach(container);
            
            sdpGtkToolbar toolbar;
            toolbar.Create(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_ICONS);
            toolbar.Show();
            container.PackStart(toolbar, false, false);

            // Add buttons for viewports ...
            k3d::iviewport* const viewport = dynamic_cast<k3d::iviewport*>(&m_object);
            if(viewport)
                  {
                        k3d::button::control* const control = new k3d::button::control(this, "attach_viewport_window", "New Viewport Window");
                        control->signal_activate().connect(SigC::slot(*this, &auto_controls::on_attach_viewport_window));
                        MapCustomObject("attach_viewport_window", control);
                        toolbar.Append(control->RootWidget(), "Create a new viewport window", "");
                  }

            // Add buttons for viewport hosts ...
            k3d::iviewport_host* const viewport_host = dynamic_cast<k3d::iviewport_host*>(&m_object);
            if(viewport_host && !viewport)
                  {
                        k3d::button::control* const control = new k3d::button::control(this, "attach_viewport", "New Viewport");
                        control->signal_activate().connect(SigC::slot(*this, &auto_controls::on_attach_viewport));
                        MapCustomObject("attach_viewport", control);
                        toolbar.Append(control->RootWidget(), "Create a new viewport", "");
                  }

            // Add buttons for render engines ...
            k3d::istill_render_engine* const still_render_engine = dynamic_cast<k3d::istill_render_engine*>(&m_object);
            if(still_render_engine)
                  {
                        k3d::button::control* const preview_control = new k3d::button::control(this, "render_preview", "Render Preview", "renderpreview.xpm");
                        preview_control->signal_activate().connect(SigC::slot(*this, &auto_controls::on_render_preview));
                        MapCustomObject("render_preview", preview_control);
                        toolbar.Append(preview_control->RootWidget(), "Render Preview", "");

                        k3d::button::control* const frame_control = new k3d::button::control(this, "render_frame", "Render Frame", "renderframe.xpm");
                        frame_control->signal_activate().connect(SigC::slot(*this, &auto_controls::on_render_frame));
                        MapCustomObject("render_frame", frame_control);
                        toolbar.Append(frame_control->RootWidget(), "Render Frame", "");
                  }
            
            k3d::ianimation_render_engine* const animation_render_engine = dynamic_cast<k3d::ianimation_render_engine*>(&m_object);
            if(animation_render_engine)
                  {
                        k3d::button::control* const animation_control = new k3d::button::control(this, "render_animation", "Render Animation", "renderanimation.xpm");
                        animation_control->signal_activate().connect(SigC::slot(*this, &auto_controls::on_render_animation));
                        MapCustomObject("render_animation", animation_control);
                        toolbar.Append(animation_control->RootWidget(), "Render Animation", "");
                  }
            
            // Get the object properties, and group them so we can create a tabbed interface ...
            k3d::iproperty_group_collection::groups_t property_groups;

            k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(&m_object);
            if(property_collection)
                  {
                        k3d::iproperty_collection::properties_t all_properties = property_collection->properties();
                        k3d::iproperty_group_collection::groups_t groups;
                  
                        k3d::iproperty_group_collection* const property_group_collection = dynamic_cast<k3d::iproperty_group_collection*>(&m_object);
                        if(property_group_collection)
                              {
                                    groups = property_group_collection->property_groups();
                                    for(k3d::iproperty_group_collection::groups_t::const_iterator group = groups.begin(); group != groups.end(); ++group)
                                          {
                                                for(k3d::iproperty_collection::properties_t::const_iterator property = group->properties.begin(); property != group->properties.end(); ++property)
                                                      all_properties.erase(std::remove(all_properties.begin(), all_properties.end(), *property), all_properties.end());
                                          }
                              }
                  
                        property_groups.insert(property_groups.end(), k3d::iproperty_group_collection::group(m_object.factory().name(), all_properties));
                        property_groups.insert(property_groups.end(), groups.begin(), groups.end());
                  }
                  
            sdpGtkNotebook notebook;
            notebook.Create();
            notebook.Show();
            container.PackStart(notebook, true, true);
            
            // Create a tab for each group ...
            for(k3d::iproperty_group_collection::groups_t::const_iterator property_group = property_groups.begin(); property_group != property_groups.end(); ++property_group)
                  {
                        if(property_group->properties.empty())
                              continue;
                        
                        sdpGtkLabel tab_label;
                        tab_label.Create(property_group->name);
                        tab_label.Show();
                              
                        sdpGtkVBox tab_container;
                        tab_container.Create(false, 0);
                        tab_container.Show();
                        
                        notebook.AppendPage(tab_container, tab_label);
                        
                        sdpGtkTable table;
                        table.Create(property_group->properties.size(), 3, false);
                        table.Show();
                        tab_container.PackStart(table, false, false, 0);
                  
                        // For each property within the group ...
                        unsigned int row = 0;
                        for(unsigned int i = 0; i != property_group->properties.size(); ++i)
                              {
                                    k3d::iproperty& property = *property_group->properties[i];

                                    // Skip certain well-known properties ...
                                    if(skip_properties().count(property.name()))
                                          continue;   

                                    const std::type_info& property_type = property.type();
                                    const std::string tooltip(property.description().empty() ? property.name() : property.description());
                              
                                    // Label the property ...
                                    sdpGtkLabel label;
                                    label.Create(property.name());
                                    label.SetPadding(4, 4);
                                    label.Show();
                                    table.Attach(label, 2, 3, row, row+1);
                                    
                                    // Provide a property button for the property ...
                                    const std::string property_control_name = property.name() + "_property";
                                    k3d::property_button::control* const property_control = new k3d::property_button::control(this, property_control_name);
                                    MapCustomObject(property_control_name.c_str(), property_control);
                                    table.Attach(property_control->RootWidget(), 1, 2, row, row+1, static_cast<GtkAttachOptions>(0), static_cast<GtkAttachOptions>(0), 0, 0);
                                    property_control->attach(k3d::property_button::proxy(m_object.document(), property), state_recorder, property.name());

                                    if(property_type == typeid(bool))
                                          {
                                                k3d::check_button::control* const control = new k3d::check_button::control(this, property.name(), property.name());
                                                MapCustomObject(property.name().c_str(), control);
                                                table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                control->attach(k3d::check_button::proxy(property), state_recorder, property.name());
                                          }
                                    else if(property_type == typeid(double) || property_type == typeid(float) || property_type == typeid(long) || property_type == typeid(unsigned long) || property_type == typeid(int) || property_type == typeid(unsigned int))
                                          {
                                                k3d::spin_button::control* const control = new k3d::spin_button::control(this, property.name());
                                                MapCustomObject(property.name().c_str(), control);
                                                table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                k3d::imeasurement_property* const measurement_property = dynamic_cast<k3d::imeasurement_property*>(&property);
                                                if(measurement_property)
                                                      {
                                                            control->set_precision(measurement_property->precision());
                                                            control->set_step_increment(measurement_property->step_increment());
                                                            control->set_units(measurement_property->units());
                                                      }
                                                control->attach(k3d::spin_button::proxy(property), state_recorder, property.name());
                                          }
                                    else if(property_type == typeid(k3d::color))
                                          {
                                                k3d::color_chooser::control* const control = new k3d::color_chooser::control(this, property.name());
                                                MapCustomObject(property.name().c_str(), control);
                                                table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                control->attach(k3d::color_chooser::proxy(property), state_recorder, property.name());
                                          }
                                    else if(property_type == typeid(std::string))
                                          {
                                                k3d::ienumeration_property* const enumeration_property = dynamic_cast<k3d::ienumeration_property*>(&property);
                                                k3d::iscript_property* const script_property = dynamic_cast<k3d::iscript_property*>(&property);
                                                k3d::ilist_property<std::string>* const list_property = dynamic_cast<k3d::ilist_property<std::string>*>(&property);

                                                if(enumeration_property)
                                                      {
                                                            const k3d::ienumeration_property::values_t values(enumeration_property->values());
                                                            if(values.empty())
                                                                  std::cerr << warning << "Enumeration property without any values [" << property.name() << "]" << std::endl;

                                                            k3d::chooser::control* const control = new k3d::chooser::control(this, property.name(), values);
                                                            MapCustomObject(property.name().c_str(), control);
                                                            table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                            control->attach(k3d::chooser::proxy(property), state_recorder, property.name());
                                                      }
                                                else if(script_property)
                                                      {
                                                            k3d::button::control* const control = new k3d::button::control(this, property.name(), "Edit Script");
                                                            MapCustomObject(property.name().c_str(), control);
                                                            table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                            control->signal_activate().connect(SigC::bind(SigC::slot(*this, &auto_controls::new_inline_script_editor), &property));
                                                      }
                                                else if(list_property)
                                                      {
                                                            k3d::combo_box::control* const control = new k3d::combo_box::control(this, property.name());
                                                            MapCustomObject(property.name().c_str(), control);
                                                            table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                            control->attach(k3d::combo_box::proxy(property), state_recorder, property.name());
                                                            control->set_values(list_property->values());
                                                      }
                                                else
                                                      {
                                                            k3d::edit_control::control* const control = new k3d::edit_control::control(this, property.name());
                                                            MapCustomObject(property.name().c_str(), control);
                                                            table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                            control->attach(k3d::edit_control::proxy(property), state_recorder, property.name());
                                                      }
                                          }
                                    else if(property_type == typeid(k3d::iobject*))
                                          {
                                                k3d::object_chooser::control* const control = new k3d::object_chooser::control(this, property.name());
                                                MapCustomObject(property.name().c_str(), control);
                                                table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                control->attach(k3d::object_chooser::filter(property), k3d::object_chooser::proxy(property, m_object.document()), state_recorder, property.name());
                                          }
                                    else if(property_type == typeid(k3d::bitmap*))
                                          {
                                                k3d::bitmap_preview::control* const control = new k3d::bitmap_preview::control(this, property.name());
                                                MapCustomObject(property.name().c_str(), control);
                                                table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                control->attach(k3d::bitmap_preview::proxy(property), state_recorder, property.name());
                                          }
                                    else if(property_type == typeid(boost::filesystem::path))
                                          {
                                                k3d::path_chooser::control* const control = new k3d::path_chooser::control(this, property.name());
                                                MapCustomObject(property.name().c_str(), control);
                                                table.Attach(control->RootWidget(), 0, 1, row, row+1);
                                                control->attach(k3d::path_chooser::proxy(property), state_recorder, property.name());
                                          }
                                    else
                                          {
                                                //std::cerr << __PRETTY_FUNCTION__ << "Unknown property type: " << property.name() << std::endl;
                                          }
                                    
                                    ++row;
                              }
                  }

            // If the object is transformable, add appropriate tabs ...
            k3d::iproperty* const position = k3d::get_property(m_object, "position");
            if(position && (position->type() == typeid(k3d::vector3)))
                  {
                        sdpGtkLabel label;
                        label.Create("Position");
                        label.Show();
                        
                        k3d::position::control* const control = new k3d::position::control(this, "position_control");
                        MapCustomObject("position_control", control);
                        
                        notebook.AppendPage(control->RootWidget(), label);

                        control->attach(k3d::position::proxy(*position), m_object.document(), state_recorder, "Position");
                  }

            k3d::iproperty* const orientation = k3d::get_property(m_object, "orientation");
            if(orientation && (orientation->type() == typeid(k3d::angle_axis)))                                   
                  {
                        sdpGtkLabel label;
                        label.Create("Orientation");
                        label.Show();
                        
                        k3d::orientation::control* const control = new k3d::orientation::control(this, "orientation_control");
                        MapCustomObject("orientation_control", control);
                        
                        notebook.AppendPage(control->RootWidget(), label);
                        
                        control->attach(k3d::orientation::proxy(*orientation), m_object.document(), state_recorder, "Orientation");
                  }

            k3d::iproperty* const scale = k3d::get_property(m_object, "scale");
            if(scale && (scale->type() == typeid(k3d::vector3)))
                  {
                        sdpGtkLabel label;
                        label.Create("Scale");
                        label.Show();
                        
                        k3d::scale::control* const control = new k3d::scale::control(this, "scale_control");
                        MapCustomObject("scale_control", control);

                        notebook.AppendPage(control->RootWidget(), label);
                                                
                        control->attach(k3d::scale::proxy(*scale), m_object.document(), state_recorder, "Scale");
                  }
                  
            notebook.SetPage(0);
      }

      ~auto_controls()
      {
            // No more events from this point forward ...
            DisconnectAllEvents();

            // Clean-up the GTK+ tree ...
            if(Root())
                  RootWidget().Destroy();

            // Get rid of our widget pointers ...
            Clear();
      }

private:
      void on_attach_viewport_window()
      {
            k3d::iviewport* const viewport = dynamic_cast<k3d::iviewport*>(&m_object);
            return_if_fail(viewport);

            k3d::viewport::window* const viewport_window = new k3d::viewport::window(m_object.document());
            viewport_window->attach(*viewport);
      }

      void on_attach_viewport()
      {
            k3d::iviewport_host* const viewport_host = dynamic_cast<k3d::iviewport_host*>(&m_object);
            return_if_fail(viewport_host);


            k3d::iobject* const viewport_object = k3d::create_document_plugin("Viewport", m_object.document(), k3d::unique_name(m_object.document().objects(), m_object.name() + " Viewport"));
            if(viewport_object)
                  {
                        k3d::iviewport* const viewport = dynamic_cast<k3d::iviewport*>(viewport_object);
                        if(viewport)
                              {
                                    viewport->set_host(viewport_host);
                                    
                                    k3d::iproperty* const output_mesh = k3d::get_typed_property<k3d::matrix4>(*viewport_host, "output_matrix");
                                    k3d::iproperty* const input_mesh = k3d::get_typed_property<k3d::matrix4>(*viewport, "input_matrix");
                                    if(input_mesh && output_mesh)
                                          {
                                                k3d::idag::dependencies_t dependencies;
                                                dependencies.insert(std::make_pair(input_mesh, output_mesh));
                                                m_object.document().dag().set_dependencies(dependencies);
                                          }
                                    
                                    k3d::viewport::window* const window = new k3d::viewport::window(m_object.document());
                                    window->attach(*viewport);
                              }
                  }
      }
      
      void on_render_preview()
      {
            k3d::istill_render_engine* const render_engine = dynamic_cast<k3d::istill_render_engine*>(&m_object);
            return_if_fail(render_engine);

            assert_warning(render_engine->render_preview());
      }
      
      void on_render_frame()
      {
            k3d::istill_render_engine* const render_engine = dynamic_cast<k3d::istill_render_engine*>(&m_object);
            return_if_fail(render_engine);

            boost::filesystem::path file;
            if(!k3d::get_file_path("render_frame", "Render Frame:", true, boost::filesystem::path(), file))
                  return;

            assert_warning(render_engine->render_frame(file, true));
      }
      
      void on_render_animation()
      {
            k3d::ianimation_render_engine* const render_engine = dynamic_cast<k3d::ianimation_render_engine*>(&m_object);
            return_if_fail(render_engine);

            // Ensure that the document has animation capabilities, first ...
            k3d::iproperty* const start_time_property = k3d::get_start_time(m_object.document());
            k3d::iproperty* const end_time_property = k3d::get_end_time(m_object.document());
            k3d::iproperty* const frame_rate_property = k3d::get_frame_rate(m_object.document());
            return_if_fail(start_time_property && end_time_property && frame_rate_property);

            // Prompt the user for a base filename ...
            boost::filesystem::path file;
            if(!k3d::get_file_path("renderanimation", "Choose Animation Base Filepath:", false, boost::filesystem::path(), file))
                  return;

            // Make sure the supplied filepath has enough digits to render the entire animation ...
            const double start_time = boost::any_cast<double>(k3d::get_property_value(m_object.document().dag(), *start_time_property));
            const double end_time = boost::any_cast<double>(k3d::get_property_value(m_object.document().dag(), *end_time_property));
            const double frame_rate = boost::any_cast<double>(k3d::get_property_value(m_object.document().dag(), *frame_rate_property));
            
            const long start_frame = static_cast<long>(k3d::round(frame_rate * start_time));
            const long end_frame = static_cast<long>(k3d::round(frame_rate * end_time));

            k3d::frames frames(file, start_frame, end_frame);
            if(frames.max_frame() < end_frame)
                  {
                        std::string message = "The Base Filepath doesn't contain enough digits to render the entire animation.\n"
                              "Try a filepath of the form [ myanim0000.tif ] ... the placement of digits is flexible,\n"
                              "and any prefix / postfix / file extension is optional, but the path must contain\n"
                              "enough consecutive digits to enumerate all of the frames in the animation.";

                        k3d::error_message(message, "Render Animation:");
                        return;
                  }

            // See if the user wants to view frames as they're completed ...
            std::vector<std::string> buttons;
            buttons.push_back("Yes");
            buttons.push_back("No");
            buttons.push_back("Cancel");

            const unsigned long result = k3d::query_message("Do you want to see rendered frames as they're completed?", "Render Animation:", 1, buttons);
            if(0 == result || 3 == result)
                  return;

            const bool viewcompleted = (1 == result);

            assert_warning(render_engine->render_animation(file, viewcompleted));
      }

      void new_inline_script_editor(k3d::iproperty* Property)
      {
            k3d::icommand_node* const command_node = dynamic_cast<k3d::icommand_node*>(&m_object);
            return_if_fail(command_node);
            
            display_inline_script_editor(*Property, *command_node, m_object.name() + "." + Property->name());
      }

00536       k3d::iobject& m_object;
};

/////////////////////////////////////////////////////////////////////////////
// auto_dialog

/// Handes common functionality for Automatic Dialog Generation (ADG) dialogs
class auto_dialog :
      public k3dUserInterfaceElement
{
      typedef k3dUserInterfaceElement base;
      
public:
      auto_dialog(k3d::iobject& Object) :
            base(&Object, "properties"),
            m_object(Object),
            m_geometry_store(new k3d::options_window_geometry_store()),
            m_idle_handler(0)
      {
            // We want to be notified if the owning object's properties are changed
            k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(&m_object);
            if(property_collection)
                  property_collection->properties_changed_signal().connect(SigC::slot(*this, &auto_dialog::on_properties_changed));
            
            // We want to be notified if the owning object is deleted
            m_object.deleted_signal().connect(SigC::slot(*this, &auto_dialog::on_object_deleted));
      }
      
      virtual ~auto_dialog()
      {
            if(m_idle_handler)
                  gtk_idle_remove(m_idle_handler);
                  
            // If our object has the mouse focus, release it ...
            k3d::release_mouse_focus(m_object.document(), m_object);

            // Make sure we're not modal ...
            if(IsModal())
                  CancelModal();

            // No more events from this point forward ...
            DisconnectAllEvents();

            // Clean-up the GTK+ tree ...
            if(Root())
                  RootWidget().Destroy();

            // Get rid of our widget pointers ...
            Clear();
      }

      k3d::iobject& object()
      {
            return m_object;
      }

protected:
      /// Loads a GTKML user interface, and handles some common functionality
      void load_user_interface(sdpxml::Document& GTKML)
      {
            // Load GTKML ...
            assert_warning(Load(GTKML));

            gdk_window_set_role(static_cast<GtkWidget*>(RootWidget())->window, k3d::command_node_path(*this).c_str());
            
            // Set the window title ...
            on_object_renamed();
                                    
            // Set our screen position and dimensions ...
            restore_geometry();

            // We want to be notified if the owning object is renamed
            m_object.name_changed_signal().connect(SigC::slot(*this, &auto_dialog::on_object_renamed));
            
            // If our object is a mouse event observer, grab the focus ...
            k3d::set_mouse_focus(m_object.document(), m_object);
      }
      
private:
      void restore_geometry()
      {
            int left = 0;
            int top = 0;
            unsigned int width = 0;
            unsigned int height = 0;

            if(m_geometry_store.get() && m_geometry_store->get_window_geometry(k3d::command_node_path(*this), left, top, width, height))
                  {
                        RootWindow().SetDefaultSize(width, height);
                        RootWidget().Show();
#ifdef SDPWIN32
                        // Prevent minimized windows not to be restored
                        // on Win95 and gtk/gdk ver 2002.03.10 minimized windows
                        // top and left are 3000
                        //
                        int orgleft = 0;
                        int orgtop = 0;
                        gdk_window_get_root_origin(static_cast<GtkWidget*>(RootWidget())->window, &orgleft, &orgtop);

                        int orgwidth = 0;
                        int orgheight = 0;
                        gdk_window_get_size(static_cast<GtkWidget*>(RootWidget())->window, &orgwidth, &orgheight);

                        // Check/correct position/size against visible values
                        if(left   > GetSystemMetrics(SM_CXSCREEN) - 20)
                              left   = orgleft;
                        if(top    > GetSystemMetrics(SM_CYSCREEN) - 20)
                              top    = orgtop;
                        if(width  > (unsigned int) GetSystemMetrics(SM_CXSCREEN))
                              width  = orgwidth;
                        if(height > (unsigned int) GetSystemMetrics(SM_CYSCREEN))
                              height = orgheight;

                        if(left < 20 - (int) width)
                              left = 20 - (int) width;
                        if(top < 20 - (int) height)
                              top = 20 - (int) height;
#endif // SDPWIN32
                        gdk_window_move_resize(static_cast<GtkWidget*>(RootWidget())->window, left, top, width, height);
                  }
            else
                  {
                        RootWidget().Show();
                  }
      }

      // Event-handling ...
      void OnEvent(sdpGtkEvent* Event)
      {
            // Sanity checks ...
            assert_warning(Event);

            if(Event->Name() == event_configure)
                  on_configure();
            else if(Event->Name() == event_key_press)
                  on_key_press_event(Event);
            else if(Event->Name() == event_delete)
                  on_delete(Event);
            else
                  base::OnEvent(Event);
      }

      /// Called by the event system when the user hits the WM close button (template design pattern)
      virtual void on_close() = 0;

      /// Called by the event system when the user hits the WM close button
      void on_delete(sdpGtkEvent* Event)
      {
            ((sdpGtkEventWidgetDeleteEvent*)Event)->SetResult(true);
            on_close();
      }
      
      /// Called by the event system when the top-level window is resized
      void on_configure()
      {
            // Serialize our screen geometry (but not in batch mode, since it has a bad habit of trashing the user's stored settings) ...
            if(m_geometry_store.get() && k3d::application().user_interface() && (!k3d::application().user_interface()->batch_mode()))
                  {
                        int left = 0;
                        int top = 0;
                        gdk_window_get_root_origin(static_cast<GtkWidget*>(RootWidget())->window, &left, &top);

                        int width = 0;
                        int height = 0;
                        gdk_window_get_size(static_cast<GtkWidget*>(RootWidget())->window, &width, &height);

                        m_geometry_store->set_window_geometry(k3d::command_node_path(*this), left, top, width, height);
                  }
      }

      /// Called by the event system when the top-level window gets keyboard input ...
      void on_key_press_event(sdpGtkEvent* Event)
      {
            sdpGtkEventWidgetKeyPressEvent& event = static_cast<sdpGtkEventWidgetKeyPressEvent&>(*Event);
            event.SetResult(k3d::keyboard().event_signal().emit(*this, k3d::convert(static_cast<GdkModifierType>(event.Event()->state)), event.Event()->keyval));
      }

      /// Called by the signal system if the owning object gains-or-loses any properties
      void on_properties_changed()
      {
            if(!m_idle_handler)
                  m_idle_handler = gtk_idle_add(raw_update_properties, this);
      }

      static gint raw_update_properties(gpointer Data)
      {
            return reinterpret_cast<auto_dialog*>(Data)->update_properties();
      }

      /// Called once by the event system if the owning object has gained-or-lost any properties since the GUI was last idle
      gint update_properties()
      {
            on_update_properties();
            
            m_idle_handler = 0;
            return false;
      }

      /// Override this in derived classes to respond when the owning object has gained-or-lost any properties since the GUI was last idle
      virtual void on_update_properties()
      {
      }

      /// Called by the signal system if the owning object is deleted
      void on_object_deleted()
      {
            delete this;
      }
      
      /// Called by the signal system if the owning object is renamed
      void on_object_renamed()
      {
            return_if_fail(Root());
            RootWindow().SetTitle(m_object.name().c_str());
      }
      
      /// Stores a reference to the owning object
      k3d::iobject& m_object;
      /// Stores an object that can serialize our window configuration
00755       boost::scoped_ptr<k3d::window_geometry_store> m_geometry_store;
      /// Keeps track of whether we have an idle handler in-effect or not
00757       guint m_idle_handler;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// auto_object_dialog

class auto_object_dialog :
      public auto_dialog
{
      typedef auto_dialog base;
      
public:
      auto_object_dialog(k3d::iobject& Object) :
            base(Object)
      {
            // Create the user interface GTKML ...
            sdpxml::Document gtkml("gtkml");
            sdpxml::Element& window = gtkml.Append(sdpxml::Element("window", "", sdpxml::Attribute("type", "toplevel"), sdpxml::Attribute("show", "true")));
            window.Append(sdpxml::Element("event", "", sdpxml::Attribute("signal", "delete-event"), sdpxml::Attribute("name", event_delete)));
            window.Append(sdpxml::Element("event", "", sdpxml::Attribute("signal", "configure-event"), sdpxml::Attribute("name", event_configure)));
            window.Append(sdpxml::Element("event", "", sdpxml::Attribute("signal", "key-press-event"), sdpxml::Attribute("name", event_key_press)));
            sdpxml::Element& vbox = window.Append(sdpxml::Element("vbox", "", sdpxml::Attribute("homogeneous", "false")));
            vbox.Append(sdpxml::Element("eventbox", "", sdpxml::Attribute("name", "auto_controls"), sdpxml::Attribute("expand", "true"), sdpxml::Attribute("fill", "true")));
            sdpxml::Element& hbuttonbox = vbox.Append(sdpxml::Element("hbuttonbox", "", sdpxml::Attribute("layout", "end"), sdpxml::Attribute("expand", "false")));
            hbuttonbox.Append(sdpxml::Element("k3dbutton", "Close", sdpxml::Attribute("name", "close")));

            // Load the user interface ...
            load_user_interface(gtkml);

            // Bind the close button ...
            k3d::button::control* const close_button = get_button("close");
            return_if_fail(close_button);
            close_button->signal_activate().connect(SigC::slot(*this, &auto_object_dialog::on_close));

            on_update_properties();
      }

private:
      void on_update_properties()
      {
            if(m_frame.Attached())
                  m_frame.Destroy();

            m_auto_controls.reset();

            m_frame.Create();
            m_frame.Show();
            Container("auto_controls").Attach(m_frame);
            m_auto_controls.reset(new auto_controls(this, "auto_controls", object(), auto_controls::WITH_UNDO, m_frame));
      }

      void on_close()
      {
            // Delete the UI, leaving the object behind
            delete this;
      }

      sdpGtkFrame m_frame;
      std::auto_ptr<auto_controls> m_auto_controls;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// auto_tool_dialog

class auto_tool_dialog :
      public auto_dialog
{
      typedef auto_dialog base;
      
public:
      auto_tool_dialog(k3d::iobject& Object, k3d::iproperty& Output, k3d::iproperty& ToolInput, k3d::iproperty& ToolOutput, k3d::iproperty& Input) :
            base(Object),
            m_output(Output),
            m_tool_input(ToolInput),
            m_tool_output(ToolOutput),
            m_input(Input),
            m_change_set(k3d::create_state_change_set())
      {
            // Create the user interface GTKML ...
            sdpxml::Document gtkml("gtkml");
            sdpxml::Element& window = gtkml.Append(sdpxml::Element("window", "", sdpxml::Attribute("type", "toplevel"), sdpxml::Attribute("show", "true")));
            window.Append(sdpxml::Element("event", "", sdpxml::Attribute("signal", "delete-event"), sdpxml::Attribute("name", event_delete)));
            window.Append(sdpxml::Element("event", "", sdpxml::Attribute("signal", "configure-event"), sdpxml::Attribute("name", event_configure)));
            window.Append(sdpxml::Element("event", "", sdpxml::Attribute("signal", "key-press-event"), sdpxml::Attribute("name", event_key_press)));
            sdpxml::Element& vbox = window.Append(sdpxml::Element("vbox", "", sdpxml::Attribute("homogeneous", "false")));
            vbox.Append(sdpxml::Element("eventbox", "", sdpxml::Attribute("name", "auto_controls"), sdpxml::Attribute("expand", "true"), sdpxml::Attribute("fill", "true")));
            sdpxml::Element& hbuttonbox = vbox.Append(sdpxml::Element("hbuttonbox", "", sdpxml::Attribute("layout", "end"), sdpxml::Attribute("expand", "false")));
            hbuttonbox.Append(sdpxml::Element("k3dbutton", "Cancel", sdpxml::Attribute("name", "cancel")));
            hbuttonbox.Append(sdpxml::Element("k3dbutton", "OK", sdpxml::Attribute("name", "ok")));

            // Load the user interface ...
            load_user_interface(gtkml);

            // Bind the cancel button ...
            k3d::button::control* const cancel_button = get_button("cancel");
            return_if_fail(cancel_button);
            cancel_button->signal_activate().connect(SigC::slot(*this, &auto_tool_dialog::on_close));

            // Bind the OK button ...
            k3d::button::control* const ok_button = get_button("ok");
            return_if_fail(ok_button);
            ok_button->signal_activate().connect(SigC::slot(*this, &auto_tool_dialog::on_ok));

            // Hook the tool into the construction chain ...
            k3d::idag::dependencies_t dependencies;
            dependencies.insert(std::make_pair(&m_tool_input, &m_output));
            dependencies.insert(std::make_pair(&m_input, &m_tool_output));
            object().document().dag().set_dependencies(dependencies);
            
            // Setup auto controls
            on_update_properties();
      }

      void on_update_properties()
      {
            if(m_frame.Attached())
                  m_frame.Destroy();

            m_auto_controls.reset();
                                                      
            m_frame.Create();
            m_frame.Show();         
            Container("auto_controls").Attach(m_frame);
            m_auto_controls.reset(new auto_controls(this, "auto_controls", object(), auto_controls::NO_UNDO, m_frame));
      }

      void on_close()
      {
            // Restore the original construction chain ...
            k3d::idag::dependencies_t dependencies;
            dependencies.insert(std::make_pair(&m_input, &m_output));
            object().document().dag().set_dependencies(dependencies);

            // Schedule a display update ...
            k3d::viewport::redraw_all(object().document(), k3d::iviewport::ASYNCHRONOUS);

            // Delete the object (which will delete the UI automatically)
            k3d::delete_objects(object().document(), k3d::make_collection<k3d::objects_t>(&object()));
      }

      void on_ok()
      {
            // This gets a little convoluted ... turn-on state recording momentarily while we create a FrozenMesh object ...
            object().document().state_recorder().start_recording(m_change_set);
            k3d::iobject* const frozen_mesh = k3d::create_document_plugin(k3d::classes::FrozenMesh(), object().document(), object().name());
            m_change_set = object().document().state_recorder().stop_recording();
            return_if_fail(frozen_mesh);

            k3d::imesh_sink* const mesh_sink = dynamic_cast<k3d::imesh_sink*>(frozen_mesh);
            return_if_fail(mesh_sink);
            k3d::iproperty* const frozen_mesh_input = &mesh_sink->mesh_sink_input();
            
            k3d::imesh_source* const mesh_source = dynamic_cast<k3d::imesh_source*>(frozen_mesh);
            return_if_fail(mesh_source);
            k3d::iproperty* const frozen_mesh_output = &mesh_source->mesh_source_output();

            // Insert the frozen mesh into the construction chain and grab a copy of the modified output ...
            k3d::idag::dependencies_t dependencies;
            dependencies.insert(std::make_pair(frozen_mesh_input, &m_tool_output));
            object().document().dag().set_dependencies(dependencies);
            
            // Reading the frozen mesh output forces it to create a copy of its input ...
            frozen_mesh_output->value();
                                    
            // Restore the original construction chain, removing the frozen mesh and the tool from the picture ...
            dependencies.clear();
            dependencies.insert(std::make_pair(&m_input, &m_output));
            dependencies.insert(std::make_pair(&m_tool_input, static_cast<k3d::iproperty*>(0)));
            dependencies.insert(std::make_pair(frozen_mesh_input, static_cast<k3d::iproperty*>(0)));
            object().document().dag().set_dependencies(dependencies);
      
            // Turn on state recording again, and insert the frozen mesh into the construction chain ...
            object().document().state_recorder().start_recording(m_change_set);
            dependencies.clear();
            dependencies.insert(std::make_pair(frozen_mesh_input, &m_output));
            dependencies.insert(std::make_pair(&m_input, frozen_mesh_output));
            object().document().dag().set_dependencies(dependencies);
            object().document().state_recorder().commit_change_set(object().document().state_recorder().stop_recording(), object().name());
      
            // Delete the tool (which will delete the UI automatically)
            k3d::delete_objects(object().document(), k3d::make_collection<k3d::objects_t>(&object()));
      }
      
private:
      k3d::iproperty& m_output;
      k3d::iproperty& m_tool_input;
      k3d::iproperty& m_tool_output;
      k3d::iproperty& m_input;

      std::auto_ptr<k3d::istate_change_set> m_change_set;

      sdpGtkFrame m_frame;
      std::auto_ptr<auto_controls> m_auto_controls;
};

} // namespace

namespace k3d
{

void create_auto_object_dialog(k3d::iobject& Object)
{
      new auto_object_dialog(Object);
}

void create_auto_tool_dialog(k3d::iobject& Object, iproperty& Output, iproperty& ToolInput, iproperty& ToolOutput, iproperty& Input)
{
      new auto_tool_dialog(Object, Output, ToolInput, ToolOutput, Input);
}

} // namespace k3d



Generated by  Doxygen 1.6.0   Back to index