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.

215 lines
7.8 KiB

5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Created on: 11 Mar 2016, author John Beard
  5. * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "array_creator.h"
  25. #include <array_pad_number_provider.h>
  26. #include <board_commit.h>
  27. #include <pcb_group.h>
  28. #include <pad.h>
  29. #include <dialogs/dialog_create_array.h>
  30. #include <tool/tool_manager.h>
  31. #include <tools/board_reannotate_tool.h>
  32. #include <tools/pcb_selection_tool.h>
  33. /**
  34. * Transform a #BOARD_ITEM from the given #ARRAY_OPTIONS and an index into the array.
  35. *
  36. * @param aArrOpts The array options that describe the array
  37. * @param aIndex The index in the array of this item
  38. * @param aItem The item to transform
  39. */
  40. static void TransformItem( const ARRAY_OPTIONS& aArrOpts, int aIndex, BOARD_ITEM& aItem )
  41. {
  42. const ARRAY_OPTIONS::TRANSFORM transform = aArrOpts.GetTransform( aIndex, aItem.GetPosition() );
  43. aItem.Move( transform.m_offset );
  44. aItem.Rotate( aItem.GetPosition(), transform.m_rotation );
  45. }
  46. void ARRAY_CREATOR::Invoke()
  47. {
  48. // bail out if no items
  49. if( m_selection.Size() == 0 )
  50. return;
  51. FOOTPRINT* const fp = m_isFootprintEditor ? m_parent.GetBoard()->GetFirstFootprint() : nullptr;
  52. const bool enableArrayNumbering = m_isFootprintEditor;
  53. const VECTOR2I rotPoint = m_selection.GetCenter();
  54. std::unique_ptr<ARRAY_OPTIONS> array_opts;
  55. DIALOG_CREATE_ARRAY dialog( &m_parent, array_opts, enableArrayNumbering, rotPoint );
  56. int ret = dialog.ShowModal();
  57. if( ret != wxID_OK || array_opts == nullptr )
  58. return;
  59. BOARD_COMMIT commit( &m_parent );
  60. ARRAY_PAD_NUMBER_PROVIDER pad_number_provider( fp, *array_opts );
  61. EDA_ITEMS all_added_items;
  62. // The first item in list is the original item. We do not modify it
  63. for( int ptN = 0; ptN < array_opts->GetArraySize(); ptN++ )
  64. {
  65. PCB_SELECTION items_for_this_block;
  66. for ( int i = 0; i < m_selection.Size(); ++i )
  67. {
  68. BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( m_selection[ i ] );
  69. if( !item )
  70. continue;
  71. if( item->Type() == PCB_PAD_T && !m_isFootprintEditor )
  72. {
  73. // If it is not the footprint editor, then duplicate the parent footprint instead
  74. item = item->GetParentFootprint();
  75. }
  76. BOARD_ITEM* this_item = nullptr;
  77. if( ptN == 0 )
  78. {
  79. // the first point: we don't own this or add it, but
  80. // we might still modify it (position or label)
  81. this_item = item;
  82. commit.Modify( this_item );
  83. TransformItem( *array_opts, ptN, *this_item );
  84. }
  85. else
  86. {
  87. if( m_isFootprintEditor )
  88. {
  89. // Don't bother incrementing pads: the footprint won't update until commit,
  90. // so we can only do this once
  91. this_item = fp->DuplicateItem( item );
  92. }
  93. else
  94. {
  95. switch( item->Type() )
  96. {
  97. case PCB_FOOTPRINT_T:
  98. case PCB_SHAPE_T:
  99. case PCB_BITMAP_T:
  100. case PCB_TEXT_T:
  101. case PCB_TEXTBOX_T:
  102. case PCB_TRACE_T:
  103. case PCB_ARC_T:
  104. case PCB_VIA_T:
  105. case PCB_DIM_ALIGNED_T:
  106. case PCB_DIM_CENTER_T:
  107. case PCB_DIM_RADIAL_T:
  108. case PCB_DIM_ORTHOGONAL_T:
  109. case PCB_DIM_LEADER_T:
  110. case PCB_TARGET_T:
  111. case PCB_ZONE_T:
  112. this_item = item->Duplicate();
  113. break;
  114. case PCB_GROUP_T:
  115. this_item = static_cast<PCB_GROUP*>( item )->DeepDuplicate();
  116. break;
  117. default:
  118. // Silently drop other items (such as footprint texts) from duplication
  119. break;
  120. }
  121. // @TODO: we should merge zones. This is a bit tricky, because
  122. // the undo command needs saving old area, if it is merged.
  123. }
  124. // Add new items to selection (footprints in the selection will be reannotated)
  125. items_for_this_block.Add( this_item );
  126. if( this_item )
  127. {
  128. // Because aItem is/can be created from a selected item, and inherits from
  129. // it this state, reset the selected stated of aItem:
  130. this_item->ClearSelected();
  131. if( this_item->Type() == PCB_GROUP_T )
  132. {
  133. static_cast<PCB_GROUP*>( this_item )->RunOnDescendants(
  134. [&]( BOARD_ITEM* aItem )
  135. {
  136. aItem->ClearSelected();
  137. commit.Add( aItem );
  138. });
  139. }
  140. else if( this_item->Type() == PCB_FOOTPRINT_T )
  141. {
  142. static_cast<FOOTPRINT*>( this_item )->RunOnChildren(
  143. [&]( BOARD_ITEM* aItem )
  144. {
  145. aItem->ClearSelected();
  146. });
  147. }
  148. TransformItem( *array_opts, ptN, *this_item );
  149. commit.Add( this_item );
  150. }
  151. }
  152. // attempt to renumber items if the array parameters define
  153. // a complete numbering scheme to number by (as opposed to
  154. // implicit numbering by incrementing the items during creation
  155. if( this_item && array_opts->ShouldNumberItems() )
  156. {
  157. // Renumber non-aperture pads.
  158. if( this_item->Type() == PCB_PAD_T )
  159. {
  160. PAD& pad = static_cast<PAD&>( *this_item );
  161. if( pad.CanHaveNumber() )
  162. {
  163. wxString newNumber = pad_number_provider.GetNextPadNumber();
  164. pad.SetNumber( newNumber );
  165. }
  166. }
  167. }
  168. }
  169. if( !m_isFootprintEditor && array_opts->ShouldReannotateFootprints() )
  170. {
  171. m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicates( items_for_this_block,
  172. all_added_items );
  173. }
  174. for( EDA_ITEM* item : items_for_this_block )
  175. all_added_items.push_back( item );
  176. }
  177. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  178. m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &all_added_items );
  179. commit.Push( _( "Create an array" ) );
  180. }