// 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 k3d::check_button namespace, which provides a MVC UI for boolean data sources
		\author Tim Shead (tshead@k-3d.com)
*/

#include "check_button.h"

#include <k3dsdk/application.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/istate_recorder.h>
#include <k3dsdk/ioptions.h>

#include <sdpgtk/sdpgtkloopevents.h>
#include <sdpgtk/sdpgtkutility.h>

namespace k3d
{

namespace check_button
{

namespace detail
{

const std::string control_toggle = "toggle";

sdpxml::Document& gtkml_template()
{
	static sdpxml::Document gtkml("empty");
	if(gtkml.Name() != "gtkml")
		{
			std::istringstream uitemplate(
				"<gtkml>"
					"<eventbox>"
						"<checkbutton name=\"toggle\">"
							"<event signal=\"destroy\" name=\"destroy\"/>"
							"<event signal=\"toggled\" name=\"toggle\"/>"
						"</checkbutton>"
					"</eventbox>"
				"</gtkml>\n");

			assert(gtkml.Load(uitemplate, "check_button builtin template"));
		}

	return gtkml;
}

} // namespace detail

/////////////////////////////////////////////////////////////////////////////
// control

control::control(k3d::iunknown* const CommandNodeParent, const std::string& CommandNodeName) :
	base(CommandNodeParent, CommandNodeName)
{
	return_if_fail(Load(detail::gtkml_template()));
	RootWidget().Show();
}

control::control(k3d::iunknown* const CommandNodeParent, const std::string& CommandNodeName, const std::string& Label) :
	base(CommandNodeParent, CommandNodeName)
{
	return_if_fail(Load(detail::gtkml_template()));
	
	sdpGtkLabel label;
	label.Create(Label);
	label.SetAlignment(0, 0.5);
	Container(detail::control_toggle).Attach(label);
	
	RootWidget().ShowAll();
}

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

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

const std::string control::CustomType() const
{
	return std::string("k3dcheckbutton");
}

bool control::Create(sdpGtkIObjectContainer* const ObjectContainer, sdpxml::Document& Document, sdpxml::Element& Element)
{
	// Sanity checks ...
	assert_warning(ObjectContainer);
	assert_warning(Element.Name() == "k3dcheckbutton");

	const std::string text = sdpGtkInternationalText(Document, Element);
	if(text.size())
		{
			sdpGtkLabel label;
			label.Create(text);
			label.SetAlignment(0.0, 0.5);
			label.Show();

			Container(detail::control_toggle).Attach(label);
		}

	return true;
}

bool control::attach(std::auto_ptr<idata_proxy> Data, k3d::istate_recorder* const StateRecorder, const std::string StateChangeName)
{
	// Sanity checks ...
	return_val_if_fail(Data.get(), false);

	// Take ownership of the data source ...
	m_data = Data;

	// Complete our own initialization ...
	return_val_if_fail(base::Attach(StateRecorder, StateChangeName), false);

	// Update the display ...
	update();

	// We want to be notified if the data source changes ...
	m_data->changed_signal().connect(SigC::slot(*this, &control::update));

	return true;
}

void control::set_state(const bool State)
{
	ToggleButton(detail::control_toggle.c_str()).SetState(State);
}

bool control::get_state()
{
	return ToggleButton(detail::control_toggle.c_str()).GetState();
}

bool control::execute_command(const std::string& Command, const std::string& Arguments)
{
	if(detail::control_toggle == Command)
		{
			InteractiveToggleButton(detail::control_toggle, k3d::application().options().tutorial_speed(), true);
			return true;
		}
		
	return base::execute_command(Command, Arguments);
}

void control::update()
{
	if(!m_data.get())
		return;

	// Display the current value ...
	const bool new_value = m_data->value();
	if(new_value != ToggleButton(detail::control_toggle).GetState())
		ToggleButton(detail::control_toggle).SetState(new_value);
}

void control::OnEvent(sdpGtkEvent* Event)
{
	// Sanity checks ...
	assert_warning(Event);

	if(Event->Name() == detail::control_toggle)
		on_toggle();
	else if(Event->Name() == "destroy")
		on_destroy();
	else
		base::OnEvent(Event);
}

void control::on_destroy()
{
	DisconnectAllEvents();
	Clear();
}

void control::on_toggle()
{
	if(!m_data.get())
		return;
		
	// Get the control value ...
	bool newvalue = ToggleButton(detail::control_toggle).GetState();

	// If the value hasn't changed, we're done ...
	if(newvalue == m_data->value())
		return;

	// Record the command for posterity (tutorials) ...
	k3d::application().command_signal().emit(this, k3d::icommand_node::command_t::USER_INTERFACE, detail::control_toggle, "");

	// Turn this into an undo/redo -able event ...
	if(m_StateRecorder)
		m_StateRecorder->start_recording(k3d::create_state_change_set());

	// Update everything with the new value ...
	m_data->set_value(newvalue);

	// Turn this into an undo/redo -able event ...
	if(m_StateRecorder)
		m_StateRecorder->commit_change_set(m_StateRecorder->stop_recording(), newvalue ? m_StateChangeName + " \"On\"" : m_StateChangeName + " \"Off\"");
}

} // namespace check_button

} // namespace k3d


