/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008-2017 Wayne Stambaugh * Copyright (C) 2004-2019 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) { int id = event.GetId(); wxPoint pos; SCH_SCREEN* screen = GetScreen(); SCH_ITEM* item = screen->GetCurItem(); pos = wxGetMousePosition(); pos.y += 20; // If needed, stop the current command and deselect current tool switch( id ) { case ID_POPUP_CANCEL_CURRENT_COMMAND: case ID_POPUP_SCH_ENTRY_SELECT_SLASH: case ID_POPUP_SCH_ENTRY_SELECT_ANTISLASH: case ID_POPUP_END_LINE: case ID_POPUP_SCH_CLEANUP_SHEET: case ID_POPUP_SCH_END_SHEET: case ID_POPUP_SCH_RESIZE_SHEET: case ID_POPUP_IMPORT_HLABEL_TO_SHEETPIN: case ID_POPUP_SCH_INIT_CMP: case ID_POPUP_SCH_EDIT_CONVERT_CMP: case ID_POPUP_SCH_DELETE_NODE: case ID_POPUP_SCH_DELETE_CONNECTION: case ID_POPUP_SCH_ENTER_SHEET: case ID_POPUP_SCH_LEAVE_SHEET: case ID_POPUP_SCH_ADD_JUNCTION: case ID_POPUP_SCH_ADD_LABEL: case ID_POPUP_SCH_GETINFO_MARKER: /* At this point: Do nothing. these commands do not need to stop the * current command (mainly a block command) or reset the current state * They will be executed later, in next switch structure. */ break; default: // Stop the current command and deselect the current tool SetNoToolSelected(); break; } item = screen->GetCurItem(); // Can be modified by previous calls. switch( id ) { case ID_HIERARCHY: InstallHierarchyFrame( pos ); SetRepeatItem( NULL ); break; case ID_POPUP_SCH_ENTRY_SELECT_SLASH: m_canvas->MoveCursorToCrossHair(); SetBusEntryShape( nullptr, dynamic_cast( item ), '/' ); break; case ID_POPUP_SCH_ENTRY_SELECT_ANTISLASH: m_canvas->MoveCursorToCrossHair(); SetBusEntryShape( nullptr, dynamic_cast( item ), '\\' ); break; case ID_POPUP_CANCEL_CURRENT_COMMAND: if( m_canvas->IsMouseCaptured() ) { m_canvas->EndMouseCapture(); SetToolID( GetToolId(), GetGalCanvas()->GetCurrentCursor(), wxEmptyString ); } else { SetNoToolSelected(); } break; case ID_POPUP_END_LINE: case ID_POPUP_SCH_END_SHEET: m_toolManager->RunAction( SCH_ACTIONS::finishDrawing, true ); break; case ID_POPUP_SCH_DELETE_NODE: case ID_POPUP_SCH_DELETE_CONNECTION: m_canvas->MoveCursorToCrossHair(); DeleteConnection( id == ID_POPUP_SCH_DELETE_CONNECTION ); SchematicCleanUp( true ); screen->SetCurItem( NULL ); SetRepeatItem( NULL ); TestDanglingEnds(); m_canvas->Refresh(); break; case ID_POPUP_SCH_BREAK_WIRE: m_canvas->MoveCursorToCrossHair(); BreakSegments( GetCrossHairPosition() ); TestDanglingEnds(); m_canvas->Refresh(); break; case ID_POPUP_SCH_CLEANUP_SHEET: if( item != NULL && item->Type() == SCH_SHEET_T ) { SCH_SHEET* sheet = (SCH_SHEET*) item; if( !sheet->HasUndefinedPins() ) { DisplayInfoMessage( this, _( "There are no undefined labels in this sheet to clean up." ) ); return; } if( !IsOK( this, _( "Do you wish to cleanup this sheet?" ) ) ) return; /* Save sheet in undo list before cleaning up unreferenced hierarchical labels. */ SaveCopyInUndoList( sheet, UR_CHANGED ); sheet->CleanupSheet(); SyncView(); GetCanvas()->Refresh(); OnModify(); } break; case ID_POPUP_SCH_INIT_CMP: m_canvas->MoveCursorToCrossHair(); break; case ID_POPUP_SCH_EDIT_CONVERT_CMP: // Ensure the struct is a component (could be a struct of a component, like Field, text..) if( item && item->Type() == SCH_COMPONENT_T ) { m_canvas->MoveCursorToCrossHair(); ConvertPart( (SCH_COMPONENT*) item ); } break; case ID_POPUP_SCH_ENTER_SHEET: if( item && (item->Type() == SCH_SHEET_T) ) { g_CurrentSheet->push_back( (SCH_SHEET*) item ); DisplayCurrentSheet(); } break; case ID_POPUP_SCH_LEAVE_SHEET: if( g_CurrentSheet->Last() != g_RootSheet ) { g_CurrentSheet->pop_back(); DisplayCurrentSheet(); } break; case ID_POPUP_SCH_GETINFO_MARKER: if( item && item->Type() == SCH_MARKER_T ) ( (SCH_MARKER*) item )->DisplayMarkerInfo( this ); break; default: // Log error: wxFAIL_MSG( wxString::Format( "Cannot process command event ID %d", event.GetId() ) ); break; } if( GetToolId() == ID_NO_TOOL_SELECTED ) SetRepeatItem( NULL ); } void SCH_EDIT_FRAME::OnUnfoldBus( wxCommandEvent& event ) { wxMenuItem* item = static_cast( event.GetEventUserData() ); wxString net = item->GetItemLabelText(); GetToolManager()->RunAction( SCH_ACTIONS::unfoldBus, true, &net ); // Now that we have handled the chosen bus unfold, disconnect all the events so they can be // recreated with updated data on the next unfold Unbind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this ); } void SCH_EDIT_FRAME::OnCancelCurrentCommand( wxCommandEvent& aEvent ) { SCH_SCREEN* screen = GetScreen(); if( screen->IsBlockActive() ) { GetCanvas()->SetCursor( (wxStockCursor) GetGalCanvas()->GetDefaultCursor() ); screen->ClearBlockCommand(); // Stop the current command (if any) but keep the current tool m_canvas->EndMouseCapture(); } else { if( m_canvas->IsMouseCaptured() ) // Stop the current command but keep the current tool m_canvas->EndMouseCapture(); else // Deselect current tool m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, GetGalCanvas()->GetDefaultCursor() ); } GetCanvas()->GetView()->ClearHiddenFlags(); GetCanvas()->GetView()->ClearPreview(); GetCanvas()->GetView()->ShowPreview( false ); } void SCH_EDIT_FRAME::OnUpdateSelectTool( wxUpdateUIEvent& aEvent ) { if( aEvent.GetEventObject() == m_drawToolBar || aEvent.GetEventObject() == m_mainToolBar ) aEvent.Check( GetToolId() == aEvent.GetId() ); } void SCH_EDIT_FRAME::DeleteConnection( bool aFullConnection ) { PICKED_ITEMS_LIST pickList; SCH_SCREEN* screen = GetScreen(); wxPoint pos = GetCrossHairPosition(); if( screen->GetConnection( pos, pickList, aFullConnection ) != 0 ) { DeleteItemsInList( pickList ); SchematicCleanUp( true ); OnModify(); } } void SCH_EDIT_FRAME::SelectAllFromSheet( wxCommandEvent& aEvent ) { SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); SCH_SCREEN* screen = GetScreen(); SCH_ITEM* item = screen->GetCurItem(); if( item != NULL ) { item = selTool->SelectPoint( item->GetPosition() ); SendMessageToPCBNEW( item, NULL ); } else { // If we didn't get here by a hot key, then something has gone wrong. if( aEvent.GetInt() == 0 ) return; EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject(); wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); item = selTool->SelectPoint( data->GetPosition() ); SendMessageToPCBNEW( item, NULL ); } } void SCH_EDIT_FRAME::OnUnfoldBusHotkey( wxCommandEvent& aEvent ) { SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject(); SCH_ITEM* item = GetScreen()->GetCurItem(); wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); if( item == NULL ) { // If we didn't get here by a hot key, then something has gone wrong. if( aEvent.GetInt() == 0 ) return; item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::EditableItems ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) return; } if( item->Type() != SCH_LINE_T ) return; wxMenu* bus_unfold_menu = GetUnfoldBusMenu( static_cast( item ) ); if( bus_unfold_menu ) { auto controls = GetCanvas()->GetViewControls(); auto vmp = controls->GetMousePosition( false ); wxPoint mouse_pos( (int) vmp.x, (int) vmp.y ); GetGalCanvas()->PopupMenu( bus_unfold_menu, mouse_pos ); } } wxMenu* SCH_EDIT_FRAME::GetUnfoldBusMenu( SCH_LINE* aBus ) { auto connection = aBus->Connection( *g_CurrentSheet ); if( !connection || !connection->IsBus() || connection->Members().empty() ) return nullptr; int idx = 0; wxMenu* bus_unfolding_menu = new wxMenu; for( const auto& member : connection->Members() ) { int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ ); wxString name = member->Name( true ); if( member->Type() == CONNECTION_BUS ) { wxMenu* submenu = new wxMenu; bus_unfolding_menu->AppendSubMenu( submenu, _( name ) ); for( const auto& sub_member : member->Members() ) { id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ ); submenu->Append( id, sub_member->Name( true ), wxEmptyString ); // See comment in else clause below auto sub_item_clone = new wxMenuItem(); sub_item_clone->SetItemLabel( sub_member->Name( true ) ); Bind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this, id, id, sub_item_clone ); } } else { bus_unfolding_menu->Append( id, name, wxEmptyString ); // Because Bind() takes ownership of the user data item, we // make a new menu item here and set its label. Why create a // menu item instead of just a wxString or something? Because // Bind() requires a pointer to wxObject rather than a void // pointer. Maybe at some point I'll think of a better way... auto item_clone = new wxMenuItem(); item_clone->SetItemLabel( name ); Bind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this, id, id, item_clone ); } } return bus_unfolding_menu; }