/***********************************************************************************

    Copyright (C) 2007-2020 Ahmet Öztürk (aoz_2@yahoo.com)

    This file is part of Lifeograph.

    Lifeograph 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.

    Lifeograph 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 Lifeograph.  If not, see <http://www.gnu.org/licenses/>.

***********************************************************************************/


#include <cairomm/context.h>

#include "../lifeograph.hpp"
#include "../strings.hpp"
#include "../diarydata.hpp"
#include "widget_table.hpp"


using namespace LIFEO;

// WIDGETTABLE =====================================================================================
WidgetTable::WidgetTable()
{
    set_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
                Gdk::POINTER_MOTION_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::SCROLL_MASK );

    m_builder = Gtk::Builder::create();
    Lifeograph::load_gui( m_builder, Lifeograph::SHAREDIR + "/ui/table.ui" );

    m_builder->get_widget( "Po_table", m_Po_table );
    m_builder->get_widget( "Bx_edit", m_Bx_edit );
    m_builder->get_widget_derived( "E_main_filter", m_WP_main_filter );
    m_builder->get_widget_derived( "E_tag_filter", m_WEP_tag_filter );
    m_builder->get_widget( "B_add_column", m_B_col_add );
    m_builder->get_widget( "RB_entry_based", m_RB_entry_based );
    m_builder->get_widget( "RB_para_based", m_RB_para_based );
    m_builder->get_widget( "MoB_copy_delimited_text", m_MoB_copy_delimited_text );

    m_builder->get_widget( "Po_column", m_Po_column );
    m_builder->get_widget_derived( "E_col_name", m_E_col_name );
    m_builder->get_widget( "CB_col_type", m_CB_col_type );
    m_builder->get_widget_derived( "E_tag_picker", m_WEP_tag_picker );
    m_builder->get_widget( "Bx_value_type", m_Bx_value_type );
    m_builder->get_widget( "CB_value_type", m_CB_value_type );
    m_builder->get_widget( "Bx_sub_tag_type", m_Bx_sub_tag_type );
    m_builder->get_widget( "CB_sub_tag_type", m_CB_sub_tag_type );
    m_builder->get_widget( "MoB_conditional", m_MoB_conditional );
    m_builder->get_widget( "Bx_col_conditions", m_Bx_conditions );
    m_builder->get_widget( "Bx_col_cond_num", m_Bx_cond_num );
    m_builder->get_widget( "Bx_col_cond_sub", m_Bx_cond_sub );
    m_builder->get_widget( "E_cond_num_v_lo", m_E_cond_num_v_lo );
    m_builder->get_widget( "E_cond_num_v_hi", m_E_cond_num_v_hi );
    m_builder->get_widget( "CB_cond_rel_lo", m_CB_cond_rel_lo );
    m_builder->get_widget( "CB_cond_rel_hi", m_CB_cond_rel_hi );
    m_builder->get_widget_derived( "E_col_filter", m_WP_col_filter );
    m_builder->get_widget( "B_col_dismiss", m_B_col_dismiss );
    m_builder->get_widget( "B_col_move_prev", m_B_col_move_prev );
    m_builder->get_widget( "B_col_move_next", m_B_col_move_next );

    m_Bx_main = Gtk::manage( new Gtk::Box( Gtk::ORIENTATION_HORIZONTAL ) );
    m_Aj_vert = Gtk::Adjustment::create( 0.0, 0.0, 100.0 );
    m_Sb_vert = Gtk::manage( new Gtk::Scrollbar( m_Aj_vert, Gtk::ORIENTATION_VERTICAL ) );

    m_Sb_vert->set_halign( Gtk::ALIGN_END );
    m_Sb_vert->set_valign( Gtk::ALIGN_FILL );
    m_Sb_vert->set_round_digits( 0 );

    m_Bx_main->pack_start( *this, Gtk::PACK_EXPAND_WIDGET );
    m_Bx_main->pack_end( *m_Sb_vert, false, true );
    m_Bx_main->show_all();

    m_WP_main_filter->set_clearable( true );
    m_WP_main_filter->set_select_only( true );
    m_WEP_tag_picker->set_offer_new( false );

    m_WP_col_filter->set_clearable( true );
    m_WP_col_filter->set_select_only( true );

    m_Po_table->set_relative_to( *this );
    m_Po_column->set_relative_to( *this );

    m_Aj_vert->signal_value_changed().connect(
            [ this ](){ m_i_line_top = m_Aj_vert->get_value(); refresh(); } );

    m_RB_entry_based->signal_toggled().connect( [ this ](){ handle_base_changed(); } );

    m_WP_main_filter->signal_sel_changed().connect(
            [ this ]( const Ustring& filter_name ){ set_filter( filter_name ); } );

    m_WEP_tag_filter->signal_updated().connect(
                [ this ]( Entry* e ){ handle_tag_filter_changed( e ); } );

    m_MoB_copy_delimited_text->signal_clicked().connect( [ this ](){ copy_delimited_text(); } );

    m_CB_col_type->signal_changed().connect( [ this ](){ handle_col_type_changed(); } );

    m_E_col_name->signal_changed().connect( [ this ](){ handle_col_renamed(); } );

    m_WEP_tag_picker->signal_updated().connect(
            [ this ]( Entry* e ){ handle_col_tag_changed( e ); } );

    m_CB_value_type->signal_changed().connect( [ this ](){ handle_col_value_type_changed(); } );
    m_CB_sub_tag_type->signal_changed().connect( [ this ](){ handle_col_value_type_changed(); } );

    m_E_cond_num_v_lo->signal_changed().connect( [ this ](){ handle_col_cond_num_changed(); } );
    m_E_cond_num_v_hi->signal_changed().connect( [ this ](){ handle_col_cond_num_changed(); } );

    m_CB_cond_rel_lo->signal_changed().connect( [ this ](){ handle_col_cond_num_changed(); } );
    m_CB_cond_rel_hi->signal_changed().connect( [ this ](){ handle_col_cond_num_changed(); } );

    m_WP_col_filter->signal_sel_changed().connect(
            [ this ]( const Ustring& filter_name )
            {
                m_p2col_sel->set_condition_sub_filter( m_p2diary->get_filter( filter_name ) );
                calculate_and_plot();
            } );

    m_B_col_add->signal_clicked().connect(
            [ this ]()
            {
                m_data.add_column();
                update_col_geom();
                calculate_and_plot();
            } );

    m_B_col_dismiss->signal_clicked().connect(
            [ this ]()
            {
                m_Po_column->hide();
                m_data.dismiss_column( m_i_col_cur );
                update_col_geom();
                calculate_and_plot();
            } );

    m_B_col_move_prev->signal_clicked().connect(
            [ this ]()
            {
                m_Po_column->hide();
                m_data.move_column( m_i_col_cur, m_i_col_cur-1 );
                update_col_geom();
                calculate_and_plot();
            } );

    m_B_col_move_next->signal_clicked().connect(
            [ this ]()
            {
                m_Po_column->hide();
                m_data.move_column( m_i_col_cur, m_i_col_cur+1 );
                update_col_geom();
                calculate_and_plot();
            } );

    m_A_conditional = Lifeograph::p->add_action_bool( "table_col_use_conditional",
            [ this ](){ handle_col_conditional_toggled(); } , false );

    m_A_group_by_first_col = Lifeograph::p->add_action_bool( "table_group_by_first_col",
            [ this ](){ handle_grouping_toggled(); } , false );
}

bool
WidgetTable::on_scroll_event( GdkEventScroll* ev )
{
    if( m_line_c_vis > m_line_c_total )
        return false;
    else
    if( ev->direction == GDK_SCROLL_UP )
        scroll( ( ev->state & Gdk::CONTROL_MASK ) ? -10 : -1 );
    else
    if( ev->direction == GDK_SCROLL_DOWN )
        scroll( ( ev->state & Gdk::CONTROL_MASK ) ? 10 : 1 );

    m_Aj_vert->set_value( m_i_line_top );

    return true;
}

bool
WidgetTable::on_button_press_event( GdkEventButton* event )
{
    if( event->button == 1 )
    {
        if( m_i_line_cur == 0 && event->type == GDK_DOUBLE_BUTTON_PRESS )
        {
            if( m_data.m_i_col_sort == m_i_col_cur )
                m_data.m_f_sort_desc = !m_data.m_f_sort_desc;
            else
            {
                m_data.m_f_sort_desc = false;
                m_data.m_i_col_sort = m_i_col_cur;
            }
            calculate_and_plot( false );
        }
        if( m_i_line_cur > 0 )
        {
            auto&& it_line{ m_data.m_lines_sorted->begin() };
            std::advance( it_line, m_i_line_cur + m_i_line_top - 1 );
            switch( ( *it_line )->m_entries.size() )
            {
                case 0: break;
                case 1: m_Sg_entry_clicked.emit( ( *it_line )->m_entries[ 0 ] ); break;
                default:
                    show_entry_selection_list(
                            ( *it_line )->m_entries,
                            [ this ]( LIFEO::Entry* e ){ m_Sg_entry_clicked.emit( e ); },
                            *this );
                    break;
            }
        }
        m_flag_button_pressed = true;
    }
    else if( event->button == 3 )
    {
        if( m_flag_editable && m_i_col_cur >= 0 && m_i_line_cur == 0 )
            show_column_Po();
        else
            show_table_Po( event->x, event->y );
    }

    return true;
}

bool
WidgetTable::on_button_release_event( GdkEventButton* event )
{
    if( event->button == 1 )
        m_flag_button_pressed = false;

    return true;
}

bool
WidgetTable::on_motion_notify_event( GdkEventMotion* ev )
{
//    if( m_flag_button_pressed )
//    {
//        int col_start = ( event->x / m_width ) * m_span - m_step_count / 2;
//        if( col_start > int( m_span - m_step_count ) )
//            col_start = m_span - m_step_count;
//        else
//        if( col_start < 0 )
//            col_start = 0;
//
//        if( col_start != ( int ) m_step_start )
//        {
//            m_step_start = col_start;
//            refresh();
//        }
//    }
    int i_col_hover{ int( ( ev->x - S_MARGIN ) / m_col_w ) };

    if( i_col_hover < 0 || i_col_hover >= int( m_data.m_columns.size() ) )
        i_col_hover = -1;

    int i_line_hover{ int( ( ev->y - S_MARGIN ) / m_line_h ) };

    if( i_line_hover < 0 || i_line_hover > int( m_line_c_total ) )
        i_line_hover = -1;

    if( i_col_hover != m_i_col_cur || i_line_hover != m_i_line_cur )
    {
        m_i_col_cur = i_col_hover;
        m_i_line_cur = i_line_hover;
        refresh();
    }

    m_flag_widget_hovered = true;

    return Gtk::DrawingArea::on_motion_notify_event( ev );
}

bool
WidgetTable::on_leave_notify_event( GdkEventCrossing* event )
{
    m_flag_widget_hovered = false;
    //refresh(); // should not be needed

    return true;
}

void
WidgetTable::on_size_allocate( Gtk::Allocation& allocation )
{
    Gtk::Widget::on_size_allocate( allocation );
    if( allocation.get_width() != m_width || allocation.get_height() != m_height )
    {
        resize( allocation.get_width(), allocation.get_height() );
        update_scrollbar();
    }
}

bool
WidgetTable::on_draw( const Cairo::RefPtr< Cairo::Context >& cr )
{
    return( Table::draw( cr ) );
}

void
WidgetTable::set_diary( Diary* diary )
{
    Table::set_diary( diary );
    m_WEP_tag_picker->set_diary( diary );
    m_WP_main_filter->set_map( diary->get_p2filters(), &m_data.m_filter );
    m_WEP_tag_filter->set_diary( diary );
    refresh_editability(); // per the editability of the new diary
}

void
WidgetTable::set_filter( const Ustring& filter_name )
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_data.m_filter = m_p2diary->get_filter( filter_name );

    calculate_and_plot();
}

void
WidgetTable::handle_base_changed()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_data.m_flag_para_based = m_RB_para_based->get_active();

    m_WEP_tag_filter->set_visible( m_data.m_flag_para_based );

    calculate_and_plot();
}

void
WidgetTable::handle_tag_filter_changed( Entry* tag )
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_data.m_tag_filter = tag;

    calculate_and_plot();
}

void
WidgetTable::handle_grouping_toggled()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    bool state;
    m_A_group_by_first_col->get_state( state );

    m_data.enable_grouping( !state );

    // The action's state does not change automatically:
    m_A_group_by_first_col->change_state( !state );

    calculate_and_plot();
}

void
WidgetTable::show_table_Po( int x, int y )
{
    Gdk::Rectangle rect{ x-1, y-1, 2, 2 };

    m_MoB_copy_delimited_text->set_sensitive( m_data.m_columns.empty() == false );
    m_Po_table->set_pointing_to( rect );
    m_Po_table->show();
}

void
WidgetTable::show_column_Po()
{
    Gdk::Rectangle rect{ int( S_MARGIN + m_col_w * m_i_col_cur ), int( S_MARGIN ),
                         int( m_col_w ), int( m_line_h ) };

    Lifeograph::START_INTERNAL_OPERATIONS();

    auto&& iter_col{ m_data.m_columns.begin() };
    std::advance( iter_col, m_i_col_cur );
    m_p2col_sel = *iter_col;

    m_E_col_name->set_text( m_p2col_sel->get_name() );

    m_WEP_tag_picker->set_entry( nullptr );
    m_A_conditional->change_state( false );
    m_WP_col_filter->clear();
    m_E_cond_num_v_lo->set_text( "" );
    m_E_cond_num_v_hi->set_text( "" );
    m_CB_cond_rel_lo->set_active( 0 );
    m_CB_cond_rel_hi->set_active( 0 );

    m_CB_col_type->set_active( m_p2col_sel->get_type() );

    // COLUMN TYPE
    switch( m_p2col_sel->get_type() )
    {
        case TableColumn::TCT_TAG_V:
            m_WEP_tag_picker->set_entry( m_p2col_sel->get_tag() );

            switch( m_p2col_sel->get_value_type() )
            {
                case TableColumn::VT_TOTAL_PLANNED:   m_CB_value_type->set_active( 0 ); break;
                case TableColumn::VT_TOTAL_REALIZED:  m_CB_value_type->set_active( 1 ); break;
                case TableColumn::VT_TOTAL_REMAINING: m_CB_value_type->set_active( 2 ); break;
                case TableColumn::VT_AVG_PLANNED:     m_CB_value_type->set_active( 3 ); break;
                case TableColumn::VT_AVG_REALIZED:    m_CB_value_type->set_active( 4 ); break;
                case TableColumn::VT_AVG_REMAINING:   m_CB_value_type->set_active( 5 ); break;
                default: break;
            }
            m_A_conditional->change_state( m_p2col_sel->get_conditional() );

            m_CB_cond_rel_lo->set_active(
                    m_p2col_sel->get_condition_rel_lo() == TableColumn::C_LESS ? 0 : 1 );

            switch( m_p2col_sel->get_condition_rel_hi() )
            {
                case TableColumn::C_EQUAL: m_CB_cond_rel_hi->set_active( 0 ); break;
                case TableColumn::C_LESS:  m_CB_cond_rel_hi->set_active( 1 ); break;
                default:                   m_CB_cond_rel_hi->set_active( 2 ); break;
            }

            if( m_p2col_sel->get_condition_rel_lo() != TableColumn::C_IGNORE )
                m_E_cond_num_v_lo->set_text(
                        std::to_string( m_p2col_sel->get_condition_num_lo() ) );

            if( m_p2col_sel->get_condition_rel_hi() != TableColumn::C_IGNORE )
                m_E_cond_num_v_hi->set_text(
                        std::to_string( m_p2col_sel->get_condition_num_hi() ) );
            break;
        case TableColumn::TCT_SUB:
            m_WEP_tag_picker->set_entry( m_p2col_sel->get_tag() );
            m_CB_sub_tag_type->set_active( m_p2col_sel->get_value_type() - 2 );
            m_A_conditional->change_state( m_p2col_sel->get_conditional() );
            if( m_p2col_sel->get_condition_sub_filter() )
                m_WP_col_filter->set_text( m_p2col_sel->get_condition_sub_filter()->get_name() );
            else
                m_WP_col_filter->set_text( "" );
            break;
        default:
            break;
    }

    m_WP_col_filter->set_map( m_p2diary->get_p2filters(),
                              m_p2col_sel->get_p2condition_sub_filter() );

    m_B_col_move_prev->set_sensitive( m_i_col_cur > 0 );
    m_B_col_move_next->set_sensitive( m_i_col_cur < int( m_data.m_columns.size() - 1 ) );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();

    update_child_widget_visibilities();

    m_Po_column->set_pointing_to( rect );
    m_Po_column->show();
}

void
WidgetTable::handle_col_renamed()
{
    m_p2col_sel->set_name( m_E_col_name->get_text() );
    refresh();
    m_Sg_changed.emit(); // in the absence of calculate_and_plot()
}

void
WidgetTable::handle_col_type_changed()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    switch( m_CB_col_type->property_active() )
    {
        case 0: m_p2col_sel->set_type( TableColumn::TCT_NAME ); break;
        case 1: m_p2col_sel->set_type( TableColumn::TCT_DATE ); break;
        case 2: m_p2col_sel->set_type( TableColumn::TCT_COUNT ); break;
        case 3: m_p2col_sel->set_type( TableColumn::TCT_TAG_V );
            Lifeograph::START_INTERNAL_OPERATIONS();
            m_CB_value_type->set_active( 1 ); // VT_TOTAL_REALIZED is default
            Lifeograph::FINISH_INTERNAL_OPERATIONS();
        break;
        case 4: m_p2col_sel->set_type( TableColumn::TCT_SUB );
            Lifeograph::START_INTERNAL_OPERATIONS();
            m_CB_sub_tag_type->set_active( 0 ); // VT_FIRST is default
            Lifeograph::FINISH_INTERNAL_OPERATIONS();
            break;
        case 5: m_p2col_sel->set_type( TableColumn::TCT_COMPLETION ); break;
        case 6: m_p2col_sel->set_type( TableColumn::TCT_PATH_LENGTH ); break;
    }

    update_child_widget_visibilities();
    calculate_and_plot();
}

void
WidgetTable::handle_col_tag_changed( Entry* tag )
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_p2col_sel->set_tag( tag );

    calculate_and_plot();
}

void
WidgetTable::handle_col_value_type_changed()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    if( m_p2col_sel->get_type() == TableColumn::TCT_TAG_V )
    {
        switch( m_CB_value_type->get_active_row_number() )
        {
            case 0: m_p2col_sel->set_value_type( TableColumn::VT_TOTAL_PLANNED ); break;
            case 1: m_p2col_sel->set_value_type( TableColumn::VT_TOTAL_REALIZED ); break;
            case 2: m_p2col_sel->set_value_type( TableColumn::VT_TOTAL_REMAINING ); break;
            case 3: m_p2col_sel->set_value_type( TableColumn::VT_AVG_PLANNED ); break;
            case 4: m_p2col_sel->set_value_type( TableColumn::VT_AVG_REALIZED ); break;
            case 5: m_p2col_sel->set_value_type( TableColumn::VT_AVG_REMAINING ); break;
        }
    }
    else
    if( m_p2col_sel->get_type() == TableColumn::TCT_SUB )
    {
        switch( m_CB_sub_tag_type->get_active_row_number() )
        {
            case 0: m_p2col_sel->set_value_type( TableColumn::VT_FIRST ); break;
            case 1: m_p2col_sel->set_value_type( TableColumn::VT_LAST ); break;
            case 2: m_p2col_sel->set_value_type( TableColumn::VT_LOWEST ); break;
            case 3: m_p2col_sel->set_value_type( TableColumn::VT_HIGHEST ); break;
        }
    }

    calculate_and_plot();
}

void
WidgetTable::handle_col_conditional_toggled()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    bool state;
    m_A_conditional->get_state( state );

    m_p2col_sel->set_conditional( !state );

    // The action's state does not change automatically:
    m_A_conditional->change_state( !state );

    update_child_widget_visibilities();
    calculate_and_plot();
}

void
WidgetTable::handle_col_cond_num_changed()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    if( m_E_cond_num_v_lo->get_text().empty() )
        m_p2col_sel->set_condition_rel_lo( TableColumn::C_IGNORE );
    else
    {
        m_p2col_sel->set_condition_rel_lo( m_CB_cond_rel_lo->property_active() == 0 ?
                                           TableColumn::C_LESS : TableColumn::C_LESS_OR_EQUAL );

        m_p2col_sel->set_condition_num_lo( STR::get_d( m_E_cond_num_v_lo->get_text() ) );
    }

    if( m_E_cond_num_v_hi->get_text().empty() )
        m_p2col_sel->set_condition_rel_hi( TableColumn::C_IGNORE );
    else
    {
        switch( m_CB_cond_rel_hi->property_active() )
        {
            case 0: m_p2col_sel->set_condition_rel_hi( TableColumn::C_EQUAL ); break;
            case 1: m_p2col_sel->set_condition_rel_hi( TableColumn::C_LESS ); break;
            case 2: m_p2col_sel->set_condition_rel_hi( TableColumn::C_LESS_OR_EQUAL ); break;
        }

        m_p2col_sel->set_condition_num_hi( STR::get_d( m_E_cond_num_v_hi->get_text() ) );
    }

    m_CB_cond_rel_lo->set_visible( m_CB_cond_rel_hi->property_active() > 0 ); // not =
    m_E_cond_num_v_lo->set_visible( m_CB_cond_rel_hi->property_active() > 0 ); // not =

    calculate_and_plot();
}

void
WidgetTable::calculate_and_plot( bool flag_emit_changed )
{
    m_i_line_top = 0;
    m_data.populate_lines();
    m_line_c_total = m_data.m_lines_sorted ? m_data.m_lines_sorted->size() : 0;

    if( m_line_h == 0.0 ) // pre size allocate call, so only calculate the line_h and update later
    {
        calculate_line_h( get_pango_context() );
    }
    else
    {
        update_scrollbar();
        refresh();

        if( flag_emit_changed )
            m_Sg_changed.emit();
    }
}

void
WidgetTable::update_child_widget_visibilities()
{
    // COLUMN TYPE
    m_WEP_tag_picker->hide();
    m_Bx_value_type->hide();
    m_Bx_sub_tag_type->hide();
    m_MoB_conditional->hide();
    m_Bx_conditions->hide();
    m_Bx_cond_num->hide();
    m_Bx_cond_sub->hide();

    switch( m_p2col_sel->get_type() )
    {
        case TableColumn::TCT_NAME:
        case TableColumn::TCT_DATE:
        case TableColumn::TCT_COUNT:
        case TableColumn::TCT_COMPLETION:
        case TableColumn::TCT_PATH_LENGTH:
            break;
        case TableColumn::TCT_TAG_V:
            m_WEP_tag_picker->show();
            m_Bx_value_type->show();
            m_MoB_conditional->show();
            m_Bx_conditions->show();
            if( m_p2col_sel->get_conditional() )
            {
                m_Bx_cond_num->show();
                m_CB_cond_rel_lo->set_visible( m_CB_cond_rel_hi->property_active() > 0 ); // not =
                m_E_cond_num_v_lo->set_visible( m_CB_cond_rel_hi->property_active() > 0 ); // not =
            }
            break;
        case TableColumn::TCT_SUB:
            m_WEP_tag_picker->show();
            m_Bx_sub_tag_type->show();
            m_MoB_conditional->show();
            m_Bx_conditions->show();
            if( m_p2col_sel->get_conditional() )
                m_Bx_cond_sub->show();
            break;
    }
}

void
WidgetTable::update_scrollbar()
{
    if( m_line_c_vis <= m_line_c_total )
    {
        m_Sb_vert->show();

        m_Aj_vert->configure( 0.0, 0.0, m_line_c_total + S_MIN_LINES - 1,
                              2.0, m_line_c_vis, m_line_c_vis );
    }
    else
        m_Sb_vert->hide();
}

std::string
WidgetTable::get_as_string()
{
    return m_data.get_as_string();
}

void
WidgetTable::set_from_string( const Ustring& table_def )
{
    m_data.set_from_string( table_def );

    Lifeograph::START_INTERNAL_OPERATIONS();

    m_A_group_by_first_col->change_state( m_data.m_flag_group_by_first );
    m_WP_main_filter->set_text( m_data.m_filter ? m_data.m_filter->get_name() : "" );
    if( m_data.m_flag_para_based )
    {
        m_RB_para_based->set_active();
        m_WEP_tag_filter->set_entry( m_data.m_tag_filter );
        m_WEP_tag_filter->set_visible( true );
    }
    else
    {
        m_RB_entry_based->set_active();
        m_WEP_tag_filter->clear();
        m_WEP_tag_filter->set_visible( false );
    }

    Lifeograph::FINISH_INTERNAL_OPERATIONS();

    update_col_geom();
    calculate_and_plot( false );
}

void
WidgetTable::copy_delimited_text()
{
    Ustring str;
    bool f_first{ true };

    for( auto& col : m_data.m_columns )
    {
        if( f_first )
            f_first = false;
        else
            str += '\t';

        str += col->get_name();
    }

    str += '\n';

    for( MapTableLines::size_type i = 0; i < m_line_c_total; i++ )
    {
        f_first = true;

        for( ListTableColumns::size_type j = 0; j < m_data.m_columns.size(); j++ )
        {
            if( f_first )
                f_first = false;
            else
                str += '\t';

            str += m_data.get_value_str( i, j );
        }

        str += '\n';
    }

    Gtk::Clipboard::get()->set_text( str );
}
