d7460bc5fe
DIALOG_SHIM::Show() and finishDialogSettings() both call SetSizeHints(), which computes the best size from the full unconstrained scrolled content area. This inflated the dialog to unusable heights. Move size correction logic into the base class by overriding Show() to cap minimum height at 500px and resetting to m_initialSize after finishDialogSettings(). Remove the duplicate size fixups from the derived DRC editor's TransferDataToWindow(). Increase default initial height from 680 to 800.
1207 lines
39 KiB
C++
1207 lines
39 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/dragimag.h> // For wxDragImage
|
|
#include <wx/wx.h>
|
|
#include <wx/dnd.h>
|
|
#include <wx/dcmemory.h> // Include for wxMemoryDC
|
|
#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>
|
|
|
|
|
|
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_defaultSashPosition( 200 ),
|
|
m_title( aTitle ),
|
|
m_selectedData( nullptr ),
|
|
m_previousId( nullptr ),
|
|
m_draggedItem( nullptr ),
|
|
m_dragImage( 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,
|
|
wxSP_3D | 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( true, true, true, 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 | wxBOTTOM, 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 );
|
|
m_ruleTreeCtrl->SetFont( KIUI::GetControlFont( this ) );
|
|
|
|
// Adjust the tree control's window style to remove the border
|
|
long treeCtrlFlags = m_ruleTreeCtrl->GetWindowStyleFlag();
|
|
treeCtrlFlags = ( treeCtrlFlags & ~wxBORDER_MASK ) | wxBORDER_NONE;
|
|
m_ruleTreeCtrl->SetWindowStyleFlag( treeCtrlFlags );
|
|
|
|
// Add the tree control to its sizer
|
|
treeCtrlSizer->Add( m_ruleTreeCtrl, 1, wxEXPAND | wxBOTTOM, 5 );
|
|
|
|
// Create a sizer for the action buttons
|
|
wxBoxSizer* actionButtonsSizer = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
// Create the action buttons
|
|
m_addRuleButton =
|
|
new wxBitmapButton( treeCtrlPanel, wxID_ANY, KiBitmapBundle( BITMAPS::small_plus ),
|
|
wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_copyRuleButton = new wxBitmapButton( treeCtrlPanel, wxID_ANY, KiBitmapBundle( BITMAPS::copy ),
|
|
wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_moveTreeItemUpButton =
|
|
new wxBitmapButton( treeCtrlPanel, wxID_ANY, KiBitmapBundle( BITMAPS::small_up ),
|
|
wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_moveTreeItemDownButton =
|
|
new wxBitmapButton( treeCtrlPanel, wxID_ANY, KiBitmapBundle( BITMAPS::small_down ),
|
|
wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_deleteRuleButton =
|
|
new wxBitmapButton( treeCtrlPanel, wxID_ANY, KiBitmapBundle( BITMAPS::small_trash ),
|
|
wxDefaultPosition, wxDefaultSize, 0 );
|
|
|
|
actionButtonsSizer->Add( m_addRuleButton, 0, wxLEFT | wxRIGHT, 5 );
|
|
actionButtonsSizer->Add( m_copyRuleButton, 0, wxLEFT | wxRIGHT, 5 );
|
|
actionButtonsSizer->Add( m_moveTreeItemUpButton, 0, wxLEFT | wxRIGHT, 5 );
|
|
actionButtonsSizer->Add( m_moveTreeItemDownButton, 0, wxLEFT | wxRIGHT, 5 );
|
|
|
|
// Add a spacer between the "Move" buttons and the "Delete" button
|
|
actionButtonsSizer->AddStretchSpacer( 1 ); // 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();
|
|
|
|
// 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 < 200 )
|
|
minTreeWidth = 200;
|
|
|
|
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;
|
|
|
|
if( m_dragImage )
|
|
{
|
|
delete m_dragImage;
|
|
m_dragImage = 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;
|
|
|
|
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->SelectItem( m_ruleTreeCtrl->GetFirstVisibleItem() );
|
|
m_ruleTreeCtrl->CollapseAll();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::SetContentPanel( wxPanel* aContentPanel )
|
|
{
|
|
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();
|
|
CallAfter( [this]() { RefreshContentScrollArea(); } );
|
|
Refresh();
|
|
}
|
|
|
|
|
|
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 };
|
|
}
|
|
|
|
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" ) );
|
|
}
|
|
|
|
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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::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;
|
|
|
|
wxString text = m_ruleTreeCtrl->GetItemText( item );
|
|
wxMemoryDC dc;
|
|
wxBitmap bitmap( 200, 30 );
|
|
dc.SelectObject( bitmap );
|
|
dc.SetBackground( *wxWHITE_BRUSH );
|
|
dc.Clear();
|
|
dc.SetFont( *wxNORMAL_FONT );
|
|
dc.DrawText( text, 5, 5 );
|
|
dc.SelectObject( wxNullBitmap );
|
|
|
|
m_dragImage = new wxDragImage( bitmap );
|
|
m_isDragging = false;
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemMouseMotion( wxMouseEvent& aEvent )
|
|
{
|
|
if( aEvent.Dragging() && m_draggedItem.IsOk() && m_dragImage )
|
|
{
|
|
wxPoint currentPos = aEvent.GetPosition();
|
|
|
|
if( !m_isDragging )
|
|
{
|
|
if( !m_dragImage->BeginDrag( wxPoint( 0, 0 ), m_ruleTreeCtrl, true ) )
|
|
{
|
|
delete m_dragImage;
|
|
m_dragImage = nullptr;
|
|
return;
|
|
}
|
|
|
|
m_dragImage->Show();
|
|
m_isDragging = true;
|
|
}
|
|
else
|
|
{
|
|
m_dragImage->Move( currentPos );
|
|
}
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void RULE_EDITOR_DIALOG_BASE::onRuleTreeItemLeftUp( wxMouseEvent& aEvent )
|
|
{
|
|
if( m_draggedItem.IsOk() && m_isDragging )
|
|
{
|
|
if( m_dragImage )
|
|
{
|
|
m_dragImage->Hide();
|
|
m_dragImage->EndDrag();
|
|
delete m_dragImage;
|
|
m_dragImage = nullptr;
|
|
m_isDragging = false;
|
|
}
|
|
|
|
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->DeleteAllItems();
|
|
|
|
restoreRuleTree( nullptr, m_defaultTreeItems[0].m_nodeId );
|
|
|
|
wxTreeItemId root = m_ruleTreeCtrl->GetRootItem();
|
|
|
|
if( root.IsOk() )
|
|
{
|
|
filterRuleTree( root, searchStr );
|
|
}
|
|
}
|
|
|
|
|
|
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 )
|
|
{
|
|
wxTreeItemId currentTreeItemId = m_ruleTreeCtrl->AppendItem( aParent, aNode.m_nodeName );
|
|
|
|
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 ) );
|
|
}
|