265b00d994
During LoadExistingRules, each call to AppendNewRuleTreeItem triggered SelectItem, which fired the selection-changed handler. That handler created a full PANEL_DRC_RULE_EDITOR per rule, compiling 14 regex patterns, instantiating Scintilla, running layout, then immediately destroying it when the next rule was appended. Also fix other potential slow downs
1221 lines
40 KiB
C++
1221 lines
40 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include <widgets/resettable_panel.h>
|
|
#include <widgets/wx_infobar.h>
|
|
#include <widgets/wx_panel.h>
|
|
#include <widgets/ui_common.h>
|
|
|
|
#include <wx/button.h>
|
|
#include <wx/grid.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/treectrl.h>
|
|
#include <wx/listctrl.h>
|
|
#include <wx/stc/stc.h>
|
|
#include <wx/menu.h>
|
|
#include <wx/log.h>
|
|
#include <wx/wx.h>
|
|
#include <wx/dnd.h>
|
|
#include <wx/dcclient.h> // Include for wxClientDC if needed
|
|
#include <wx/dcbuffer.h> // Include for double-buffered drawing
|
|
#include <wx/bitmap.h>
|
|
|
|
#include <confirm.h>
|
|
#include <paths.h>
|
|
#include <bitmaps.h>
|
|
#include <launch_ext.h>
|
|
#include <algorithm>
|
|
#include <dialogs/rule_editor_dialog_base.h>
|
|
#include <widgets/std_bitmap_button.h>
|
|
|
|
|
|
static wxSearchCtrl* CreateTextFilterBox( wxWindow* aParent, const wxString& aDescriptiveText )
|
|
{
|
|
wxSearchCtrl* search_widget = new wxSearchCtrl( aParent, wxID_ANY );
|
|
|
|
search_widget->ShowSearchButton( false );
|
|
search_widget->ShowCancelButton( true );
|
|
|
|
search_widget->SetDescriptiveText( aDescriptiveText );
|
|
|
|
#ifdef __WXGTK__
|
|
// wxSearchCtrl vertical height is not calculated correctly on some GTK setups
|
|
// See https://gitlab.com/kicad/code/kicad/-/issues/9019
|
|
search_widget->SetMinSize( wxSize( -1, aParent->GetTextExtent( wxT( "qb" ) ).y + 10 ) );
|
|
#endif
|
|
|
|
return search_widget;
|
|
}
|
|
|
|
|
|
RULE_EDITOR_DIALOG_BASE::RULE_EDITOR_DIALOG_BASE( wxWindow* aParent, const wxString& aTitle,
|
|
const wxSize& aInitialSize ) :
|
|
DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, aInitialSize,
|
|
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
|
|
m_isDragging( false ),
|
|
m_enableMoveUp( false ),
|
|
m_enableMoveDown( false ),
|
|
m_enableAddRule( false ),
|
|
m_enableDuplicateRule( false ),
|
|
m_enableDeleteRule( false ),
|
|
m_modified( false ),
|
|
m_suppressSelectionEvents( false ),
|
|
m_defaultSashPosition( 200 ),
|
|
m_title( aTitle ),
|
|
m_selectedData( nullptr ),
|
|
m_previousId( nullptr ),
|
|
m_draggedItem( nullptr )
|
|
{
|
|
wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
|
|
SetSizer( mainSizer );
|
|
|
|
wxBoxSizer* infoBarSizer = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
m_infoBar = new WX_INFOBAR( this );
|
|
infoBarSizer->Add( m_infoBar, 0, wxEXPAND, 0 );
|
|
|
|
// Add the tree panel to the content sizer
|
|
m_splitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
|
wxBORDER_NONE | wxSP_LIVE_UPDATE | wxSP_3DSASH );
|
|
m_splitter->SetMinimumPaneSize( 50 ); // Set a minimum pane size
|
|
|
|
// Create the tree control panel
|
|
WX_PANEL* treeCtrlPanel = new WX_PANEL( m_splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
|
wxBORDER_NONE | wxTAB_TRAVERSAL );
|
|
treeCtrlPanel->SetBorders( false, false, false, true );
|
|
wxBoxSizer* treeCtrlSizer = new wxBoxSizer( wxVERTICAL );
|
|
treeCtrlPanel->SetSizer( treeCtrlSizer );
|
|
|
|
// Add a search text box above the tree control
|
|
m_filterSearch = CreateTextFilterBox( treeCtrlPanel, _( "Type filter text" ) );
|
|
treeCtrlSizer->Add( m_filterSearch, 0, wxEXPAND, 5 );
|
|
|
|
// Create the tree control and set its font
|
|
m_ruleTreeCtrl = new wxTreeCtrl( treeCtrlPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
|
wxTR_DEFAULT_STYLE | wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS | wxBORDER_NONE );
|
|
m_ruleTreeCtrl->SetFont( KIUI::GetControlFont( this ) );
|
|
|
|
// Add the tree control to its sizer
|
|
treeCtrlSizer->Add( m_ruleTreeCtrl, 1, wxEXPAND | wxBOTTOM, 3 );
|
|
|
|
// Create a sizer for the action buttons
|
|
wxBoxSizer* actionButtonsSizer = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
// Create the action buttons
|
|
m_addRuleButton = new STD_BITMAP_BUTTON( treeCtrlPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
|
wxDefaultSize, wxBU_AUTODRAW );
|
|
m_copyRuleButton = new STD_BITMAP_BUTTON( treeCtrlPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
|
wxDefaultSize, wxBU_AUTODRAW );
|
|
m_moveTreeItemUpButton = new STD_BITMAP_BUTTON( treeCtrlPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
|
wxDefaultSize, wxBU_AUTODRAW );
|
|
m_moveTreeItemDownButton = new STD_BITMAP_BUTTON( treeCtrlPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
|
wxDefaultSize, wxBU_AUTODRAW );
|
|
m_deleteRuleButton = new STD_BITMAP_BUTTON( treeCtrlPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
|
wxDefaultSize, wxBU_AUTODRAW );
|
|
|
|
actionButtonsSizer->Add( m_addRuleButton, 0, wxLEFT, 5 );
|
|
actionButtonsSizer->Add( m_copyRuleButton, 0, wxLEFT, 5 );
|
|
actionButtonsSizer->Add( m_moveTreeItemUpButton, 0, wxLEFT, 5 );
|
|
actionButtonsSizer->Add( m_moveTreeItemDownButton, 0, wxLEFT, 5 );
|
|
|
|
// Add a spacer between the "Move" buttons and the "Delete" button
|
|
actionButtonsSizer->AddSpacer( 20 ); // Spacer between the Move and Delete buttons
|
|
|
|
// Add the "Delete" button at the end
|
|
actionButtonsSizer->Add( m_deleteRuleButton, 0, wxRIGHT, 5 );
|
|
|
|
// Add the action buttons sizer to the tree control sizer
|
|
treeCtrlSizer->Add( actionButtonsSizer, 0, wxBOTTOM | wxEXPAND, 5 );
|
|
|
|
treeCtrlPanel->Layout();
|
|
|
|
m_addRuleButton->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
|
|
m_copyRuleButton->SetBitmap( KiBitmapBundle( BITMAPS::copy ) );
|
|
m_moveTreeItemUpButton->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
|
|
m_moveTreeItemDownButton->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
|
|
m_deleteRuleButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
|
|
|
|
// Create a scrolled window for the dynamic content panel
|
|
m_scrolledContentWin = new wxScrolledWindow( m_splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
|
wxVSCROLL | wxBORDER_NONE );
|
|
m_scrolledContentWin->SetScrollRate( 0, 10 );
|
|
m_scrolledContentWin->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
|
|
m_contentPanel = nullptr;
|
|
|
|
// Add the tree control panel to the splitter.
|
|
// Calculate the minimum tree panel width based on the tree control's best size.
|
|
treeCtrlPanel->Layout();
|
|
int minTreeWidth = treeCtrlPanel->GetBestSize().GetWidth();
|
|
|
|
// Ensure a reasonable minimum width for the tree panel
|
|
if( minTreeWidth < 240 )
|
|
minTreeWidth = 240;
|
|
|
|
m_defaultSashPosition = minTreeWidth;
|
|
|
|
m_splitter->SplitVertically( treeCtrlPanel, m_scrolledContentWin, m_defaultSashPosition );
|
|
|
|
// Set gravity to 0 so the tree panel maintains its size and the content panel resizes
|
|
m_splitter->SetSashGravity( 0.0 );
|
|
|
|
wxBoxSizer* m_sizerButtons = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
wxStdDialogButtonSizer* m_sdbSizer = new wxStdDialogButtonSizer();
|
|
m_cancelRuleButton = new wxButton( this, wxID_CANCEL );
|
|
m_cancelRuleButton->SetLabelText( _( "Close" ) );
|
|
m_sdbSizer->AddButton( m_cancelRuleButton );
|
|
m_saveRuleButton = new wxButton( this, wxID_OK );
|
|
m_saveRuleButton->SetLabelText( _( "Save" ) );
|
|
m_sdbSizer->AddButton( m_saveRuleButton );
|
|
m_sdbSizer->Realize();
|
|
|
|
m_sizerButtons->Add( m_sdbSizer, 1, wxEXPAND, 5 );
|
|
|
|
|
|
// Add the info bar sizer to the main sizer
|
|
mainSizer->Add( infoBarSizer, 0, wxEXPAND, 0 );
|
|
mainSizer->Add( m_splitter, 1, wxEXPAND | wxBOTTOM, 5 );
|
|
mainSizer->Add( m_sizerButtons, 0, wxALL | wxEXPAND, 5 );
|
|
|
|
if( aInitialSize != wxDefaultSize )
|
|
SetSize( aInitialSize );
|
|
|
|
Layout();
|
|
|
|
// Bind the context menu event
|
|
m_filterSearch->Bind( wxEVT_COMMAND_TEXT_UPDATED, &RULE_EDITOR_DIALOG_BASE::onFilterSearch, this );
|
|
m_ruleTreeCtrl->Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemRightClick, this );
|
|
m_ruleTreeCtrl->Bind( wxEVT_TREE_SEL_CHANGED, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemSelectionChanged, this );
|
|
m_ruleTreeCtrl->Bind( wxEVT_TREE_ITEM_ACTIVATED, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemActivated, this );
|
|
m_ruleTreeCtrl->Bind( wxEVT_LEFT_DOWN, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftDown, this );
|
|
m_ruleTreeCtrl->Bind( wxEVT_MOTION, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemMouseMotion, this );
|
|
m_ruleTreeCtrl->Bind( wxEVT_LEFT_UP, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftUp, this );
|
|
|
|
m_addRuleButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onNewRuleOptionClick, this );
|
|
m_copyRuleButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onDuplicateRuleOptionClick, this );
|
|
m_moveTreeItemUpButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onMoveUpRuleOptionClick, this );
|
|
m_moveTreeItemDownButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onMoveDownRuleOptionClick, this );
|
|
m_deleteRuleButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onDeleteRuleOptionClick, this );
|
|
m_saveRuleButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::OnSave, this );
|
|
m_cancelRuleButton->Bind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::OnCancel, this );
|
|
|
|
// Initially disable Save button until modifications are made
|
|
m_saveRuleButton->Enable( false );
|
|
|
|
this->Bind( wxEVT_SIZE, &RULE_EDITOR_DIALOG_BASE::onResize, this );
|
|
|
|
this->Bind( wxEVT_CLOSE_WINDOW, &RULE_EDITOR_DIALOG_BASE::onClose, this );
|
|
|
|
m_scrolledContentWin->Bind( wxEVT_SIZE,
|
|
[this]( wxSizeEvent& evt )
|
|
{
|
|
RefreshContentScrollArea();
|
|
evt.Skip();
|
|
} );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::finishInitialization()
|
|
{
|
|
finishDialogSettings();
|
|
|
|
// finishDialogSettings() calls GetSizer()->SetSizeHints() which inflates the dialog
|
|
// to the full unconstrained best size of the scrolled content area. Reset to the
|
|
// caller's initial size and a reasonable minimum so Show() doesn't inherit the
|
|
// inflated values.
|
|
SetMinSize( wxSize( 400, 500 ) );
|
|
SetSize( m_initialSize );
|
|
|
|
wxLogTrace( "debug_dlg_size", "finishInitialization: size=%dx%d minSize=%dx%d bestSize=%dx%d",
|
|
GetSize().x, GetSize().y, GetMinSize().x, GetMinSize().y, GetBestSize().x,
|
|
GetBestSize().y );
|
|
}
|
|
|
|
|
|
bool RULE_EDITOR_DIALOG_BASE::Show( bool show )
|
|
{
|
|
bool ret = DIALOG_SHIM::Show( show );
|
|
|
|
if( show )
|
|
{
|
|
// DIALOG_SHIM::Show() sets SetMinSize(GetBestSize()) which includes the unconstrained
|
|
// height of the scrolled content area. Cap the minimum to a usable value so the user
|
|
// can resize the dialog smaller.
|
|
wxSize minSize = GetMinSize();
|
|
|
|
if( minSize.GetHeight() > 500 )
|
|
SetMinSize( wxSize( minSize.GetWidth(), 500 ) );
|
|
|
|
wxLogTrace( "debug_dlg_size", "Show: size=%dx%d minSize=%dx%d",
|
|
GetSize().x, GetSize().y, GetMinSize().x, GetMinSize().y );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
RULE_EDITOR_DIALOG_BASE::~RULE_EDITOR_DIALOG_BASE()
|
|
{
|
|
m_filterSearch->Unbind( wxEVT_COMMAND_TEXT_UPDATED, &RULE_EDITOR_DIALOG_BASE::onFilterSearch, this );
|
|
m_ruleTreeCtrl->Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemRightClick, this );
|
|
m_ruleTreeCtrl->Unbind( wxEVT_TREE_SEL_CHANGED, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemSelectionChanged, this );
|
|
m_ruleTreeCtrl->Unbind( wxEVT_TREE_ITEM_ACTIVATED, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemActivated, this );
|
|
m_ruleTreeCtrl->Unbind( wxEVT_LEFT_DOWN, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftDown, this );
|
|
m_ruleTreeCtrl->Unbind( wxEVT_MOTION, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemMouseMotion, this );
|
|
m_ruleTreeCtrl->Unbind( wxEVT_LEFT_UP, &RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftUp, this );
|
|
|
|
m_addRuleButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onNewRuleOptionClick, this );
|
|
m_copyRuleButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onDuplicateRuleOptionClick, this );
|
|
m_moveTreeItemUpButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onMoveUpRuleOptionClick, this );
|
|
m_moveTreeItemDownButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onMoveDownRuleOptionClick, this );
|
|
m_deleteRuleButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::onDeleteRuleOptionClick, this );
|
|
m_saveRuleButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::OnSave, this );
|
|
m_cancelRuleButton->Unbind( wxEVT_BUTTON, &RULE_EDITOR_DIALOG_BASE::OnCancel, this );
|
|
|
|
this->Unbind( wxEVT_SIZE, &RULE_EDITOR_DIALOG_BASE::onResize, this );
|
|
this->Unbind( wxEVT_CLOSE_WINDOW, &RULE_EDITOR_DIALOG_BASE::onClose, this );
|
|
|
|
m_selectedData = nullptr;
|
|
m_previousId = nullptr;
|
|
}
|
|
|
|
|
|
bool RULE_EDITOR_DIALOG_BASE::TransferDataToWindow()
|
|
{
|
|
finishInitialization();
|
|
|
|
// Call TransferDataToWindow() only once:
|
|
// this is enough on wxWidgets 3.1
|
|
if( !DIALOG_SHIM::TransferDataToWindow() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool RULE_EDITOR_DIALOG_BASE::TransferDataFromWindow()
|
|
{
|
|
bool ret = true;
|
|
|
|
// Call TransferDataFromWindow() only once:
|
|
// this is enough on wxWidgets 3.1
|
|
if( !DIALOG_SHIM::TransferDataFromWindow() )
|
|
ret = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::OnCharHook( wxKeyEvent& aEvt )
|
|
{
|
|
if( aEvt.GetKeyCode() == 'S' && aEvt.ControlDown() && !aEvt.ShiftDown() && !aEvt.AltDown() )
|
|
{
|
|
wxCommandEvent evt;
|
|
OnSave( evt );
|
|
return;
|
|
}
|
|
|
|
DIALOG_SHIM::OnCharHook( aEvt );
|
|
}
|
|
|
|
|
|
RULE_EDITOR_DIALOG_BASE* RULE_EDITOR_DIALOG_BASE::GetDialog( wxWindow* aParent )
|
|
{
|
|
while( aParent )
|
|
{
|
|
if( RULE_EDITOR_DIALOG_BASE* currentDialog = dynamic_cast<RULE_EDITOR_DIALOG_BASE*>( aParent ) )
|
|
return currentDialog;
|
|
|
|
aParent = aParent->GetParent();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::InitRuleTreeItems( const std::vector<RULE_TREE_NODE>& aNodes )
|
|
{
|
|
if( aNodes.empty() )
|
|
return;
|
|
|
|
m_defaultTreeItems = aNodes;
|
|
|
|
m_ruleTreeCtrl->Freeze();
|
|
m_suppressSelectionEvents = true;
|
|
|
|
wxTreeItemId rootId = m_ruleTreeCtrl->AddRoot( aNodes[0].m_nodeName );
|
|
|
|
RULE_TREE_ITEM_DATA* itemData = new RULE_TREE_ITEM_DATA( aNodes[0].m_nodeId, nullptr, rootId );
|
|
m_ruleTreeCtrl->SetItemData( rootId, itemData );
|
|
|
|
std::vector<RULE_TREE_NODE> childNodes;
|
|
getRuleTreeChildNodes( aNodes, aNodes[0].m_nodeId, childNodes );
|
|
|
|
for( const auto& child : childNodes )
|
|
{
|
|
populateRuleTreeCtrl( aNodes, child, rootId );
|
|
}
|
|
|
|
m_treeHistoryData.clear();
|
|
saveRuleTreeState( m_ruleTreeCtrl->GetRootItem(), m_defaultTreeItems[0].m_nodeId );
|
|
|
|
m_ruleTreeCtrl->CollapseAll();
|
|
|
|
m_suppressSelectionEvents = false;
|
|
m_ruleTreeCtrl->Thaw();
|
|
|
|
m_ruleTreeCtrl->SelectItem( m_ruleTreeCtrl->GetFirstVisibleItem() );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::SetContentPanel( wxPanel* aContentPanel )
|
|
{
|
|
m_scrolledContentWin->Freeze();
|
|
|
|
if( m_contentPanel )
|
|
{
|
|
unregisterUnitBinders( m_contentPanel );
|
|
m_contentPanel->Destroy();
|
|
m_contentPanel = nullptr;
|
|
}
|
|
|
|
m_contentPanel = aContentPanel;
|
|
m_scrolledContentWin->SetBackgroundColour( m_contentPanel->GetBackgroundColour() );
|
|
|
|
resetUndoRedoForNewContent( m_contentPanel->GetChildren() );
|
|
|
|
Layout();
|
|
m_scrolledContentWin->Thaw();
|
|
|
|
CallAfter(
|
|
[this]()
|
|
{
|
|
RefreshContentScrollArea();
|
|
} );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::RefreshContentScrollArea()
|
|
{
|
|
if( !m_contentPanel || !m_scrolledContentWin )
|
|
return;
|
|
|
|
m_contentPanel->Layout();
|
|
|
|
wxSize clientSize = m_scrolledContentWin->GetClientSize();
|
|
wxSize bestSize = m_contentPanel->GetBestSize();
|
|
int width = clientSize.GetWidth();
|
|
int height = std::max( clientSize.GetHeight(), bestSize.GetHeight() );
|
|
|
|
m_contentPanel->SetSize( width, height );
|
|
m_scrolledContentWin->SetVirtualSize( width, height );
|
|
m_scrolledContentWin->Refresh();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::AppendNewRuleTreeItem( const RULE_TREE_NODE& aNode,
|
|
wxTreeItemId aParent )
|
|
{
|
|
wxTreeItemId currentTreeItemId = appendRuleTreeItem( aNode, aParent );
|
|
|
|
auto it = m_treeHistoryData.find( aNode.m_nodeData->GetParentId() );
|
|
|
|
if( it != m_treeHistoryData.end() )
|
|
{
|
|
std::vector<int>& existingChildren = std::get<1>( it->second );
|
|
existingChildren.push_back( aNode.m_nodeId );
|
|
|
|
m_treeHistoryData[aNode.m_nodeId] = { aNode.m_nodeName, {}, currentTreeItemId };
|
|
}
|
|
|
|
if( !m_suppressSelectionEvents )
|
|
m_ruleTreeCtrl->SelectItem( currentTreeItemId );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::UpdateRuleTreeItemText( wxTreeItemId aItemId, wxString aItemText )
|
|
{
|
|
m_ruleTreeCtrl->SetItemText( aItemId, aItemText );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::SetControlsEnabled( bool aEnable )
|
|
{
|
|
m_ruleTreeCtrl->Enable( aEnable );
|
|
m_filterSearch->Enable( aEnable );
|
|
|
|
updateRuleTreeActionButtonsState( m_selectedData );
|
|
|
|
if( m_cancelRuleButton )
|
|
{
|
|
if( !aEnable )
|
|
{
|
|
m_cancelRuleButton->SetLabelText( _( "Cancel" ) );
|
|
}
|
|
else if( m_modified )
|
|
{
|
|
m_cancelRuleButton->SetLabelText( _( "Discard" ) );
|
|
}
|
|
else
|
|
{
|
|
m_cancelRuleButton->SetLabelText( _( "Close" ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::SetModified()
|
|
{
|
|
m_modified = true;
|
|
|
|
if( m_saveRuleButton )
|
|
m_saveRuleButton->Enable( true );
|
|
|
|
if( m_cancelRuleButton && m_ruleTreeCtrl->IsEnabled() )
|
|
m_cancelRuleButton->SetLabelText( _( "Discard" ) );
|
|
|
|
if( m_selectedData )
|
|
{
|
|
wxTreeItemId itemId = m_selectedData->GetTreeItemId();
|
|
if( itemId.IsOk() )
|
|
{
|
|
wxString currentText = m_ruleTreeCtrl->GetItemText( itemId );
|
|
|
|
if( !currentText.EndsWith( wxS( " *" ) ) )
|
|
{
|
|
m_ruleTreeCtrl->SetItemText( itemId, currentText + wxS( " *" ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::ClearModified()
|
|
{
|
|
m_modified = false;
|
|
|
|
if( m_saveRuleButton )
|
|
m_saveRuleButton->Enable( false );
|
|
|
|
if( m_cancelRuleButton && m_ruleTreeCtrl->IsEnabled() )
|
|
m_cancelRuleButton->SetLabelText( _( "Close" ) );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::DeleteRuleTreeItem( wxTreeItemId aItemId, const int& aNodeId )
|
|
{
|
|
if( m_selectedData && m_selectedData->GetNodeId() == aNodeId )
|
|
m_selectedData = nullptr;
|
|
|
|
m_ruleTreeCtrl->Delete( aItemId );
|
|
m_treeHistoryData.erase( aNodeId );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::populateRuleTreeCtrl( const std::vector<RULE_TREE_NODE>& aNodes,
|
|
const RULE_TREE_NODE& aNode, wxTreeItemId aParent )
|
|
{
|
|
wxTreeItemId currentId = appendRuleTreeItem( aNode, aParent );
|
|
|
|
std::vector<RULE_TREE_NODE> childNodes;
|
|
getRuleTreeChildNodes( aNodes, aNode.m_nodeId, childNodes );
|
|
|
|
for( const auto& child : childNodes )
|
|
{
|
|
populateRuleTreeCtrl( aNodes, child, currentId );
|
|
}
|
|
|
|
m_ruleTreeCtrl->Expand( currentId );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemRightClick( wxTreeEvent& aEvent )
|
|
{
|
|
wxTreeItemId item = aEvent.GetItem();
|
|
|
|
if( !item.IsOk() )
|
|
return;
|
|
|
|
m_ruleTreeCtrl->SelectItem( item );
|
|
|
|
RULE_TREE_ITEM_DATA* ruleTreeItemData = dynamic_cast<RULE_TREE_ITEM_DATA*>( m_ruleTreeCtrl->GetItemData( item ) );
|
|
|
|
if( !ruleTreeItemData )
|
|
return;
|
|
|
|
updateRuleTreeActionButtonsState( ruleTreeItemData );
|
|
|
|
wxMenu menu;
|
|
|
|
if( m_enableAddRule )
|
|
KIUI::AddMenuItem( &menu, ID_NEWRULE, _( "New Rule" ), KiBitmap( BITMAPS::small_plus ) );
|
|
|
|
if( m_enableAddRule )
|
|
KIUI::AddMenuItem( &menu, ID_COPYRULE, _( "Copy Rule" ), KiBitmap( BITMAPS::copy ) );
|
|
|
|
if( m_enableDeleteRule )
|
|
KIUI::AddMenuItem( &menu, ID_DELETERULE, _( "Delete Rule" ), KiBitmap( BITMAPS::small_trash ) );
|
|
|
|
if( isEnabled( ruleTreeItemData, RULE_EDITOR_TREE_CONTEXT_OPT::MOVE_UP ) && m_enableMoveUp )
|
|
KIUI::AddMenuItem( &menu, ID_MOVEUP, _( "Move Up" ), KiBitmap( BITMAPS::small_up ) );
|
|
|
|
if( isEnabled( ruleTreeItemData, RULE_EDITOR_TREE_CONTEXT_OPT::MOVE_DOWN ) && m_enableMoveDown )
|
|
KIUI::AddMenuItem( &menu, ID_MOVEDOWN, _( "Move Down" ), KiBitmap( BITMAPS::small_down ) );
|
|
|
|
menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
|
|
[&]( wxCommandEvent& aCmd )
|
|
{
|
|
switch( aCmd.GetId() )
|
|
{
|
|
case ID_NEWRULE: onNewRuleOptionClick( aCmd ); break;
|
|
|
|
case ID_COPYRULE: onDuplicateRuleOptionClick( aCmd ); break;
|
|
|
|
case ID_DELETERULE: onDeleteRuleOptionClick( aCmd ); break;
|
|
|
|
case ID_MOVEUP: onMoveUpRuleOptionClick( aCmd ); break;
|
|
|
|
case ID_MOVEDOWN: onMoveDownRuleOptionClick( aCmd ); break;
|
|
default: aCmd.Skip();
|
|
}
|
|
} );
|
|
|
|
PopupMenu( &menu );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemSelectionChanged( wxTreeEvent& aEvent )
|
|
{
|
|
if( m_suppressSelectionEvents )
|
|
return;
|
|
|
|
wxTreeItemId selectedItem = aEvent.GetItem();
|
|
|
|
RULE_TREE_ITEM_DATA* selectedItemData =
|
|
dynamic_cast<RULE_TREE_ITEM_DATA*>( m_ruleTreeCtrl->GetItemData( selectedItem ) );
|
|
|
|
if( !selectedItemData )
|
|
return;
|
|
|
|
if( m_selectedData )
|
|
{
|
|
if( m_selectedData->GetNodeId() == selectedItemData->GetNodeId() )
|
|
{
|
|
m_selectedData = selectedItemData;
|
|
updateRuleTreeActionButtonsState( selectedItemData );
|
|
return;
|
|
}
|
|
|
|
m_previousId = m_selectedData->GetTreeItemId();
|
|
}
|
|
|
|
m_selectedData = selectedItemData;
|
|
|
|
RuleTreeItemSelectionChanged( m_selectedData );
|
|
|
|
updateRuleTreeActionButtonsState( selectedItemData );
|
|
}
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemActivated( wxTreeEvent& aEvent )
|
|
{
|
|
RULE_TREE_ITEM_DATA* itemData =
|
|
dynamic_cast<RULE_TREE_ITEM_DATA*>( m_ruleTreeCtrl->GetItemData( aEvent.GetItem() ) );
|
|
|
|
if( itemData &&
|
|
isEnabled( itemData, RULE_EDITOR_TREE_CONTEXT_OPT::DUPLICATE_RULE ) )
|
|
{
|
|
DuplicateRule( itemData );
|
|
}
|
|
else if( itemData &&
|
|
isEnabled( itemData, RULE_EDITOR_TREE_CONTEXT_OPT::ADD_RULE ) )
|
|
{
|
|
AddNewRule( itemData );
|
|
}
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onNewRuleOptionClick( wxCommandEvent& aEvent )
|
|
{
|
|
AddNewRule( m_selectedData );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onDuplicateRuleOptionClick( wxCommandEvent& aEvent )
|
|
{
|
|
DuplicateRule( m_selectedData );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onDeleteRuleOptionClick( wxCommandEvent& aEvent )
|
|
{
|
|
RemoveRule( m_selectedData->GetNodeId() );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onMoveUpRuleOptionClick( wxCommandEvent& aEvent )
|
|
{
|
|
if( !m_enableMoveUp )
|
|
return;
|
|
|
|
wxTreeItemId selectedItem = m_ruleTreeCtrl->GetSelection();
|
|
|
|
if( !selectedItem.IsOk() )
|
|
return;
|
|
|
|
wxTreeItemId parent = m_ruleTreeCtrl->GetItemParent( selectedItem );
|
|
|
|
if( !parent.IsOk() )
|
|
return;
|
|
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId current = m_ruleTreeCtrl->GetFirstChild( parent, cookie );
|
|
wxTreeItemId previous;
|
|
wxTreeItemId beforePrevious;
|
|
|
|
while( current.IsOk() && current != selectedItem )
|
|
{
|
|
beforePrevious = previous;
|
|
previous = current;
|
|
current = m_ruleTreeCtrl->GetNextChild( parent, cookie );
|
|
}
|
|
|
|
if( !previous.IsOk() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
wxTreeItemId insertPosition = beforePrevious.IsOk() ? beforePrevious : wxTreeItemId();
|
|
|
|
wxTreeItemId newItem = m_ruleTreeCtrl->InsertItem( parent, insertPosition,
|
|
m_ruleTreeCtrl->GetItemText( selectedItem ) );
|
|
|
|
moveRuleTreeItemChildrensTooOnDrag( selectedItem, newItem );
|
|
|
|
wxTreeItemData* itemData = m_ruleTreeCtrl->GetItemData( selectedItem );
|
|
|
|
if( itemData )
|
|
{
|
|
RULE_TREE_ITEM_DATA* ruleTreeItemData = dynamic_cast<RULE_TREE_ITEM_DATA*>( itemData );
|
|
ruleTreeItemData->SetTreeItemId( newItem );
|
|
|
|
m_ruleTreeCtrl->SetItemData( newItem, itemData );
|
|
m_ruleTreeCtrl->SetItemData( selectedItem, nullptr ); // Detach data from the old item
|
|
|
|
saveRuleTreeState( newItem, ruleTreeItemData->GetNodeId() );
|
|
|
|
m_ruleTreeCtrl->SelectItem( newItem );
|
|
|
|
m_ruleTreeCtrl->Expand( newItem );
|
|
m_ruleTreeCtrl->ExpandAllChildren( newItem );
|
|
}
|
|
|
|
m_ruleTreeCtrl->DeleteChildren( selectedItem );
|
|
m_ruleTreeCtrl->Delete( selectedItem );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onMoveDownRuleOptionClick( wxCommandEvent& aEvent )
|
|
{
|
|
if( !m_enableMoveDown )
|
|
return;
|
|
|
|
wxTreeItemId selectedItem = m_ruleTreeCtrl->GetSelection();
|
|
|
|
if( !selectedItem.IsOk() )
|
|
return;
|
|
|
|
wxTreeItemId parent = m_ruleTreeCtrl->GetItemParent( selectedItem );
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId current = m_ruleTreeCtrl->GetFirstChild( parent, cookie );
|
|
|
|
while( current.IsOk() && current != selectedItem )
|
|
{
|
|
current = m_ruleTreeCtrl->GetNextChild( parent, cookie );
|
|
}
|
|
|
|
wxTreeItemId next = m_ruleTreeCtrl->GetNextChild( parent, cookie );
|
|
|
|
if( !next.IsOk() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
wxString itemText = m_ruleTreeCtrl->GetItemText( selectedItem );
|
|
wxTreeItemData* itemData = m_ruleTreeCtrl->GetItemData( selectedItem );
|
|
|
|
wxTreeItemId newItem = m_ruleTreeCtrl->InsertItem( parent, next, itemText );
|
|
|
|
moveRuleTreeItemChildrensTooOnDrag( selectedItem, newItem );
|
|
|
|
if( itemData )
|
|
{
|
|
RULE_TREE_ITEM_DATA* ruleTreeItemData = dynamic_cast<RULE_TREE_ITEM_DATA*>( itemData );
|
|
ruleTreeItemData->SetTreeItemId( newItem );
|
|
|
|
m_ruleTreeCtrl->SetItemData( newItem, itemData );
|
|
m_ruleTreeCtrl->SetItemData( selectedItem, nullptr ); // Detach data
|
|
|
|
saveRuleTreeState( newItem, ruleTreeItemData->GetNodeId() );
|
|
|
|
m_ruleTreeCtrl->SelectItem( newItem );
|
|
|
|
m_ruleTreeCtrl->Expand( newItem );
|
|
}
|
|
|
|
m_ruleTreeCtrl->DeleteChildren( selectedItem );
|
|
m_ruleTreeCtrl->Delete( selectedItem );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftDown( wxMouseEvent& aEvent )
|
|
{
|
|
wxPoint pt = aEvent.GetPosition();
|
|
int flags;
|
|
wxTreeItemId item = m_ruleTreeCtrl->HitTest( pt, flags );
|
|
|
|
// Check if click is on the expand/collapse button
|
|
if( flags & wxTREE_HITTEST_ONITEMBUTTON )
|
|
{
|
|
// Prevent wxEVT_LEFT_DOWN from propagating further
|
|
aEvent.Skip();
|
|
return;
|
|
}
|
|
|
|
if( item.IsOk() )
|
|
{
|
|
m_draggedItem = item;
|
|
m_isDragging = false;
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemMouseMotion( wxMouseEvent& aEvent )
|
|
{
|
|
if( aEvent.Dragging() && m_draggedItem.IsOk() )
|
|
{
|
|
m_isDragging = true;
|
|
|
|
// Clear previous highlight
|
|
if( m_dropTargetItem.IsOk() )
|
|
{
|
|
m_ruleTreeCtrl->SetItemBackgroundColour( m_dropTargetItem, wxNullColour );
|
|
m_dropTargetItem = wxTreeItemId();
|
|
}
|
|
|
|
// Highlight item under cursor
|
|
int flags;
|
|
wxTreeItemId hitItem = m_ruleTreeCtrl->HitTest( aEvent.GetPosition(), flags );
|
|
|
|
if( hitItem.IsOk() && hitItem != m_draggedItem )
|
|
{
|
|
wxTreeItemId hitParent = m_ruleTreeCtrl->GetItemParent( hitItem );
|
|
wxTreeItemId dragParent = m_ruleTreeCtrl->GetItemParent( m_draggedItem );
|
|
|
|
if( hitParent == dragParent )
|
|
{
|
|
m_ruleTreeCtrl->SetItemBackgroundColour( hitItem,
|
|
wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
|
|
m_dropTargetItem = hitItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftUp( wxMouseEvent& aEvent )
|
|
{
|
|
if( m_draggedItem.IsOk() && m_isDragging )
|
|
{
|
|
m_isDragging = false;
|
|
|
|
if( m_dropTargetItem.IsOk() )
|
|
{
|
|
m_ruleTreeCtrl->SetItemBackgroundColour( m_dropTargetItem, wxNullColour );
|
|
m_dropTargetItem = wxTreeItemId();
|
|
}
|
|
|
|
wxPoint pos = aEvent.GetPosition();
|
|
int flags;
|
|
wxTreeItemId targetItem = m_ruleTreeCtrl->HitTest( pos, flags );
|
|
|
|
if( !targetItem.IsOk() )
|
|
return;
|
|
|
|
wxTreeItemId draggedParent = m_ruleTreeCtrl->GetItemParent( m_draggedItem );
|
|
wxTreeItemId targetParent = m_ruleTreeCtrl->GetItemParent( targetItem );
|
|
|
|
if( draggedParent != targetParent )
|
|
return;
|
|
|
|
if( draggedParent == targetParent )
|
|
{
|
|
wxTreeItemId newItem;
|
|
wxTreeItemIdValue cookie;
|
|
|
|
if( flags & wxTREE_HITTEST_ONITEMLABEL
|
|
&& targetItem == m_ruleTreeCtrl->GetFirstChild( targetParent, cookie ) )
|
|
{
|
|
// Prepend item as the first child of target parent
|
|
newItem = m_ruleTreeCtrl->PrependItem( targetParent,
|
|
m_ruleTreeCtrl->GetItemText( m_draggedItem ) );
|
|
}
|
|
else
|
|
{
|
|
wxTreeItemId current = m_ruleTreeCtrl->GetFirstChild( targetParent, cookie );
|
|
wxTreeItemId previous;
|
|
|
|
// Traverse through siblings to find the position of the target item
|
|
while( current.IsOk() && current != targetItem )
|
|
{
|
|
previous = current;
|
|
current = m_ruleTreeCtrl->GetNextChild( targetParent, cookie );
|
|
}
|
|
|
|
if( !previous.IsOk() )
|
|
{
|
|
// If no previous item, insert as the first child
|
|
newItem = m_ruleTreeCtrl->PrependItem( targetParent,
|
|
m_ruleTreeCtrl->GetItemText( m_draggedItem ) );
|
|
}
|
|
else
|
|
{
|
|
wxTreeItemId nextSibling = m_ruleTreeCtrl->GetNextChild( targetParent, cookie );
|
|
|
|
if( nextSibling.IsOk() )
|
|
{
|
|
// If there is a next sibling, insert after the previous item
|
|
newItem = m_ruleTreeCtrl->InsertItem( targetParent, previous,
|
|
m_ruleTreeCtrl->GetItemText( m_draggedItem ) );
|
|
}
|
|
else
|
|
{
|
|
// If no next sibling, append the item as the last child
|
|
newItem = m_ruleTreeCtrl->AppendItem( targetParent,
|
|
m_ruleTreeCtrl->GetItemText( m_draggedItem ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
moveRuleTreeItemChildrensTooOnDrag( m_draggedItem, newItem );
|
|
|
|
wxTreeItemData* itemData = m_ruleTreeCtrl->GetItemData( m_draggedItem );
|
|
|
|
if( itemData )
|
|
{
|
|
RULE_TREE_ITEM_DATA* ruleTreeItemData =
|
|
dynamic_cast<RULE_TREE_ITEM_DATA*>( itemData );
|
|
ruleTreeItemData->SetTreeItemId( newItem );
|
|
|
|
m_ruleTreeCtrl->SetItemData( newItem, ruleTreeItemData );
|
|
m_ruleTreeCtrl->SetItemData( m_draggedItem, nullptr );
|
|
|
|
saveRuleTreeState( newItem, ruleTreeItemData->GetNodeId() );
|
|
|
|
m_ruleTreeCtrl->SelectItem( newItem );
|
|
|
|
m_ruleTreeCtrl->Expand( newItem );
|
|
}
|
|
|
|
m_ruleTreeCtrl->DeleteChildren( m_draggedItem );
|
|
m_ruleTreeCtrl->Delete( m_draggedItem );
|
|
}
|
|
|
|
m_draggedItem = nullptr;
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onFilterSearch( wxCommandEvent& aEvent )
|
|
{
|
|
const auto searchStr = aEvent.GetString().Lower();
|
|
|
|
m_ruleTreeCtrl->Freeze();
|
|
m_suppressSelectionEvents = true;
|
|
|
|
m_ruleTreeCtrl->DeleteAllItems();
|
|
|
|
restoreRuleTree( nullptr, m_defaultTreeItems[0].m_nodeId );
|
|
|
|
wxTreeItemId root = m_ruleTreeCtrl->GetRootItem();
|
|
|
|
if( root.IsOk() )
|
|
{
|
|
filterRuleTree( root, searchStr );
|
|
}
|
|
|
|
m_suppressSelectionEvents = false;
|
|
m_ruleTreeCtrl->Thaw();
|
|
}
|
|
|
|
|
|
bool RULE_EDITOR_DIALOG_BASE::filterRuleTree( const wxTreeItemId& aItem, const wxString& aFilter )
|
|
{
|
|
bool matches = m_ruleTreeCtrl->GetItemText( aItem ).Lower().Contains( aFilter );
|
|
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId child = m_ruleTreeCtrl->GetFirstChild( aItem, cookie );
|
|
bool hasVisibleChildren = false;
|
|
|
|
std::vector<wxTreeItemId> itemsToDelete;
|
|
|
|
while( child.IsOk() )
|
|
{
|
|
if( filterRuleTree( child, aFilter ) )
|
|
{
|
|
hasVisibleChildren = true;
|
|
}
|
|
else
|
|
{
|
|
itemsToDelete.push_back( child );
|
|
}
|
|
|
|
child = m_ruleTreeCtrl->GetNextChild( aItem, cookie );
|
|
}
|
|
|
|
for( const auto& id : itemsToDelete )
|
|
{
|
|
m_ruleTreeCtrl->Delete( id );
|
|
}
|
|
|
|
return matches || hasVisibleChildren;
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::saveRuleTreeState( const wxTreeItemId& aItem, const int& aNodeId )
|
|
{
|
|
wxString itemText = m_ruleTreeCtrl->GetItemText( aItem );
|
|
std::vector<int> children;
|
|
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId child = m_ruleTreeCtrl->GetFirstChild( aItem, cookie );
|
|
|
|
while( child.IsOk() )
|
|
{
|
|
RULE_TREE_ITEM_DATA* childItemData =
|
|
dynamic_cast<RULE_TREE_ITEM_DATA*>( m_ruleTreeCtrl->GetItemData( child ) );
|
|
int childId = childItemData->GetNodeId();
|
|
|
|
children.push_back( childId );
|
|
|
|
saveRuleTreeState( child, childId );
|
|
|
|
child = m_ruleTreeCtrl->GetNextChild( aItem, cookie );
|
|
}
|
|
|
|
m_treeHistoryData[aNodeId] = { itemText, children, aItem };
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::restoreRuleTree( const wxTreeItemId& aParent, const int& aNodeId )
|
|
{
|
|
auto it = m_treeHistoryData.find( aNodeId );
|
|
|
|
if( it == m_treeHistoryData.end() )
|
|
return;
|
|
|
|
const auto& [itemText, children, itemId] = it->second;
|
|
|
|
wxTreeItemId newItem;
|
|
|
|
if( aParent )
|
|
newItem = m_ruleTreeCtrl->AppendItem( aParent, itemText );
|
|
else
|
|
newItem = m_ruleTreeCtrl->AddRoot( itemText );
|
|
|
|
if( newItem )
|
|
{
|
|
wxTreeItemId& treeItemId = std::get<2>( it->second );
|
|
treeItemId = newItem;
|
|
}
|
|
|
|
RULE_TREE_ITEM_DATA* itemData = new RULE_TREE_ITEM_DATA( aNodeId, aParent, newItem );
|
|
m_ruleTreeCtrl->SetItemData( newItem, itemData );
|
|
|
|
for( const auto& childId : children )
|
|
{
|
|
restoreRuleTree( newItem, childId );
|
|
}
|
|
|
|
// Don't expand the root item because it is hidden
|
|
if( aParent )
|
|
m_ruleTreeCtrl->Expand( newItem );
|
|
}
|
|
|
|
|
|
wxTreeItemId RULE_EDITOR_DIALOG_BASE::appendRuleTreeItem( const RULE_TREE_NODE& aNode, wxTreeItemId aParent )
|
|
{
|
|
wxString displayName = aNode.m_nodeName;
|
|
|
|
if( aNode.m_nodeData && aNode.m_nodeData->IsNew() )
|
|
displayName += wxS( " *" );
|
|
|
|
wxTreeItemId currentTreeItemId = m_ruleTreeCtrl->AppendItem( aParent, displayName );
|
|
|
|
RULE_TREE_ITEM_DATA* itemData = new RULE_TREE_ITEM_DATA( aNode.m_nodeId, aParent, currentTreeItemId );
|
|
m_ruleTreeCtrl->SetItemData( currentTreeItemId, itemData );
|
|
|
|
m_ruleTreeCtrl->Expand( currentTreeItemId );
|
|
|
|
return currentTreeItemId;
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::getRuleTreeChildNodes( const std::vector<RULE_TREE_NODE>& aNodes,
|
|
int aParentId,
|
|
std::vector<RULE_TREE_NODE>& aResult )
|
|
{
|
|
std::vector<RULE_TREE_NODE> filteredNodes;
|
|
|
|
std::copy_if( aNodes.begin(), aNodes.end(), std::back_inserter( filteredNodes ),
|
|
[&aParentId]( const RULE_TREE_NODE& node )
|
|
{
|
|
return node.m_nodeData->GetParentId() == aParentId;
|
|
} );
|
|
|
|
if( filteredNodes.size() > 0 )
|
|
{
|
|
aResult.insert( aResult.end(), filteredNodes.begin(), filteredNodes.end() );
|
|
}
|
|
}
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::moveRuleTreeItemChildrensTooOnDrag( wxTreeItemId aSrc,
|
|
wxTreeItemId aDest )
|
|
{
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId child = m_ruleTreeCtrl->GetFirstChild( aSrc, cookie );
|
|
|
|
while( child.IsOk() )
|
|
{
|
|
wxTreeItemId newChild = m_ruleTreeCtrl->AppendItem( aDest, m_ruleTreeCtrl->GetItemText( child ) );
|
|
|
|
RULE_TREE_ITEM_DATA* ruleTreeItemData =
|
|
dynamic_cast<RULE_TREE_ITEM_DATA*>( m_ruleTreeCtrl->GetItemData( child ) );
|
|
ruleTreeItemData->SetParentTreeItemId( aDest );
|
|
ruleTreeItemData->SetTreeItemId( newChild );
|
|
|
|
m_ruleTreeCtrl->SetItemData( newChild, ruleTreeItemData );
|
|
m_ruleTreeCtrl->SetItemData( child, nullptr );
|
|
|
|
moveRuleTreeItemChildrensTooOnDrag( child, newChild );
|
|
|
|
child = m_ruleTreeCtrl->GetNextChild( aSrc, cookie );
|
|
}
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::updateRuleTreeItemMoveOptionState()
|
|
{
|
|
wxTreeItemId selectedItem = m_ruleTreeCtrl->GetSelection();
|
|
|
|
m_enableMoveUp = false;
|
|
m_enableMoveDown = false;
|
|
|
|
if( !selectedItem.IsOk() )
|
|
return;
|
|
|
|
if( selectedItem == m_ruleTreeCtrl->GetRootItem() )
|
|
return;
|
|
|
|
RULE_TREE_ITEM_DATA* itemData =
|
|
dynamic_cast<RULE_TREE_ITEM_DATA*>( m_ruleTreeCtrl->GetItemData( selectedItem ) );
|
|
|
|
if( !itemData ||
|
|
!isEnabled( itemData, RULE_EDITOR_TREE_CONTEXT_OPT::MOVE_UP ) ||
|
|
!isEnabled( itemData, RULE_EDITOR_TREE_CONTEXT_OPT::MOVE_DOWN ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
wxTreeItemId parent = m_ruleTreeCtrl->GetItemParent( selectedItem );
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId firstChild = m_ruleTreeCtrl->GetFirstChild( parent, cookie );
|
|
wxTreeItemId lastChild = firstChild;
|
|
|
|
while( lastChild.IsOk() )
|
|
{
|
|
wxTreeItemId next = m_ruleTreeCtrl->GetNextChild( parent, cookie );
|
|
|
|
if( !next.IsOk() )
|
|
break;
|
|
|
|
lastChild = next;
|
|
}
|
|
|
|
m_enableMoveUp = ( selectedItem != firstChild );
|
|
m_enableMoveDown = ( selectedItem != lastChild );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::updateRuleTreeActionButtonsState( RULE_TREE_ITEM_DATA* aData )
|
|
{
|
|
wxTreeItemId selectedItem = m_ruleTreeCtrl->GetSelection();
|
|
|
|
if( !selectedItem.IsOk() || !m_ruleTreeCtrl->IsEnabled() )
|
|
{
|
|
m_addRuleButton->Enable( false );
|
|
m_copyRuleButton->Enable( false );
|
|
m_moveTreeItemUpButton->Enable( false );
|
|
m_moveTreeItemDownButton->Enable( false );
|
|
m_deleteRuleButton->Enable( false );
|
|
|
|
return;
|
|
}
|
|
|
|
m_enableAddRule = false;
|
|
m_enableDuplicateRule = false;
|
|
m_enableDeleteRule = false;
|
|
|
|
if( isEnabled( aData, RULE_EDITOR_TREE_CONTEXT_OPT::ADD_RULE ) )
|
|
m_enableAddRule = true;
|
|
|
|
if( isEnabled( aData, RULE_EDITOR_TREE_CONTEXT_OPT::DUPLICATE_RULE ) )
|
|
m_enableDuplicateRule = true;
|
|
|
|
if( isEnabled( aData, RULE_EDITOR_TREE_CONTEXT_OPT::DELETE_RULE ) )
|
|
m_enableDeleteRule = true;
|
|
|
|
updateRuleTreeItemMoveOptionState();
|
|
|
|
m_addRuleButton->Enable( m_enableAddRule );
|
|
m_copyRuleButton->Enable( m_enableDuplicateRule );
|
|
m_moveTreeItemUpButton->Enable( m_enableMoveUp );
|
|
m_moveTreeItemDownButton->Enable( m_enableMoveDown );
|
|
m_deleteRuleButton->Enable( m_enableDeleteRule );
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onResize( wxSizeEvent& event )
|
|
{
|
|
Layout();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onClose( wxCloseEvent& aEvt )
|
|
{
|
|
wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL ) );
|
|
}
|