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

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

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


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef _WIN32
#include <winsock2.h> // to silence warnings on Windows
#endif

extern "C"
{
#include <champlain-gtk/champlain-gtk.h>
}

#include "lifeograph.hpp"
#include "app_window.hpp"
#include "ui_entry.hpp"
#include "ui_extra.hpp"
#include "widgets/widget_map.hpp"


using namespace LIFEO;

// UIEXTRA =========================================================================================
UIExtra::UIExtra()
{
    // WIDGETS
    Gtk::Viewport*          Vp_cal;
    Gtk::ScrolledWindow*    SW_filter;
    Gtk::Box*               Bx_chart;
    Gtk::Box*               Bx_table;
    Gtk::ScrolledWindow*    SW_themes;
    Gtk::Button*            B_theme_close;

    // COMMON
    auto builder{ Lifeograph::get_builder() };
    builder->get_widget( "Bx_panel_extra", m_Bx_panel );
    builder->get_widget( "St_pextra_view", m_St_view );
    builder->get_widget( "Po_pextra_view", m_Po_extra_view );

    // CALENDAR VIEW
    m_W_calendar = Gtk::manage( new WidgetCalendar );
    builder->get_widget( "Vp_calendar", Vp_cal );
    builder->get_widget_derived( "E_chapter_ctg_picker", m_WP_chapter_ctg );

    // SEARCH VIEW
    builder->get_widget( "E_search", m_E_search );
    builder->get_widget( "TB_search_in_filtered", m_TB_search_in_filtered );
    builder->get_widget( "Bx_replace", m_Bx_replace );
    builder->get_widget_derived( "E_replace", m_E_replace );
    builder->get_widget( "B_replace_all", m_B_replace_all );
    builder->get_widget_derived( "TvD_search", m_TvD_search );

    // FILTER VIEW
    m_W_filter = Gtk::manage( new WidgetFilter( Diary::d, nullptr ) );
    builder->get_widget_derived( "E_filter_picker", m_WP_filter );
    builder->get_widget( "B_filter_apply", m_B_filter_apply );
    builder->get_widget( "B_filter_revert", m_B_filter_revert );
    builder->get_widget( "L_filter_result", m_L_filter_result );
    builder->get_widget( "SW_filter", SW_filter );

    // CHART VIEW
    m_W_chart = new WidgetChart;
    builder->get_widget_derived( "E_chart_picker", m_WP_chart );
    builder->get_widget( "Bx_chart_main", Bx_chart );

    // TABLE VIEW
    m_W_table = new WidgetTable;
    builder->get_widget_derived( "E_table_picker", m_WP_table );
    builder->get_widget( "Bx_table_main", Bx_table );

    // THEME VIEW
    m_FBx_themes = Gtk::manage( new Gtk::FlowBox );
    builder->get_widget( "St_themes", m_St_themes );
    builder->get_widget( "SW_themes", SW_themes );
    builder->get_widget( "B_theme_close", B_theme_close );
    builder->get_widget_derived( "TVD_theme_preview", m_TVD_theme_edit );

    // MAP VIEW
    builder->get_widget( "Bx_map", m_Bx_map );
    m_W_map = new WidgetMap;
    m_Bx_map->pack_start( m_W_map->get_widget(), Gtk::PACK_EXPAND_WIDGET );
    m_Bx_map->show_all();

    // COMMON
    m_St_view->property_visible_child().signal_changed().connect(
            [ this ](){ handle_active_child_changed(); } );

    // CALENDAR VIEW
    Vp_cal->add( *m_W_calendar );
    m_W_calendar->show();
    m_W_calendar->signal_day_selected().connect(
            [ this ]( date_t d ){ handle_calendar_day_selected( d ); } );
    m_W_calendar->signal_day_add().connect(
            [ this ]( date_t d, const Ustring& text )
            { AppWindow::p->UI_diary->add_entry( d, text ); } );

    m_WP_chapter_ctg->signal_add().connect( [ this ](){ add_new_chapter_ctg(); } );
    m_WP_chapter_ctg->signal_dismiss().connect(
            [ this ]( const Ustring& name ){ dismiss_chapter_ctg( name ); } );
    m_WP_chapter_ctg->signal_sel_changed().connect(
            [ this ]( const Ustring& name ){ handle_chapter_ctg_selected( name ); } );
    m_WP_chapter_ctg->signal_name_edited().connect(
            [ this ]( const Ustring& name )->bool{ return handle_chapter_ctg_renamed( name ); } );

    // SEARCH VIEW
    m_E_search->signal_search_changed().connect(       [ this ](){ do_search(); } );
    m_E_search->signal_activate().connect(             [ this ](){ go_to_match_at( 1 ); } );
    m_TB_search_in_filtered->signal_toggled().connect( [ this ](){ do_search(); } );
    m_TvD_search->signal_replace().connect(
            [ this ]( Match& m ){ replace_match( m ); } );
    m_B_replace_all->signal_clicked().connect(         [ this ](){ replace_all_matches(); } );

    // FILTER VIEW
    SW_filter->add( *m_W_filter );

    m_WP_filter->signal_add().connect( [ this ](){ add_new_filter(); } );
    m_WP_filter->signal_dismiss().connect(
            [ this ]( const Ustring& name ){ dismiss_filter( name ); } );
    m_WP_filter->signal_sel_changed().connect(
            [ this ]( const Ustring& name ){ handle_filter_selected( name ); } );
    m_WP_filter->signal_name_edited().connect(
            [ this ]( const Ustring& name )->bool{ return handle_filter_renamed( name ); } );

    m_B_filter_apply->signal_clicked().connect( [ this ](){ apply_filter(); } );
    m_B_filter_revert->signal_clicked().connect( [ this ](){ revert_filter(); } );

    m_W_filter->signal_changed().connect( [ this ](){ handle_filter_edited(); } );

    // CHART VIEW
    Bx_chart->pack_start( *m_W_chart->get_widget() );

    m_WP_chart->set_icon_drag_source(
            Gtk::TargetList::create( { Lifeograph::p->DTE_text_plain } ) );
    m_WP_chart->signal_drag_begin().connect(
            [ this ]( const Glib::RefPtr< Gdk::DragContext >& ctx )
            {
                ctx->set_icon_name( "chart-16-symbolic", 0, 0 );
            } );
    m_WP_chart->signal_drag_data_get().connect(
            [ this ]( const Glib::RefPtr< Gdk::DragContext >&,
                      Gtk::SelectionData& data, guint, guint )
            {
                data.set_text( STR::compose("\nchart:", m_WP_chart->get_text(), "\n" ) );
            } );
    m_WP_chart->signal_add().connect( [ this ](){ add_new_chart(); } );
    m_WP_chart->signal_dismiss().connect(
            [ this ]( const Ustring& name ){ dismiss_chart( name ); } );
    m_WP_chart->signal_sel_changed().connect(
            [ this ]( const Ustring& name ){ handle_chart_selected( name ); } );
    m_WP_chart->signal_name_edited().connect(
            [ this ]( const Ustring& name )->bool{ return handle_chart_renamed( name ); } );

    m_W_chart->signal_changed().connect( [ this ](){ handle_chart_edited(); } );

    // TABLE VIEW
    Bx_table->pack_start( *m_W_table->get_widget() );

    m_WP_table->signal_add().connect( [ this ](){ add_new_table(); } );
    m_WP_table->signal_dismiss().connect(
            [ this ]( const Ustring& name ){ dismiss_table( name ); } );
    m_WP_table->signal_sel_changed().connect(
            [ this ]( const Ustring& name ){ handle_table_selected( name ); } );
    m_WP_table->signal_name_edited().connect(
            [ this ]( const Ustring& name )->bool{ return handle_table_renamed( name ); } );

    m_W_table->signal_changed().connect( [ this ](){ handle_table_edited(); } );
    m_W_table->signal_entry_clicked().connect(
            [ this ]( Entry* entry ){ AppWindow::p->show_entry( entry ); } );

    // THEME VIEW
    m_FBx_themes->set_homogeneous();
    m_FBx_themes->set_activate_on_single_click( false );
    m_FBx_themes->show();
    SW_themes->add( *m_FBx_themes ); // this is due to a Glade bug...

    m_FBx_themes->signal_child_activated().connect(
            [ this ]( Gtk::FlowBoxChild* child )
            {
                set_TvD_cur_from_FBxCh( child );
                assign_theme();
            } );
    m_FBx_themes->signal_drag_begin().connect(
            [ this ]( const Glib::RefPtr< Gdk::DragContext >& ctx )
            {
                ctx->set_icon_name( "applications-graphics-symbolic", 0, 0 );
                Lifeograph::set_dragged_elem(
                        const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) );
            } );
    m_FBx_themes->signal_drag_data_get().connect(
            [ this ]( const Glib::RefPtr< Gdk::DragContext >&, Gtk::SelectionData&, guint, guint )
            {
                // this is a very strange way but it works:
                m_FBx_themes->drag_source_unset();
            } );

    B_theme_close->signal_clicked().connect(
            [ this ](){ m_St_themes->set_visible_child( "list" ); update_theme_list(); } );
    m_TVD_theme_edit->signal_theme_edited().connect(
            [ this ]()
            {
                m_TVD_theme_edit->set_text_edit(); // update text
                if( AppWindow::p->UI_entry->get_cur_entry()->get_theme() ==
                    m_p2TvD_theme_prvw->get_theme() )
                    AppWindow::p->UI_entry->refresh_theme();
            } );

    // COMMON
    Lifeograph::p->add_action( "show_extra_panel",      [ this ](){ set_view( "" ); } );
    Lifeograph::p->add_action( "hide_extra_panel",      [ this ](){ set_view( "H" ); } );
    Lifeograph::p->add_action( "show_extra_p_calendar", [ this ](){ set_view( "calendar" ); } );
    Lifeograph::p->add_action( "show_extra_p_search",   [ this ](){ set_view( "search" ); } );
    Lifeograph::p->add_action( "show_extra_p_filter",   [ this ](){ set_view( "filter" ); } );
    Lifeograph::p->add_action( "show_extra_p_chart",    [ this ](){ set_view( "chart" ); } );
    Lifeograph::p->add_action( "show_extra_p_table",    [ this ](){ set_view( "table" ); } );
    Lifeograph::p->add_action( "show_extra_p_theme",    [ this ](){ set_view( "theme" ); } );
    Lifeograph::p->add_action( "show_extra_p_map",      [ this ](){ set_view( "map" ); } );
    // SEARCH VIEW
    Lifeograph::p->add_action( "begin_searching",       [ this ](){ set_view( "search" );
                                                                    m_E_search->grab_focus(); } );
    Lifeograph::p->add_action( "go_prev_match",         [ this ](){ go_to_match_at( -1 ); } );
    Lifeograph::p->add_action( "go_next_match",         [ this ](){ go_to_match_at( 1 ); } );
    Lifeograph::p->add_action( "replace_match",         [ this ](){ replace_match_cur(); } );
    // THEME
    Lifeograph::p->add_action( "theme_duplicate",       [ this ](){ duplicate_theme(); } );
    Lifeograph::p->add_action( "theme_reset",           [ this ](){ reset_theme_to_default(); } );
    Lifeograph::p->add_action( "theme_assign",          [ this ](){ assign_theme(); } );
    Lifeograph::p->add_action( "theme_edit",            [ this ](){ edit_theme(); } );

    // ACCELERATORS
    Lifeograph::p->set_accel_for_action( "app.hide_extra_panel",      "<Ctrl>0" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_calendar", "<Ctrl>1" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_search",   "<Ctrl>2" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_filter",   "<Ctrl>3" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_chart",    "<Ctrl>4" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_table",    "<Ctrl>5" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_theme",    "<Ctrl>6" );
    Lifeograph::p->set_accel_for_action( "app.show_extra_p_map",      "<Ctrl>7" );

    Lifeograph::p->set_accel_for_action( "app.begin_searching",       "<Ctrl>F" );
    Lifeograph::p->set_accel_for_action( "app.go_prev_match",         "<Shift>F3" );
    Lifeograph::p->set_accel_for_action( "app.go_next_match",         "F3" );
    Lifeograph::p->set_accel_for_action( "app.replace_match",         "F4" );
}

void
UIExtra::handle_login()
{
    m_WP_chapter_ctg->set_select_only( true );
    m_WP_chapter_ctg->set_map( Diary::d->get_p2chapter_ctgs(), Diary::d->get_p2chapter_ctg_cur() );
    m_WP_chapter_ctg->set_text( Diary::d->get_chapter_ctg_cur()->get_name() );

    m_TB_search_in_filtered->set_active( true );
    m_TvD_search->set_diary( Diary::d );

    m_WP_filter->set_select_only( true );
    m_WP_filter->set_map( Diary::d->get_p2filters(), Diary::d->get_p2filter_active() );
    set_active_filter( Diary::d->get_filter_active_name() );

    m_WP_chart->set_select_only( true );
    m_WP_chart->set_map( Diary::d->get_p2charts(), Diary::d->get_p2chart_active() );
    m_W_chart->set_diary( Diary::d );
    set_active_chart( Diary::d->get_chart_active_name() );

    m_WP_table->set_select_only( true );
    m_WP_table->set_map( Diary::d->get_p2tables(), Diary::d->get_p2table_active() );
    m_W_table->set_diary( Diary::d );
    set_active_table( Diary::d->get_table_active_name() );

    update_theme_list();

    m_W_map->set_diary( Diary::d );

    Lifeograph::START_INTERNAL_OPERATIONS();

    switch( Diary::d->get_opt_ext_panel_cur() )
    {
        case 0: m_Bx_panel->hide(); break;
        case 1: set_view( "calendar" ); break;
        case 2: set_view( "search" ); break;
        case 3: set_view( "filter" ); break;
        case 4: set_view( "chart" ); break;
        case 5: set_view( "table" ); break;
        case 6: set_view( "theme" ); break;
        case 7: set_view( "map" ); break;
    }

    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

void
UIExtra::handle_edit_enabled()
{
    m_WP_chapter_ctg->set_select_only( false );
    m_W_calendar->set_editable( true );

    m_Bx_replace->set_sensitive( true );

    m_WP_filter->set_select_only( false );
    if( m_B_filter_revert->get_visible() )
        m_B_filter_apply->show();

    m_WP_chart->set_select_only( false );
    m_W_chart->refresh_editabilitiy();

    m_WP_table->set_select_only( false );
    m_W_table->refresh_editability();

    m_W_map->handle_editing_enabled();
}

void
UIExtra::handle_logout()
{
    Lifeograph::START_INTERNAL_OPERATIONS();

    m_W_calendar->set_editable( false );

    m_E_search->set_text( "" );
    m_E_replace->set_text( "" );
    m_Bx_replace->set_sensitive( false );
    m_TvD_search->set_text_from_matches( nullptr );

    m_B_filter_apply->hide();
    m_B_filter_revert->hide();

    m_TVD_theme_edit->set_theme( ThemeSystem::get() );
    m_St_themes->set_visible_child( "list" );
    m_FBx_themes->foreach( [ this ]( Gtk::Widget& w ){ m_FBx_themes->remove( w ); } );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

void
UIExtra::set_view( const Ustring& view_name )
{
    if( Diary::d->is_open() )
    {
        if( view_name.empty() ) // show the last child
        {
            m_Bx_panel->show();
            set_diary_view_cur( m_St_view->get_visible_child_name() );
        }
        else
        if( view_name == "H" )
        {
            m_Bx_panel->hide();
            Diary::d->set_opt_ext_panel_cur( 0 );
        }
        else
        {
            m_Bx_panel->show();
            m_St_view->set_visible_child( view_name );
        }
    }
}

void
UIExtra::set_entry( Entry* entry )
{
    m_W_calendar->set_day_shown( entry->get_date_t() );
    m_W_calendar->set_date( entry->get_date_t() );

    m_W_map->set_entry( entry );
}

void
UIExtra::refresh_active_panel()
{
    const auto&& active_view{ m_St_view->get_visible_child_name() };

    if( active_view == "chart" )
        m_W_chart->calculate_and_plot( false );
    else
    if( active_view == "table" )
        m_W_table->calculate_and_plot( false );
    else
    if( active_view == "search" )
        do_search();
}

void
UIExtra::refresh_after_sync()
{
    update_calendar();
    m_WP_chapter_ctg->update_list();
    m_WP_filter->update_list();
    m_WP_chart->update_list();
    m_WP_table->update_list();
    update_theme_list();
}

void
UIExtra::handle_active_child_changed()
{
    const auto&& view_name{ m_St_view->get_visible_child_name() };

    if( m_Po_extra_view->is_visible() )
        m_Po_extra_view->hide();

    if( view_name == "search" )
        m_E_search->grab_focus();

    if( Lifeograph::is_internal_operations_ongoing() == false )
        set_diary_view_cur( view_name );
}

int
UIExtra::set_diary_view_cur( const std::string& name )
{
    int i = 0;
    for( const auto& n : { "calendar", "search", "filter", "chart", "table", "theme", "map" } )
    {
        if( name == n )
        {
            Diary::d->set_opt_ext_panel_cur( i + 1 );
            return( i + 1 );
        }
        i++;
    }
    print_error( "Unrecognized view name: ", name );
    return 0;
}

// CALENDAR VIEW
void
UIExtra::handle_calendar_day_selected( date_t date )
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    auto&& entries{ Diary::d->get_entries_by_date( date, true ) };

    switch( entries.size() )
    {
        case 0:
            break;
        case 1:
            AppWindow::p->show_entry( entries.at( 0 ) );
            break;
        default:
            show_entry_selection_list(
                    entries,
                    [ this ]( LIFEO::Entry* e )
                    { AppWindow::p->show_entry( const_cast< Entry* >( e ) ); },
                    *m_W_calendar );
            break;
    }
}

void
UIExtra::update_calendar()
{
    m_W_calendar->update();
}

void
UIExtra::show_date_in_cal( date_t date )
{
    m_St_view->set_visible_child( "calendar" );
    m_W_calendar->set_selected_date( date );
    m_W_calendar->update();
}

void
UIExtra::add_new_chapter_ctg()
{
    Diary::d->set_current_chapter_ctg( Diary::d->create_chapter_ctg() );
    AppWindow::p->UI_diary->update_entry_list();
}

void
UIExtra::dismiss_chapter_ctg( const Ustring& name )
{
    if( AppWindow::p->confirm_dismiss_element( name ) &&
        Diary::d->dismiss_chapter_ctg( Diary::d->get_chapter_ctg( name ) ) )
    {
        m_WP_chapter_ctg->update_list();
    }
}

void
UIExtra::handle_chapter_ctg_selected( const Ustring& name )
{
    Diary::d->set_current_chapter_ctg( name );
    AppWindow::p->UI_diary->update_entry_list();
}

bool
UIExtra::handle_chapter_ctg_renamed( const Ustring& name )
{
    if( name.empty() )
        return false;
    Diary::d->rename_chapter_ctg( Diary::d->get_chapter_ctg_cur(), name );
    return true;
}

// SEARCH VIEW
void
UIExtra::do_search()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    auto&& search_str{ STR::lowercase( m_E_search->get_text() ) };

    m_index_match_cur = -1;
    m_match_count = Diary::d->set_search_text( search_str, m_TB_search_in_filtered->get_active() );

    // the below 3 should be combined into 1
    m_TvD_search->set_text_from_matches( nullptr );
    m_TvD_search->set_search_str( search_str );
    m_TvD_search->set_text_from_matches( &Diary::d->get_matches() );

    if( m_match_count > 0 )
    {
        AppWindow::p->UI_entry->get_textview()->set_search_str( search_str );
        m_E_search->set_tooltip_markup(
                Ustring::compose( _( "<b>%1</b> instances found" ), m_match_count ) );
    }
    else
    {
        AppWindow::p->UI_entry->get_textview()->set_search_str( "" );
        m_E_search->set_tooltip_text( _( "Search text within entries" ) );
    }
}

void
UIExtra::remove_entry_from_search( Entry* entry )
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    Diary::d->remove_entry_from_search( entry );
    m_match_count = Diary::d->get_matches().size();

    // the below 2 should be combined into 1
    m_TvD_search->set_text_from_matches( nullptr );
    m_TvD_search->set_text_from_matches( &Diary::d->get_matches() );
}

void
UIExtra::update_search_for_active_entry()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    Diary::d->update_search_for_entry( AppWindow::p->UI_entry->get_cur_entry(),
                                       m_TB_search_in_filtered->get_active() );
    m_match_count = Diary::d->get_matches().size();

    // the below 2 should be combined into 1
    m_TvD_search->set_text_from_matches( nullptr );
    m_TvD_search->set_text_from_matches( &Diary::d->get_matches() );
}

void
UIExtra::go_to_match_at( int offset )
{
    if( m_match_count < 1 ) return;

    if( m_index_match_cur < 0 )
        m_index_match_cur = 0;
    else
        m_index_match_cur = ( m_index_match_cur + m_match_count + offset ) % m_match_count;

    Match* match{ Diary::d->get_match_at( m_index_match_cur ) };

    AppWindow::p->UI_entry->show( *match );
    m_TvD_search->set_para_sel( *match );
}

void
UIExtra::replace_match( Match& match )
{
    const Ustring&& str_new{ m_E_replace->get_text() };

    Diary::d->replace_match( match, str_new );
    // update current entry:
    if( AppWindow::p->UI_entry->is_cur_entry( match.para->m_host ) )
        AppWindow::p->UI_entry->get_textview()->replace_match( match, str_new );
    // update search results:
    m_TvD_search->replace_match( match, str_new );
}

void
UIExtra::replace_match_cur()
{
    if( m_index_match_cur < 0 || m_match_count < 1 || !Diary::d->is_in_edit_mode() ) return;

    auto match_cur{ Diary::d->get_match_at( m_index_match_cur ) };

    AppWindow::p->UI_entry->show( *match_cur );

    replace_match( *match_cur );
}

void
UIExtra::replace_all_matches()
{
    Diary::d->replace_all_matches( m_E_replace->get_text() );

    do_search();
    AppWindow::p->UI_entry->refresh();
}

// FILTER VIEW
void
UIExtra::set_active_filter( const Ustring& filter_name )
{
    Filter* filter{ Diary::d->get_filter( filter_name ) };
    if( !filter )
        return;

    Lifeograph::START_INTERNAL_OPERATIONS();
    m_W_filter->set_from_string( filter->get_definition() );
    Lifeograph::FINISH_INTERNAL_OPERATIONS();
    m_WP_filter->set_text( filter_name );
    update_all_entries_filter_status();
    AppWindow::p->UI_diary->update_entry_list();
}

void
UIExtra::add_new_filter()
{
    Ustring filter_def;
    m_W_filter->clear();
    m_W_filter->get_as_string( filter_def );
    Filter* filter{ Diary::d->create_filter( _( "New Filter" ), filter_def ) };
    Diary::d->set_filter_active( filter->get_name() );

    update_all_entries_filter_status();
    AppWindow::p->UI_diary->update_entry_list();
}

void
UIExtra::dismiss_filter( const Ustring& name )
{
    if( Diary::d->dismiss_filter( name ) )
    {
        set_active_filter( Diary::d->get_filter_active_name() );
        m_WP_filter->update_list();
    }
}

void
UIExtra::handle_filter_selected( const Ustring& name )
{
    if( Diary::d->set_filter_active( name ) == false )
        return;

    m_W_filter->set_from_string( Diary::d->get_filter_active()->get_definition() );

    update_all_entries_filter_status();

    AppWindow::p->UI_diary->update_entry_list();

    m_B_filter_apply->hide();
    m_B_filter_revert->hide();
}

bool
UIExtra::handle_filter_renamed( const Ustring& new_name )
{
    return( Diary::d->rename_filter( Diary::d->get_filter_active_name(), new_name ) );
}

void
UIExtra::handle_filter_edited()
{
    if( Lifeograph::is_internal_operations_ongoing() )
        return;

    update_all_entries_filter_status();

    AppWindow::p->UI_diary->update_entry_list();

    if( Diary::d->is_in_edit_mode() )
        m_B_filter_apply->show();
    m_B_filter_revert->show();
}

void
UIExtra::update_all_entries_filter_status()
{
    int count{ 0 };

    for( auto& kv_entry : Diary::d->get_entries() )
    {
        const bool flag_filtered_in{ m_W_filter->filter( kv_entry.second ) };
        kv_entry.second->set_filtered_out( ! flag_filtered_in );
        if( flag_filtered_in ) count++;
    }

    m_L_filter_result->set_text( STR::compose( count, " / ", Diary::d->get_size() ) );
}

bool
UIExtra::update_entry_filter_status( Entry* entry )
{
    const bool old_status{ entry->get_filtered_out() };
    const bool new_status{ ! m_W_filter->filter( entry ) };
    entry->set_filtered_out( new_status );

    return ( old_status != new_status );
}

void
UIExtra::apply_filter() // apply temporary changes to the definition
{
    Filter* filter{ Diary::d->get_filter( m_WP_filter->get_text() ) };

    if( !filter )
        return;

    Ustring filter_def;
    m_W_filter->get_as_string( filter_def );
    filter->set_definition( filter_def );

    m_B_filter_apply->hide();
    m_B_filter_revert->hide();
}

void
UIExtra::revert_filter()
{
    handle_filter_selected( m_WP_filter->get_text() );
}

// CHART VIEW
void
UIExtra::set_active_chart( const Ustring& name )
{
    ChartElem* chart = Diary::d->get_chart( name );
    if( !chart )
        return;

    m_W_chart->set_from_string( chart->get_definition() );
    m_WP_chart->set_text( name );
}

void
UIExtra::add_new_chart()
{
    ChartElem* chart{ Diary::d->create_chart( _( "New Chart" ), ChartElem::DEFINITION_DEFAULT ) };
    Diary::d->set_chart_active( chart->get_name() );
    m_W_chart->set_from_string( chart->get_definition() );
}

void
UIExtra::dismiss_chart( const Ustring& name )
{
    if( Diary::d->dismiss_chart( name ) )
    {
        set_active_chart( Diary::d->get_chart_active_name() );
        m_WP_chart->update_list();
    }
}

void
UIExtra::handle_chart_selected( const Ustring& name )
{
    if( Diary::d->set_chart_active( name ) == false )
        return;

    m_W_chart->set_from_string( Diary::d->get_chart_active()->get_definition() );
}

bool
UIExtra::handle_chart_renamed( const Ustring& new_name )
{
    return( Diary::d->rename_chart( Diary::d->get_chart_active_name(), new_name ) );
}

void
UIExtra::handle_chart_edited()
{
    if( Lifeograph::is_internal_operations_ongoing() )
        return;

    ChartElem* chart{ Diary::d->get_chart( m_WP_chart->get_text() ) };

    if( !chart )
        return;

    chart->set_definition( m_W_chart->get_as_string() );
}

void
UIExtra::refresh_chart()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_W_chart->calculate_and_plot( false );
}

// TABLE VIEW
void
UIExtra::set_active_table( const Ustring& name )
{
    TableElem* table = Diary::d->get_table( name );
    if( !table )
        return;

    m_W_table->set_from_string( table->get_definition() );
    m_WP_table->set_text( name );
}

void
UIExtra::add_new_table()
{
    TableElem* table{ Diary::d->create_table( _( "New Table" ), TableElem::DEFINITION_DEFAULT ) };
    Diary::d->set_table_active( table->get_name() );
    m_W_table->set_from_string( table->get_definition() );
}

void
UIExtra::dismiss_table( const Ustring& name )
{
    if( Diary::d->dismiss_table( name ) )
    {
        set_active_table( Diary::d->get_table_active_name() );
        m_WP_table->update_list();
    }
}

void
UIExtra::handle_table_selected( const Ustring& name )
{
    if( Diary::d->set_table_active( name ) == false )
        return;

    m_W_table->set_from_string( Diary::d->get_table_active()->get_definition() );
}

bool
UIExtra::handle_table_renamed( const Ustring& new_name )
{
    return( Diary::d->rename_table( Diary::d->get_table_active_name(), new_name ) );
}

void
UIExtra::handle_table_edited()
{
    if( Lifeograph::is_internal_operations_ongoing() )
        return;

    TableElem* table{ Diary::d->get_table( m_WP_table->get_text() ) };

    if( !table )
        return;

    table->set_definition( m_W_table->get_as_string() );
}

void
UIExtra::refresh_table()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_W_table->calculate_and_plot( false );
}

// THEME VIEW
void
UIExtra::set_TvD_cur_from_FBxCh( Gtk::FlowBoxChild* child )
{
    m_p2TvD_theme_prvw = dynamic_cast< TextviewDiaryTheme* >(
                                dynamic_cast< Gtk::Button* >(
                                        dynamic_cast< Gtk::EventBox* >(
                           child->get_child() )->get_child() )->get_child() );
}

void
UIExtra::update_theme_list()
{
    int index{ 0 };

    auto add_theme_item = [ this ]( Theme* theme, int index )
    {
        // Button only serves visually here. we use an EventBox to prevent events from reaching it
        auto EBx     { Gtk::manage( new Gtk::EventBox ) };
        auto B_theme { Gtk::manage( new Gtk::Button ) };
        auto TvD     { Gtk::manage( new TextviewDiaryTheme ) };

        EBx->set_above_child( true );
        EBx->signal_button_press_event().connect(
                sigc::bind( sigc::mem_fun( this, &UIExtra::handle_theme_click ), index ) );

        B_theme->set_sensitive( false );
        set_widget_css( B_theme,
                "button {"
                    "background-color: " + theme->color_base.to_string() + "; "
                    "background-image: " + ( theme->image_bg.empty() ?
                        "none }" : STR::compose( "url(\"", theme->image_bg, "\") }" ) ) );

        TvD->set_theme( theme );
        TvD->set_text_preview();

        B_theme->add( *TvD );

        EBx->add( *B_theme );
        m_FBx_themes->add( *EBx );
        EBx->show_all();
    };

    m_FBx_themes->foreach( [ this ]( Gtk::Widget& w ){ m_FBx_themes->remove( w ); } );

    for( auto& kv_theme : *Diary::d->get_p2themes() )
        add_theme_item( kv_theme.second, index++ );
}

void
UIExtra::duplicate_theme()
{
    Theme* theme{ Diary::d->create_theme( m_p2TvD_theme_prvw->get_theme()->get_name() ) };
    m_p2TvD_theme_prvw->get_theme()->copy_to( theme );
    update_theme_list();
}

void
UIExtra::dismiss_theme()
{
    Theme* theme{ const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) };

    if( ! AppWindow::p->confirm_dismiss_element( theme->get_name() ) )
        return;

    Diary::d->dismiss_theme( theme );
    update_theme_list();
    AppWindow::p->UI_entry->refresh_theme(); // in case the theme was current entry's theme
}

void
UIExtra::make_theme_default()
{
    Diary::d->set_theme_default( const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) );
    update_theme_list();
    AppWindow::p->UI_entry->refresh_theme(); // in case the theme was current entry's theme
}

void
UIExtra::edit_theme()
{
    m_St_themes->set_visible_child( "editor" );

    m_TVD_theme_edit->set_theme( const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) );
    m_TVD_theme_edit->set_text_edit();
}

void
UIExtra::assign_theme()
{
    if( Diary::d->is_in_edit_mode() == false )
        return;

    AppWindow::p->UI_entry->set_theme( const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) );
}

void
UIExtra::assign_theme_to_sel()
{
    if( Diary::d->is_in_edit_mode() == false )
        return;

    Theme* ptr2theme{ const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) };
    auto&& entries{ AppWindow::p->UI_diary->get_selected_entries() };

    for( auto& entry : entries )
    {
        entry->set_theme( ptr2theme );
        if( AppWindow::p->UI_entry->is_cur_entry( entry ) )
            AppWindow::p->UI_entry->refresh_theme();
    }
}

void
UIExtra::reset_theme_to_default()
{
    Theme* theme{ const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) };
    ThemeSystem::get()->copy_to( theme );
    update_theme_list();
    AppWindow::p->UI_entry->refresh_theme();
}

bool
UIExtra::handle_theme_click( GdkEventButton* event, int index )
{
    if( Diary::d->is_in_edit_mode() == false )
        return false;

    Gtk::FlowBoxChild* FBxCh{ m_FBx_themes->get_child_at_index( index ) };
    m_FBx_themes->select_child( *FBxCh );
    set_TvD_cur_from_FBxCh( FBxCh );
    m_FBx_themes->drag_source_set( { Lifeograph::p->DTE_theme } );

    if( event->button == 3 )
    {
        const Theme* theme{ m_p2TvD_theme_prvw->get_theme() };

        if( !m_Po_theme )
        {
            auto builder{ Lifeograph::get_builder2() };
            builder->get_widget( "Po_theme", m_Po_theme );
            builder->get_widget( "E_theme_name", m_E_theme_name );
            builder->get_widget( "MoB_theme_dismiss", m_MoB_theme_dismiss );
            builder->get_widget( "MoB_theme_make_default", m_MoB_theme_make_dflt );
            builder->get_widget( "MoB_theme_to_sel", m_MoB_theme_to_sel );

            m_E_theme_name->signal_changed().connect( [ this ](){ handle_theme_name_changed(); } );
            m_MoB_theme_dismiss->signal_clicked().connect( [ this ](){ dismiss_theme(); } );
            m_MoB_theme_make_dflt->signal_clicked().connect( [ this ](){ make_theme_default(); } );
            m_MoB_theme_to_sel->signal_clicked().connect( [ this ](){ assign_theme_to_sel(); } );
        }

        m_MoB_theme_dismiss->set_visible( theme->is_default() == false );
        m_MoB_theme_make_dflt->set_visible( theme->is_default() == false );
        m_MoB_theme_to_sel->set_visible( AppWindow::p->UI_diary->get_selected_count() > 1 );

        m_Po_theme->set_relative_to( *m_FBx_themes );
        m_Po_theme->set_pointing_to( FBxCh->get_allocation() );
        m_Po_theme->show();

        m_E_theme_name->set_text( theme->get_name() );
        m_E_theme_name->grab_focus();

        return true;
    }
    return false;
}

void
UIExtra::handle_theme_name_changed()
{
    Ustring new_name{ m_E_theme_name->get_text() };
    Theme* theme{ const_cast< Theme* >( m_p2TvD_theme_prvw->get_theme() ) };

    if( not( new_name.empty() ) && new_name != theme->get_name() )
    {
        Diary::d->rename_theme( theme, new_name );
        m_p2TvD_theme_prvw->set_text_preview();
    }
}
