Hi everyone,
I am a new developer in kicad community. I have been working on adding
zones to modules, so I would like to share this work.
Find attached a patch to have this functionality.
Regards,
Simon
From c4781992a8d4cd17e4a4592fea3bdaf3ec25e31f Mon Sep 17 00:00:00 2001
From: Simon Santesteban <t...@embeblue.com>
Date: Mon, 19 Mar 2018 01:10:18 +0100
Subject: [PATCH] Add support for Zone keepout areas within modules
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.7.4"
This is a multi-part message in MIME format.
--------------2.7.4
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit
---
pcbnew/board_commit.cpp | 4 +
pcbnew/class_board.cpp | 6 ++
pcbnew/class_board.h | 74 +++++++++++++
pcbnew/class_module.cpp | 102 ++++++++++++++++++
pcbnew/class_module.h | 45 ++++++++
pcbnew/collectors.cpp | 2 +
pcbnew/controle.cpp | 1 +
pcbnew/drc.cpp | 24 ++---
pcbnew/footprint_editor_onclick.cpp | 91 +++++++++++++++-
pcbnew/footprint_editor_utils.cpp | 4 +
pcbnew/kicad_clipboard.cpp | 7 +-
pcbnew/kicad_plugin.cpp | 21 +++-
pcbnew/menubar_footprint_editor.cpp | 5 +
pcbnew/pcb_parser.cpp | 23 +++-
pcbnew/pcbnew_id.h | 1 +
pcbnew/tool_footprint_editor.cpp | 4 +
pcbnew/tools/drawing_tool.cpp | 8 +-
pcbnew/tools/pcb_actions.cpp | 1 +
pcbnew/tools/pcbnew_control.cpp | 5 +
pcbnew/tools/selection_tool.cpp | 11 ++
pcbnew/tools/zone_create_helper.cpp | 9 ++
pcbnew/zone_filler.cpp | 5 +-
pcbnew/zones_functions_for_undo_redo.cpp | 173 +++++++++++++++++++++++++++++++
pcbnew/zones_functions_for_undo_redo.h | 24 +++++
24 files changed, 629 insertions(+), 21 deletions(-)
--------------2.7.4
Content-Type: text/x-patch;
name="0001-Add-support-for-Zone-keepout-areas-within-modules.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment;
filename="0001-Add-support-for-Zone-keepout-areas-within-modules.patch"
diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp
index 57a5d62..1ae83e3 100644
--- a/pcbnew/board_commit.cpp
+++ b/pcbnew/board_commit.cpp
@@ -297,6 +297,10 @@ EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem )
const
case PCB_MODULE_EDGE_T:
case PCB_MODULE_TEXT_T:
return aItem->GetParent();
+ case PCB_ZONE_AREA_T:
+ if(aItem->GetParent() && aItem->GetParent()->Type() ==
PCB_MODULE_T)
+ return aItem->GetParent();
+ // else do default
default:
return aItem;
}
diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp
index c5486d8..3067f95 100644
--- a/pcbnew/class_board.cpp
+++ b/pcbnew/class_board.cpp
@@ -955,6 +955,12 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem )
break;
}
}
+
+ // search inside modules
+ for( MODULE* module = m_Modules; module; module =
module->Next() )
+ {
+ module->Remove(aBoardItem);
+ }
break;
case PCB_MODULE_T:
diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h
index 81a048e..22389cd 100644
--- a/pcbnew/class_board.h
+++ b/pcbnew/class_board.h
@@ -47,6 +47,8 @@
#include <memory>
+#include <class_module.h>
+
using std::unique_ptr;
class PCB_BASE_FRAME;
@@ -1013,6 +1015,78 @@ public:
return (int) m_ZoneDescriptorList.size();
}
+
+ /**
+ * Function GetAreaIncludingModules
+ * returns the Area (Zone Container) at a given index, including modules.
+ * @param index The array type index into a collection of ZONE_CONTAINER *.
+ * @return ZONE_CONTAINER* - a pointer to the Area or NULL if index out of
range.
+ */
+ ZONE_CONTAINER* GetAreaIncludingModules( int index ) const
+ {
+ if( (unsigned) index < m_ZoneDescriptorList.size() )
+ return m_ZoneDescriptorList[index];
+
+ int count = (int) m_ZoneDescriptorList.size();
+ for( MODULE* module = m_Modules; module; module =
module->Next() )
+ {
+ int n_mod = module->GetAreaCount();
+
+ if(index < count + n_mod) {
+ // is inside this module
+ int idx = index - count;
+ return module->GetArea(idx);
+ }
+ count += n_mod;
+ }
+
+ return NULL;
+ }
+
+ /**
+ * Function GetAreaIndexIncludingModules
+ * returns the Area Index for the given Zone Container, including modules.
+ * @param aArea :The ZONE_CONTAINER to find.
+ * @return an Area Index in m_ZoneDescriptorList or -1 if non found.
+ */
+ int GetAreaIndexIncludingModules( const ZONE_CONTAINER* aArea ) const
+ {
+ for( int ii = 0; ii < GetAreaCount(); ii++ ) // Search for aArea in
list
+ {
+ if( aArea == GetArea( ii ) ) // Found !
+ return ii;
+ }
+
+ int count = GetAreaCount();
+ for( MODULE* module = m_Modules; module; module =
module->Next() )
+ {
+ int n_mod = module->GetAreaCount();
+ int idx = module->GetAreaIndex(aArea);
+
+ if(idx >= 0) {
+ return idx + count;
+ }
+ count += n_mod;
+ }
+ return -1;
+ }
+
+ /**
+ * Function GetAreaCountIncludingModules
+ * @return int - The number of Areas or ZONE_CONTAINER including modules.
+ */
+ int GetAreaCountIncludingModules() const
+ {
+ int ret = GetAreaCount();
+
+ for( MODULE* module = m_Modules; module; module = module->Next() )
+ {
+ ret += module->GetAreaCount();
+ }
+
+ return ret;
+ }
+
/* Functions used in test, merge and cut outlines */
/**
diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp
index c201a0f..5204965 100644
--- a/pcbnew/class_module.cpp
+++ b/pcbnew/class_module.cpp
@@ -77,6 +77,8 @@ MODULE::MODULE( BOARD* parent ) :
m_Value = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_VALUE );
m_3D_Drawings.clear();
+
+ m_ZoneDescriptorList.clear();
}
@@ -139,6 +141,15 @@ MODULE::MODULE( const MODULE& aModule ) :
m_arflag = 0;
+ // Copy zones
+ m_ZoneDescriptorList.clear();
+ for( int ii = 0; ii < aModule.GetAreaCount(); ii++ )
+ {
+ ZONE_CONTAINER* zone = aModule.GetArea( ii );
+ ZONE_CONTAINER* zone_clone = new ZONE_CONTAINER(*zone);
+ Add(static_cast<BOARD_ITEM*>(zone_clone), ADD_APPEND );
+ }
+
// Ensure auxiliary data is up to date
CalculateBoundingBox();
@@ -149,6 +160,12 @@ MODULE::MODULE( const MODULE& aModule ) :
MODULE::~MODULE()
{
+ while( m_ZoneDescriptorList.size() )
+ {
+ ZONE_CONTAINER* area_to_remove = m_ZoneDescriptorList[0];
+ Delete( area_to_remove );
+ }
+
delete m_Reference;
delete m_Value;
delete m_initial_comments;
@@ -217,6 +234,15 @@ MODULE& MODULE::operator=( const MODULE& aOther )
m_Doc = aOther.m_Doc;
m_KeyWord = aOther.m_KeyWord;
+ // Copy zones
+ m_ZoneDescriptorList.clear();
+ for( int ii = 0; ii < aOther.GetAreaCount(); ii++ )
+ {
+ ZONE_CONTAINER* zone = aOther.GetArea( ii );
+ ZONE_CONTAINER* zone_clone = new ZONE_CONTAINER(*zone);
+ Add(static_cast<BOARD_ITEM*>(zone_clone), ADD_APPEND );
+ }
+
// Ensure auxiliary data is up to date
CalculateBoundingBox();
@@ -224,6 +250,16 @@ MODULE& MODULE::operator=( const MODULE& aOther )
}
+void MODULE::DeleteZONEOutlines()
+{
+ // the vector does not know how to delete the ZONE Outlines, it holds
+ // pointers
+ for( unsigned i = 0; i<m_ZoneDescriptorList.size(); ++i )
+ delete m_ZoneDescriptorList[i];
+
+ m_ZoneDescriptorList.clear();
+}
+
void MODULE::ClearAllNets()
{
// Force the ORPHANED dummy net info for all pads.
@@ -273,6 +309,11 @@ void MODULE::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
m_Pads.PushFront( static_cast<D_PAD*>( aBoardItem ) );
break;
+ // this one uses a vector
+ case PCB_ZONE_AREA_T:
+ m_ZoneDescriptorList.push_back( (ZONE_CONTAINER*)
aBoardItem );
+ break;
+
default:
{
wxString msg;
@@ -301,6 +342,11 @@ void MODULE::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
static_cast<D_PAD*>( aBoardItem )->SetLocalCoord();
break;
+ case PCB_ZONE_AREA_T:
+ //static_cast<ZONE_CONTAINER*>( aBoardItem )->SetLocalCoord();
+ // TODO : create setlocalcoord function
+ break;
+
default:
// Huh? It should have been filtered out by the previous switch
assert(false);
@@ -327,6 +373,18 @@ void MODULE::Remove( BOARD_ITEM* aBoardItem )
m_Pads.Remove( static_cast<D_PAD*>( aBoardItem ) );
break;
+ case PCB_ZONE_AREA_T: // this one uses a vector
+ // find the item in the vector, then delete then erase it.
+ for( unsigned i = 0; i<m_ZoneDescriptorList.size(); ++i )
+ {
+ if( m_ZoneDescriptorList[i] == (ZONE_CONTAINER*) aBoardItem )
+ {
+ m_ZoneDescriptorList.erase( m_ZoneDescriptorList.begin() + i );
+ break;
+ }
+ }
+ break;
+
default:
{
wxString msg;
@@ -813,6 +871,19 @@ SEARCH_RESULT MODULE::Visit( INSPECTOR inspector, void*
testData, const KICAD_T
break;
+ case PCB_ZONE_AREA_T:
+ // PCB_ZONE_AREA_T are in the m_ZoneDescriptorList std::vector
+ for( unsigned i = 0; i< m_ZoneDescriptorList.size(); ++i )
+ {
+ result = m_ZoneDescriptorList[i]->Visit( inspector, testData,
p );
+
+ if( result == SEARCH_QUIT )
+ break;
+ }
+
+ ++p;
+ break;
+
default:
done = true;
break;
@@ -859,6 +930,15 @@ void MODULE::RunOnChildren( const std::function<void
(BOARD_ITEM*)>& aFunction )
for( BOARD_ITEM* drawing = m_Drawings; drawing; drawing =
drawing->Next() )
aFunction( drawing );
+ // for each module, its zones
+ for( int ii = 0; ii < m_ZoneDescriptorList.size(); ii++ )
+ {
+ ZONE_CONTAINER* zone = this->GetArea( ii );
+
+
+ aFunction( static_cast<BOARD_ITEM*>( zone ) );
+ }
+
aFunction( static_cast<BOARD_ITEM*>( m_Reference ) );
aFunction( static_cast<BOARD_ITEM*>( m_Value ) );
}
@@ -1014,6 +1094,13 @@ void MODULE::Flip( const wxPoint& aCentre )
break;
}
}
+
+ for( int ii = 0; ii < m_ZoneDescriptorList.size(); ii++ )
+ {
+ ZONE_CONTAINER* zone = this->GetArea( ii );
+
+ zone->Flip( m_Pos );
+ }
CalculateBoundingBox();
}
@@ -1056,6 +1143,14 @@ void MODULE::SetPosition( const wxPoint& newpos )
break;
}
}
+
+ for( int ii = 0; ii < m_ZoneDescriptorList.size(); ii++ )
+ {
+ ZONE_CONTAINER* zone = this->GetArea( ii );
+
+ zone->Move( delta );
+ }
+
CalculateBoundingBox();
}
@@ -1151,6 +1246,13 @@ void MODULE::SetOrientation( double newangle )
}
}
+ for( int ii = 0; ii < m_ZoneDescriptorList.size(); ii++ )
+ {
+ ZONE_CONTAINER* zone = this->GetArea( ii );
+
+ zone->Rotate( GetPosition(), angleChange );
+ }
+
CalculateBoundingBox();
}
diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h
index 3eca1a0..e811ca7 100644
--- a/pcbnew/class_module.h
+++ b/pcbnew/class_module.h
@@ -47,6 +47,9 @@
#include <functional>
+#include <class_zone.h>
+
+
class LINE_READER;
class EDA_3D_CANVAS;
class EDA_DRAW_PANEL;
@@ -101,6 +104,8 @@ class MODULE_3D_SETTINGS
wxString m_Filename; ///< The 3D shape filename in 3D library
};
+DECL_VEC_FOR_SWIG(ZONE_CONTAINERS, ZONE_CONTAINER*)
+
class MODULE : public BOARD_ITEM_CONTAINER
{
public:
@@ -682,13 +687,53 @@ public:
*/
bool BuildPolyCourtyard();
+ void DeleteZONEOutlines();
+
virtual void SwapData( BOARD_ITEM* aImage ) override;
+ ZONE_CONTAINERS& Zones() { return m_ZoneDescriptorList; }
+
+ ZONE_CONTAINER* GetArea( int index ) const
+ {
+ if( (unsigned) index < m_ZoneDescriptorList.size() )
+ return m_ZoneDescriptorList[index];
+
+ return NULL;
+ }
+
+ /**
+ * Function GetAreaIndex
+ * returns the Area Index for the given Zone Container.
+ * @param aArea :The ZONE_CONTAINER to find.
+ * @return an Area Index in m_ZoneDescriptorList or -1 if non found.
+ */
+ int GetAreaIndex( const ZONE_CONTAINER* aArea ) const
+ {
+ for( int ii = 0; ii < GetAreaCount(); ii++ ) // Search for aArea in
list
+ {
+ if( aArea == GetArea( ii ) ) // Found !
+ return ii;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Function GetAreaCount
+ * @return int - The number of Areas or ZONE_CONTAINER.
+ */
+ int GetAreaCount() const
+ {
+ return (int) m_ZoneDescriptorList.size();
+ }
+
#if defined(DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override {
ShowDummy( os ); }
#endif
private:
+ ZONE_CONTAINERS m_ZoneDescriptorList;
+
DLIST<D_PAD> m_Pads; ///< Linked list of pads.
DLIST<BOARD_ITEM> m_Drawings; ///< Linked list of graphical items.
std::list<MODULE_3D_SETTINGS> m_3D_Drawings; ///< Linked list of 3D
models.
diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp
index 8b3e7d3..1d19da1 100644
--- a/pcbnew/collectors.cpp
+++ b/pcbnew/collectors.cpp
@@ -115,6 +115,7 @@ const KICAD_T GENERAL_COLLECTOR::ModulesAndTheirItems[] = {
PCB_MODULE_EDGE_T,
PCB_PAD_T,
PCB_MODULE_T,
+ PCB_ZONE_AREA_T,
EOT
};
@@ -123,6 +124,7 @@ const KICAD_T GENERAL_COLLECTOR::ModuleItems[] = {
PCB_MODULE_TEXT_T,
PCB_MODULE_EDGE_T,
PCB_PAD_T,
+ PCB_ZONE_AREA_T,
EOT
};
diff --git a/pcbnew/controle.cpp b/pcbnew/controle.cpp
index 8ae024a..9e5e617 100644
--- a/pcbnew/controle.cpp
+++ b/pcbnew/controle.cpp
@@ -143,6 +143,7 @@ BOARD_ITEM* PCB_BASE_FRAME::PcbGeneralLocateAndDisplay( int
aHotKeyCode )
case ID_PCB_ZONES_BUTT:
case ID_PCB_KEEPOUT_AREA_BUTT:
+ case ID_MODEDIT_KEEPOUT_AREA_TOOL:
scanList = GENERAL_COLLECTOR::Zones;
break;
diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp
index f1bb551..4c7ac7f 100644
--- a/pcbnew/drc.cpp
+++ b/pcbnew/drc.cpp
@@ -186,9 +186,9 @@ int DRC::TestZoneToZoneOutline( ZONE_CONTAINER* aZone, bool
aCreateMarkers )
int nerrors = 0;
// iterate through all areas
- for( int ia = 0; ia < board->GetAreaCount(); ia++ )
+ for( int ia = 0; ia < board->GetAreaCountIncludingModules(); ia++ )
{
- ZONE_CONTAINER* zoneRef = board->GetArea( ia );
+ ZONE_CONTAINER* zoneRef = board->GetAreaIncludingModules( ia );
SHAPE_POLY_SET refSmoothedPoly;
zoneRef->BuildSmoothedPoly( refSmoothedPoly );
@@ -200,9 +200,9 @@ int DRC::TestZoneToZoneOutline( ZONE_CONTAINER* aZone, bool
aCreateMarkers )
if( aZone && ( aZone != zoneRef) )
continue;
- for( int ia2 = 0; ia2 < board->GetAreaCount(); ia2++ )
+ for( int ia2 = 0; ia2 < board->GetAreaCountIncludingModules(); ia2++ )
{
- ZONE_CONTAINER* zoneToTest = board->GetArea( ia2 );
+ ZONE_CONTAINER* zoneToTest = board->GetAreaIncludingModules( ia2 );
SHAPE_POLY_SET testSmoothedPoly;
zoneToTest->BuildSmoothedPoly( testSmoothedPoly );
@@ -758,9 +758,9 @@ void DRC::testZones()
// In recent Pcbnew versions, the netcode is always >= 0, but an internal
net name
// is stored, and initialized from the file or the zone properties editor.
// if it differs from the net name from net code, there is a DRC issue
- for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
+ for( int ii = 0; ii < m_pcb->GetAreaCountIncludingModules(); ii++ )
{
- ZONE_CONTAINER* test_area = m_pcb->GetArea( ii );
+ ZONE_CONTAINER* test_area = m_pcb->GetAreaIncludingModules( ii
);
if( !test_area->IsOnCopperLayer() )
continue;
@@ -788,10 +788,10 @@ void DRC::testZones()
void DRC::testKeepoutAreas()
{
- // Test keepout areas for vias, tracks and pads inside keepout areas
- for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
+ // Test keepout areas for vias, tracks and pads inside keepout areas,
including modules'
+ for( int ii = 0; ii < m_pcb->GetAreaCountIncludingModules(); ii++ )
{
- ZONE_CONTAINER* area = m_pcb->GetArea( ii );
+ ZONE_CONTAINER* area = m_pcb->GetAreaIncludingModules( ii );
if( !area->GetIsKeepout() )
{
@@ -966,10 +966,10 @@ void DRC::testTexts()
bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
{
- // Test keepout areas for vias, tracks and pads inside keepout areas
- for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
+ // Test keepout areas for vias, tracks and pads inside keepout areas,
including modules'
+ for( int ii = 0; ii < m_pcb->GetAreaCountIncludingModules(); ii++ )
{
- ZONE_CONTAINER* area = m_pcb->GetArea( ii );
+ ZONE_CONTAINER* area = m_pcb->GetAreaIncludingModules( ii );
if( !area->GetIsKeepout() )
continue;
diff --git a/pcbnew/footprint_editor_onclick.cpp
b/pcbnew/footprint_editor_onclick.cpp
index 0277cee..d698c6a 100644
--- a/pcbnew/footprint_editor_onclick.cpp
+++ b/pcbnew/footprint_editor_onclick.cpp
@@ -44,6 +44,12 @@
#include <dialog_edit_module_for_Modedit.h>
#include <menus_helpers.h>
+#include <zones_functions_for_undo_redo.h>
+#include <board_commit.h>
+#include <zone_filler.h>
+
+static PICKED_ITEMS_LIST s_PickedList; // a picked list to save
zones for undo/redo command
+static PICKED_ITEMS_LIST s_AuxiliaryList; // a picked list to store
zones that are deleted or added when combined
void FOOTPRINT_EDIT_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos )
{
@@ -519,7 +525,6 @@ void FOOTPRINT_EDIT_FRAME::OnLeftDClick( wxDC* DC, const
wxPoint& MousePos )
}
}
-
void FOOTPRINT_EDIT_FRAME::OnEditItemRequest( wxDC* aDC, BOARD_ITEM* aItem )
{
switch( aItem->Type() )
@@ -550,6 +555,90 @@ void FOOTPRINT_EDIT_FRAME::OnEditItemRequest( wxDC* aDC,
BOARD_ITEM* aItem )
m_canvas->Refresh();
break;
+ case PCB_ZONE_AREA_T:
+ {
+ ZONE_EDIT_T edited;
+ ZONE_SETTINGS zoneInfo = GetZoneSettings();
+
+ BOARD_COMMIT commit( this );
+ m_canvas->SetIgnoreMouseEvents( true );
+
+ ZONE_CONTAINER* aZone = static_cast<ZONE_CONTAINER*> (
aItem );
+ wxDC* DC = aDC;
+
+ MODULE* parentModule = static_cast<MODULE*> (
aZone->GetParent() );
+
+ if( !parentModule ) {
+ return;
+ }
+
+ // Save initial zones configuration, for undo/redo,
before adding new zone
+ // note the net name and the layer can be changed, so
we must save all zones
+ s_AuxiliaryList.ClearListAndDeleteItems();
+ s_PickedList.ClearListAndDeleteItems();
+ SaveCopyOfZones( s_PickedList, parentModule, -1,
UNDEFINED_LAYER );
+
+ if( aZone->GetIsKeepout() )
+ {
+ // edit a keepout area on a copper layer
+ zoneInfo << *aZone;
+ edited = InvokeKeepoutAreaEditor( this,
&zoneInfo );
+ }
+ else if( IsCopperLayer( aZone->GetLayer() ) )
+ {
+ // edit a zone on a copper layer
+
+ zoneInfo << *aZone;
+
+ edited = InvokeCopperZonesEditor( this,
&zoneInfo );
+ }
+ else
+ {
+ edited = InvokeNonCopperZonesEditor( this,
aZone, &zoneInfo );
+ }
+
+ m_canvas->MoveCursorToCrossHair();
+ m_canvas->SetIgnoreMouseEvents( false );
+
+ if( edited == ZONE_ABORT )
+ {
+ s_AuxiliaryList.ClearListAndDeleteItems();
+ s_PickedList.ClearListAndDeleteItems();
+ return;
+ }
+
+ if( edited == ZONE_EXPORT_VALUES )
+ {
+ UpdateCopyOfZonesList( s_PickedList,
s_AuxiliaryList, parentModule );
+ commit.Stage( s_PickedList );
+ commit.Push( _( "Modify zone properties" ) );
+ s_PickedList.ClearItemsList(); //
s_ItemsListPicker is no more owner of picked items
+ return;
+ }
+
+ wxBusyCursor dummy;
+
+ // Undraw old zone outlines
+ for( int ii = 0; ii < parentModule->GetAreaCount();
ii++ )
+ {
+ ZONE_CONTAINER* edge_zone =
parentModule->GetArea( ii );
+ edge_zone->Draw( m_canvas, DC, GR_XOR );
+
+ if( IsGalCanvasActive() )
+ {
+ GetGalCanvas()->GetView()->Update(
edge_zone );
+ }
+ }
+
+ zoneInfo.ExportSetting( *aZone );
+
+ commit.Stage( s_PickedList );
+ commit.Push( _( "Modify zone properties" ) );
+
+ s_PickedList.ClearItemsList(); // s_ItemsListPicker is
no longer owner of picked items
+ }
+ break;
+
default:
break;
}
diff --git a/pcbnew/footprint_editor_utils.cpp
b/pcbnew/footprint_editor_utils.cpp
index b49ee80..04a6eb8 100644
--- a/pcbnew/footprint_editor_utils.cpp
+++ b/pcbnew/footprint_editor_utils.cpp
@@ -959,6 +959,10 @@ void FOOTPRINT_EDIT_FRAME::OnVerticalToolbar(
wxCommandEvent& aEvent )
SetToolID( id, wxCURSOR_PENCIL, _( "Set grid origin" ) );
break;
+ case ID_MODEDIT_KEEPOUT_AREA_TOOL:
+ SetToolID( id, wxCURSOR_PENCIL, _( "Add keepout" ) );
+ break;
+
case ID_MODEDIT_PAD_TOOL:
if( GetBoard()->m_Modules )
{
diff --git a/pcbnew/kicad_clipboard.cpp b/pcbnew/kicad_clipboard.cpp
index a5680c4..05bc033 100644
--- a/pcbnew/kicad_clipboard.cpp
+++ b/pcbnew/kicad_clipboard.cpp
@@ -78,10 +78,13 @@ void CLIPBOARD_IO::SaveSelection( const SELECTION&
aSelected )
bool onlyModuleParts = true;
for( const auto i : aSelected )
{
- // check if it not one of the module primitives
+ // check if it not one of the module primitives
if( ( i->Type() != PCB_MODULE_EDGE_T ) &&
( i->Type() != PCB_MODULE_TEXT_T ) &&
- ( i->Type() != PCB_PAD_T ) )
+ ( i->Type() != PCB_PAD_T ) &&
+ !( (i->Type() == PCB_ZONE_AREA_T ) &&
+ (i->GetParent()) &&
+ (i->GetParent()->Type() ==
PCB_MODULE_T) ) )
{
onlyModuleParts = false;
continue;
diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp
index d2c0da2..2311891 100644
--- a/pcbnew/kicad_plugin.cpp
+++ b/pcbnew/kicad_plugin.cpp
@@ -1171,7 +1171,14 @@ void PCB_IO::format( MODULE* aModule, int aNestLevel )
const
}
++bs3D;
}
-
+
+ // save zones
+ for( int ii = 0; ii < aModule->GetAreaCount(); ii++ )
+ {
+ ZONE_CONTAINER* zone = aModule->GetArea( ii );
+ format( zone, aNestLevel+1 );
+ };
+
m_out->Print( aNestLevel, ")\n" );
}
@@ -1193,6 +1200,11 @@ void PCB_IO::formatLayers( LSET aLayerMask, int
aNestLevel ) const
static const LSET mask( 2, B_Mask, F_Mask );
static const LSET crt_yd(2, B_CrtYd, F_CrtYd );
static const LSET fab( 2, B_Fab, F_Fab );
+ static const LSET inner( 30,
+ In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu, In7_Cu, In8_Cu,
In9_Cu, In10_Cu,
+ In11_Cu, In12_Cu, In13_Cu, In14_Cu, In15_Cu, In16_Cu, In17_Cu,
In18_Cu, In19_Cu, In20_Cu,
+ In21_Cu, In22_Cu, In23_Cu, In24_Cu, In25_Cu, In26_Cu, In27_Cu,
In28_Cu, In29_Cu, In30_Cu
+ );
LSET cu_mask = cu_all;
@@ -1248,6 +1260,13 @@ void PCB_IO::formatLayers( LSET aLayerMask, int
aNestLevel ) const
aLayerMask &= ~fab;
}
+ if( ( aLayerMask & inner ) == inner )
+ {
+ output += " In*.Cu";
+ aLayerMask &= ~inner;
+ }
+
+
// output any individual layers not handled in wildcard combos above
if( m_board )
diff --git a/pcbnew/menubar_footprint_editor.cpp
b/pcbnew/menubar_footprint_editor.cpp
index a3bf894..6db3211 100644
--- a/pcbnew/menubar_footprint_editor.cpp
+++ b/pcbnew/menubar_footprint_editor.cpp
@@ -315,6 +315,11 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar()
_( "Place footprint reference anchor" ),
KiBitmap( anchor_xpm ) );
+ placeMenu->AppendSeparator();
+
+ AddMenuItem( placeMenu, ID_MODEDIT_KEEPOUT_AREA_TOOL,
+ _( "&Keepout Area" ), _( "Add keepout areas" ), KiBitmap(
add_keepout_area_xpm ) );
+
//----- Preferences menu -----------------
wxMenu* prefs_menu = new wxMenu;
diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp
index f6049dd..c062411 100644
--- a/pcbnew/pcb_parser.cpp
+++ b/pcbnew/pcb_parser.cpp
@@ -79,6 +79,11 @@ void PCB_PARSER::init()
m_layerMasks[ "*.SilkS" ] = LSET( 2, B_SilkS, F_SilkS );
m_layerMasks[ "*.Fab" ] = LSET( 2, B_Fab, F_Fab );
m_layerMasks[ "*.CrtYd" ] = LSET( 2, B_CrtYd, F_CrtYd );
+ m_layerMasks[ "In*.Cu" ] = LSET( 30,
+ In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu, In7_Cu, In8_Cu,
In9_Cu, In10_Cu,
+ In11_Cu, In12_Cu, In13_Cu, In14_Cu, In15_Cu, In16_Cu, In17_Cu,
In18_Cu, In19_Cu, In20_Cu,
+ In21_Cu, In22_Cu, In23_Cu, In24_Cu, In25_Cu, In26_Cu, In27_Cu,
In28_Cu, In29_Cu, In30_Cu
+ );
// This is for the first pretty & *.kicad_pcb formats, which had
// Inner1_Cu - Inner14_Cu with the numbering sequence
@@ -2009,6 +2014,10 @@ MODULE* PCB_PARSER::parseMODULE_unchecked(
wxArrayString* aInitialComments )
module->Add3DModel( parse3DModel() );
break;
+ case T_zone:
+ module->Add( parseZONE_CONTAINER(), ADD_APPEND );
+ break;
+
default:
Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
"autoplace_cost90, autoplace_cost180,
solder_mask_margin, "
@@ -2834,7 +2843,19 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER()
// bigger scope since each filled_polygon is concatenated in here
SHAPE_POLY_SET pts;
- std::unique_ptr< ZONE_CONTAINER > zone( new ZONE_CONTAINER( m_board ) );
+ std::unique_ptr< ZONE_CONTAINER > zone;
+ if(m_board != nullptr)
+ {
+ zone = std::unique_ptr< ZONE_CONTAINER > ( new ZONE_CONTAINER(
m_board ) );
+ }else {
+ std::unique_ptr<BOARD> aBoard( new BOARD( ) );
+ //zone = std::unique_ptr< ZONE_CONTAINER > ( new
ZONE_CONTAINER( nullptr ) );
+ //aBoard.get()->GetZoneSettings().ExportSetting( *(zone.get())
);
+ //aBoard->GetZoneSettings().ExportSetting( *zone );
+
+ zone = std::unique_ptr< ZONE_CONTAINER > ( new ZONE_CONTAINER(
aBoard.get() ) );
+ zone->SetParent( nullptr );
+ }
zone->SetPriority( 0 );
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 02d35e4..04924ed 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -340,6 +340,7 @@ enum pcbnew_ids
// Module editor right vertical tool bar commands.
ID_MODEDIT_PAD_TOOL,
+ ID_MODEDIT_KEEPOUT_AREA_TOOL,
ID_MODEDIT_LINE_TOOL,
ID_MODEDIT_CIRCLE_TOOL,
ID_MODEDIT_ARC_TOOL,
diff --git a/pcbnew/tool_footprint_editor.cpp b/pcbnew/tool_footprint_editor.cpp
index 62c6b54..a77ec34 100644
--- a/pcbnew/tool_footprint_editor.cpp
+++ b/pcbnew/tool_footprint_editor.cpp
@@ -164,6 +164,10 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
m_drawToolBar->AddTool( ID_MODEDIT_PAD_TOOL, wxEmptyString,
KiScaledBitmap( pad_xpm, this ),
_( "Add pad" ), wxITEM_CHECK );
+ m_drawToolBar->AddTool( ID_MODEDIT_KEEPOUT_AREA_TOOL, wxEmptyString,
+ KiScaledBitmap( add_keepout_area_xpm, this ),
+ _( "Add keepout areas" ), wxITEM_CHECK );
+
KiScaledSeparator( m_drawToolBar, this );
m_drawToolBar->AddTool( ID_MODEDIT_LINE_TOOL, wxEmptyString,
KiScaledBitmap( add_graphical_segments_xpm, this ),
_( "Add graphic line" ), wxITEM_CHECK );
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
index 6fc8914..791ec90 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -643,7 +643,13 @@ int DRAWING_TOOL::DrawZoneKeepout( const TOOL_EVENT&
aEvent )
{
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::KEEPOUT );
- m_frame->SetToolID( ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL, _( "Add
keepout" ) );
+ if( m_editModules )
+ {
+ m_frame->SetToolID( ID_MODEDIT_KEEPOUT_AREA_TOOL,
wxCURSOR_PENCIL, _( "Add keepout" ) );
+ } else
+ {
+ m_frame->SetToolID( ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL,
_( "Add keepout" ) );
+ }
return drawZone( true, ZONE_MODE::ADD );
}
diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp
index bc10bc2..2a5ba40 100644
--- a/pcbnew/tools/pcb_actions.cpp
+++ b/pcbnew/tools/pcb_actions.cpp
@@ -61,6 +61,7 @@ OPT<TOOL_EVENT> PCB_ACTIONS::TranslateLegacyId( int aId )
case ID_PCB_DRAW_VIA_BUTT:
return PCB_ACTIONS::drawVia.MakeEvent();
+ case ID_MODEDIT_KEEPOUT_AREA_TOOL:
case ID_PCB_KEEPOUT_AREA_BUTT:
return PCB_ACTIONS::drawZoneKeepout.MakeEvent();
diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp
index b8bc07e..225ebea 100644
--- a/pcbnew/tools/pcbnew_control.cpp
+++ b/pcbnew/tools/pcbnew_control.cpp
@@ -795,6 +795,11 @@ int PCBNEW_CONTROL::PasteItemsFromClipboard( const
TOOL_EVENT& aEvent )
item->SetParent ( board()->m_Modules.GetFirst() );
items.push_back( item );
}
+ for( auto zone : mod->Zones() )
+ {
+ zone->SetParent (
board()->m_Modules.GetFirst() );
+ items.push_back( zone );
+ }
}
else
{
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index f651785..1ae8ac0 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -1524,6 +1524,17 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem
) const
{
auto* zone = static_cast<const ZONE_CONTAINER*>( aItem );
+ //check if zone has a parent of type module
+ if( zone && zone->GetParent() &&
(zone->GetParent()->Type() == PCB_MODULE_T) )
+ {
+ // only if not editing module
+ if(m_frame->IsType(FRAME_PCB_MODULE_EDITOR) ==
false)
+ {
+ return false;
+ }
+
+ }
+
if( zone && zone->GetIsKeepout() )
{
auto zoneLayers = zone->GetLayerSet().Seq();
diff --git a/pcbnew/tools/zone_create_helper.cpp
b/pcbnew/tools/zone_create_helper.cpp
index 1e14a91..315f7cc 100644
--- a/pcbnew/tools/zone_create_helper.cpp
+++ b/pcbnew/tools/zone_create_helper.cpp
@@ -92,6 +92,15 @@ std::unique_ptr<ZONE_CONTAINER>
ZONE_CREATE_HELPER::createNewZone( bool aKeepout
// Apply the selected settings
zoneInfo.ExportSetting( *newZone );
+
+ if(frame.IsType(FRAME_PCB_MODULE_EDITOR)) {
+ for( auto module : board.Modules() )
+ {
+ newZone->SetParent(module);
+ break;
+ }
+
+ }
return newZone;
}
diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp
index 0bd9e34..bcb42f9 100644
--- a/pcbnew/zone_filler.cpp
+++ b/pcbnew/zone_filler.cpp
@@ -528,10 +528,9 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const
ZONE_CONTAINER* aZone,
}
}
- // Add zones outlines having an higher priority and keepout
- for( int ii = 0; ii < m_board->GetAreaCount(); ii++ )
+ for( int ii = 0; ii < m_board->GetAreaCountIncludingModules(); ii++ )
{
- ZONE_CONTAINER* zone = m_board->GetArea( ii );
+ ZONE_CONTAINER* zone = m_board->GetAreaIncludingModules( ii );
// If the zones share no common layers
if( !aZone->CommonLayerExists( zone->GetLayerSet() ) )
diff --git a/pcbnew/zones_functions_for_undo_redo.cpp
b/pcbnew/zones_functions_for_undo_redo.cpp
index 97b02cb..9322f96 100644
--- a/pcbnew/zones_functions_for_undo_redo.cpp
+++ b/pcbnew/zones_functions_for_undo_redo.cpp
@@ -162,6 +162,33 @@ int SaveCopyOfZones( PICKED_ITEMS_LIST& aPickList, BOARD*
aPcb, int aNetCode, LA
return copyCount;
}
+/**
+ * Function SaveCopyOfZones
+ * creates a copy of zones in a module having a given netcode on a given layer,
+ * and fill a pick list with pickers to handle these copies
+ * the UndoRedo status is set to UR_CHANGED for all items in list
+ * Later, UpdateCopyOfZonesList will change and update these pickers after a
zone edition
+ * @param aPickList = the pick list
+ * @param aModule = the Module
+ * @param aNetCode = the reference netcode. if aNetCode < 0 all netcodes are
used
+ * @param aLayer = the layer of zones. if aLayer < 0, all layers are used
+ * @return the count of saved copies
+ */
+int SaveCopyOfZones( PICKED_ITEMS_LIST& aPickList, MODULE* aModule, int
aNetCode, LAYER_NUM aLayer )
+{
+ int copyCount = 0;
+
+ MODULE* moduleDup = new MODULE( *aModule );
+ moduleDup->SetParent ( aModule->GetParent () );
+ ITEM_PICKER picker( aModule, UR_CHANGED );
+ picker.SetLink( moduleDup );
+ aPickList.PushItem( picker );
+ copyCount++;
+
+ return copyCount;
+}
+
+
/**
* Function UpdateCopyOfZonesList
@@ -307,3 +334,149 @@ void UpdateCopyOfZonesList( PICKED_ITEMS_LIST& aPickList,
wxASSERT_MSG( aAuxiliaryList.GetCount() == 0,
wxT( "UpdateCopyOfZonesList() error: aAuxiliaryList not
empty." ) );
}
+
+/**
+ * Function UpdateCopyOfZonesList
+ * check a pick list to remove zones identical to their copies in a module
+ * and set the type of operation in picker (UR_DELETED, UR_CHANGED)
+ * if an item is deleted, the initial values are retrievered,
+ * because they can have changed in edition
+ * @param aPickList = the main pick list
+ * @param aAuxiliaryList = the list of deleted or added (new created) items
after calculations
+ * @param aModule = the Module
+ *
+ * aAuxiliaryList is a list of pickers updated by zone algorithms:
+ * This list contains zones which were added or deleted during the zones
combine process
+ * aPickList :is a list of zones that can be modified (changed or deleted, or
not modified)
+ * Typically, this is the list of existing zones on the layer of the edited
zone,
+ * before any change.
+ * >> if the picked zone is not changed, it is removed from list
+ * >> if the picked zone was deleted (i.e. not found in board list), the
picker is modified:
+ * its status becomes UR_DELETED
+ * the aAuxiliaryList corresponding picker is removed (if not found :
set an error)
+ * >> if the picked zone was flagged as UR_NEW, and was after deleted ,
+ * perhaps combined with an other zone (i.e. not found in board list):
+ * the picker is removed
+ * the zone itself if really deleted
+ * the aAuxiliaryList corresponding picker is removed (if not found :
set an error)
+ * After aPickList is cleaned, the aAuxiliaryList is read
+ * All pickers flagged UR_NEW are moved to aPickList
+ * (the corresponding zones are zone that were created by the zone normalize
and combine process,
+ * mainly when adding cutout areas, or creating self intersecting contours)
+ * All pickers flagged UR_DELETED are removed, and the coresponding zones
actually deleted
+ * (the corresponding zones are new zone that were created by the zone
normalize process,
+ * when creating self intersecting contours, and after combined with an
existing zone.
+ * At the end of the update process the aAuxiliaryList must be void,
+ * because all pickers created by the combine process
+ * must have been removed (removed for new and deleted zones, or moved in
aPickList.)
+ * If not an error is set.
+ */
+void UpdateCopyOfZonesList( PICKED_ITEMS_LIST& aPickList,
+ PICKED_ITEMS_LIST& aAuxiliaryList,
+ MODULE* aModule )
+{
+ for( unsigned kk = 0; kk < aPickList.GetCount(); kk++ )
+ {
+ UNDO_REDO_T status = aPickList.GetPickedItemStatus( kk );
+
+ ZONE_CONTAINER* ref = (ZONE_CONTAINER*) aPickList.GetPickedItem( kk );
+
+ for( unsigned ii = 0; ; ii++ ) // analyse the main picked list
+ {
+ ZONE_CONTAINER* zone = aModule->GetArea( ii );
+
+ if( zone == NULL )
+ {
+ /* End of list: the stored item is not found:
+ * it must be in aDeletedList:
+ * search it and restore initial values
+ * or
+ * if flagged UR_NEW: remove it definitively
+ */
+ if( status == UR_NEW )
+ {
+ delete ref;
+ ref = NULL;
+ aPickList.RemovePicker( kk );
+ kk--;
+ }
+ else
+ {
+ ZONE_CONTAINER* zcopy = (ZONE_CONTAINER*)
aPickList.GetPickedItemLink( kk );
+ aPickList.SetPickedItemStatus( UR_DELETED, kk );
+
+ wxASSERT_MSG( zcopy != NULL,
+ wxT( "UpdateCopyOfZonesList() error: link =
NULL" ) );
+
+ *ref = *zcopy;
+
+ // the copy was deleted; the link does not exists now.
+ aPickList.SetPickedItemLink( NULL, kk );
+ delete zcopy;
+ }
+
+ // Remove this item from aAuxiliaryList, mainly for tests
purpose
+ bool notfound = true;
+
+ for( unsigned nn = 0; nn < aAuxiliaryList.GetCount(); nn++ )
+ {
+ if( ref != NULL && aAuxiliaryList.GetPickedItem( nn ) ==
ref )
+ {
+ aAuxiliaryList.RemovePicker( nn );
+ notfound = false;
+ break;
+ }
+ }
+
+ if( notfound ) // happens when the new zone overlaps an
existing zone
+ // and these zones are combined
+ {
+ DBG( printf(
+ "UpdateCopyOfZonesList(): item not found in
aAuxiliaryList,"
+ "combined with an other zone\n" ) );
+ }
+ break;
+ }
+
+ if( zone == ref ) // picked zone found
+ {
+ if( aPickList.GetPickedItemStatus( kk ) != UR_NEW )
+ {
+ ZONE_CONTAINER* zcopy = (ZONE_CONTAINER*)
aPickList.GetPickedItemLink( kk );
+
+ if( zone->IsSame( *zcopy ) ) // Remove picked, because
no changes
+ {
+ delete zcopy; // Delete copy
+ aPickList.RemovePicker( kk );
+ kk--;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Add new zones in main pick list, and remove pickers from Auxiliary List
+ for( unsigned ii = 0; ii < aAuxiliaryList.GetCount(); )
+ {
+ if( aAuxiliaryList.GetPickedItemStatus( ii ) == UR_NEW )
+ {
+ ITEM_PICKER picker = aAuxiliaryList.GetItemWrapper( ii );
+ aPickList.PushItem( picker );
+ aAuxiliaryList.RemovePicker( ii );
+ }
+ else if( aAuxiliaryList.GetPickedItemStatus( ii ) == UR_DELETED )
+ {
+ delete aAuxiliaryList.GetPickedItemLink( ii );
+ aAuxiliaryList.RemovePicker( ii );
+ }
+ else
+ ii++;
+ }
+
+ // Should not occur:
+ wxASSERT_MSG( aAuxiliaryList.GetCount() == 0,
+ wxT( "UpdateCopyOfZonesList() error: aAuxiliaryList not
empty." ) );
+}
+
diff --git a/pcbnew/zones_functions_for_undo_redo.h
b/pcbnew/zones_functions_for_undo_redo.h
index a5260a3..c420dde 100644
--- a/pcbnew/zones_functions_for_undo_redo.h
+++ b/pcbnew/zones_functions_for_undo_redo.h
@@ -58,6 +58,17 @@
*/
int SaveCopyOfZones(PICKED_ITEMS_LIST & aPickList, BOARD* aPcb, int aNetCode,
LAYER_NUM aLayer );
+/**
+ * Function SaveCopyOfZones
+ * creates a copy of zones in a module having a given netcode on a given layer,
+ * and fill a pick list with pickers to handle these copies
+ * @param aPickList = the pick list
+ * @param aModule = the Module
+ * @param aNetCode = the reference netcode. if aNetCode < 0 all netcodes are
used
+ * @param aLayer = the layer of zones. if aLayer < 0, all layers are used
+ * @return the count of saved copies
+ */
+int SaveCopyOfZones( PICKED_ITEMS_LIST& aPickList, MODULE* aModule, int
aNetCode, LAYER_NUM aLayer );
/**
* Function UpdateCopyOfZonesList
@@ -69,4 +80,17 @@ int SaveCopyOfZones(PICKED_ITEMS_LIST & aPickList, BOARD*
aPcb, int aNetCode, LA
*/
void UpdateCopyOfZonesList( PICKED_ITEMS_LIST& aPickList, PICKED_ITEMS_LIST&
aAuxiliaryList, BOARD* aPcb );
+
+/**
+ * Function UpdateCopyOfZonesList
+ * check a pick list to remove zones identical to their copies in a module
+ * and set the type of operation in picker (UR_DELETED, UR_CHANGED)
+ * @param aPickList = the main pick list
+ * @param aAuxiliaryList = the list of deleted or added (new created) items
after calculations
+ * @param aModule = the Module
+ */
+void UpdateCopyOfZonesList( PICKED_ITEMS_LIST& aPickList,
+ PICKED_ITEMS_LIST& aAuxiliaryList,
+ MODULE* aModule );
+
#endif // ZONES_FUNCTIONS_TO_UNDO_REDO_H
--------------2.7.4--
_______________________________________________
Mailing list: https://launchpad.net/~kicad-developers
Post to : kicad-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~kicad-developers
More help : https://help.launchpad.net/ListHelp