Shutdown safety. (Possibly KICAD-8S0.)

This commit is contained in:
Jeff Young 2025-07-18 11:48:54 +01:00
parent 8556bd45c2
commit be0da70839
6 changed files with 95 additions and 87 deletions

View File

@ -207,6 +207,16 @@ CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) :
// Connect Events // Connect Events
setupEventHandlers(); setupEventHandlers();
// Toolbar events
Bind( wxEVT_TEXT, &CVPCB_MAINFRAME::onTextFilterChanged, this );
// Attach the events to the tool dispatcher
Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
m_filterTimer = new wxTimer( this );
Bind( wxEVT_TIMER, &CVPCB_MAINFRAME::onTextFilterChangedTimer, this, m_filterTimer->GetId() );
// Start the main processing loop // Start the main processing loop
m_toolManager->InvokeTool( "cvpcb.Control" ); m_toolManager->InvokeTool( "cvpcb.Control" );
@ -225,13 +235,6 @@ CVPCB_MAINFRAME::~CVPCB_MAINFRAME()
Unbind( wxEVT_TIMER, &CVPCB_MAINFRAME::onTextFilterChangedTimer, this, m_filterTimer->GetId() ); Unbind( wxEVT_TIMER, &CVPCB_MAINFRAME::onTextFilterChangedTimer, this, m_filterTimer->GetId() );
Unbind( wxEVT_IDLE, &CVPCB_MAINFRAME::updateFootprintViewerOnIdle, this ); Unbind( wxEVT_IDLE, &CVPCB_MAINFRAME::updateFootprintViewerOnIdle, this );
// Stop the timer during destruction early to avoid potential race conditions (that do happen)
m_filterTimer->Stop();
// Shutdown all running tools
if( m_toolManager )
m_toolManager->ShutdownAllTools();
// Clean up the tool infrastructure // Clean up the tool infrastructure
delete m_actions; delete m_actions;
delete m_toolManager; delete m_toolManager;
@ -289,11 +292,10 @@ void CVPCB_MAINFRAME::setupUIConditions()
#define ENABLE( x ) ACTION_CONDITIONS().Enable( x ) #define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
#define CHECK( x ) ACTION_CONDITIONS().Check( x ) #define CHECK( x ) ACTION_CONDITIONS().Check( x )
mgr->SetConditions( CVPCB_ACTIONS::saveAssociationsToSchematic, mgr->SetConditions( CVPCB_ACTIONS::saveAssociationsToSchematic, ENABLE( cond.ContentModified() ) );
ENABLE( cond.ContentModified() ) ); mgr->SetConditions( CVPCB_ACTIONS::saveAssociationsToFile, ENABLE( cond.ContentModified() ) );
mgr->SetConditions( CVPCB_ACTIONS::saveAssociationsToFile, ENABLE( cond.ContentModified() ) ); mgr->SetConditions( ACTIONS::undo, ENABLE( cond.UndoAvailable() ) );
mgr->SetConditions( ACTIONS::undo, ENABLE( cond.UndoAvailable() ) ); mgr->SetConditions( ACTIONS::redo, ENABLE( cond.RedoAvailable() ) );
mgr->SetConditions( ACTIONS::redo, ENABLE( cond.RedoAvailable() ) );
auto compFilter = auto compFilter =
[this] ( const SELECTION& ) [this] ( const SELECTION& )
@ -372,22 +374,12 @@ void CVPCB_MAINFRAME::setupEventHandlers()
Close( false ); Close( false );
}, wxID_EXIT ); }, wxID_EXIT );
// Toolbar events
Bind( wxEVT_TEXT, &CVPCB_MAINFRAME::onTextFilterChanged, this );
// Just skip the resize events // Just skip the resize events
Bind( wxEVT_SIZE, Bind( wxEVT_SIZE,
[]( wxSizeEvent& aEvent ) []( wxSizeEvent& aEvent )
{ {
aEvent.Skip(); aEvent.Skip();
} ); } );
// Attach the events to the tool dispatcher
Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
m_filterTimer = new wxTimer( this );
Bind( wxEVT_TIMER, &CVPCB_MAINFRAME::onTextFilterChangedTimer, this, m_filterTimer->GetId() );
} }
@ -396,14 +388,10 @@ bool CVPCB_MAINFRAME::canCloseWindow( wxCloseEvent& aEvent )
if( m_modified ) if( m_modified )
{ {
// Shutdown blocks must be determined and vetoed as early as possible // Shutdown blocks must be determined and vetoed as early as possible
if( KIPLATFORM::APP::SupportsShutdownBlockReason() if( KIPLATFORM::APP::SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION )
&& aEvent.GetId() == wxEVT_QUERY_END_SESSION )
{
return false; return false;
}
if( !HandleUnsavedChanges( this, _( "Symbol to Footprint links have been modified. " if( !HandleUnsavedChanges( this, _( "Symbol to Footprint links have been modified. Save changes?" ),
"Save changes?" ),
[&]() -> bool [&]() -> bool
{ {
return SaveFootprintAssociation( false ); return SaveFootprintAssociation( false );
@ -422,11 +410,21 @@ bool CVPCB_MAINFRAME::canCloseWindow( wxCloseEvent& aEvent )
void CVPCB_MAINFRAME::doCloseWindow() void CVPCB_MAINFRAME::doCloseWindow()
{ {
m_symbolsListBox->Shutdown();
m_footprintListBox->Shutdown();
if( GetFootprintViewerFrame() ) if( GetFootprintViewerFrame() )
GetFootprintViewerFrame()->Close( true ); GetFootprintViewerFrame()->Close( true );
m_modified = false; m_modified = false;
// Stop the timer during destruction early to avoid potential race conditions (that do happen)
m_filterTimer->Stop();
// Shutdown all running tools
if( m_toolManager )
m_toolManager->ShutdownAllTools();
// clear symbol selection in schematic: // clear symbol selection in schematic:
SendComponentSelectionToSch( true ); SendComponentSelectionToSch( true );
} }
@ -533,9 +531,8 @@ void CVPCB_MAINFRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
{ {
EDA_BASE_FRAME::LoadSettings( aCfg ); EDA_BASE_FRAME::LoadSettings( aCfg );
CVPCB_SETTINGS* cfg = static_cast<CVPCB_SETTINGS*>( aCfg ); if( CVPCB_SETTINGS* cfg = dynamic_cast<CVPCB_SETTINGS*>( aCfg ) )
m_filteringOptions = cfg->m_FilterFlags;
m_filteringOptions = cfg->m_FilterFlags;
} }
@ -543,12 +540,14 @@ void CVPCB_MAINFRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
{ {
EDA_BASE_FRAME::SaveSettings( aCfg ); EDA_BASE_FRAME::SaveSettings( aCfg );
CVPCB_SETTINGS* cfg = static_cast<CVPCB_SETTINGS*>( aCfg ); if( CVPCB_SETTINGS* cfg = dynamic_cast<CVPCB_SETTINGS*>( aCfg ) )
cfg->m_FilterFlags = m_filteringOptions; {
cfg->m_FilterString = m_tcFilterString->GetValue(); cfg->m_FilterFlags = m_filteringOptions;
cfg->m_FilterString = m_tcFilterString->GetValue();
cfg->m_LibrariesWidth = m_librariesListBox->GetSize().x; cfg->m_LibrariesWidth = m_librariesListBox->GetSize().x;
cfg->m_FootprintsWidth = m_footprintListBox->GetSize().x; cfg->m_FootprintsWidth = m_footprintListBox->GetSize().x;
}
} }

View File

@ -33,8 +33,7 @@
#include <tools/cvpcb_actions.h> #include <tools/cvpcb_actions.h>
FOOTPRINTS_LISTBOX::FOOTPRINTS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ) : FOOTPRINTS_LISTBOX::FOOTPRINTS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ) :
ITEMS_LISTBOX_BASE( parent, id, wxDefaultPosition, wxDefaultSize, ITEMS_LISTBOX_BASE( parent, id, wxDefaultPosition, wxDefaultSize, wxLC_SINGLE_SEL | wxNO_BORDER )
wxLC_SINGLE_SEL | wxNO_BORDER )
{ {
} }
@ -187,18 +186,27 @@ END_EVENT_TABLE()
void FOOTPRINTS_LISTBOX::OnLeftClick( wxListEvent& event ) void FOOTPRINTS_LISTBOX::OnLeftClick( wxListEvent& event )
{ {
if( m_isClosing )
return;
GetParent()->RefreshFootprintViewer(); GetParent()->RefreshFootprintViewer();
} }
void FOOTPRINTS_LISTBOX::OnLeftDClick( wxListEvent& event ) void FOOTPRINTS_LISTBOX::OnLeftDClick( wxListEvent& event )
{ {
if( m_isClosing )
return;
GetParent()->GetToolManager()->RunAction( CVPCB_ACTIONS::associate ); GetParent()->GetToolManager()->RunAction( CVPCB_ACTIONS::associate );
} }
void FOOTPRINTS_LISTBOX::OnChar( wxKeyEvent& event ) void FOOTPRINTS_LISTBOX::OnChar( wxKeyEvent& event )
{ {
if( m_isClosing )
return;
wxLogTrace( kicadTraceKeyEvent, wxS( "FOOTPRINTS_LISTBOX::OnChar %s" ), dump( event ) ); wxLogTrace( kicadTraceKeyEvent, wxS( "FOOTPRINTS_LISTBOX::OnChar %s" ), dump( event ) );
int key = event.GetKeyCode(); int key = event.GetKeyCode();

View File

@ -34,18 +34,14 @@
ITEMS_LISTBOX_BASE::ITEMS_LISTBOX_BASE( CVPCB_MAINFRAME* aParent, wxWindowID aId, ITEMS_LISTBOX_BASE::ITEMS_LISTBOX_BASE( CVPCB_MAINFRAME* aParent, wxWindowID aId,
const wxPoint& aLocation, const wxSize& aSize, const wxPoint& aLocation, const wxSize& aSize,
long aStyle ) : long aStyle ) :
wxListView( aParent, aId, aLocation, aSize, LISTBOX_STYLE | aStyle ), wxListView( aParent, aId, aLocation, aSize, LISTBOX_STYLE | aStyle ),
columnWidth( 0 ) m_isClosing( false ),
m_columnWidth( 0 )
{ {
InsertColumn( 0, wxEmptyString ); InsertColumn( 0, wxEmptyString );
} }
ITEMS_LISTBOX_BASE::~ITEMS_LISTBOX_BASE()
{
}
void ITEMS_LISTBOX_BASE::UpdateWidth( int aLine ) void ITEMS_LISTBOX_BASE::UpdateWidth( int aLine )
{ {
wxClientDC dc( this ); wxClientDC dc( this );
@ -54,12 +50,10 @@ void ITEMS_LISTBOX_BASE::UpdateWidth( int aLine )
// Less than zero: recalculate width of all items. // Less than zero: recalculate width of all items.
if( aLine < 0 ) if( aLine < 0 )
{ {
columnWidth = 0; m_columnWidth = 0;
for( int ii = 0; ii < itemCount; ii++ ) for( int ii = 0; ii < itemCount; ii++ )
{
UpdateLineWidth( (unsigned)ii, dc ); UpdateLineWidth( (unsigned)ii, dc );
}
} }
// Zero or above: update from a single line. // Zero or above: update from a single line.
else else
@ -87,10 +81,10 @@ void ITEMS_LISTBOX_BASE::UpdateLineWidth( unsigned aLine, wxClientDC& dc )
dc.GetTextExtent( str, &w, nullptr ); dc.GetTextExtent( str, &w, nullptr );
newWidth += w; newWidth += w;
if( newWidth > columnWidth ) if( newWidth > m_columnWidth )
{ {
columnWidth = newWidth; m_columnWidth = newWidth;
SetColumnWidth( 0, columnWidth ); SetColumnWidth( 0, m_columnWidth );
} }
} }

View File

@ -46,7 +46,7 @@ public:
const wxPoint& aLocation = wxDefaultPosition, const wxPoint& aLocation = wxDefaultPosition,
const wxSize& aSize = wxDefaultSize, long aStyle = 0 ); const wxSize& aSize = wxDefaultSize, long aStyle = 0 );
~ITEMS_LISTBOX_BASE(); virtual ~ITEMS_LISTBOX_BASE() = default;
/** /**
* @return the index of the selected item in lists allowing only one item selected * @return the index of the selected item in lists allowing only one item selected
@ -70,6 +70,8 @@ public:
*/ */
void UpdateWidth( int aLine = -1 ); void UpdateWidth( int aLine = -1 );
void Shutdown() { m_isClosing = true; }
private: private:
/** /**
* Calculate the width of the given line, and increase the column width * Calculate the width of the given line, and increase the column width
@ -78,7 +80,11 @@ private:
*/ */
void UpdateLineWidth( unsigned aLine, wxClientDC& dc ); void UpdateLineWidth( unsigned aLine, wxClientDC& dc );
int columnWidth; protected:
bool m_isClosing;
private:
int m_columnWidth;
}; };
@ -98,7 +104,7 @@ public:
}; };
FOOTPRINTS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ); FOOTPRINTS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id );
~FOOTPRINTS_LISTBOX() override {}; virtual ~FOOTPRINTS_LISTBOX() = default;
int GetCount(); int GetCount();
void SetSelection( int aIndex, bool aState = true ); void SetSelection( int aIndex, bool aState = true );
@ -143,7 +149,7 @@ class LIBRARY_LISTBOX : public ITEMS_LISTBOX_BASE
{ {
public: public:
LIBRARY_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ); LIBRARY_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id );
~LIBRARY_LISTBOX() override {}; virtual ~LIBRARY_LISTBOX() = default;
int GetCount(); int GetCount();
void SetSelection( int index, bool State = true ); void SetSelection( int index, bool State = true );
@ -181,8 +187,7 @@ class SYMBOLS_LISTBOX : public ITEMS_LISTBOX_BASE
{ {
public: public:
SYMBOLS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ); SYMBOLS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id );
virtual ~SYMBOLS_LISTBOX() = default;
~SYMBOLS_LISTBOX();
void Clear(); void Clear();
int GetCount(); int GetCount();
@ -223,7 +228,7 @@ public:
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
public: public:
wxArrayString m_SymbolList; wxArrayString m_SymbolList;
private: private:
std::vector<long> m_symbolWarning; std::vector<long> m_symbolWarning;

View File

@ -35,19 +35,14 @@
SYMBOLS_LISTBOX::SYMBOLS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ) : SYMBOLS_LISTBOX::SYMBOLS_LISTBOX( CVPCB_MAINFRAME* parent, wxWindowID id ) :
ITEMS_LISTBOX_BASE( parent, id ), ITEMS_LISTBOX_BASE( parent, id ),
m_warningAttr( std::make_unique<wxListItemAttr>() ) m_warningAttr( std::make_unique<wxListItemAttr>() )
{ {
m_warningAttr->SetBackgroundColour( KIPLATFORM::UI::IsDarkTheme() ? wxColour( 112, 96, 32 ) m_warningAttr->SetBackgroundColour( KIPLATFORM::UI::IsDarkTheme() ? wxColour( 112, 96, 32 )
: wxColour( 255, 248, 212 ) ); : wxColour( 255, 248, 212 ) );
} }
SYMBOLS_LISTBOX::~SYMBOLS_LISTBOX()
{
}
;
BEGIN_EVENT_TABLE( SYMBOLS_LISTBOX, ITEMS_LISTBOX_BASE ) BEGIN_EVENT_TABLE( SYMBOLS_LISTBOX, ITEMS_LISTBOX_BASE )
EVT_CHAR( SYMBOLS_LISTBOX::OnChar ) EVT_CHAR( SYMBOLS_LISTBOX::OnChar )
EVT_LIST_ITEM_SELECTED( ID_CVPCB_COMPONENT_LIST, SYMBOLS_LISTBOX::OnSelectComponent ) EVT_LIST_ITEM_SELECTED( ID_CVPCB_COMPONENT_LIST, SYMBOLS_LISTBOX::OnSelectComponent )
@ -117,9 +112,8 @@ wxString SYMBOLS_LISTBOX::OnGetItemText( long item, long column ) const
wxListItemAttr* SYMBOLS_LISTBOX::OnGetItemAttr( long item ) const wxListItemAttr* SYMBOLS_LISTBOX::OnGetItemAttr( long item ) const
{ {
if( std::count( m_symbolWarning.begin(), m_symbolWarning.end(), item ) ) if( std::count( m_symbolWarning.begin(), m_symbolWarning.end(), item ) )
{
return m_warningAttr.get(); return m_warningAttr.get();
}
return nullptr; return nullptr;
} }
@ -143,6 +137,9 @@ void SYMBOLS_LISTBOX::SetSelection( int index, bool State )
void SYMBOLS_LISTBOX::OnChar( wxKeyEvent& event ) void SYMBOLS_LISTBOX::OnChar( wxKeyEvent& event )
{ {
if( m_isClosing )
return;
wxLogTrace( kicadTraceKeyEvent, wxS( "SYMBOLS_LISTBOX::OnChar %s" ), dump( event ) ); wxLogTrace( kicadTraceKeyEvent, wxS( "SYMBOLS_LISTBOX::OnChar %s" ), dump( event ) );
int key = event.GetKeyCode(); int key = event.GetKeyCode();
@ -201,6 +198,9 @@ void SYMBOLS_LISTBOX::OnChar( wxKeyEvent& event )
void SYMBOLS_LISTBOX::OnSelectComponent( wxListEvent& event ) void SYMBOLS_LISTBOX::OnSelectComponent( wxListEvent& event )
{ {
if( m_isClosing )
return;
SetFocus(); SetFocus();
GetParent()->OnSelectComponent( event ); GetParent()->OnSelectComponent( event );
} }

View File

@ -43,35 +43,35 @@ void PANEL_PCBNEW_DISPLAY_ORIGIN::loadSettings( APP_SETTINGS_BASE* aCfg )
{ {
if( m_frameType == FRAME_FOOTPRINT_EDITOR ) if( m_frameType == FRAME_FOOTPRINT_EDITOR )
{ {
FOOTPRINT_EDITOR_SETTINGS* cfg = static_cast<FOOTPRINT_EDITOR_SETTINGS*>( aCfg ); FOOTPRINT_EDITOR_SETTINGS* cfg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" );
if( cfg->m_DisplayInvertXAxis ) if( cfg && cfg->m_DisplayInvertXAxis )
m_xIncreasesLeft->SetValue( true ); m_xIncreasesLeft->SetValue( true );
else else
m_xIncreasesRight->SetValue( true ); m_xIncreasesRight->SetValue( true );
if( cfg->m_DisplayInvertYAxis ) if( cfg && cfg->m_DisplayInvertYAxis )
m_yIncreasesUp->SetValue( true ); m_yIncreasesUp->SetValue( true );
else else
m_yIncreasesDown->SetValue( true ); m_yIncreasesDown->SetValue( true );
} }
else else
{ {
PCBNEW_SETTINGS* cfg = static_cast<PCBNEW_SETTINGS*>( aCfg ); PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
if( cfg->m_Display.m_DisplayOrigin == PCB_DISPLAY_ORIGIN::PCB_ORIGIN_PAGE ) if( cfg && cfg->m_Display.m_DisplayOrigin == PCB_DISPLAY_ORIGIN::PCB_ORIGIN_PAGE )
m_pageOrigin->SetValue( true ); m_pageOrigin->SetValue( true );
else if( cfg->m_Display.m_DisplayOrigin == PCB_DISPLAY_ORIGIN::PCB_ORIGIN_GRID ) else if( cfg && cfg->m_Display.m_DisplayOrigin == PCB_DISPLAY_ORIGIN::PCB_ORIGIN_GRID )
m_gridOrigin->SetValue( true ); m_gridOrigin->SetValue( true );
else else
m_drillPlaceOrigin->SetValue( true ); m_drillPlaceOrigin->SetValue( true );
if( cfg->m_Display.m_DisplayInvertXAxis ) if( cfg &&cfg->m_Display.m_DisplayInvertXAxis )
m_xIncreasesLeft->SetValue( true ); m_xIncreasesLeft->SetValue( true );
else else
m_xIncreasesRight->SetValue( true ); m_xIncreasesRight->SetValue( true );
if( cfg->m_Display.m_DisplayInvertYAxis ) if( cfg && cfg->m_Display.m_DisplayInvertYAxis )
m_yIncreasesUp->SetValue( true ); m_yIncreasesUp->SetValue( true );
else else
m_yIncreasesDown->SetValue( true ); m_yIncreasesDown->SetValue( true );
@ -91,24 +91,26 @@ bool PANEL_PCBNEW_DISPLAY_ORIGIN::TransferDataFromWindow()
{ {
if( m_frameType == FRAME_FOOTPRINT_EDITOR ) if( m_frameType == FRAME_FOOTPRINT_EDITOR )
{ {
FOOTPRINT_EDITOR_SETTINGS* cfg = static_cast<FOOTPRINT_EDITOR_SETTINGS*>( m_cfg ); if( FOOTPRINT_EDITOR_SETTINGS* cfg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" ) )
{
cfg->m_DisplayInvertXAxis = m_xIncreasesLeft->GetValue(); cfg->m_DisplayInvertXAxis = m_xIncreasesLeft->GetValue();
cfg->m_DisplayInvertYAxis = m_yIncreasesUp->GetValue(); cfg->m_DisplayInvertYAxis = m_yIncreasesUp->GetValue();
}
} }
else else
{ {
PCBNEW_SETTINGS* cfg = static_cast<PCBNEW_SETTINGS*>( m_cfg ); if( PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) )
{
if( m_pageOrigin->GetValue() )
cfg->m_Display.m_DisplayOrigin = PCB_DISPLAY_ORIGIN::PCB_ORIGIN_PAGE;
else if( m_gridOrigin->GetValue() )
cfg->m_Display.m_DisplayOrigin = PCB_DISPLAY_ORIGIN::PCB_ORIGIN_GRID;
else
cfg->m_Display.m_DisplayOrigin = PCB_DISPLAY_ORIGIN::PCB_ORIGIN_AUX;
if( m_pageOrigin->GetValue() ) cfg->m_Display.m_DisplayInvertXAxis = m_xIncreasesLeft->GetValue();
cfg->m_Display.m_DisplayOrigin = PCB_DISPLAY_ORIGIN::PCB_ORIGIN_PAGE; cfg->m_Display.m_DisplayInvertYAxis = m_yIncreasesUp->GetValue();
else if( m_gridOrigin->GetValue() ) }
cfg->m_Display.m_DisplayOrigin = PCB_DISPLAY_ORIGIN::PCB_ORIGIN_GRID;
else
cfg->m_Display.m_DisplayOrigin = PCB_DISPLAY_ORIGIN::PCB_ORIGIN_AUX;
cfg->m_Display.m_DisplayInvertXAxis = m_xIncreasesLeft->GetValue();
cfg->m_Display.m_DisplayInvertYAxis = m_yIncreasesUp->GetValue();
} }
return true; return true;