Logo Search packages:      
Sourcecode: k3d version File versions

document_window.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
            \brief Implements the document_window class, which encapsulates an open K-3D document's UI
            \author Tim Shead (tshead@k-3d.com)
*/

#include "button.h"
#include "cursors.h"
#include "dag_control.h"
#include "dag_window.h"
#include "dnd.h"
#include "document_window.h"
#include "filter_selector.h"
#include "menu_item.h"
#include "toggle_button.h"
#include "viewport_window.h"

#include <k3dsdk/application.h>
#include <k3dsdk/basic_math.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/file_filter.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/imesh_sink.h>
#include <k3dsdk/imesh_source.h>
#include <k3dsdk/igeometry_read_format.h>
#include <k3dsdk/igeometry_write_format.h>
#include <k3dsdk/iplugin_factory.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iviewport.h>
#include <k3dsdk/iviewport_host.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/ioptions.h>
#include <k3dsdk/property.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/selection.h>
#include <k3dsdk/serialization.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/time_source.h>
#include <k3dsdk/user_interface.h>
#include <k3dsdk/viewport.h>

#include <sdpgtk/sdpgtkfileselector.h>
#include <sdpgtk/sdpgtkevents.h>

#include <boost/filesystem/fstream.hpp>

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

namespace
{

/////////////////////////////////////////////////////////////////////////////
// insert_factories

/// Inserts plugin factories into the user interface
class insert_factories
{
public:
      insert_factories(sdpGtkCList ListControl) :
            m_list_control(ListControl)
      {
      }

      void operator()(k3d::iplugin_factory* Factory)
      {
            if(Factory->default_category() != "Tools" && Factory->default_category() != "Objects")
                  return;
      
            const std::string name = Factory->name() +
                  (k3d::iplugin_factory::EXPERIMENTAL == Factory->quality() ? " (Experimental)" : "") +
                  (k3d::iplugin_factory::DEPRECATED == Factory->quality() ? " (Deprecated)" : "");

            const char* labels[] = { name.c_str() };
            const int row = m_list_control.Append(labels);
            m_list_control.SetRowData(row, dynamic_cast<k3d::iunknown*>(Factory));

            if(k3d::iplugin_factory::EXPERIMENTAL == Factory->quality())
                  {
                        GdkColor color;
                        color.red = 0x0000;
                        color.green = 0x0000;
                        color.blue = 0xffff;
                        gdk_colormap_alloc_color(gdk_colormap_get_system(), &color, FALSE, TRUE);

                        m_list_control.SetForegroundColor(row, &color);
                  }
            else if(k3d::iplugin_factory::DEPRECATED == Factory->quality())
                  {
                        GdkColor color;
                        color.red = 0xffff;
                        color.green = 0x0000;
                        color.blue = 0x0000;
                        gdk_colormap_alloc_color(gdk_colormap_get_system(), &color, FALSE, TRUE);

                        m_list_control.SetForegroundColor(row, &color);
                  }
      }

private:
      sdpGtkCList m_list_control;
};

const std::string control_undostack = "undostack";
const std::string control_animationscrollbar = "animationscrollbar";
const std::string control_animationframe = "animationframe";
const std::string control_focusin = "focusin";
const std::string control_dragdataget = "dragdataget";
const std::string control_pluginsclicked = "pluginsclicked";
const std::string control_highlight_plugin = "highlight_plugin";

} // namespace

namespace k3d
{

/////////////////////////////////////////////////////////////////////////////
// document_window

document_window::document_window(idocument& Document) :
      base(dynamic_cast<icommand_node*>(&Document), "window", new options_window_geometry_store()),
      property_collection(Document.dag()),
      m_document(Document),
      m_dag_control(0),
      m_animation_timer(0),
      m_ignore_animation_timer(false),
      m_playback_mode(init_name("playback_mode") + init_description("Playback mode [enumeration]") + init_value(STOP) + init_document(Document)),
      m_rewind(init_value(false)),
      m_reverse_loop_play(init_value(false)),
      m_reverse_play(init_value(false)),
      m_stop(init_value(true)),
      m_play(init_value(false)),
      m_loop_play(init_value(false)),
      m_fast_forward(init_value(false)),
      m_ignore_playback_change(false)
{
      // Register public properties
      register_property(m_playback_mode);

      // We want to be asked before closing the document ...
      m_document.safe_to_close_signal().connect(SigC::slot(*this, &document_window::safe_to_close_document));

      // We want to be notified when changes are made to the document
      m_document.close_signal().connect(SigC::slot(*this, &document_window::on_document_closed));
      m_document.title_signal().connect(SigC::slot(*this, &document_window::on_document_title_changed));

      // We want to be notified whenever the playback mode changes
      m_playback_mode.changed_signal().connect(SigC::slot(*this, &document_window::on_playback_mode_changed));

      return_if_fail(LoadGTKMLTemplate("document_window.gtkml"));

      if(get_menu_item("file_new_dag_window"))
            get_menu_item("file_new_dag_window")->signal_activate().connect(SigC::slot(*this, &document_window::on_file_new_dag_window));
      if(get_menu_item("file_new_viewport"))
            get_menu_item("file_new_viewport")->signal_activate().connect(SigC::slot(*this, &document_window::on_file_new_viewport));
      if(get_menu_item("file_export"))
            get_menu_item("file_export")->signal_activate().connect(SigC::slot(*this, &document_window::on_file_export));
      if(get_menu_item("file_import"))
            get_menu_item("file_import")->signal_activate().connect(SigC::slot(*this, &document_window::on_file_import));
      if(get_menu_item("file_close"))
            get_menu_item("file_close")->signal_activate().connect(SigC::slot(*this, &document_window::on_file_close));
      if(get_menu_item("edit_undo"))
            get_menu_item("edit_undo")->signal_activate().connect(SigC::slot(*this, &document_window::on_edit_undo));
      if(get_menu_item("edit_redo"))
            get_menu_item("edit_redo")->signal_activate().connect(SigC::slot(*this, &document_window::on_edit_redo));
      if(get_menu_item("tools_play_script"))
            get_menu_item("tools_play_script")->signal_activate().connect(SigC::slot(*this, &document_window::on_tools_play_script));

      if(get_button("undo"))
            get_button("undo")->signal_activate().connect(SigC::slot(*this, &document_window::on_edit_undo));
      if(get_button("redo"))
            get_button("redo")->signal_activate().connect(SigC::slot(*this, &document_window::on_edit_redo));

      if(get_button("create_object"))
            get_button("create_object")->signal_activate().connect(SigC::slot(*this, &document_window::OnCreatePlugin));

      if(get_toggle_button("rewind"))
            get_toggle_button("rewind")->attach(toggle_button::proxy(m_rewind), 0, "");
      if(get_toggle_button("loopreverseplay"))
            get_toggle_button("loopreverseplay")->attach(toggle_button::proxy(m_reverse_loop_play), 0, "");
      if(get_toggle_button("reverseplay"))
            get_toggle_button("reverseplay")->attach(toggle_button::proxy(m_reverse_play), 0, "");
      if(get_toggle_button("stop"))
            get_toggle_button("stop")->attach(toggle_button::proxy(m_stop), 0, "");
      if(get_toggle_button("play"))
            get_toggle_button("play")->attach(toggle_button::proxy(m_play), 0, "");
      if(get_toggle_button("loopplay"))
            get_toggle_button("loopplay")->attach(toggle_button::proxy(m_loop_play), 0, "");
      if(get_toggle_button("fastforward"))
            get_toggle_button("fastforward")->attach(toggle_button::proxy(m_fast_forward), 0, "");

      m_rewind.changed_signal().connect(SigC::slot(*this, &document_window::on_rewind));
      m_reverse_loop_play.changed_signal().connect(SigC::slot(*this, &document_window::on_reverse_loop_play));
      m_reverse_play.changed_signal().connect(SigC::slot(*this, &document_window::on_reverse_play));
      m_stop.changed_signal().connect(SigC::slot(*this, &document_window::on_stop));
      m_play.changed_signal().connect(SigC::slot(*this, &document_window::on_play));
      m_loop_play.changed_signal().connect(SigC::slot(*this, &document_window::on_loop_play));
      m_fast_forward.changed_signal().connect(SigC::slot(*this, &document_window::on_fast_forward));

      // Create the DAG control ...
      m_dag_control = new dag_control::control(m_document, this, "dag");
      Container("dag").Attach(m_dag_control->root_widget());

      // Create an event for our animation scrollbar ...
      sdpGtkAdjustment adjustment = Scrollbar(control_animationscrollbar).Adjustment();
      MapEvent("value-changed", control_animationscrollbar, false, adjustment, true);

      // Update our titlebar ...
      UpdateTitlebar();

      // We want to be notified whenever the undo/redo stack is modified ...
      m_document.state_recorder().stack_changed_signal().connect(SigC::slot(*this, &document_window::on_undo_stack_changed));
      m_document.state_recorder().mark_saved_signal().connect(SigC::slot(*this, &document_window::on_undo_mark_saved));

      // Setup plugins tab ...
      PluginsList().SetDragSource(GDK_BUTTON1_MASK, &dnd_create_object_target(), 1, GDK_ACTION_COPY);
      MapEvent("drag-data-get", control_dragdataget, false, PluginsList(), true);

      PluginsList().Clear();

      // Insert plugin types the new way ...
      std::for_each(application().plugins().begin(), application().plugins().end(), insert_factories(PluginsList()));

      PluginsList().Sort();

      // Update remaining state ...
      CallUpdateControls();

      // Make ourselves visible ...
      Show();
}

document_window::~document_window()
{
      // Kill the animation timer ...
      if(m_animation_timer)
            gtk_timeout_remove(m_animation_timer);

      delete m_dag_control;
}

00264 bool document_window::safe_to_close_document()
{
      // If we don't have a user interface, go ahead ...
      if(!application().user_interface())
            return true;

      // If the UI is in batch mode, close away ... !
      if(application().user_interface()->batch_mode())
            return true;

      // If there aren't any unsaved changes, we're good to go ...
      if(!m_document.state_recorder().unsaved_changes())
            return true;

      // Prompt the user to save changes ...
      std::vector<std::string> buttons;
      buttons.push_back("Save Changes");
      buttons.push_back("Discard Changes");
      buttons.push_back("Cancel");

      const std::string message = "Close " + m_document.title() + "? Unsaved changes will be lost (No Undo)";

      switch(application().user_interface()->query_message(message, "Close Document:", 1, buttons))
            {
                  case 0:
                        return false;
                  case 1:
                        return on_file_save();
                  case 2:
                        return true;
                  case 3:
                        return false;
            }

      return false;
}

00301 void document_window::on_document_closed()
{
      delete this;
}

00306 void document_window::on_document_title_changed()
{
      UpdateTitlebar();
}

00311 void document_window::on_undo_stack_changed()
{
      UpdateUndoStack();
}

00316 void document_window::on_undo_mark_saved()
{
      UpdateUndoStack();
}

00321 void document_window::OnEvent(sdpGtkEvent* Event)
{
      if(Event->Name() == control_undostack)
            OnUndoStack(Event);
      else if(Event->Name() == control_animationscrollbar)
            OnAnimationScrollbar();
      else if(Event->Name() == control_animationframe)
            OnAnimationFrame();
      else if(Event->Name() == control_pluginsclicked)
            OnPluginsClicked(Event);
      else if(Event->Name() == control_dragdataget)
            OnDragDataGet(Event);
      else
            base::OnEvent(Event);
}

00337 void document_window::OnDelete(sdpGtkEvent* Event)
{
      // Cancel the normal window close ...
      ((sdpGtkEventWidgetDeleteEvent*)Event)->SetResult(true);
      on_file_close();
}

00344 void document_window::on_file_new_dag_window()
{
      new dag_window(m_document);
}

00349 void document_window::on_file_new_viewport()
{
      return_if_fail(create_document_plugin("Viewport", m_document, "Viewport"));
}

00354 void document_window::on_file_import()
{
      // Make sure we have some file formats to choose from ...
      if(0 == plugins<igeometry_read_format>().size())
            {
                  error_message("No geometry import file filters available", "Import Geometry:");
                  return;
            }

      // Prompt the user for a file ...
      boost::filesystem::path filepath;
      if(!get_file_path("geometry", "Import Geometry:", false, boost::filesystem::path(), filepath))
            return;

      // Prompt the user to choose an import filter ...
      filter_selector<igeometry_read_format> filterselector("Select Import File Format:", filepath);
      if(!filterselector.do_modal(RootWindow()))
            return;

      // Get the import filter that the user chose (could be NULL, if they chose "automatic" and there wasn't a match)
      auto_ptr<igeometry_read_format> filter(filterselector.filter());
      if(!filter.get())
            {
                  error_message(
                        "Couldn't find a filter for this file.  If you chose \"Automatic\" as the filter type,\n"
                        "try choosing a specific filter that matches the type of file you're importing.",
                        "Import Geometry:");
                  return;
            }

      // Record undo/redo information ...
      record_state_change_set changeset(m_document, "Import " + filepath.native_file_string());

      // Import the file ...
      if(!import_file(m_document, *filter, filepath))
            {
                  error_message(
                        "Error importing geometry file.  If you chose \"Automatic\" as the filter type,\n"
                        "try choosing a specific filter that matches the type of file you're importing.",
                        "Import Geometry:");
                  return;
            }

      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}

00400 void document_window::on_file_export()
{
      // Make sure we have some file formats to choose from ...
      if(0 == plugins<igeometry_write_format>().size())
            {
                  error_message("No geometry export file filters available", "Export Geometry:");
                  return;
            }

      // Prompt the user for a file to export ...
      boost::filesystem::path filepath;
      if(!get_file_path("geometry", "Export Geometry:", true, boost::filesystem::path(), filepath))
            return;

      // Prompt the user to choose an export filter ...
      filter_selector<igeometry_write_format> filterselector("Select Export File Format:", filepath);
      if(!filterselector.do_modal(RootWindow()))
            return;

      // Get the import filter that the user chose (could be NULL, if they chose "automatic" and there wasn't a match)
      auto_ptr<igeometry_write_format> filter(filterselector.filter());
      if(!filter.get())
            {
                  error_message(
                        "Couldn't find a filter for this file.  If you chose \"Automatic\" as the filter type,\n"
                        "try choosing a specific filter that matches the file type you want to export.",
                        "Export Geometry:");
                  return;
            }

      // Export the file ...
      if(!export_file(m_document, *filter, filepath))
            {
                  error_message(
                        "Error exporting geometry file.  If you chose \"Automatic\" as the filter type,\n"
                        "try choosing a specific filter that matches the file type you want to export.",
                        "Export Geometry:");
                  return;
            }

      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}

00443 bool document_window::on_file_save()
{
      // If the user hasn't chosen a name, yet, turn it into a "Save As" ...
      if(m_document.path().empty())
            return on_file_save_as();

      wait_cursor wc(RootWidget());
      
      return m_document.save(m_document.path());
}

00454 bool document_window::on_file_save_as()
{
      boost::filesystem::path filepath;
      if(!get_file_path("document", "Save K-3D Document As:", true, boost::filesystem::path(), filepath))
            return false;

      wait_cursor wc(RootWidget());
      
      return m_document.save(filepath);
}

00465 void document_window::on_file_close()
{
      // Don't close without asking!
      if(m_document.safe_to_close_signal().emit())
            application().close_document(m_document);
}

00472 void document_window::on_edit_undo()
{
      // Ignore it if there aren't any actions to undo ...
      if(!m_document.state_recorder().undo_count())
            return;

      // Tell the document to undo the last action ...
      m_document.state_recorder().undo();

      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}

00484 void document_window::on_edit_redo()
{
      // Ignore it if there aren't any actions to redo ...
      if(!m_document.state_recorder().redo_count())
            return;

      // Tell the document to redo the last action ...
      m_document.state_recorder().redo();

      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}


00497 void document_window::on_tools_play_script()
{
      boost::filesystem::path filepath;
      if(!get_file_path("script", "Play K-3D Script:", false, boost::filesystem::path(), filepath))
            return;

      // Make it happen ...
      bool recognized = false;
      bool executed = false;
      boost::filesystem::ifstream file(filepath);

      execute_script(file, filepath.native_file_string(), iscript_engine::context_t(1, &m_document), recognized, executed);

      if(!recognized)
            {
                  error_message(
                        "Could not determine scripting language.  K-3D supports multiple scripting languages, but the language for this script was\n"
                        "not recognized. Most K-3D script engines use some type of \"magic token\" at the beginning of a script to recognize it, e.g. \"//javascript\"\n"
                        "in the first 12 characters of a script for K-3D's built-in JavaScript engine.  If you are writing a K-3D script, check the documentation\n"
                        "for the scripting language you're writing in to see how to make it recognizable.",
                        "Play " + filepath.native_file_string() + ":");
                  return;
            }

      if(!executed)
            {
                  error_message("Error executing script", "Play " + filepath.native_file_string() + ":");
                  return;
            }
}

00528 void document_window::OnUndoStack(sdpGtkEvent* Event)
{
      // Get the row we clicked on ...
      int row;
      int column;
      sdpGtkEventWidgetButtonPressEvent* event = (sdpGtkEventWidgetButtonPressEvent*)Event;
      int rowselected = CList(control_undostack).GetHitInfo(int(event->Event()->x), int(event->Event()->y), &row, &column);

      // Cache a reference to the state recorder ...
      istate_recorder& staterecorder = m_document.state_recorder();

      // If the user clicked on a row, call Undo() or Redo() until we reach the selected state ...
      if(rowselected)
            {
                  while((unsigned long)(row) < staterecorder.undo_count())
                        staterecorder.undo();

                  while((unsigned long)(row) > staterecorder.undo_count())
                        staterecorder.redo();
            }

      // Otherwise, the user must have clicked after the last row, so redo everything ...
      else
            {
                  while(staterecorder.redo_count())
                        staterecorder.redo();
            }

      // Update the display ...
      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);

      gtk_signal_emit_stop_by_name(CList(control_undostack).Object(), "button-press-event");
}

00562 void document_window::on_rewind()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(REWIND);
}

00568 void document_window::on_reverse_loop_play()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(LOOP_REVERSE_PLAY);
}

00574 void document_window::on_reverse_play()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(REVERSE_PLAY);
}

00580 void document_window::on_stop()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(STOP);
}

00586 void document_window::on_play()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(PLAY);
}

00592 void document_window::on_loop_play()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(LOOP_PLAY);
}

00598 void document_window::on_fast_forward()
{
      if(!m_ignore_playback_change)
            m_playback_mode.set_value(FAST_FORWARD);
}

00604 void document_window::OnAnimationScrollbar()
{
      // Update our scrollbar ...
      sdpGtkAdjustment adjustment = Scrollbar(control_animationscrollbar).Adjustment();

      // If the document doesn't have a time source, we're done ...
      iproperty* const frame_rate_property = get_frame_rate(m_document);
      iwritable_property* const writable_time_property = dynamic_cast<iwritable_property*>(get_time(m_document));
      
      if(!frame_rate_property || !writable_time_property)
            return;

      const double frame_rate = boost::any_cast<double>(get_property_value(m_document.dag(), *frame_rate_property));

      writable_time_property->set_value(adjustment.Value() / frame_rate);

      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}

00623 void document_window::OnAnimationFrame()
{
      // If the document doesn't have a time source, we're done ...
      iproperty* const frame_rate_property = get_frame_rate(m_document);
      iwritable_property* const writable_time_property = dynamic_cast<iwritable_property*>(get_time(m_document));
      
      if(!frame_rate_property || !writable_time_property)
            return;

      const double frame_rate = boost::any_cast<double>(get_property_value(m_document.dag(), *frame_rate_property));

      writable_time_property->set_value(atol(Entry(control_animationframe).GetText()) / frame_rate);

      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}

void document_window::UpdateControls()
{
      UpdateUndoStack();
      on_playback_mode_changed();

      base::UpdateControls();
}

00647 void document_window::UpdateTitlebar()
{
      // Get our title and stuff it in the titlebar ...
      RootWindow().SetTitle(Title().c_str());
}

00653 std::string document_window::Title()
{
      return m_document.title();
}

/// Populates the contents of the Undo Stack with a current list of Undo-able changes
00659 class document_windowPopulateUndoStack :
      public istate_recorder::ichange_set_visitor
{
public:
      document_windowPopulateUndoStack(sdpGtkCList List, istate_recorder& StateRecorder) :
            m_List(List)
      {
            // Setup our colors
            m_RedoForeground.red = 0x4444;
            m_RedoForeground.green = 0x4444;
            m_RedoForeground.blue = 0x4444;
            gdk_colormap_alloc_color(gdk_colormap_get_system(), &m_RedoBackground, FALSE, TRUE);

            m_RedoBackground.red = 0xffff;
            m_RedoBackground.green = 0xffff;
            m_RedoBackground.blue = 0xaaaa;
            gdk_colormap_alloc_color(gdk_colormap_get_system(), &m_RedoBackground, FALSE, TRUE);

            // Update the list ...
            m_List.Freeze();
            m_List.Clear();

            StateRecorder.visit_change_sets(*this);

            m_List.Thaw();
      }

      virtual ~document_windowPopulateUndoStack()
      {
      }

      void visit_undo_change_set(const std::string& UndoLabel, const bool Saved)
      {
            // If this is the channge set where we last saved, then append [Saved] to the name
            std::string name(UndoLabel);
            if(Saved)
                  name += " [Saved]";

            m_List.Append(name.c_str());
      }

      void visit_redo_change_set(const std::string& RedoLabel, const bool Saved)
      {
            // If this is the channge set where we last saved, then append [Saved] to the name
            std::string name(RedoLabel);
            if(Saved)
                  name += " [Saved]";

            m_List.Append(name.c_str());

            m_List.SetForegroundColor(m_List.RowCount() - 1, &m_RedoForeground);
            m_List.SetBackgroundColor(m_List.RowCount() - 1, &m_RedoBackground);
      }

private:
      sdpGtkCList m_List;
      GdkColor m_RedoForeground;
      GdkColor m_RedoBackground;
};

00719 void document_window::UpdateUndoStack()
{
      // Setup the undo menu item ...
      if(m_document.state_recorder().undo_count())
            {
                  get_menu_item("edit_undo")->set_text("Undo " + m_document.state_recorder().next_undo_label());
                  get_menu_item("edit_undo")->set_sensitive(true);
                  get_button("undo")->set_sensitive(true);
            }
      else
            {
                  get_menu_item("edit_undo")->set_text("Can't Undo");
                  get_menu_item("edit_undo")->set_sensitive(false);
                  get_button("undo")->set_sensitive(false);
            }

      // Setup the redo menu item ...
      if(m_document.state_recorder().redo_count())
            {
                  get_menu_item("edit_redo")->set_text("Redo " + m_document.state_recorder().next_redo_label());
                  get_menu_item("edit_redo")->set_sensitive(true);
                  get_button("redo")->set_sensitive(true);
            }
      else
            {
                  get_menu_item("edit_redo")->set_text("Can't Redo");
                  get_menu_item("edit_redo")->set_sensitive(false);
                  get_button("redo")->set_sensitive(false);
            }

      document_windowPopulateUndoStack populateundostack(CList(control_undostack), m_document.state_recorder());
}

00752 void document_window::on_playback_mode_changed()
{
      m_ignore_playback_change = true;

      // Set all our buttons to "un-toggled" ...
      m_rewind.set_value(false);
      m_reverse_loop_play.set_value(LOOP_REVERSE_PLAY == m_playback_mode.value());
      m_reverse_play.set_value(REVERSE_PLAY == m_playback_mode.value());
      m_stop.set_value(STOP == m_playback_mode.value());
      m_play.set_value(PLAY == m_playback_mode.value());
      m_loop_play.set_value(LOOP_PLAY == m_playback_mode.value());
      m_fast_forward.set_value(false);

      m_ignore_playback_change = false;

      // Start the animation timer ...
      if(!m_animation_timer)
            m_animation_timer = gtk_timeout_add(33, raw_animation_timer, this);
}

00772 void document_window::update_time()
{
      // Make sure this document contains time information ...
      iproperty* const start_time_property = get_start_time(m_document);
      iproperty* const end_time_property = get_end_time(m_document);
      iproperty* const frame_rate_property = get_frame_rate(m_document);
      iproperty* const time_property = get_time(m_document);
      
      if(!start_time_property || !end_time_property || !frame_rate_property || !time_property)
            return;

      BlockAllEvents();

      // Convert from time to frames ...
      const double start_time = boost::any_cast<double>(get_property_value(m_document.dag(), *start_time_property));
      const double end_time = boost::any_cast<double>(get_property_value(m_document.dag(), *end_time_property));
      const double frame_rate = boost::any_cast<double>(get_property_value(m_document.dag(), *frame_rate_property));
      const double time = boost::any_cast<double>(get_property_value(m_document.dag(), *time_property));
      
      const double start_frame = round(frame_rate * start_time);
      const double view_frame = round(frame_rate * time);
      const double end_frame = round(frame_rate * end_time);

      // Display the frame number ...
      Entry(control_animationframe).SetText(sdpToString(view_frame));

      // Update our scrollbar ...
      const unsigned long page_size = static_cast<unsigned long>(round(frame_rate));

      sdpGtkAdjustment adjustment = Scrollbar(control_animationscrollbar).Adjustment();
      adjustment.SetLower(start_frame);
      adjustment.SetUpper(end_frame + page_size - 1);
      adjustment.SetValue(view_frame);
      adjustment.SetStepIncrement(1);
      adjustment.SetPageIncrement(page_size);
      adjustment.SetPageSize(page_size);
      adjustment.Changed();
      adjustment.ValueChanged();

      UnblockAllEvents();
}

00814 bool document_window::SafeToClose()
{
      /// No UI, so it's safe ...
      if(!application().user_interface())
            return true;

      std::vector<std::string> buttons;
      buttons.push_back("Yes");
      buttons.push_back("No");

      return 1 == application().user_interface()->query_message("Close the document?  Unsaved changes will be lost (No Undo)", "Close Document:", 1, buttons);
}

00827 void document_window::OnCreatePlugin()
{
      sdpGtkCList list = PluginsList();
      sdpGtkCList::Rows selection = list.GetSelectedRows();
      for(sdpGtkCList::RowIterator row = selection.begin(); row != selection.end(); row++)
            {
                  iplugin_factory* const factory = dynamic_cast<iplugin_factory*>(reinterpret_cast<iunknown*>(list.GetRowData(*row)));
                  if(factory)
                        {
                              // Create the requested object ...
                              record_state_change_set changeset(m_document, "Create " + factory->name());
                              const std::string object_name = unique_name(m_document.objects(), factory->name());
                              iobject* const object = create_document_plugin(*factory, m_document, object_name);
                              
                              // If this object is a mesh source, create a MeshInstance object and attach it so it's immediately visible ...
                              imesh_source* const mesh_source = dynamic_cast<imesh_source*>(object);
                              if(mesh_source)
                                    {
                                          // But don't create a MeshInstance for a MeshInstance object!
                                          if(classes::MeshInstance() != factory->class_id())
                                                {
                                                      imesh_sink* const mesh_sink = dynamic_cast<imesh_sink*>(create_document_plugin(classes::MeshInstance(), m_document, unique_name(m_document.objects(), object_name + " Instance")));
                                                      if(mesh_sink)
                                                            {
                                                                  idag::dependencies_t dependencies;
                                                                  dependencies.insert(std::make_pair(&mesh_sink->mesh_sink_input(), &mesh_source->mesh_source_output()));
                                                                  m_document.dag().set_dependencies(dependencies);
                                                            }
                                                }
                                    }
                                    
                              // If this object is a viewport, create a viewport window and attach it ...
                              iviewport* const viewport = dynamic_cast<iviewport*>(object);
                              if(viewport)
                                    {
                                          viewport::window* const window = new viewport::window(m_document);
                                          window->attach(*viewport);
                                    }
                                    
                              // If the object is a viewport host, create a viewport and attach it ...
                              iviewport_host* const viewport_host = dynamic_cast<iviewport_host*>(object);
                              if(viewport_host)
                                    {
                                          iobject* const viewport_object = create_document_plugin("Viewport", m_document, unique_name(m_document.objects(), object->name() + " Viewport"));
                                          if(viewport_object)
                                                {
                                                      iviewport* const viewport = dynamic_cast<iviewport*>(viewport_object);
                                                      if(viewport)
                                                            {
                                                                  viewport->set_host(viewport_host);
                                                                  
                                                                  iproperty* const output_mesh = get_typed_property<matrix4>(*viewport_host, "output_matrix");
                                                                  iproperty* const input_mesh = get_typed_property<matrix4>(*viewport, "input_matrix");
                                                                  if(input_mesh && output_mesh)
                                                                        {
                                                                              idag::dependencies_t dependencies;
                                                                              dependencies.insert(std::make_pair(input_mesh, output_mesh));
                                                                              m_document.dag().set_dependencies(dependencies);
                                                                        }
                                                                  
                                                                  viewport::window* const window = new viewport::window(m_document);
                                                                  window->attach(*viewport);
                                                            }
                                                }
                                    }
                              
                              if(object && application().user_interface())
                                    application().user_interface()->show(*object);
                        }
            }

      // Update the display ...
      viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
}

00902 void document_window::OnDragDataGet(sdpGtkEvent* Event)
{
      // Sanity checks ...
      assert_warning(Event);

      sdpGtkEventWidgetDragDataGet* const event = static_cast<sdpGtkEventWidgetDragDataGet*>(Event);

      // We're going to generate a K3DML document ...
      sdpxml::Document xmldocument("k3dml");

      sdpxml::Element& application = xmldocument.Append(sdpxml::Element("application"));
      sdpxml::Element& objects = application.Append(sdpxml::Element("objects"));

      // For each selected object ...
      sdpGtkCList::Rows selection = PluginsList().GetSelectedRows();
      for(sdpGtkCList::RowIterator row = selection.begin(); row != selection.end(); row++)
            {
                  iplugin_factory* const factory = dynamic_cast<iplugin_factory*>(reinterpret_cast<iunknown*>(PluginsList().GetRowData(*row)));
                  assert_warning(factory);

                  // Create an XML element for the object ...
                  objects.Append(sdpxml::Element("object", "", sdpxml::Attribute("name", factory->name()), sdpxml::Attribute("class", sdpToString(factory->class_id()))));
            }

      // Buffer the data (since drag-and-drop is asynchronous) ...
      std::stringstream stream;
      stream << xmldocument;

      m_DNDBuffer = stream.str();

      // Hand the data off to the drag-and-drop mechanism ...
      gtk_selection_data_set(event->Selection(), GDK_SELECTION_TYPE_STRING, 8, reinterpret_cast<const unsigned char*>(m_DNDBuffer.c_str()), m_DNDBuffer.size());
}

00936 void document_window::OnPluginsClicked(sdpGtkEvent* Event)
{
      // Sanity checks ...
      assert_warning(Event);

      // Get the row we clicked on ...
      int row;
      int column;
      sdpGtkEventWidgetButtonPressEvent* event = (sdpGtkEventWidgetButtonPressEvent*)Event;
      PluginsList().GetHitInfo(int(event->Event()->x), int(event->Event()->y), &row, &column);

      if(-1 == row)
            return;

      iplugin_factory* const factory = dynamic_cast<iplugin_factory*>(reinterpret_cast<iunknown*>(PluginsList().GetRowData(row)));
      if(factory)
            record_command(*this, icommand_node::command_t::USER_INTERFACE, control_highlight_plugin, factory->name());

      if(event->Event()->button == 1 && event->Event()->type == GDK_2BUTTON_PRESS)
            {
                  PluginsList().SelectRow(row, 0);
                  OnCreatePlugin();
            }
}

00961 bool document_window::execute_command(const std::string& Command, const std::string& Arguments)
{
      if(Command == control_highlight_plugin)
            {
                  // Lookup the matching plugin factory ...
                  const iplugin_factory_collection::factories_t factories(plugins(Arguments));
                  return_val_if_fail(1 == factories.size(), false);

                  iplugin_factory* const factory = *factories.begin();

                  // Find a row that matches ...
                  sdpGtkCList list = PluginsList();
                  int row = list.FindRowFromData(factory);

                  // Move the pointer to the row ...
                  list.InteractiveShow(application().options().tutorial_speed(), true);
                  list.InteractiveWarpPointer(row, application().options().tutorial_speed(), true);
                  list.SelectRow(row, 0);

                  if(application().user_interface())
                        application().user_interface()->tutorial_mouse_message("Highlight Plugin:", iuser_interface::LMB_CLICK, key_modifiers());

                  return true;
            }

      return base::execute_command(Command, Arguments);
}

// Called by the animation interval timer
00990 int document_window::raw_animation_timer(gpointer Data)
{
      // Call the object-instance version ...
      return static_cast<document_window*>(Data)->animation_timer();
}

// Called by the animation interval timer
00997 int document_window::animation_timer()
{
      // We want to be notified whenever the current document time changes
      if(!m_time_connection.connected())
            {
                  iproperty* const time_property = get_time(m_document);
                  if(time_property)
                        {
                              m_time_connection = time_property->changed_signal().connect(SigC::slot(*this, &document_window::update_time));
                              update_time();
                        }
            }

      // We only pay attention to every-other timer signal ...
      m_ignore_animation_timer = !m_ignore_animation_timer;
      if(m_ignore_animation_timer)
            return true;

      // If the document doesn't have a time source, we're done ...
      iproperty* const start_time_property = get_start_time(m_document);
      iproperty* const end_time_property = get_end_time(m_document);
      iproperty* const frame_rate_property = get_frame_rate(m_document);
      iproperty* const time_property = get_time(m_document);
      iwritable_property* const writable_time_property = dynamic_cast<iwritable_property*>(time_property);
      
      if(!start_time_property || !end_time_property || !frame_rate_property || !time_property || !writable_time_property)
            return true;

      const double start_time = boost::any_cast<double>(get_property_value(m_document.dag(), *start_time_property));
      const double end_time = boost::any_cast<double>(get_property_value(m_document.dag(), *end_time_property));
      const double frame_rate = boost::any_cast<double>(get_property_value(m_document.dag(), *frame_rate_property));
      const double time = boost::any_cast<double>(get_property_value(m_document.dag(), *time_property));

      const double start_frame = round(start_time * frame_rate);
      const double view_frame = round(time * frame_rate);
      const double end_frame = round(end_time * frame_rate) - 1;

      switch(m_playback_mode.value())
            {
                  case REWIND:
                        writable_time_property->set_value(start_frame / frame_rate);
                        m_playback_mode.set_value(STOP);
                        viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
                        break;

                  case LOOP_REVERSE_PLAY:
                        {
                              // Calculate our new time ...
                              double new_view_frame = view_frame - 1;
                              if(new_view_frame < start_frame)
                                    {
                                          // We passed the beginning of the animation, so wrap around ...
                                          new_view_frame = end_frame;
                                    }

                              // Set the new time ...
                              writable_time_property->set_value(new_view_frame / frame_rate);

                              // Redraw everything ...
                              viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
                        }
                        break;

                  case REVERSE_PLAY:
                        {
                              // Calculate our new time ...
                              double new_view_frame = view_frame - 1;
                              if(new_view_frame < start_frame)
                                    {
                                          // We passed the beginning of the animation, so stop ...
                                          new_view_frame = start_frame;
                                          m_playback_mode.set_value(STOP);
                                    }

                              // Set the new time ...
                              writable_time_property->set_value(new_view_frame / frame_rate);

                              // Redraw everything ...
                              viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
                        }
                        break;

                  case STOP:
                        break;

                  case PLAY:
                        {
                              // Calculate our new time ...
                              double new_view_frame = view_frame + 1;
                              if(new_view_frame > end_frame)
                                    {
                                          // We passed the end of the animation, so stop ...
                                          new_view_frame = end_frame;
                                          m_playback_mode.set_value(STOP);
                                    }

                              // Set the new time ...
                              writable_time_property->set_value(new_view_frame / frame_rate);

                              // Redraw everything ...
                              viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
                        }
                        break;

                  case LOOP_PLAY:
                        {
                              // Calculate our new time ...
                              double new_view_frame = view_frame + 1;
                              if(new_view_frame > end_frame)
                                    {
                                          // We passed the end of the animation, so wrap around ...
                                          new_view_frame = start_frame;
                                    }

                              // Set the new time ...
                              writable_time_property->set_value(new_view_frame / frame_rate);

                              // Redraw everything ...
                              viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
                        }
                        break;

                  case FAST_FORWARD:
                        writable_time_property->set_value(end_frame / frame_rate);
                        m_playback_mode.set_value(STOP);
                        viewport::redraw_all(m_document, iviewport::ASYNCHRONOUS);
                        break;

                  default:
                        return_val_if_fail(0, true);
            }

      // Continue indefinitely ...
      return true;
}

} // namespace k3d


Generated by  Doxygen 1.6.0   Back to index