/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <svtools/editbrowsebox.hxx>
#include <vcl/svapp.hxx>
 
namespace svt
{
    //= ComboBoxControl
    ComboBoxControl::ComboBoxControl(BrowserDataWin* pParent)
        : ControlBase(pParent, u"svt/ui/combocontrol.ui"_ustr, u"ComboControl"_ustr)
        , m_xWidget(m_xBuilder->weld_combo_box(u"combobox"_ustr))
    {
        InitControlBase(m_xWidget.get());
        m_xWidget->set_entry_width_chars(1); // so a smaller than default width can be used
        m_xWidget->connect_changed(LINK(this, ComboBoxControl, SelectHdl));
        m_xWidget->connect_key_press(LINK(this, ControlBase, KeyInputHdl));
        m_xWidget->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl));
        m_xWidget->connect_focus_in(LINK(this, ControlBase, FocusInHdl));
        m_xWidget->connect_focus_out(LINK(this, ControlBase, FocusOutHdl));
        m_xWidget->connect_mouse_press(LINK(this, ControlBase, MousePressHdl));
        m_xWidget->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl));
        m_xWidget->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl));
    }
 
    void ComboBoxControl::SetPointFont(const vcl::Font& rFont)
    {
        m_xWidget->set_entry_font(rFont);
    }
 
    void ComboBoxControl::dispose()
    {
        m_xWidget.reset();
        ControlBase::dispose();
    }
 
    IMPL_LINK_NOARG(ComboBoxControl, SelectHdl, weld::ComboBox&, void)
    {
        CallModifyHdls();
    }
 
    //= ComboBoxCellController
    ComboBoxCellController::ComboBoxCellController(ComboBoxControl* pWin)
                             :CellController(pWin)
    {
        static_cast<ComboBoxControl&>(GetWindow()).SetModifyHdl(LINK(this, ComboBoxCellController, ModifyHdl));
    }
 
    IMPL_LINK_NOARG(ComboBoxCellController, ModifyHdl, LinkParamNone*, void)
    {
        callModifyHdl();
    }
 
    bool ComboBoxCellController::MoveAllowed(const KeyEvent& rEvt) const
    {
        weld::ComboBox& rBox = GetComboBox();
        switch (rEvt.GetKeyCode().GetCode())
        {
            case KEY_END:
            case KEY_RIGHT:
            {
                int nStartPos, nEndPos;
                bool bNoSelection = rBox.get_entry_selection_bounds(nStartPos, nEndPos);
                return bNoSelection && nEndPos == rBox.get_active_text().getLength();
            }
            case KEY_HOME:
            case KEY_LEFT:
            {
                int nStartPos, nEndPos;
                bool bNoSelection = rBox.get_entry_selection_bounds(nStartPos, nEndPos);
                return bNoSelection && nStartPos == 0;
            }
            case KEY_UP:
            case KEY_DOWN:
                if (rBox.get_popup_shown())
                    return false;
                if (!rEvt.GetKeyCode().IsShift() &&
                     rEvt.GetKeyCode().IsMod1())
                    return false;
                // drop down the list box
                else if (rEvt.GetKeyCode().IsMod2() && rEvt.GetKeyCode().GetCode() == KEY_DOWN)
                    return false;
                [[fallthrough]];
            case KEY_PAGEUP:
            case KEY_PAGEDOWN:
            case KEY_RETURN:
                if (rBox.get_popup_shown())
                    return false;
                [[fallthrough]];
            default:
                return true;
        }
    }
 
    bool ComboBoxCellController::IsValueChangedFromSaved() const
    {
        return GetComboBox().get_value_changed_from_saved();
    }
 
    void ComboBoxCellController::SaveValue()
    {
        GetComboBox().save_value();
    }
 
    //= ListBoxControl
    ListBoxControl::ListBoxControl(BrowserDataWin* pParent)
        : ControlBase(pParent, u"svt/ui/listcontrol.ui"_ustr, u"ListControl"_ustr)
        , m_xWidget(m_xBuilder->weld_combo_box(u"listbox"_ustr))
    {
        InitControlBase(m_xWidget.get());
        m_xWidget->set_size_request(42, -1); // so a later narrow size request can stick
        m_xWidget->connect_changed(LINK(this, ListBoxControl, SelectHdl));
        m_xWidget->connect_key_press(LINK(this, ControlBase, KeyInputHdl));
        m_xWidget->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl));
        m_xWidget->connect_focus_in(LINK(this, ControlBase, FocusInHdl));
        m_xWidget->connect_focus_out(LINK(this, ControlBase, FocusOutHdl));
        m_xWidget->connect_mouse_press(LINK(this, ControlBase, MousePressHdl));
        m_xWidget->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl));
        m_xWidget->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl));
    }
 
    void ListBoxControl::SetPointFont(const vcl::Font& rFont)
    {
        m_xWidget->set_font(rFont);
    }
 
    void ListBoxControl::dispose()
    {
        m_xWidget.reset();
        ControlBase::dispose();
    }
 
    IMPL_LINK_NOARG(ListBoxControl, SelectHdl, weld::ComboBox&, void)
    {
        CallModifyHdls();
    }
 
    //= ListBoxCellController
    ListBoxCellController::ListBoxCellController(ListBoxControl* pWin)
                             :CellController(pWin)
    {
        static_cast<ListBoxControl&>(GetWindow()).SetModifyHdl(LINK(this, ListBoxCellController, ListBoxSelectHdl));
    }
 
    bool ListBoxCellController::MoveAllowed(const KeyEvent& rEvt) const
    {
        const weld::ComboBox& rBox = GetListBox();
        switch (rEvt.GetKeyCode().GetCode())
        {
            case KEY_UP:
            case KEY_DOWN:
                if (!rEvt.GetKeyCode().IsShift() &&
                     rEvt.GetKeyCode().IsMod1())
                    return false;
                // drop down the list box
                else
                    if (rEvt.GetKeyCode().IsMod2() && rEvt.GetKeyCode().GetCode() == KEY_DOWN)
                        return false;
                [[fallthrough]];
            case KEY_PAGEUP:
            case KEY_PAGEDOWN:
                if (rBox.get_popup_shown())
                    return false;
                [[fallthrough]];
            default:
                return true;
        }
    }
 
    bool ListBoxCellController::IsValueChangedFromSaved() const
    {
        return GetListBox().get_value_changed_from_saved();
    }
 
    void ListBoxCellController::SaveValue()
    {
        GetListBox().save_value();
    }
 
    IMPL_LINK_NOARG(ListBoxCellController, ListBoxSelectHdl, LinkParamNone*, void)
    {
        callModifyHdl();
    }
 
    //= CheckBoxControl
    CheckBoxControl::CheckBoxControl(BrowserDataWin* pParent)
        : ControlBase(pParent, u"svt/ui/checkboxcontrol.ui"_ustr, u"CheckBoxControl"_ustr)
        , m_xBox(m_xBuilder->weld_check_button(u"checkbox"_ustr))
    {
        m_aModeState.bTriStateEnabled = true;
        InitControlBase(m_xBox.get());
        m_xBox->connect_key_press(LINK(this, ControlBase, KeyInputHdl));
        m_xBox->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl));
        m_xBox->connect_focus_in(LINK(this, ControlBase, FocusInHdl));
        m_xBox->connect_focus_out(LINK(this, ControlBase, FocusOutHdl));
        m_xBox->connect_mouse_press(LINK(this, ControlBase, MousePressHdl));
        m_xBox->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl));
        m_xBox->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl));
        m_xBox->connect_toggled(LINK(this, CheckBoxControl, OnToggle));
    }
 
    void CheckBoxControl::SetPointFont(const vcl::Font& /*rFont*/)
    {
    }
 
    void CheckBoxControl::EnableTriState( bool bTriState )
    {
        if (m_aModeState.bTriStateEnabled != bTriState)
        {
            m_aModeState.bTriStateEnabled = bTriState;
 
            if (!m_aModeState.bTriStateEnabled && GetState() == TRISTATE_INDET)
                SetState(TRISTATE_FALSE);
        }
    }
 
    void CheckBoxControl::SetState(TriState eState)
    {
        if (!m_aModeState.bTriStateEnabled && (eState == TRISTATE_INDET))
            eState = TRISTATE_FALSE;
        m_aModeState.eState = eState;
        m_xBox->set_state(eState);
    }
 
    CheckBoxControl::~CheckBoxControl()
    {
        disposeOnce();
    }
 
    void CheckBoxControl::dispose()
    {
        m_xBox.reset();
        ControlBase::dispose();
    }
 
    void CheckBoxControl::Clicked()
    {
        // if tristate is enabled, m_aModeState will take care of setting the
        // next state in the sequence via TriStateEnabled::ButtonToggled
        if (!m_aModeState.bTriStateEnabled)
            m_xBox->set_active(!m_xBox->get_active());
        OnToggle(*m_xBox);
    }
 
    IMPL_LINK_NOARG(CheckBoxControl, OnToggle, weld::Toggleable&, void)
    {
        m_aModeState.ButtonToggled(*m_xBox);
        m_aToggleLink.Call(*m_xBox);
        CallModifyHdls();
    }
 
    //= CheckBoxCellController
    CheckBoxCellController::CheckBoxCellController(CheckBoxControl* pWin)
        : CellController(pWin)
    {
        static_cast<CheckBoxControl &>(GetWindow()).SetModifyHdl( LINK(this, CheckBoxCellController, ModifyHdl) );
    }
 
    void CheckBoxCellController::ActivatingMouseEvent(const BrowserMouseEvent& rEvt, bool /*bUp*/)
    {
        CheckBoxControl& rControl = static_cast<CheckBoxControl&>(GetWindow());
        rControl.GrabFocus();
 
        // we have to adjust the position of the event relative to the controller's window
        Point aPos = rEvt.GetPosPixel() - rEvt.GetRect().TopLeft();
 
        Size aControlSize = rControl.GetSizePixel();
        Size aBoxSize = rControl.GetBox().get_preferred_size();
        tools::Rectangle aHotRect(Point((aControlSize.Width() - aBoxSize.Width()) / 2,
                                        (aControlSize.Height() - aBoxSize.Height()) / 2),
                                  aBoxSize);
 
        // we want the initial mouse event to act as if it was performed on the checkbox
        if (aHotRect.Contains(aPos))
            rControl.Clicked();
    }
 
    weld::CheckButton& CheckBoxCellController::GetCheckBox() const
    {
        return static_cast<CheckBoxControl &>(GetWindow()).GetBox();
    }
 
    bool CheckBoxCellController::IsValueChangedFromSaved() const
    {
        return GetCheckBox().get_state_changed_from_saved();
    }
 
    void CheckBoxCellController::SaveValue()
    {
        GetCheckBox().save_state();
    }
 
    IMPL_LINK_NOARG(CheckBoxCellController, ModifyHdl, LinkParamNone*, void)
    {
        callModifyHdl();
    }
 
    //= MultiLineEditImplementation
    OUString MultiLineEditImplementation::GetText(LineEnd eSeparator) const
    {
        weld::TextView& rEntry = m_rEdit.get_widget();
        return convertLineEnd(rEntry.get_text(), eSeparator);
    }
 
    OUString MultiLineEditImplementation::GetSelected(LineEnd eSeparator) const
    {
        int nStartPos, nEndPos;
        weld::TextView& rEntry = m_rEdit.get_widget();
        rEntry.get_selection_bounds(nStartPos, nEndPos);
        return convertLineEnd(rEntry.get_text().copy(nStartPos, nEndPos - nStartPos), eSeparator);
    }
 
    IMPL_LINK_NOARG(MultiLineEditImplementation, ModifyHdl, weld::TextView&, void)
    {
        CallModifyHdls();
    }
 
    EditCellController::EditCellController( IEditImplementation* _pImplementation )
        :CellController( &_pImplementation->GetControl() )
        ,m_pEditImplementation( _pImplementation )
        ,m_bOwnImplementation( false )
    {
        m_pEditImplementation->SetModifyHdl( LINK(this, EditCellController, ModifyHdl) );
    }
 
    IMPL_LINK_NOARG(EntryImplementation, ModifyHdl, weld::Entry&, void)
    {
        CallModifyHdls();
    }
 
    ControlBase::ControlBase(BrowserDataWin* pParent, const OUString& rUIXMLDescription, const OUString& rID)
        : InterimItemWindow(pParent, rUIXMLDescription, rID)
    {
    }
 
    void ControlBase::SetEditableReadOnly(bool /*bReadOnly*/)
    {
        // expected to be overridden for Entry, TextView or the editable entry part of a ComboBox
    }
 
    EditControlBase::EditControlBase(BrowserDataWin* pParent)
        : ControlBase(pParent, u"svt/ui/thineditcontrol.ui"_ustr, u"EditControl"_ustr) // *thin*editcontrol has no frame/border
        , m_pEntry(nullptr) // inheritors are expected to call InitEditControlBase
    {
    }
 
    void EditControlBase::InitEditControlBase(weld::Entry* pEntry)
    {
        InitControlBase(pEntry);
        m_pEntry = pEntry;
        m_pEntry->show();
        m_pEntry->set_width_chars(1); // so a smaller than default width can be used
        connect_focus_in(LINK(this, ControlBase, FocusInHdl));   // need to chain with pattern handler
        connect_focus_out(LINK(this, ControlBase, FocusOutHdl)); // need to chain with pattern handler
        connect_key_press(LINK(this, ControlBase, KeyInputHdl)); // need to chain with pattern handler
        m_pEntry->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl));
        m_pEntry->connect_mouse_press(LINK(this, ControlBase, MousePressHdl));
        m_pEntry->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl));
        m_pEntry->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl));
    }
 
    bool ControlBase::ProcessKey(const KeyEvent& rKEvt)
    {
        return static_cast<BrowserDataWin*>(GetParent())->GetParent()->ProcessKey(rKEvt);
    }
 
    IMPL_LINK(ControlBase, KeyInputHdl, const KeyEvent&, rKEvt, bool)
    {
        m_aKeyInputHdl.Call(rKEvt);
        return ProcessKey(rKEvt);
    }
 
    IMPL_LINK(ControlBase, KeyReleaseHdl, const KeyEvent&, rKEvt, bool)
    {
        m_aKeyReleaseHdl.Call(rKEvt);
        return false;
    }
 
    IMPL_LINK_NOARG(ControlBase, FocusInHdl, weld::Widget&, void)
    {
        m_aFocusInHdl.Call(nullptr);
        static_cast<BrowserDataWin*>(GetParent())->GetParent()->ChildFocusIn();
    }
 
    IMPL_LINK_NOARG(ControlBase, FocusOutHdl, weld::Widget&, void)
    {
        m_aFocusOutHdl.Call(nullptr);
        static_cast<BrowserDataWin*>(GetParent())->GetParent()->ChildFocusOut();
    }
 
    IMPL_LINK(ControlBase, MousePressHdl, const MouseEvent&, rEvent, bool)
    {
        m_aMousePressHdl.Call(rEvent);
        return false;
    }
 
    IMPL_LINK(ControlBase, MouseReleaseHdl, const MouseEvent&, rEvent, bool)
    {
        m_aMouseReleaseHdl.Call(rEvent);
        return false;
    }
 
    IMPL_LINK(ControlBase, MouseMoveHdl, const MouseEvent&, rEvent, bool)
    {
        m_aMouseMoveHdl.Call(rEvent);
        return false;
    }
 
    void EditControlBase::dispose()
    {
        m_pEntry = nullptr;
        ControlBase::dispose();
    }
 
    EditControl::EditControl(BrowserDataWin* pParent)
        : EditControlBase(pParent)
        , m_xWidget(m_xBuilder->weld_entry(u"entry"_ustr))
    {
        InitEditControlBase(m_xWidget.get());
    }
 
    void EditControl::dispose()
    {
        m_xWidget.reset();
        EditControlBase::dispose();
    }
 
    FormattedControlBase::FormattedControlBase(BrowserDataWin* pParent, bool bSpinVariant)
        : EditControlBase(pParent)
        , m_bSpinVariant(bSpinVariant)
        , m_xEntry(m_xBuilder->weld_entry(u"entry"_ustr))
        , m_xSpinButton(m_xBuilder->weld_formatted_spin_button(u"spinbutton"_ustr))
    {
    }
 
    void FormattedControlBase::InitFormattedControlBase()
    {
        InitEditControlBase(m_bSpinVariant ? m_xSpinButton.get() : m_xEntry.get());
    }
 
    void FormattedControlBase::connect_changed(const Link<weld::Entry&, void>& rLink)
    {
        get_formatter().connect_changed(rLink);
    }
 
    void FormattedControlBase::connect_focus_in(const Link<weld::Widget&, void>& rLink)
    {
        get_widget().connect_focus_in(rLink);
    }
 
    void FormattedControlBase::connect_focus_out(const Link<weld::Widget&, void>& rLink)
    {
        get_formatter().connect_focus_out(rLink);
    }
 
    void FormattedControlBase::connect_key_press(const Link<const KeyEvent&, bool>& rLink)
    {
        get_widget().connect_key_press(rLink);
    }
 
    weld::EntryFormatter& FormattedControlBase::get_formatter()
    {
        return *m_xEntryFormatter;
    }
 
    void FormattedControlBase::dispose()
    {
        m_xEntryFormatter.reset();
        m_xSpinButton.reset();
        m_xEntry.reset();
        EditControlBase::dispose();
    }
 
    FormattedControl::FormattedControl(BrowserDataWin* pParent, bool bSpinVariant)
        : FormattedControlBase(pParent, bSpinVariant)
    {
        if (bSpinVariant)
            m_xEntryFormatter.reset(new weld::EntryFormatter(*m_xSpinButton));
        else
            m_xEntryFormatter.reset(new weld::EntryFormatter(*m_xEntry));
        InitFormattedControlBase();
    }
 
    DoubleNumericControl::DoubleNumericControl(BrowserDataWin* pParent, bool bSpinVariant)
        : FormattedControlBase(pParent, bSpinVariant)
    {
        if (bSpinVariant)
            m_xEntryFormatter.reset(new weld::DoubleNumericFormatter(*m_xSpinButton));
        else
            m_xEntryFormatter.reset(new weld::DoubleNumericFormatter(*m_xEntry));
        InitFormattedControlBase();
    }
 
    LongCurrencyControl::LongCurrencyControl(BrowserDataWin* pParent, bool bSpinVariant)
        : FormattedControlBase(pParent, bSpinVariant)
    {
        if (bSpinVariant)
            m_xEntryFormatter.reset(new weld::LongCurrencyFormatter(*m_xSpinButton));
        else
            m_xEntryFormatter.reset(new weld::LongCurrencyFormatter(*m_xEntry));
        InitFormattedControlBase();
    }
 
    TimeControl::TimeControl(BrowserDataWin* pParent, bool bSpinVariant)
        : FormattedControlBase(pParent, bSpinVariant)
    {
        if (bSpinVariant)
            m_xEntryFormatter.reset(new weld::TimeFormatter(*m_xSpinButton));
        else
            m_xEntryFormatter.reset(new weld::TimeFormatter(*m_xEntry));
        InitFormattedControlBase();
    }
 
    DateControl::DateControl(BrowserDataWin* pParent, bool bDropDown)
        : FormattedControlBase(pParent, false)
        , m_xMenuButton(m_xBuilder->weld_menu_button(u"button"_ustr))
        , m_xCalendarBuilder(Application::CreateBuilder(m_xMenuButton.get(), u"svt/ui/datewindow.ui"_ustr))
        , m_xTopLevel(m_xCalendarBuilder->weld_widget(u"date_popup_window"_ustr))
        , m_xCalendar(m_xCalendarBuilder->weld_calendar(u"date_picker"_ustr))
        , m_xExtras(m_xCalendarBuilder->weld_widget(u"extras"_ustr))
        , m_xTodayBtn(m_xCalendarBuilder->weld_button(u"today"_ustr))
        , m_xNoneBtn(m_xCalendarBuilder->weld_button(u"none"_ustr))
    {
        m_xEntryFormatter.reset(new weld::DateFormatter(*m_xEntry));
        InitFormattedControlBase();
 
        m_xMenuButton->set_popover(m_xTopLevel.get());
        m_xMenuButton->set_visible(bDropDown);
        m_xMenuButton->connect_toggled(LINK(this, DateControl, ToggleHdl));
 
        m_xExtras->show();
 
        m_xTodayBtn->connect_clicked(LINK(this, DateControl, ImplClickHdl));
        m_xNoneBtn->connect_clicked(LINK(this, DateControl, ImplClickHdl));
 
        m_xCalendar->connect_activated(LINK(this, DateControl, ActivateHdl));
    }
 
    IMPL_LINK(DateControl, ImplClickHdl, weld::Button&, rBtn, void)
    {
        m_xMenuButton->set_active(false);
        get_widget().grab_focus();
 
        if (&rBtn == m_xTodayBtn.get())
        {
            Date aToday(Date::SYSTEM);
            SetDate(aToday);
        }
        else if (&rBtn == m_xNoneBtn.get())
        {
            get_widget().set_text(OUString());
        }
    }
 
    IMPL_LINK(DateControl, ToggleHdl, weld::Toggleable&, rButton, void)
    {
        if (rButton.get_active())
            m_xCalendar->set_date(static_cast<weld::DateFormatter&>(get_formatter()).GetDate());
    }
 
    IMPL_LINK_NOARG(DateControl, ActivateHdl, weld::Calendar&, void)
    {
        if (m_xMenuButton->get_active())
            m_xMenuButton->set_active(false);
        static_cast<weld::DateFormatter&>(get_formatter()).SetDate(m_xCalendar->get_date());
    }
 
    void DateControl::SetDate(const Date& rDate)
    {
        static_cast<weld::DateFormatter&>(get_formatter()).SetDate(rDate);
        m_xCalendar->set_date(rDate);
    }
 
    void DateControl::SetEditableReadOnly(bool bReadOnly)
    {
        FormattedControlBase::SetEditableReadOnly(bReadOnly);
        m_xMenuButton->set_sensitive(!bReadOnly);
    }
 
    void DateControl::dispose()
    {
        m_xTodayBtn.reset();
        m_xNoneBtn.reset();
        m_xExtras.reset();
        m_xCalendar.reset();
        m_xTopLevel.reset();
        m_xCalendarBuilder.reset();
        m_xMenuButton.reset();
        FormattedControlBase::dispose();
    }
 
    PatternControl::PatternControl(BrowserDataWin* pParent)
        : EditControlBase(pParent)
        , m_xWidget(m_xBuilder->weld_entry(u"entry"_ustr))
    {
        m_xEntryFormatter.reset(new weld::PatternFormatter(*m_xWidget));
        InitEditControlBase(m_xWidget.get());
    }
 
    void PatternControl::connect_changed(const Link<weld::Entry&, void>& rLink)
    {
        m_xEntryFormatter->connect_changed(rLink);
    }
 
    void PatternControl::connect_focus_in(const Link<weld::Widget&, void>& rLink)
    {
        m_xEntryFormatter->connect_focus_in(rLink);
    }
 
    void PatternControl::connect_focus_out(const Link<weld::Widget&, void>& rLink)
    {
        m_xEntryFormatter->connect_focus_out(rLink);
    }
 
    void PatternControl::connect_key_press(const Link<const KeyEvent&, bool>& rLink)
    {
        m_xEntryFormatter->connect_key_press(rLink);
    }
 
    void PatternControl::dispose()
    {
        m_xEntryFormatter.reset();
        m_xWidget.reset();
        EditControlBase::dispose();
    }
 
    EditCellController::EditCellController(EditControlBase* pEdit)
        : CellController(pEdit)
        , m_pEditImplementation(new EntryImplementation(*pEdit))
        , m_bOwnImplementation(true)
    {
        m_pEditImplementation->SetModifyHdl( LINK(this, EditCellController, ModifyHdl) );
    }
 
    EditCellController::~EditCellController( )
    {
        if ( m_bOwnImplementation )
            delete m_pEditImplementation;
    }
 
    void EditCellController::SaveValue()
    {
        m_pEditImplementation->SaveValue();
    }
 
    bool EditCellController::MoveAllowed(const KeyEvent& rEvt) const
    {
        bool bResult;
        switch (rEvt.GetKeyCode().GetCode())
        {
            case KEY_END:
            case KEY_RIGHT:
            {
                Selection aSel = m_pEditImplementation->GetSelection();
                bResult = !aSel && aSel.Max() == m_pEditImplementation->GetText( LINEEND_LF ).getLength();
                break;
            }
            case KEY_HOME:
            case KEY_LEFT:
            {
                Selection aSel = m_pEditImplementation->GetSelection();
                bResult = !aSel && aSel.Min() == 0;
                break;
            }
            case KEY_DOWN:
            {
                bResult = !m_pEditImplementation->CanDown();
                break;
            }
            case KEY_UP:
            {
                bResult = !m_pEditImplementation->CanUp();
                break;
            }
            default:
                bResult = true;
        }
        return bResult;
    }
 
    bool EditCellController::IsValueChangedFromSaved() const
    {
        return m_pEditImplementation->IsValueChangedFromSaved();
    }
 
    IMPL_LINK_NOARG(EditCellController, ModifyHdl, LinkParamNone*, void)
    {
        callModifyHdl();
    }
 
    //= FormattedFieldCellController
    FormattedFieldCellController::FormattedFieldCellController( FormattedControlBase* _pFormatted )
        : EditCellController(_pFormatted)
    {
    }
 
    void FormattedFieldCellController::CommitModifications()
    {
        static_cast<FormattedControl&>(GetWindow()).get_formatter().Commit();
    }
 
    MultiLineTextCell::MultiLineTextCell(BrowserDataWin* pParent)
        : ControlBase(pParent, u"svt/ui/textviewcontrol.ui"_ustr, u"TextViewControl"_ustr)
        , m_xWidget(m_xBuilder->weld_text_view(u"textview"_ustr))
    {
        InitControlBase(m_xWidget.get());
        m_xWidget->connect_key_press(LINK(this, ControlBase, KeyInputHdl));
        m_xWidget->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl));
        m_xWidget->connect_focus_in(LINK(this, ControlBase, FocusInHdl));
        m_xWidget->connect_focus_out(LINK(this, ControlBase, FocusOutHdl));
        m_xWidget->connect_mouse_press(LINK(this, ControlBase, MousePressHdl));
        m_xWidget->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl));
        m_xWidget->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl));
        // so any the natural size doesn't have an effect
        m_xWidget->set_size_request(1, 1);
    }
 
    void MultiLineTextCell::GetFocus()
    {
        if (m_xWidget)
            m_xWidget->select_region(-1, 0);
        ControlBase::GetFocus();
    }
 
    void MultiLineTextCell::dispose()
    {
        m_xWidget.reset();
        ControlBase::dispose();
    }
 
    bool MultiLineTextCell::ProcessKey(const KeyEvent& rKEvt)
    {
        bool bSendToDataWindow = true;
 
        sal_uInt16 nCode  = rKEvt.GetKeyCode().GetCode();
        bool bShift = rKEvt.GetKeyCode().IsShift();
        bool bCtrl = rKEvt.GetKeyCode().IsMod1();
        bool bAlt =  rKEvt.GetKeyCode().IsMod2();
 
        if (!bAlt && !bCtrl && !bShift)
        {
            switch (nCode)
            {
                case KEY_DOWN:
                    bSendToDataWindow = !m_xWidget->can_move_cursor_with_down();
                    break;
                case KEY_UP:
                    bSendToDataWindow = !m_xWidget->can_move_cursor_with_up();
                    break;
            }
        }
 
        if (bSendToDataWindow)
            return ControlBase::ProcessKey(rKEvt);
        return false;
    }
}   // namespace svt
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1053 Calling the 'connect_focus_in' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'ebbcontrols.cxx:434', 'ebbcontrols.cxx:368', 'editbrowsebox.hxx:276'.