/*
 *	subtitleeditor -- a tool to create or edit subtitle
 *
 *	https://kitone.github.io/subtitleeditor/
 *	https://github.com/kitone/subtitleeditor/
 *
 *	Copyright @ 2005-2012, kitone
 *
 *	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 3 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, see <http://www.gnu.org/licenses/>.
 */


#include <iostream>
#include <gdk/gdkkeysyms.h>

#include "cfg.h"
#include "subtitlemodel.h"
#include "subtitles.h"
#include "document.h"
#include "subtitleview.h"
#include "utility.h"
#include "subtitleeditorwindow.h"

#include "gui/textviewcell.h"
#include "gui/cellrenderercustom.h"

/*
 *
 */
class TimeCell : public Gtk::CellEditable, public Gtk::TextView
{
public:

	TimeCell()
	:Glib::ObjectBase(typeid(TimeCell)), Gtk::CellEditable()
	{
		se_debug(SE_DEBUG_VIEW);
	}

	/*
	 *
	 */
	Glib::ustring get_text()
	{
		se_debug(SE_DEBUG_VIEW);

		Glib::RefPtr<Gtk::TextBuffer> buffer = get_buffer();

		Gtk::TextBuffer::iterator start, end;

		buffer->get_bounds(start,end);
		return buffer->get_text(start,end);
	}

	/*
	 *
	 */
	void set_text(const Glib::ustring &text)
	{
		se_debug_message(SE_DEBUG_VIEW, "text=<%s>", text.c_str());

		get_buffer()->set_text(text);
	}

	/*
	 * bug #23569 : Cursor cannot be moved with mouse when editing subtitles
	 */
	bool on_button_press_event (GdkEventButton*event)
	{
		se_debug(SE_DEBUG_VIEW);

		Gtk::TextView::on_button_press_event(event);
		return true;
	}

protected:
	/*
	 *
	 */
	bool on_key_press_event(GdkEventKey* event)
	{
		se_debug(SE_DEBUG_VIEW);

		if(event->keyval == GDK_KEY_Escape)
		{
			//m_canceled = true;
			remove_widget();
			return true;
		}

		bool st_enter = (
				 event->keyval == GDK_KEY_Return ||
				 event->keyval == GDK_KEY_KP_Enter ||
				 event->keyval == GDK_KEY_ISO_Enter ||
				 event->keyval == GDK_KEY_3270_Enter );

		if(st_enter)
		{
			editing_done();
			remove_widget();
			return true;
		}

		Gtk::TextView::on_key_press_event(event);
		return true;
	}

	/*
	 *
	 */
	bool on_scroll_event(GdkEventScroll *ev)
	{
		se_debug(SE_DEBUG_VIEW);

		Glib::ustring text = get_text();
		long frame;

		if(SubtitleTime::validate(text)) // TIME
		{
			SubtitleTime time(get_text());

			long step = 100;

			if(ev->state & GDK_SHIFT_MASK && ev->state & GDK_CONTROL_MASK)
				step *= 100;
			else if(ev->state & GDK_CONTROL_MASK)
				step *= 10;

			SubtitleTime val(step);

			if(ev->direction == GDK_SCROLL_UP)
			{
				time = time + val;
				set_text(time.str());
				return true;
			}
			else if(ev->direction == GDK_SCROLL_DOWN)
			{
				time = time - val;
				set_text(time.str());
				return true;
			}
		}
		else if(from_string(text, frame)) // FRAME
		{
			long step = 1;

			if(ev->state & GDK_SHIFT_MASK && ev->state & GDK_CONTROL_MASK)
				step *= 100;
			else if(ev->state & GDK_CONTROL_MASK)
				step *= 10;

			if(ev->direction == GDK_SCROLL_UP)
				frame += step;
			else if(ev->direction == GDK_SCROLL_DOWN)
				frame -= step;

			set_text(to_string(frame));

			return true;
		}
		return false;
	}

};

/*
 * Basic cell renderer template
 *
 * Disable all actions at the beginning of the editing and
 * enable the actions when it's finished.
 *
 * Support also the flash message.
 */
template<class T>
class SubtitleViewCellRendererCustom : public CellRendererCustom<T>
{
public:

	/*
	 *
	 */
	SubtitleViewCellRendererCustom(Document *doc)
	: CellRendererCustom<T>(),
	 m_document(doc)
	{
		se_debug(SE_DEBUG_VIEW);
	}

	/*
	 *
	 */
	virtual Gtk::CellEditable* start_editing_vfunc(
			GdkEvent* event,
			Gtk::Widget &widget,
			const Glib::ustring &path,
			const Gdk::Rectangle& background_area,
			const Gdk::Rectangle& cell_area,
			Gtk::CellRendererState flags)
	{

		Gtk::CellEditable *editable = CellRendererCustom<T>::start_editing_vfunc(event, widget, path, background_area, cell_area, flags);

		// display flash message
		if(m_document)
			on_flash_message();

		return editable;
	}

	/*
	 * If need to display flash message.
	 */
	virtual void on_flash_message()
	{
	}
protected:

	/*
	 * Enable or disable all actions so as not to interfere with editing.
	 * As a simple shorcuts.
	 */
	void set_action_groups_sensitives(bool state)
	{
		if(Config::getInstance().get_value_bool("subtitle-view", "do-not-disable-actions-during-editing"))
			return;

		std::vector< Glib::RefPtr<Gtk::ActionGroup> > actions =
			SubtitleEditorWindow::get_instance()->get_ui_manager()->get_action_groups();

		std::vector< Glib::RefPtr<Gtk::ActionGroup> >::iterator it;
		for(it = actions.begin(); it != actions.end(); ++it)
		{
			(*it)->set_sensitive(state);
		}
	}

	/*
	 * Disable all actions.
	 */
	void begin_editing()
	{
		set_action_groups_sensitives(false);
	}

	/*
	 * Enable all actions.
	 */
	void finish_editing()
	{
		set_action_groups_sensitives(true);
	}

protected:
	Document* m_document;
};

/*
 * Represents a cell time.
 */
class CellRendererTime : public SubtitleViewCellRendererCustom<TimeCell>
{
public:
	CellRendererTime(Document *doc)
	:SubtitleViewCellRendererCustom<TimeCell>(doc)
	{
		property_editable() = true;
		property_yalign() = 0.0;
		property_xalign() = 1.0;
		property_alignment() = Pango::ALIGN_RIGHT;
	}
};

/*
 */
class CellRendererCPS : public Gtk::CellRendererText
{
public:
	CellRendererCPS()
	:Glib::ObjectBase(typeid(CellRendererCPS)), Gtk::CellRendererText()
	{
		property_yalign() = 0;
		//property_weight() = Pango::WEIGHT_ULTRALIGHT;
		property_xalign() = 1.0;
		property_alignment() = Pango::ALIGN_RIGHT;
	}
};

/*
 *
 */
class CellRendererTextMultiline : public SubtitleViewCellRendererCustom<TextViewCell>
{
public:
	CellRendererTextMultiline(Document *doc)
	:SubtitleViewCellRendererCustom<TextViewCell>(doc)
	{
		property_editable() = true;
		property_yalign() = 0.0;

		if(Config::getInstance().get_value_bool("subtitle-view", "property-alignment-center"))
		{
			property_xalign() = 0.5;
			property_alignment() = Pango::ALIGN_CENTER;
		}
	}

	/*
	 * Need to display a flash message for the behavior of line-break and exit.
	 */
	void on_flash_message()
	{
		if(Config::getInstance().get_value_bool("subtitle-view", "used-ctrl-enter-to-confirm-change"))
			m_document->flash_message(_("Use Ctrl+Return for exit and Return for line-break"));
		else
			m_document->flash_message(_("Use Return for exit and Ctrl+Return for line-break"));
	}
};

/*
 * SubtitleView Constructor
 */
SubtitleView::SubtitleView(Document &doc)
{
	m_currentColumn = NULL;

	m_refDocument = &doc;

	m_subtitleModel = m_refDocument->get_subtitle_model();
	m_styleModel = m_refDocument->m_styleModel;

	set_model(m_subtitleModel);

	createColumns();

	set_rules_hint(true);
	set_enable_search(false);
	set_search_column(m_column.num);

	// config
	loadCfg();


	get_selection()->signal_changed().connect(
			sigc::mem_fun(*this, &SubtitleView::on_selection_changed));

	get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);

	Config::getInstance().signal_changed("subtitle-view").connect(
			sigc::mem_fun(*this, &SubtitleView::on_config_subtitle_view_changed));

	// DnD
	set_reorderable(true);

	// We need to update the view if the framerate of the document changed
	m_refDocument->get_signal("framerate-changed").connect(
			sigc::mem_fun(*this, &SubtitleView::update_visible_range));

	// Update the columns size
	m_refDocument->get_signal("edit-timing-mode-changed").connect(
			sigc::mem_fun(*this, &Gtk::TreeView::columns_autosize));


	// Setup my own copy of needed timing variables
	Config &cfg = Config::getInstance();
	min_duration = cfg.get_value_int("timing", "min-display");
	min_gap = cfg.get_value_int("timing", "min-gap-between-subtitles");
	min_cps = cfg.get_value_double("timing", "min-characters-per-second");
	max_cps = cfg.get_value_double("timing", "max-characters-per-second");

	check_timing = cfg.get_value_bool("timing", "do-auto-timing-check");

	// keep trace of timing settings
	cfg.signal_changed("timing").connect(
			sigc::mem_fun(*this, &SubtitleView::on_config_timing_changed));
}

/*
 */
void SubtitleView::on_config_timing_changed(const Glib::ustring &key, const Glib::ustring &value)
{
	if(key == "min-gap-between-subtitles")
		min_gap = utility::string_to_long(value);
	else if(key == "do-auto-timing-check")
		check_timing = utility::string_to_bool(value);
	else if( key == "min-display" )
		min_duration = Config::getInstance().get_value_int("timing", "min-display");
	else if( key == "min-characters-per-second" )
		min_cps = utility::string_to_double(value);
	else if( key == "max-characters-per-second" )
		max_cps = utility::string_to_double(value);

	update_visible_range();
}

/*
 * Update the visible range
 * We need to update after timing change or framerate change
 */
void SubtitleView::update_visible_range()
{
	//tell Gtk it should update all visible rows in the subtitle list
	Gtk::TreePath cur_path, end_path;
	if( get_visible_range( cur_path, end_path) )
	{
		while(cur_path <= end_path)
		{
			m_subtitleModel->row_changed( cur_path, m_subtitleModel->get_iter( cur_path ) );
			cur_path.next();
		}
	}
}

/*
 *
 */
SubtitleView::~SubtitleView()
{
}

/*
 *
 */
void SubtitleView::loadCfg()
{
	se_debug(SE_DEBUG_VIEW);

	bool state = false;

	Config &cfg = Config::getInstance();

	cfg.get_value_bool("subtitle-view", "enable-rubberband-selection", state);

	set_rubber_banding(state);
}

/*
 *
 */
void SubtitleView::set_tooltips(Gtk::TreeViewColumn *column, const Glib::ustring &text)
{
	se_debug_message(SE_DEBUG_VIEW, "[%s]=%s", column->get_title().c_str(), text.c_str());

	Gtk::Widget* widget = column->get_widget();
	if(widget)
		widget->set_tooltip_text(text);
}

/*
 * Return a new column (already manage) with Gtk::Label in title.
 */
Gtk::TreeViewColumn* SubtitleView::create_treeview_column(const Glib::ustring &name)
{
	Glib::ustring title = get_column_label_by_name(name);

	Gtk::TreeViewColumn* column = manage(new Gtk::TreeViewColumn);

	Gtk::Label* label = manage(new Gtk::Label(title)); //, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
	label->show();
	column->set_widget(*label);

	m_columns[name] = column;

	return column;
}

/*
 *
 */
void SubtitleView::createColumns()
{
	createColumnNum();
	createColumnLayer();
	createColumnStart();
	createColumnEnd();
	createColumnDuration();
	createColumnStyle();
	createColumnName();
	createColumnMarginR();
	createColumnMarginL();
	createColumnMarginV();
	createColumnEffect();
	createColumnText();
	createColumnCPS();
	createColumnTranslation();
	createColumnNote();

	update_columns_displayed_from_config();
}

/*
 *	create columns
 */
void SubtitleView::createColumnNum()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererText* renderer = NULL;

	column = create_treeview_column("number");
	renderer = manage(new Gtk::CellRendererText);
	renderer->property_editable() = false;
	renderer->property_yalign() = 0;
	renderer->property_xalign() = 1.0;
	renderer->property_alignment() = Pango::ALIGN_RIGHT;

	column->pack_start(*renderer);
	column->add_attribute(renderer->property_text(), m_column.num);

	append_column(*column);

	set_tooltips(column, _("The line number"));
}

/*
 *
 */
void SubtitleView::createColumnLayer()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererText* renderer = NULL;

	column = create_treeview_column("layer");
	renderer = manage(new Gtk::CellRendererText);

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.layer);

	renderer->property_editable() = true;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_layer));

	append_column(*column);

	//set_tooltips(column, _("Layer number."));
}

/*
 */
void SubtitleView::cps_data_func( const Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter )
{
	CellRendererTime *trenderer = (CellRendererTime*)renderer;
	Subtitle cur_sub( m_refDocument, iter );

	Glib::ustring color("black"); // default

	if(check_timing)
	{
		const int cmp = cur_sub.check_cps_text( min_cps, max_cps );
		if( cmp > 0 )
			color = "red";
		else if( cmp < 0 )
			color = "blue";
	}
	trenderer->property_markup() = Glib::ustring::compose("<span foreground=\"%1\">%2</span>", color, cur_sub.get_characters_per_second_text_string());
}


/*
 */
void SubtitleView::duration_data_func( const Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter )
{
	CellRendererTime *trenderer = (CellRendererTime*)renderer;
	Subtitle cur_sub( m_refDocument, iter );

	Glib::ustring color;

	// Display text in red if the check timing option is enabled and
	// if the current subtitle don't respect the minimum duration
	if(check_timing)
	{
		if(cur_sub.get_duration().totalmsecs < min_duration )		//duration in msec
			color = "red";
	}

	trenderer->property_markup() = cur_sub.convert_value_to_time_string( (*iter)[m_column.duration_value], color );
}

/*
 */
void SubtitleView::start_time_data_func( const Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter )
{
	CellRendererTime *trenderer = (CellRendererTime*)renderer;
	Subtitle cur_sub( m_refDocument, iter );

	Glib::ustring color;

	// Display text in red if the check timing option is enabled and
	// if the current subtitle don't respect gap before subtitle
	if(check_timing)
	{
		if(cur_sub.check_gap_before( min_gap ) == false)
			color = "red";
	}

	trenderer->property_markup() = cur_sub.convert_value_to_time_string( (*iter)[m_column.start_value], color );
}

/*
 */
void SubtitleView::end_time_data_func( const Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter )
{
	CellRendererTime *trenderer = (CellRendererTime*)renderer;
	Subtitle cur_sub( m_refDocument, iter );

	Glib::ustring color;

	// Display text in red if the check timing option is enabled and
	// if the current subtitle don't respect gap before subtitle
	if(check_timing)
	{
		if(cur_sub.check_gap_after( min_gap ) == false)
			color = "red";
	}

	trenderer->property_markup() = cur_sub.convert_value_to_time_string( (*iter)[m_column.end_value], color );

}

/*
 */
void SubtitleView::create_column_time(
		const Glib::ustring &name,
		const Gtk::TreeModelColumnBase& /*column_attribute*/,
		const sigc::slot<void, const Glib::ustring&, const Glib::ustring&> &slot_edited,
		const sigc::slot<void, const Gtk::CellRenderer*, const Gtk::TreeModel::iterator&> &slot_cell_data_func,
		const Glib::ustring &tooltips)
{
	se_debug_message(SE_DEBUG_VIEW, "name=%s tooltips=%s",
			name.c_str(), tooltips.c_str());


	CellRendererTime* renderer = manage(new CellRendererTime(m_refDocument));

	Gtk::TreeViewColumn* column = create_treeview_column(name);
	column->pack_start(*renderer);

	column->set_cell_data_func( *renderer, slot_cell_data_func);

	renderer->signal_edited().connect(slot_edited);

	append_column(*column);

	set_tooltips(column, tooltips);
}

/*
 *
 */
void SubtitleView::createColumnStart()
{
	create_column_time(
			"start",
			m_column.start_value,
			sigc::mem_fun(*this, &SubtitleView::on_edited_start),
			sigc::mem_fun(*this, &SubtitleView::start_time_data_func),
			_("When a subtitle appears on the screen."));
}

/*
 *
 */
void SubtitleView::createColumnEnd()
{
	create_column_time(
			"end",
			m_column.end_value,
			sigc::mem_fun(*this, &SubtitleView::on_edited_end),
			sigc::mem_fun(*this, &SubtitleView::end_time_data_func),
			_("When a subtitle disappears from the screen."));

}

/*
 *
 */
void SubtitleView::createColumnDuration()
{
	create_column_time(
			"duration",
			m_column.duration_value,
			sigc::mem_fun(*this, &SubtitleView::on_edited_duration),
			sigc::mem_fun(*this, &SubtitleView::duration_data_func),
			 _("The duration of the subtitle."));
}

/*
 *
 */
void SubtitleView::createColumnStyle()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererCombo* renderer = NULL;

	column = create_treeview_column("style");
	renderer = manage(new Gtk::CellRendererCombo);

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.style);

	renderer->property_model() =	m_styleModel;
	renderer->property_text_column() = 0;
	renderer->property_editable() = true;
	renderer->property_has_entry() = false;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_style));

	append_column(*column);
}

/*
 *
 */
void SubtitleView::createColumnName()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = create_treeview_column("name");

	SubtitleViewCellRendererCustom<TextViewCell>* renderer = manage(new SubtitleViewCellRendererCustom<TextViewCell>(m_refDocument));

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.name);

	renderer->property_editable() = true;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_name));

	append_column(*column);
}

/*
 *
 */
void SubtitleView::createColumnCPS()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = create_treeview_column("cps");

	CellRendererCPS* renderer = manage(new CellRendererCPS);

	column->pack_start(*renderer);

	column->set_cell_data_func( *renderer, sigc::mem_fun( *this, &SubtitleView::cps_data_func ) );

	append_column(*column);

	set_tooltips(column, _("The number of characters per second"));
}

/*
 *
 */
void SubtitleView::createColumnText()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = create_treeview_column("text");

	append_column(*column);

	// text
	{
		CellRendererTextMultiline* renderer = manage(new CellRendererTextMultiline(m_refDocument));

		column->pack_start(*renderer, true);
		column->add_attribute(renderer->property_text(), m_column.text);
		column->property_expand() = true;

		renderer->property_ellipsize() = Pango::ELLIPSIZE_END;
		renderer->signal_edited().connect(
				sigc::mem_fun(*this, &SubtitleView::on_edited_text));
	}
	// cpl
	{
		Gtk::CellRendererText* renderer = NULL;
		renderer = manage(new Gtk::CellRendererText);

		column->pack_start(*renderer, false);
		column->add_attribute(renderer->property_text(), m_column.characters_per_line_text);
		renderer->property_yalign() = 0;
		renderer->property_weight() = Pango::WEIGHT_ULTRALIGHT;
		renderer->property_xalign() = 1.0;
		renderer->property_alignment() = Pango::ALIGN_RIGHT;

		bool show=true;

		Config::getInstance().get_value_bool("subtitle-view", "show-character-per-line", show);

		renderer->property_visible() = show;
	}

	column->set_resizable(true);
}

/*
 *
 */
void SubtitleView::createColumnTranslation()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = create_treeview_column("translation");

	//translation
	{
		CellRendererTextMultiline* renderer = manage(new CellRendererTextMultiline(m_refDocument));

		column->pack_start(*renderer, true);
		column->add_attribute(renderer->property_text(), m_column.translation);
		column->property_expand() = true;

		renderer->property_ellipsize() = Pango::ELLIPSIZE_END;
		append_column(*column);

		renderer->signal_edited().connect(
			sigc::mem_fun(*this, &SubtitleView::on_edited_translation));
	}
	// cpl
	{
		Gtk::CellRendererText* renderer = NULL;
		renderer = manage(new Gtk::CellRendererText);

		column->pack_end(*renderer, false);
		column->add_attribute(renderer->property_text(), m_column.characters_per_line_translation);
		renderer->property_yalign() = 0;
		renderer->property_weight() = Pango::WEIGHT_ULTRALIGHT;
		bool show=true;
		Config::getInstance().get_value_bool("subtitle-view", "show-character-per-line", show);

		renderer->property_visible() = show;
	}

	column->set_resizable(true);
}

/*
 *
 */
void SubtitleView::createColumnNote()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = create_treeview_column("note");;

	CellRendererTextMultiline* renderer = manage(new CellRendererTextMultiline(m_refDocument));

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.note);

	append_column(*column);

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_note));

	column->set_resizable(true);
}

/*
 *
 */
void SubtitleView::createColumnEffect()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererText* renderer = NULL;

	column = create_treeview_column("effect");
	renderer = manage(new Gtk::CellRendererText);

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.effect);

	append_column(*column);

	renderer->property_editable() = true;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_effect));

	column->set_resizable(true);
}

/*
 *
 */
void SubtitleView::createColumnMarginR()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererText* renderer = NULL;

	column = create_treeview_column("margin-r");
	renderer = manage(new Gtk::CellRendererText);

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.marginR);

	renderer->property_editable() = true;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_margin_r));

	append_column(*column);
}

/*
 *
 */
void SubtitleView::createColumnMarginL()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererText* renderer = NULL;

	column = create_treeview_column("margin-l");
	renderer = manage(new Gtk::CellRendererText);

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.marginL);

	renderer->property_editable() = true;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_margin_l));

	append_column(*column);
}

/*
 *
 */
void SubtitleView::createColumnMarginV()
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn* column = NULL;
	Gtk::CellRendererText* renderer = NULL;

	column = create_treeview_column("margin-v");
	renderer = manage(new Gtk::CellRendererText);

	column->pack_start(*renderer, false);
	column->add_attribute(renderer->property_text(), m_column.marginV);

	renderer->property_editable() = true;
	renderer->property_yalign() = 0;

	renderer->signal_edited().connect(
		sigc::mem_fun(*this, &SubtitleView::on_edited_margin_v));

	append_column(*column);
}

/*
 *	retourne l'item select ou NULL
 */
Gtk::TreeIter SubtitleView::getSelected()
{
	se_debug(SE_DEBUG_VIEW);

	Glib::RefPtr<Gtk::TreeSelection> selection = get_selection();

	std::vector<Gtk::TreeModel::Path> rows = selection->get_selected_rows();

	if(rows.size() > 0)
	{
		return selection->get_model()->get_iter(rows[0]);
	}

	Gtk::TreeIter null;
	return null;
}

/*
 *
 */
void SubtitleView::on_selection_changed()
{
	se_debug(SE_DEBUG_VIEW);

	m_refDocument->emit_signal("subtitle-selection-changed");
}

/*
 *
 */
void SubtitleView::on_edited_layer( const Glib::ustring &path, const Glib::ustring &value)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		unsigned int val = 0;
		if(from_string(value, val))
		{
			m_refDocument->start_command(_("Editing layer"));
			subtitle.set_layer(value);
			m_refDocument->finish_command();
		}
	}
}


/*
 *	callback utiliser pour modifier le temps directement depuis la list (treeview)
 */
void SubtitleView::on_edited_start( const Glib::ustring &path,
																		const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(!subtitle)
		return;

	if(subtitle.get("start") == newtext)
		return;

	if(m_refDocument->get_edit_timing_mode() == TIME)
	{
		if(!SubtitleTime::validate(newtext))
			return;

		m_refDocument->start_command(_("Editing start"));
		subtitle.set_start(newtext);
		m_refDocument->emit_signal("subtitle-time-changed");
		m_refDocument->finish_command();
	}
	else // edit_mode == FRAME
	{
		long frame = 0;
		if(!from_string(newtext, frame))
			return;

		m_refDocument->start_command(_("Editing start"));
		subtitle.set_start_frame(frame);
		m_refDocument->emit_signal("subtitle-time-changed");
		m_refDocument->finish_command();
	}
}

/*
 *	callback utiliser pour modifier le temps directement depuis la list (treeview)
 */
void SubtitleView::on_edited_end( const Glib::ustring &path,
																	const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(!subtitle)
		return;

	if(subtitle.get("end") == newtext)
		return;

	if(m_refDocument->get_edit_timing_mode() == TIME)
	{
		if(!SubtitleTime::validate(newtext))
			return;

		m_refDocument->start_command(_("Editing end"));
		subtitle.set_end(newtext);
		m_refDocument->emit_signal("subtitle-time-changed");
		m_refDocument->finish_command();
	}
	else // edit_mode == FRAME
	{
		long frame = 0;
		if(!from_string(newtext, frame))
			return;

		m_refDocument->start_command(_("Editing end"));
		subtitle.set_end_frame(frame);
		m_refDocument->emit_signal("subtitle-time-changed");
		m_refDocument->finish_command();
	}
}

/*
 *	callback utiliser pour modifier le temps directement depuis la list (treeview)
 */
void SubtitleView::on_edited_duration( const Glib::ustring &path,
																	const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(!subtitle)
		return;

	if(subtitle.get("duration") == newtext)
		return;

	if(m_refDocument->get_edit_timing_mode() == TIME)
	{
		if(!SubtitleTime::validate(newtext))
			return;

		m_refDocument->start_command(_("Editing duration"));
		subtitle.set_duration(newtext);
		m_refDocument->emit_signal("subtitle-time-changed");
		m_refDocument->finish_command();
	}
	else // edit_mode == FRAME
	{
		long frame = 0;
		if(!from_string(newtext, frame))
			return;

		m_refDocument->start_command(_("Editing duration"));
		subtitle.set_duration_frame(frame);
		m_refDocument->emit_signal("subtitle-time-changed");
		m_refDocument->finish_command();
	}
}

/*
 *	callback utiliser pour modifier le texte
 */
void SubtitleView::on_edited_text( const Glib::ustring &path,
																		const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		if(subtitle.get("text") != newtext)
		{
			m_refDocument->start_command(_("Editing text"));

			subtitle.set_text(newtext);

			m_refDocument->finish_command();
		}
	}
}

/*
 *	callback utiliser pour modifier le texte
 */
void SubtitleView::on_edited_translation( const Glib::ustring &path,
																		const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		if(subtitle.get("translation") != newtext)
		{
			m_refDocument->start_command(_("Editing translation"));
			subtitle.set_translation(newtext);
			m_refDocument->finish_command();
		}
	}
}

/*
 *	callback utiliser pour modifier le texte
 */
void SubtitleView::on_edited_note( const Glib::ustring &path,
																		const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		if(subtitle.get("note") != newtext)
		{
			m_refDocument->start_command(_("Editing note"));
			subtitle.set_note(newtext);
			m_refDocument->finish_command();
		}
	}
}

/*
 */
void SubtitleView::on_edited_effect( const Glib::ustring &path,
																		const Glib::ustring &newtext)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		if(subtitle.get("effect") != newtext)
		{
			m_refDocument->start_command(_("Editing effect"));
			subtitle.set_effect(newtext);
			m_refDocument->finish_command();
		}
	}
}


/*
 *	callback utiliser pour modifier le style a partir d'un menu
 */
void SubtitleView::on_edited_style( const Glib::ustring &path,
																		const Glib::ustring &newstyle)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newstyle.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		if(subtitle.get("style") != newstyle)
		{
			m_refDocument->start_command(_("Editing style"));
			subtitle.set_style(newstyle);
			m_refDocument->finish_command();
		}
	}
}

/*
 *	callback utiliser pour modifier le nom
 */
void SubtitleView::on_edited_name( const Glib::ustring &path,
																		const Glib::ustring &newname)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newname.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		if(subtitle.get("name") != newname)
		{
			m_refDocument->start_command(_("Editing name"));
			subtitle.set_name(newname);
			m_refDocument->finish_command();
		}
	}
}

/*
 *
 */
void SubtitleView::on_edited_margin_l( const Glib::ustring &path, const Glib::ustring &value)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		unsigned int val = 0;
		if(from_string(value, val))
		{
			m_refDocument->start_command(_("Editing margin-l"));
			subtitle.set_margin_l(value);
			m_refDocument->finish_command();
		}
	}
}

/*
 *
 */
void SubtitleView::on_edited_margin_r( const Glib::ustring &path, const Glib::ustring &value)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		unsigned int val = 0;
		if(from_string(value, val))
		{
			m_refDocument->start_command(_("Editing margin-r"));
			subtitle.set_margin_r(value);
			m_refDocument->finish_command();
		}
	}
}

/*
 *
 */
void SubtitleView::on_edited_margin_v( const Glib::ustring &path, const Glib::ustring &value)
{
	se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str());

	Subtitle subtitle(m_refDocument, path);
	if(subtitle)
	{
		unsigned int val = 0;
		if(from_string(value, val))
		{
			m_refDocument->start_command(_("Editing margin-v"));
			subtitle.set_margin_v(value);
			m_refDocument->finish_command();
		}
	}
}

/*
 *
 */
void SubtitleView::select_and_set_cursor(const Gtk::TreeIter &iter, bool start_editing)
{
	se_debug(SE_DEBUG_VIEW);

	Gtk::TreeViewColumn *column = NULL;
	Gtk::TreeModel::Path path;
	get_cursor(path, column);

	if(column == NULL)
		column = m_columns["text"];

	get_selection()->select(iter);

	const Gtk::TreeModel::Path path_from_iter = m_subtitleModel->get_path(iter);
	// Select the row and the column (cell)
	set_cursor(path_from_iter, *column, start_editing);
	// Scroll to center the current selection in the view
	scroll_to_row(path_from_iter, 0.5);
}




/*
 *
 */
bool SubtitleView::on_key_press_event(GdkEventKey *event)
{
	if(event->string != NULL)
	{
		int num;
		std::istringstream ss(event->string);
		bool is_num = static_cast<bool>(ss >> num) != 0;
		// Update only if it's different
		if(is_num != get_enable_search())
			set_enable_search(is_num);
	}
	return Gtk::TreeView::on_key_press_event(event);
}

/*
 *
 */
bool SubtitleView::on_button_press_event(GdkEventButton *ev)
{
	// FIXME: remove this functions
	return Gtk::TreeView::on_button_press_event(ev);
}

/*
 *
 */
void SubtitleView::on_config_subtitle_view_changed(const Glib::ustring &key, const Glib::ustring &value)
{
	if(key == "columns-displayed")
	{
		update_columns_displayed_from_config();
	}
	else if(key == "property-alignment-center")
	{
		bool state;
		if(from_string(value, state))
		{
			Gtk::CellRendererText *renderer = NULL;

			renderer = dynamic_cast<Gtk::CellRendererText*>(m_columns["text"]->get_first_cell());
			renderer->property_xalign() = state ? 0.5 : 0.0;
			renderer->property_alignment() = state ? Pango::ALIGN_CENTER : Pango::ALIGN_LEFT;

			renderer = dynamic_cast<Gtk::CellRendererText*>(m_columns["translation"]->get_first_cell());
			renderer->property_xalign() = state ? 0.5 : 0.0;
			renderer->property_alignment() = state ? Pango::ALIGN_CENTER : Pango::ALIGN_LEFT;
		}

		queue_draw();
	}
	else if(key == "show-character-per-line")
	{
		bool state;
		if(from_string(value, state))
		{
			std::vector<Gtk::CellRenderer*> cells;

			cells = m_columns["text"]->get_cells();
			cells[1]->property_visible() = state;

			cells = m_columns["translation"]->get_cells();
			cells[1]->property_visible() = state;
		}
		queue_draw();
	}
	else if(key == "enable-rubberband-selection")
	{
		set_rubber_banding(utility::string_to_bool(value));
	}
}

/*
 *
 */
void SubtitleView::on_set_style_to_selection(const Glib::ustring &name)
{
	std::vector<Subtitle> selection = m_refDocument->subtitles().get_selection();

	if(selection.empty())
		return;

	m_refDocument->start_command(_("Set style to selection"));
	for(unsigned int i=0; i<selection.size(); ++i)
	{
		selection[i].set("style", name);
	}
	m_refDocument->finish_command();
}

/*
 *	COLUMN
 */

/*
 *	retourne la colonne par rapport a son nom (interne)
 */
Gtk::TreeViewColumn* SubtitleView::get_column_by_name(const Glib::ustring &name)
{
	std::map<Glib::ustring, Gtk::TreeViewColumn*>::iterator it = m_columns.find(name);

	if(it != m_columns.end())
		return it->second;

	se_debug_message(SE_DEBUG_VIEW, "column: %s return NULL", name.c_str());

	return NULL;
}

/*
 *	retourne le nom utiliser en interne de la column
 */
Glib::ustring SubtitleView::get_name_of_column(Gtk::TreeViewColumn *column)
{
	std::map<Glib::ustring, Gtk::TreeViewColumn*>::iterator it;
	for(it = m_columns.begin(); it != m_columns.end(); ++it)
	{
		if(it->second == column)
			return it->first;
	}

	return Glib::ustring();
}

/*
 *
 */
void SubtitleView::set_column_visible(const Glib::ustring& name, bool state)
{
	se_debug_message(SE_DEBUG_VIEW, "%s=%s", name.c_str(), state ? "true" : "false");

	Gtk::TreeViewColumn *column = get_column_by_name(name);

	g_return_if_fail(column);

	column->set_visible(state);
}

/*
 *
 */
bool SubtitleView::get_column_visible(const Glib::ustring &name)
{
	Gtk::TreeViewColumn *column = get_column_by_name(name);

	g_return_val_if_fail(column, false);

	se_debug_message(SE_DEBUG_VIEW, "<%s> = %s", name.c_str(), column->get_visible() ? "true" : "false");

	return column->get_visible();
}

/*
 *	Get the columns displayed from the configuration and updates.
 */
void SubtitleView::update_columns_displayed_from_config()
{
	se_debug(SE_DEBUG_VIEW);

	Glib::ustring columns;

	if(!Config::getInstance().get_value_string("subtitle-view", "columns-displayed", columns))
	{
		g_warning("update_columns_displayed_from_config FAILED");
		return;
	}

	// get columns order
	std::vector<std::string> cols;

	utility::split(columns, ';', cols);

	// hide all columns
	std::map<Glib::ustring, Gtk::TreeViewColumn*>::iterator it;
	for( it = m_columns.begin(); it != m_columns.end(); ++it)
	{
		it->second->set_visible(false);
	}

	// reorder columns
	Gtk::TreeViewColumn *current_column = NULL;

	for(unsigned int i=0; i< cols.size(); ++i)
	{
		Glib::ustring name = cols[i];

		if(current_column)
		{
			Gtk::TreeViewColumn *tmp = get_column_by_name(name);
			if(tmp)
				move_column_after(*tmp, *current_column);
			current_column = tmp;
		}
		else	// it's the first, put at start
		{
			current_column = get_column_by_name(name);
			if(current_column)
				move_column_to_start(*current_column);
		}

		// display column
		if(current_column)
			current_column->set_visible(true);
	}

}

/*
 * This is a static function.
 * Return the humain label by the internal name of the column.
 */
Glib::ustring SubtitleView::get_column_label_by_name(const Glib::ustring &name)
{
	std::map<Glib::ustring, Glib::ustring> columns_labels;

	columns_labels["cps"] = _("CPS");
	columns_labels["duration"] = _("Duration");
	columns_labels["effect"] = _("Effect");
	columns_labels["end"] = _("End");
	columns_labels["layer"] = _("Layer");
	columns_labels["margin-l"] = _("L");
	columns_labels["margin-r"] = _("R");
	columns_labels["margin-v"] = _("V");
	columns_labels["name"] = _("Name");
	columns_labels["note"] = _("Note");
	columns_labels["number"] = _("Num");
	columns_labels["start"] = _("Start");
	columns_labels["style"] = _("Style");
	columns_labels["text"] = _("Text");
	columns_labels["translation"] = _("Translation");

	std::map<Glib::ustring, Glib::ustring>::iterator it = columns_labels.find(name);
	if(it != columns_labels.end())
		return it->second;

	return Glib::ustring("Invalid : ") + name;
}

/*
 * The position of the cursor (focused cell) has changed.
 * Update the column title (bold).
 */
void SubtitleView::on_cursor_changed()
{
	se_debug(SE_DEBUG_VIEW);

	Pango::AttrList normal;
	Pango::AttrInt att_normal = Pango::Attribute::create_attr_weight(Pango::WEIGHT_NORMAL);
	normal.insert(att_normal);

	Pango::AttrList active;
	Pango::AttrInt att_active = Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD);
	active.insert(att_active);

	// get the focused column
	Gtk::TreeViewColumn *focused_column = NULL;
	Gtk::TreeModel::Path path;
	get_cursor(path, focused_column);

	// if it's the same, doesn't needs update
	if(m_currentColumn == focused_column)
		return;

	// unbold the old column
	if(m_currentColumn != NULL)
	{
		Gtk::Label *label = dynamic_cast<Gtk::Label*>(m_currentColumn->get_widget());
		label->set_attributes(normal);

		m_currentColumn = NULL;
	}
	// bold the new current column
	if(focused_column)
	{
		Gtk::Label *label = dynamic_cast<Gtk::Label*>(focused_column->get_widget());
		label->set_attributes(active);

		m_currentColumn = focused_column;
	}
}

/*
 * Return the name of the current column focus.
 * (start, end, duration, text, translation ...)
 */
Glib::ustring SubtitleView::get_current_column_name()
{
	if(m_currentColumn)
		return get_name_of_column(m_currentColumn);
	return Glib::ustring();
}
