You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

386 lines
11 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <kiplatform/ui.h>
  24. #include <tool/tool_manager.h>
  25. #include <tools/pcb_actions.h>
  26. #include <tools/pcb_selection_tool.h>
  27. #include <tools/group_tool.h>
  28. #include <tools/pcb_picker_tool.h>
  29. #include <status_popup.h>
  30. #include <board_commit.h>
  31. #include <bitmaps.h>
  32. #include <dialogs/dialog_group_properties.h>
  33. #include <pcb_group.h>
  34. #include <collectors.h>
  35. #include <footprint.h>
  36. class GROUP_CONTEXT_MENU : public ACTION_MENU
  37. {
  38. public:
  39. GROUP_CONTEXT_MENU( ) : ACTION_MENU( true )
  40. {
  41. SetIcon( BITMAPS::group ); // fixme
  42. SetTitle( _( "Grouping" ) );
  43. Add( PCB_ACTIONS::group );
  44. Add( PCB_ACTIONS::ungroup );
  45. Add( PCB_ACTIONS::removeFromGroup );
  46. }
  47. ACTION_MENU* create() const override
  48. {
  49. return new GROUP_CONTEXT_MENU();
  50. }
  51. private:
  52. void update() override
  53. {
  54. PCB_SELECTION_TOOL* selTool = getToolManager()->GetTool<PCB_SELECTION_TOOL>();
  55. BOARD* board = static_cast<BOARD*>( getToolManager()->GetModel() );
  56. const auto& selection = selTool->GetSelection();
  57. wxString check = board->GroupsSanityCheck();
  58. wxCHECK_RET( check == wxEmptyString, _( "Group is in inconsistent state:" ) + wxS( " " ) + check );
  59. BOARD::GroupLegalOpsField legalOps = board->GroupLegalOps( selection );
  60. Enable( PCB_ACTIONS::group.GetUIId(), legalOps.create );
  61. Enable( PCB_ACTIONS::ungroup.GetUIId(), legalOps.ungroup );
  62. Enable( PCB_ACTIONS::removeFromGroup.GetUIId(), legalOps.removeItems );
  63. }
  64. };
  65. GROUP_TOOL::GROUP_TOOL() :
  66. PCB_TOOL_BASE( "pcbnew.Groups" ),
  67. m_frame( nullptr ),
  68. m_propertiesDialog( nullptr ),
  69. m_selectionTool( nullptr )
  70. {
  71. }
  72. void GROUP_TOOL::Reset( RESET_REASON aReason )
  73. {
  74. m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  75. if( aReason != RUN )
  76. m_commit = std::make_unique<BOARD_COMMIT>( this );
  77. }
  78. bool GROUP_TOOL::Init()
  79. {
  80. m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  81. // Find the selection tool, so they can cooperate
  82. m_selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  83. // Add the group control menus to relevant other tools
  84. if( m_selectionTool )
  85. {
  86. TOOL_MENU& selToolMenu = m_selectionTool->GetToolMenu();
  87. std::shared_ptr<GROUP_CONTEXT_MENU> groupMenu = std::make_shared<GROUP_CONTEXT_MENU>();
  88. groupMenu->SetTool( this );
  89. selToolMenu.RegisterSubMenu( groupMenu );
  90. selToolMenu.GetMenu().AddMenu( groupMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
  91. }
  92. return true;
  93. }
  94. int GROUP_TOOL::GroupProperties( const TOOL_EVENT& aEvent )
  95. {
  96. PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  97. PCB_GROUP* group = aEvent.Parameter<PCB_GROUP*>();
  98. if( m_propertiesDialog )
  99. m_propertiesDialog->Destroy();
  100. m_propertiesDialog = new DIALOG_GROUP_PROPERTIES( editFrame, group );
  101. m_propertiesDialog->Show( true );
  102. return 0;
  103. }
  104. int GROUP_TOOL::PickNewMember( const TOOL_EVENT& aEvent )
  105. {
  106. PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
  107. STATUS_TEXT_POPUP statusPopup( frame() );
  108. bool done = false;
  109. if( m_propertiesDialog )
  110. m_propertiesDialog->Show( false );
  111. Activate();
  112. statusPopup.SetText( _( "Click on new member..." ) );
  113. picker->SetClickHandler(
  114. [&]( const VECTOR2D& aPoint ) -> bool
  115. {
  116. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  117. const PCB_SELECTION& sel = m_selectionTool->RequestSelection(
  118. []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector,
  119. PCB_SELECTION_TOOL* sTool )
  120. {
  121. } );
  122. if( sel.Empty() )
  123. return true; // still looking for an item
  124. statusPopup.Hide();
  125. if( m_propertiesDialog )
  126. {
  127. EDA_ITEM* elem = sel.Front();
  128. if( !m_isFootprintEditor )
  129. {
  130. while( elem->GetParent() && elem->GetParent()->Type() != PCB_T )
  131. elem = elem->GetParent();
  132. }
  133. m_propertiesDialog->DoAddMember( elem );
  134. m_propertiesDialog->Show( true );
  135. }
  136. return false; // got our item; don't need any more
  137. } );
  138. picker->SetMotionHandler(
  139. [&] ( const VECTOR2D& aPos )
  140. {
  141. statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
  142. } );
  143. picker->SetCancelHandler(
  144. [&]()
  145. {
  146. if( m_propertiesDialog )
  147. m_propertiesDialog->Show( true );
  148. statusPopup.Hide();
  149. } );
  150. picker->SetFinalizeHandler(
  151. [&]( const int& aFinalState )
  152. {
  153. done = true;
  154. } );
  155. statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
  156. statusPopup.Popup();
  157. canvas()->SetStatusPopup( statusPopup.GetPanel() );
  158. m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
  159. while( !done )
  160. {
  161. // Pass events unless we receive a null event, then we must shut down
  162. if( TOOL_EVENT* evt = Wait() )
  163. evt->SetPassEvent();
  164. else
  165. break;
  166. }
  167. canvas()->SetStatusPopup( nullptr );
  168. return 0;
  169. }
  170. int GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
  171. {
  172. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  173. PCB_SELECTION selection;
  174. if( m_isFootprintEditor )
  175. {
  176. selection = selTool->RequestSelection(
  177. []( const VECTOR2I& , GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* )
  178. {
  179. } );
  180. }
  181. else
  182. {
  183. selection = selTool->RequestSelection(
  184. []( const VECTOR2I& , GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* )
  185. {
  186. // Iterate from the back so we don't have to worry about removals.
  187. for( int i = aCollector.GetCount() - 1; i >= 0; --i )
  188. {
  189. BOARD_ITEM* item = aCollector[ i ];
  190. if( item->GetParentFootprint() )
  191. aCollector.Remove( item );
  192. }
  193. } );
  194. }
  195. if( selection.Empty() )
  196. return 0;
  197. BOARD* board = getModel<BOARD>();
  198. BOARD_COMMIT commit( m_toolMgr );
  199. PCB_GROUP* group = nullptr;
  200. if( m_isFootprintEditor )
  201. group = new PCB_GROUP( board->GetFirstFootprint() );
  202. else
  203. group = new PCB_GROUP( board );
  204. for( EDA_ITEM* eda_item : selection )
  205. {
  206. if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( eda_item ) )
  207. {
  208. if( item->IsLocked() )
  209. group->SetLocked( true );
  210. }
  211. }
  212. commit.Add( group );
  213. for( EDA_ITEM* eda_item : selection )
  214. {
  215. if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( eda_item ) )
  216. commit.Stage( item, CHT_GROUP );
  217. }
  218. commit.Push( _( "Group Items" ) );
  219. selTool->ClearSelection();
  220. selTool->select( group );
  221. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  222. m_frame->OnModify();
  223. return 0;
  224. }
  225. int GROUP_TOOL::Ungroup( const TOOL_EVENT& aEvent )
  226. {
  227. const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
  228. BOARD_COMMIT commit( m_toolMgr );
  229. EDA_ITEMS toSelect;
  230. if( selection.Empty() )
  231. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor );
  232. PCB_SELECTION selCopy = selection;
  233. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  234. for( EDA_ITEM* item : selCopy )
  235. {
  236. PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item );
  237. if( group )
  238. {
  239. for( BOARD_ITEM* member : group->GetItems() )
  240. {
  241. commit.Stage( member, CHT_UNGROUP );
  242. toSelect.push_back( member );
  243. }
  244. group->SetSelected();
  245. commit.Remove( group );
  246. }
  247. }
  248. commit.Push( _( "Ungroup Items" ) );
  249. m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &toSelect );
  250. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  251. m_frame->OnModify();
  252. return 0;
  253. }
  254. int GROUP_TOOL::RemoveFromGroup( const TOOL_EVENT& aEvent )
  255. {
  256. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  257. const PCB_SELECTION& selection = selTool->GetSelection();
  258. BOARD_COMMIT commit( m_frame );
  259. if( selection.Empty() )
  260. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor );
  261. for( EDA_ITEM* item : selection )
  262. {
  263. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
  264. PCB_GROUP* group = boardItem->GetParentGroup();
  265. if( group )
  266. commit.Stage( boardItem, CHT_UNGROUP );
  267. }
  268. commit.Push( _( "Remove Group Items" ) );
  269. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  270. m_frame->OnModify();
  271. return 0;
  272. }
  273. int GROUP_TOOL::EnterGroup( const TOOL_EVENT& aEvent )
  274. {
  275. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  276. const PCB_SELECTION& selection = selTool->GetSelection();
  277. if( selection.GetSize() == 1 && selection[0]->Type() == PCB_GROUP_T )
  278. selTool->EnterGroup();
  279. return 0;
  280. }
  281. int GROUP_TOOL::LeaveGroup( const TOOL_EVENT& aEvent )
  282. {
  283. m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->ExitGroup( true /* Select the group */ );
  284. return 0;
  285. }
  286. void GROUP_TOOL::setTransitions()
  287. {
  288. Go( &GROUP_TOOL::GroupProperties, PCB_ACTIONS::groupProperties.MakeEvent() );
  289. Go( &GROUP_TOOL::PickNewMember, PCB_ACTIONS::pickNewGroupMember.MakeEvent() );
  290. Go( &GROUP_TOOL::Group, PCB_ACTIONS::group.MakeEvent() );
  291. Go( &GROUP_TOOL::Ungroup, PCB_ACTIONS::ungroup.MakeEvent() );
  292. Go( &GROUP_TOOL::RemoveFromGroup, PCB_ACTIONS::removeFromGroup.MakeEvent() );
  293. Go( &GROUP_TOOL::EnterGroup, PCB_ACTIONS::groupEnter.MakeEvent() );
  294. Go( &GROUP_TOOL::LeaveGroup, PCB_ACTIONS::groupLeave.MakeEvent() );
  295. }