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

    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

#include "lifeograph.hpp"
#include "app_window.hpp"
#include "ui_entry.hpp"
#include "printing.hpp"


using namespace LIFEO;


UIEntry::UIEntry()
{
    try
    {
        auto builder{ Lifeograph::get_builder() };
        builder->get_widget( "MB_elem", m_MB_entry );
        builder->get_widget( "I_header_elem", m_I_icon );
        builder->get_widget( "I_header_overlay", m_I_overlay );
        builder->get_widget( "L_header_title", m_L_title );
        builder->get_widget( "Bx_header_title", m_Bx_title );
        builder->get_widget( "EB_header_title", m_EB_title );
        builder->get_widget( "Bx_header_title_edit", m_Bx_title_edit );
        builder->get_widget_derived( "E_header_title", m_E_title );
        builder->get_widget( "B_header_title", m_B_title );
        builder->get_widget( "L_header_subtitle", m_L_extra_info );

        builder->get_widget( "Po_entry", m_Po_entry );
        builder->get_widget( "L_entry_created", m_L_created );
        builder->get_widget( "L_entry_edited", m_L_edited );
        builder->get_widget( "L_entry_status", m_L_status );
        builder->get_widget( "L_entry_status_changed", m_L_status_changed );
        builder->get_widget( "L_entry_completion", m_L_completion );
        builder->get_widget( "L_entry_completion_value", m_L_completion_value );
        builder->get_widget( "Bx_entry_editing", m_Bx_editing );
        builder->get_widget( "RB_entry_not_todo", m_RB_not_todo );
        builder->get_widget( "RB_entry_todo", m_RB_todo );
        builder->get_widget( "RB_entry_progressed", m_RB_progressed );
        builder->get_widget( "RB_entry_done", m_RB_done );
        builder->get_widget( "RB_entry_canceled", m_RB_canceled );
        builder->get_widget( "TB_entry_favorite", m_TB_favorite );

        builder->get_widget( "CB_entry_spellchecking", m_CB_spellcheck );

        builder->get_widget( "Bx_entry_unit", m_Bx_unit );
        builder->get_widget( "CB_tag_unit", m_CB_unit );
        builder->get_widget( "E_tag_unit", m_E_unit );

        builder->get_widget( "B_entry_trash", m_B_trash );
        builder->get_widget( "B_entry_restore", m_B_restore );
        builder->get_widget( "B_entry_dismiss", m_B_dismiss );

        builder->get_widget( "Bx_chapter_color", m_Bx_chapter_color );
        builder->get_widget( "ClB_chapter_color", m_ClB_chapter_color );

        builder->get_widget( "St_entry", m_St_entry );
        builder->get_widget( "SW_entry", m_SW_entry );
        builder->get_widget_derived( "TV_entry", m_TvDE );
    }
    catch( ... )
    {
        throw LIFEO::Error( "Failed to create the entry view" );
    }

    m_TvDE->set_editable( false );

    // SIGNALS
    m_EB_title->signal_button_press_event().connect(
            sigc::mem_fun( this, &UIEntry::handle_title_click ) );
    m_EB_title->signal_enter_notify_event().connect(
            sigc::mem_fun( this, &UIEntry::handle_title_hover ) );
    m_EB_title->signal_leave_notify_event().connect(
            sigc::mem_fun( this, &UIEntry::handle_title_hover_out ) );
    m_E_title->signal_changed().connect( [ this ](){ handle_title_changed(); } );
    m_E_title->signal_key_press_event().connect(
            sigc::mem_fun( this, &UIEntry::handle_title_key_press ) );
    m_E_title->signal_activate().connect( [ this ](){ apply_title_edit(); } );
    m_B_title->signal_clicked().connect( [ this ](){ handle_title_button_clicked(); } );

    m_Po_entry->signal_show().connect( [ this ](){ refresh_Po_fields(); } );

    m_RB_not_todo->signal_toggled().connect(
            [ this ](){ set_entries_todo( VecEntries( { m_p2entry } ), ES::NOT_TODO ); } );
    m_RB_todo->signal_toggled().connect(
            [ this ](){ set_entries_todo( VecEntries( { m_p2entry } ), ES::TODO ); } );
    m_RB_progressed->signal_toggled().connect(
            [ this ](){ set_entries_todo( VecEntries( { m_p2entry } ), ES::PROGRESSED ); } );
    m_RB_done->signal_toggled().connect(
            [ this ](){ set_entries_todo( VecEntries( { m_p2entry } ), ES::DONE ); } );
    m_RB_canceled->signal_toggled().connect(
            [ this ](){ set_entries_todo( VecEntries( { m_p2entry } ), ES::CANCELED ); } );

    m_CB_spellcheck->signal_changed().connect( [ this ](){ handle_spellchecking_changed(); } );

    m_TB_favorite->signal_toggled().connect(
            [ this ](){ set_entries_favorite( VecEntries( { m_p2entry } ),
                                              m_TB_favorite->get_active() ); } );
    m_B_trash->signal_clicked().connect(
            [ this ](){ trash_entries( VecEntries( { m_p2entry } ), true ); } );
    m_B_restore->signal_clicked().connect(
            [ this ](){ trash_entries( VecEntries( { m_p2entry } ), false ); } );
    m_B_dismiss->signal_clicked().connect(     [ this ](){ dismiss_entry(); } );

    m_CB_unit->signal_changed().connect(       [ this ](){ handle_unit_changed(); } );

    m_ClB_chapter_color->signal_color_set().connect(
            [ this ](){ handle_chapter_color_changed(); } );

    m_TvDE->signal_changed().connect(
            [ this ]()
            {
                AppWindow::p->UI_extra->update_search_for_active_entry();
                m_flag_bg_jobs_queued = true;
            } );
    m_dispatcher.connect( sigc::mem_fun( this, &UIEntry::handle_bg_jobs_completed ) );

    // EDITOR ACTIONS
    Lifeograph::p->add_action( "browse_back",    [ this ](){ go_back(); } );
    Lifeograph::p->add_action( "browse_forward", [ this ](){ go_forward(); } );
    Lifeograph::p->add_action( "print",          [ this ](){ print(); } );
    m_A_toggle_comments = Lifeograph::p->add_action_bool( "show_comments_in_entries",
            sigc::mem_fun( this, &UIEntry::toggle_comments ), true );

    Lifeograph::p->set_accel_for_action( "app.browse_back",         "<Alt>Left");
    Lifeograph::p->set_accel_for_action( "app.browse_forward",      "<Alt>Right");
    Lifeograph::p->set_accel_for_action( "app.print",               "<Ctrl>P" );
    Lifeograph::p->set_accel_for_action( "app.show_cmnts_in_entry", "<Alt>C" );
}

void
UIEntry::handle_login()
{

}

void
UIEntry::handle_edit_enabled()
{
    m_TvDE->set_editable( true );
    m_Bx_editing->set_visible( true );
    refresh_Bx_editing();

    m_connection_bg_jobs = Glib::signal_timeout().connect_seconds(
            [ this ](){ start_bg_jobs(); return true; }, 12 );
}

void
UIEntry::handle_logout()
{
    m_connection_bg_jobs.disconnect();
    stop_bg_jobs();

    hide_popover(); // hide if is visible
    m_Bx_editing->set_visible( false );

    finish_title_edit(); // checks if title edit is in progress
    clear_history();
    m_TvDE->unset_entry(); // also calls m_textview->set_editable( false );

    m_p2entry = nullptr;
}

void
UIEntry::show( Entry* entry )
{
    if( m_flag_title_edit_in_progress )
        finish_title_edit();

    // do nothing if entry is already the current element:
    if( !entry || is_cur_entry( entry ) )
        return;

    prepare_for_hiding(); // firstly, last element must be prepared for hiding

    m_p2entry = entry;

    add_entry_to_history( entry );

    // HEADER
    refresh_title();
    refresh_icon();
    refresh_overlay_icon();

    Lifeograph::START_INTERNAL_OPERATIONS();

    // BODY
    m_TvDE->set_entry( entry );
    // word count can only be calculated after entry is set:
    m_flag_bg_jobs_queued = true;
    start_bg_jobs();

    m_St_entry->set_visible_child( "entry_transition" );
    m_St_entry->set_visible_child( "entry_editor" );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();

    PRINT_DEBUG( "entry shown: ", entry->get_date().format_string() );

    // consume all pending events first:
    flush_gtk_event_queue();
    // then set vadjustment:
    m_TvDE->get_vadjustment()->set_value( entry->get_scroll_pos() );
}

void
UIEntry::refresh()
{
    if( m_flag_title_edit_in_progress )
        finish_title_edit();

    prepare_for_hiding();

    // HEADER
    refresh_title();
    refresh_icon();
    refresh_overlay_icon();

    Lifeograph::START_INTERNAL_OPERATIONS();

    // BODY
    m_TvDE->set_entry( m_p2entry );
    // word count can only be calculated after entry is set:
    m_flag_bg_jobs_queued = true;
    start_bg_jobs();

    Lifeograph::FINISH_INTERNAL_OPERATIONS();

    PRINT_DEBUG( "entry refreshed" );

    // consume all pending events first:
    flush_gtk_event_queue();
    // then set vadjustment:
    m_TvDE->get_vadjustment()->set_value( m_p2entry->get_scroll_pos() );
}

void
UIEntry::show( const Match& match )
{
    show( const_cast< Entry* >( match.para->m_host ) );

    auto&& it_bgn{ m_TvDE->get_buffer()->get_iter_at_offset( match.pos_entry ) };
    auto   it_end = it_bgn;
    it_end.forward_chars( Diary::d->get_search_text().length() );
    m_TvDE->scroll_to( it_bgn, 0.05 );

    m_TvDE->get_buffer()->select_range( it_bgn, it_end );
    m_TvDE->grab_focus();
}

void
UIEntry::go_back()
{
    if( ( m_browsing_history.size() - m_bhist_offset ) > 1 )
    {
        auto i = m_browsing_history.begin();
        m_bhist_offset++;
        std::advance( i, m_bhist_offset );

        m_St_entry->set_transition_type( Gtk::STACK_TRANSITION_TYPE_SLIDE_RIGHT );

        m_flag_index_locked = true;

        AppWindow::p->show_entry( *i ); //TODO: move history to AppWindow

        m_flag_index_locked = false;

        m_St_entry->set_transition_type( Gtk::STACK_TRANSITION_TYPE_SLIDE_LEFT );
    }
    // logout at the immadiate back after login:
    else if( m_browsing_history.size() < 2 && not( Diary::d->is_in_edit_mode() ) )
        AppWindow::p->logout( true );
}

void
UIEntry::go_forward()
{
    if( m_bhist_offset > 0 )
    {
        auto i = m_browsing_history.begin();
        m_bhist_offset--;
        std::advance( i, m_bhist_offset );

        m_flag_index_locked = true;

        AppWindow::p->show_entry( *i ); //TODO: move history to AppWindow

        m_flag_index_locked = false;
    }
}

void
UIEntry::add_entry_to_history( Entry* entry )
{
    if( ! m_flag_index_locked ) // locked while going back
    {
        if( m_bhist_offset > 0 )
        {
            auto&& i_end{ m_browsing_history.begin() };

            std::advance( i_end, m_bhist_offset );
            m_browsing_history.erase( m_browsing_history.begin(), i_end );
        }
        m_bhist_offset = 0;
        m_browsing_history.push_front( entry );
    }
}

void
UIEntry::remove_entry_from_history( Entry* entry )
{
    auto remove_element = [ & ]()
    {
        for( auto i = m_browsing_history.begin(); i != m_browsing_history.end(); ++i )
            if( *i == entry )
            {
                m_browsing_history.erase( i );
                return true;
            }

        return false;
    };

    while( remove_element() ) continue;
}

void
UIEntry::clear_history()
{
    m_browsing_history.clear();
    m_bhist_offset = 0;
}

void
UIEntry::refresh_extra_info()
{
    if( m_p2entry && m_p2entry->get_type() == DiaryElement::ET_CHAPTER )
    {
        m_L_extra_info->set_text(
                Ustring::compose( _( "Chapter with %1 words spanning %2 days in %3" ),
                                  m_parser_word_count.get_word_count(),
                                  dynamic_cast< Chapter* >( m_p2entry )->get_time_span(),
                                  Diary::d->get_name() ) );
    }
    else // ET_ENTRY
    {
        m_L_extra_info->set_text(
                Ustring::compose( _( "Entry with %1 word(s) in %2" ),
                                  m_parser_word_count.get_word_count(),
                                  Diary::d->get_name() ) );
    }
}

void
UIEntry::refresh_status_date()
{
    ElemStatus es = m_p2entry->get_todo_status_effective();

    m_L_status->set_visible( es != ES::NOT_TODO );
    m_L_status_changed->set_visible( es != ES::NOT_TODO );

    if( es != ES::NOT_TODO )
        m_L_status_changed->set_text( m_p2entry->get_date_status_str() );

    switch( es )
    {
        case ES::TODO:
            m_L_status->set_text( _( "Opened" ) );
            break;
        case ES::PROGRESSED:
            m_L_status->set_text( _( "Started" ) );
            break;
        case ES::DONE:
            m_L_status->set_text( _( "Completed" ) );
            break;
        case ES::CANCELED:
            m_L_status->set_text( _( "Canceled" ) );
            break;
    }
}

void
UIEntry::refresh_Po_fields()
{
    const double workload{ m_p2entry->get_workload() };
    m_L_created->set_text( m_p2entry->get_date_created_str() );
    m_L_edited->set_text( m_p2entry->get_date_edited_str() );

    m_L_completion->set_visible( workload > 0.0 );
    m_L_completion_value->set_visible( workload > 0.0 );

    if( workload > 0.0 )
        m_L_completion_value->set_text(
                STR::compose( "(", m_p2entry->get_completed(), "/", workload, ")  ",
                              STR::format_percentage( m_p2entry->get_completion() ) ) );

    refresh_status_date();
    refresh_CB_spellchecking();

    if( Diary::d->is_in_edit_mode() )
        refresh_Bx_editing();

    m_Bx_chapter_color->set_visible( m_p2entry->get_type() == DiaryElement::ET_CHAPTER );
    if( m_p2entry->get_type() == DiaryElement::ET_CHAPTER )
        m_ClB_chapter_color->set_rgba( dynamic_cast< Chapter* >( m_p2entry )->get_color() );
}

void
UIEntry::refresh_Bx_editing()
{
    Lifeograph::START_INTERNAL_OPERATIONS();

    switch( m_p2entry->get_todo_status() )
    {
        case ES::TODO:
            m_RB_todo->set_active();
            break;
        case ES::PROGRESSED:
            m_RB_progressed->set_active();
            break;
        case ES::DONE:
            m_RB_done->set_active();
            break;
        case ES::CANCELED:
            m_RB_canceled->set_active();
            break;
        case ES::NOT_TODO:
        default:
            m_RB_not_todo->set_active();
            break;
    }

    m_TB_favorite->set_active( m_p2entry->is_favored() );

    // UNIT
    m_E_unit->set_text( m_p2entry->get_unit() );

    m_B_trash->set_visible( !m_p2entry->is_trashed() );
    m_B_restore->set_visible( m_p2entry->is_trashed() );
    m_B_dismiss->set_visible( m_p2entry->is_trashed() );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

void
UIEntry::refresh_CB_spellchecking()
{
    Lifeograph::START_INTERNAL_OPERATIONS();

    m_CB_spellcheck->remove_all();
    m_CB_spellcheck->append( LANG_INHERIT_DIARY, _( "Diary Default" ) );
    m_CB_spellcheck->append( "", _( STRING::OFF ) );

    for( auto iter = Lifeograph::s_lang_list.begin();
         iter != Lifeograph::s_lang_list.end(); ++iter )
    {
        m_CB_spellcheck->append( *iter, *iter );
    }

    // SOME OTHER LANGUAGE THAT IS NOT SUPPORTED BY THE CURRENT SYSTEM
    if( !m_p2entry->get_lang().empty() && m_p2entry->get_lang() != LANG_INHERIT_DIARY &&
        Lifeograph::s_lang_list.find( m_p2entry->get_lang() ) == Lifeograph::s_lang_list.end() )
    {
        m_CB_spellcheck->append( m_p2entry->get_lang(), m_p2entry->get_lang() + "(!)" );
    }

    m_CB_spellcheck->set_active_id( m_p2entry->get_lang() );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

void
UIEntry::set_theme( Theme* theme )
{
    m_p2entry->set_theme( theme );
    refresh_theme();
}

void
UIEntry::refresh_theme()
{
    m_TvDE->set_theme( nullptr ); // nullptr means just refresh
    m_TvDE->update_highlight_button();
}

void
UIEntry::print()
{
    if( not( Diary::d->is_open() ) )
        return;

    hide_popover();

    Glib::RefPtr< PrintOpr > print = PrintOpr::create();

    try
    {
        AppWindow::p->freeze_auto_logout();
        print->set_hide_comments( m_TvDE->are_comments_hidden() );
        print->run( Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, *AppWindow::p );
        AppWindow::p->unfreeze_auto_logout();
    }
    catch( const Gtk::PrintError &ex )
    {
        // See documentation for exact Gtk::PrintError error codes:
        print_error( "An error occurred while trying to run a print operation:", ex.what() );
    }
}

void
UIEntry::set_entries_favorite( VecEntries&& ev, bool flag_fav )
{
    if( Lifeograph::is_internal_operations_ongoing() )
        return;

    for( Entry* e : ev )
    {
        if( flag_fav != e->is_favored() )
            e->toggle_favored();

        if( e == m_p2entry )
            refresh_overlay_icon();

        AppWindow::p->UI_diary->refresh_row( e );
    }
}

void
UIEntry::set_entries_todo( VecEntries&& ev, ElemStatus status )
{
    if( Lifeograph::is_internal_operations_ongoing() )
        return;

    for( Entry* e : ev )
    {
        if( status == ES::NOT_TODO )
            e->update_todo_status();
        else
        {
            e->set_todo_status( status );
            e->set_date_status( time( nullptr ) );
        }

        if( e == m_p2entry )
            refresh_icon();

        AppWindow::p->UI_diary->refresh_row( e );
    }
}

void
UIEntry::trash_entries( VecEntries&& ev, bool flag_trash )
{
    if( Lifeograph::is_internal_operations_ongoing() )
        return;

    bool flag_update_cal{ false };

    hide_popover();

    for( Entry* e : ev )
    {
        e->set_trashed( flag_trash );

        if( AppWindow::p->UI_extra->update_entry_filter_status( e ) && !flag_update_cal )
            flag_update_cal = true;

        AppWindow::p->UI_diary->refresh_row( e );
    }

    if( flag_update_cal )
        AppWindow::p->UI_extra->update_calendar();
}

void
UIEntry::dismiss_entry()
{
    hide_popover();

    if( m_flag_title_edit_in_progress )
        finish_title_edit();

    Gtk::CheckButton* check{ nullptr };

    if( m_p2entry->get_type() == DiaryElement::ET_CHAPTER )
    {
        check = new Gtk::CheckButton( _( "Also dismiss the entries within" ) );
    }
    else if( Diary::d->get_entries().size() < 2 ) // do not delete the last entry
        return;

    if( ! AppWindow::p->confirm_dismiss_element( m_p2entry->get_name(), check ) )
    {
        if( check ) delete check;
        return;
    }

    Entry* closest_entry{ Diary::d->get_entry_closest_to( m_p2entry->get_date_t(), true ) };
    if( ! closest_entry )
        closest_entry = Diary::d->get_entry_closest_to( m_p2entry->get_date_t(), false );

    remove_entry_from_history( m_p2entry );
    AppWindow::p->UI_extra->remove_entry_from_search( m_p2entry );

    if( m_p2entry->get_type() == DiaryElement::ET_CHAPTER )
        Diary::d->dismiss_chapter( dynamic_cast< Chapter* >( m_p2entry ), check->get_active() );
    else
        Diary::d->dismiss_entry( m_p2entry );

    m_p2entry = nullptr;

    AppWindow::p->UI_extra->update_calendar();
    AppWindow::p->UI_extra->refresh_table();
    AppWindow::p->UI_diary->update_entry_list();
    AppWindow::p->show_entry( closest_entry );

    if( check )
        delete check;
}

void
UIEntry::toggle_comments()
{
    m_TvDE->set_comments_hidden( ! m_TvDE->are_comments_hidden() );

    // The action's state does not change automatically:
    m_A_toggle_comments->change_state( ! m_TvDE->are_comments_hidden() );
}

void
UIEntry::start_title_edit()
{
    if( Diary::d->is_in_edit_mode() )
    {
        m_Bx_title->hide();
        m_MB_entry->hide();
        m_Bx_title_edit->show();
        m_E_title->set_text( m_p2entry->get_date().format_string() );
        m_E_title->grab_focus();
        m_B_title->set_label( _( "Cancel" ) );

        m_flag_title_applicable = false;
        m_flag_title_edit_in_progress = true;
    }
}

bool
UIEntry::finish_title_edit()
{
    if( m_flag_title_edit_in_progress )
    {
        m_flag_title_edit_in_progress = false;  // must be first

        m_Bx_title_edit->hide();
        m_Bx_title->show();
        m_MB_entry->show();
        m_L_title->set_text( m_p2entry->get_title_str() );
    }

    return false; // always false for signal timeout
}

void
UIEntry::handle_title_changed()
{
    if( m_flag_title_edit_in_progress )
    {
        date_t date;
        m_flag_title_applicable =
                ( ( Date::parse_string( &date, m_E_title->get_text() ) == OK ) &&
                  Date::is_ordinal( date ) == false );

        m_B_title->set_label( m_flag_title_applicable ? _( "Apply" ) : _( "Cancel" ) );
    }
}

void
UIEntry::apply_title_edit()
{
    if( m_flag_title_edit_in_progress && m_flag_title_applicable )
    {
        Date date{ m_E_title->get_text() };

        if( date.is_set() && date.is_ordinal() == false )
        {
            if( m_p2entry->get_type() == DiaryElement::ET_ENTRY )
                Diary::d->set_entry_date( m_p2entry, date.m_date + 1 ); // +1 to set the order
            else
            if( Diary::d->get_chapter_ctg_cur()->set_chapter_date(
                             dynamic_cast< Chapter* >( m_p2entry ), date.m_date ) )
                Diary::d->update_entries_in_chapters();
        }

        AppWindow::p->UI_diary->update_entry_list();
        AppWindow::p->UI_extra->set_entry( m_p2entry );
        finish_title_edit();
    }
}

bool
UIEntry::handle_title_click( GdkEventButton* event )
{
    if( Diary::d->is_in_edit_mode() && event->button == 1 &&
        m_p2entry && !m_p2entry->is_ordinal() )
        start_title_edit();
    return false;
}

bool
UIEntry::handle_title_key_press( GdkEventKey* event )
{
    if( event->keyval == GDK_KEY_Escape )
        finish_title_edit();
    return true;
}

bool
UIEntry::handle_title_hover( GdkEventCrossing* )
{
    if( Diary::d->is_in_edit_mode() && m_p2entry && !m_p2entry->is_ordinal() )
        m_L_title->set_markup(
                "<u>" + Glib::Markup::escape_text( m_p2entry->get_title_str() ) + "</u>" );
    return false;
}

bool
UIEntry::handle_title_hover_out( GdkEventCrossing* )
{
    if( Diary::d->is_in_edit_mode() && m_p2entry && !m_p2entry->is_ordinal() )
        m_L_title->set_text( m_p2entry->get_title_str() );
    return false;
}

void
UIEntry::handle_title_button_clicked()
{
    if( m_flag_title_applicable )
        apply_title_edit();
    else
        finish_title_edit();
}

void
UIEntry::undo()
{
    m_TvDE->undo();
}

void
UIEntry::redo()
{
    m_TvDE->redo();
}

void
UIEntry::prepare_for_hiding()
{
    if( m_p2entry )
    {
        PRINT_DEBUG( "prepare_for_hiding(): ENTRY VIEW SCROLL POS=",
                     m_TvDE->get_vadjustment()->get_value() );
        m_p2entry->set_scroll_pos( m_TvDE->get_vadjustment()->get_value() );

        Diary::d->update_entry_name( m_p2entry );
    }
}

// SPELL CHECKING
void
UIEntry::handle_spellchecking_changed()
{
    if( Lifeograph::is_internal_operations_ongoing() || !m_p2entry )
        return;

    auto lang{ m_CB_spellcheck->get_active_id() };
    m_p2entry->set_lang( lang );
    m_TvDE->get_buffer()->set_language( lang );
    m_TvDE->get_buffer()->reparse();
}

void
UIEntry::handle_unit_changed()
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    m_p2entry->set_unit( m_CB_unit->get_active_text() );

    AppWindow::p->UI_extra->refresh_chart();
    AppWindow::p->UI_diary->refresh_row( m_p2entry );
    refresh_icon();
}

void
UIEntry::handle_chapter_color_changed()
{
    dynamic_cast< Chapter* >( m_p2entry )->set_color( m_ClB_chapter_color->get_rgba() );
    AppWindow::p->UI_extra->refresh_chart();
}

// INDEXING
void
UIEntry::start_bg_jobs()
{
    if( m_thread_bg_jobs )
    {
        print_info( "Background jobs are taking longer than expected!" );
    }
    else if( m_p2entry && m_flag_bg_jobs_queued )
    {
        PRINT_DEBUG( "<BACKGROUND JOBS STARTED>" );
        m_flag_bg_jobs_queued = false;
        m_parser_word_count.set_text( m_p2entry->get_text() );
        m_thread_bg_jobs = new std::thread{
            [ this ](){ m_parser_word_count.parse( m_dispatcher ); } };
    }
}

void
UIEntry::handle_bg_jobs_completed()
{
    PRINT_DEBUG( "<BACKGROUND JOBS COMPLETED>" );

    destroy_bg_jobs_thread();

    if( Diary::d->is_open() ) //if not logged out in the meantime
    {
        refresh_extra_info();
        m_L_edited->set_text( m_p2entry->get_date_edited_str() );
    }
}

void
UIEntry::destroy_bg_jobs_thread()
{
    if( !m_thread_bg_jobs )
        return;

    if( m_thread_bg_jobs->joinable() )
        m_thread_bg_jobs->join();
    delete m_thread_bg_jobs;
    m_thread_bg_jobs = nullptr;
}
