Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
kicad-source-mirror
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
kicad-source-mirror
Commits
56b692ae
Commit
56b692ae
authored
Mar 10, 2015
by
Maciej Suminski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
"Select net/connection" (GAL).
parent
190af5d9
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
205 additions
and
22 deletions
+205
-22
ratsnest_data.cpp
pcbnew/ratsnest_data.cpp
+42
-2
ratsnest_data.h
pcbnew/ratsnest_data.h
+22
-3
common_actions.cpp
pcbnew/tools/common_actions.cpp
+8
-0
common_actions.h
pcbnew/tools/common_actions.h
+6
-0
selection_conditions.cpp
pcbnew/tools/selection_conditions.cpp
+17
-0
selection_conditions.h
pcbnew/tools/selection_conditions.h
+8
-0
selection_tool.cpp
pcbnew/tools/selection_tool.cpp
+95
-16
selection_tool.h
pcbnew/tools/selection_tool.h
+7
-1
No files found.
pcbnew/ratsnest_data.cpp
View file @
56b692ae
...
@@ -730,6 +730,34 @@ std::list<RN_NODE_PTR> RN_NET::GetNodes( const BOARD_CONNECTED_ITEM* aItem ) con
...
@@ -730,6 +730,34 @@ std::list<RN_NODE_PTR> RN_NET::GetNodes( const BOARD_CONNECTED_ITEM* aItem ) con
}
}
void
RN_NET
::
GetAllItems
(
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
aType
)
const
{
if
(
aType
&
RN_PADS
)
{
BOOST_FOREACH
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
m_pads
|
boost
::
adaptors
::
map_keys
)
aOutput
.
push_back
(
const_cast
<
BOARD_CONNECTED_ITEM
*>
(
aItem
)
);
}
if
(
aType
&
RN_VIAS
)
{
BOOST_FOREACH
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
m_vias
|
boost
::
adaptors
::
map_keys
)
aOutput
.
push_back
(
const_cast
<
BOARD_CONNECTED_ITEM
*>
(
aItem
)
);
}
if
(
aType
&
RN_TRACKS
)
{
BOOST_FOREACH
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
m_tracks
|
boost
::
adaptors
::
map_keys
)
aOutput
.
push_back
(
const_cast
<
BOARD_CONNECTED_ITEM
*>
(
aItem
)
);
}
if
(
aType
&
RN_ZONES
)
{
BOOST_FOREACH
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
m_zoneConnections
|
boost
::
adaptors
::
map_keys
)
aOutput
.
push_back
(
const_cast
<
BOARD_CONNECTED_ITEM
*>
(
aItem
)
);
}
}
void
RN_NET
::
ClearSimple
()
void
RN_NET
::
ClearSimple
()
{
{
BOOST_FOREACH
(
const
RN_NODE_PTR
&
node
,
m_simpleNodes
)
BOOST_FOREACH
(
const
RN_NODE_PTR
&
node
,
m_simpleNodes
)
...
@@ -745,7 +773,7 @@ void RN_NET::ClearSimple()
...
@@ -745,7 +773,7 @@ void RN_NET::ClearSimple()
void
RN_NET
::
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
void
RN_NET
::
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
S
aTypes
)
const
RN_ITEM_TYPE
aTypes
)
const
{
{
std
::
list
<
RN_NODE_PTR
>
nodes
=
GetNodes
(
aItem
);
std
::
list
<
RN_NODE_PTR
>
nodes
=
GetNodes
(
aItem
);
assert
(
!
nodes
.
empty
()
);
assert
(
!
nodes
.
empty
()
);
...
@@ -872,7 +900,7 @@ void RN_DATA::AddSimple( const VECTOR2I& aPosition, int aNetCode )
...
@@ -872,7 +900,7 @@ void RN_DATA::AddSimple( const VECTOR2I& aPosition, int aNetCode )
void
RN_DATA
::
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
void
RN_DATA
::
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
S
aTypes
)
const
RN_ITEM_TYPE
aTypes
)
const
{
{
int
net
=
aItem
->
GetNetCode
();
int
net
=
aItem
->
GetNetCode
();
...
@@ -885,6 +913,18 @@ void RN_DATA::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem,
...
@@ -885,6 +913,18 @@ void RN_DATA::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem,
}
}
void
RN_DATA
::
GetNetItems
(
int
aNetCode
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
aTypes
)
const
{
if
(
aNetCode
<
1
)
return
;
assert
(
aNetCode
<
(
int
)
m_nets
.
size
()
);
m_nets
[
aNetCode
].
GetAllItems
(
aOutput
,
aTypes
);
}
bool
RN_DATA
::
AreConnected
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
const
BOARD_CONNECTED_ITEM
*
aOther
)
bool
RN_DATA
::
AreConnected
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
const
BOARD_CONNECTED_ITEM
*
aOther
)
{
{
int
net1
=
aItem
->
GetNetCode
();
int
net1
=
aItem
->
GetNetCode
();
...
...
pcbnew/ratsnest_data.h
View file @
56b692ae
...
@@ -50,7 +50,7 @@ class ZONE_CONTAINER;
...
@@ -50,7 +50,7 @@ class ZONE_CONTAINER;
class
CPolyPt
;
class
CPolyPt
;
///> Types of items that are handled by the class
///> Types of items that are handled by the class
enum
RN_ITEM_TYPE
S
enum
RN_ITEM_TYPE
{
{
RN_PADS
=
0x01
,
RN_PADS
=
0x01
,
RN_VIAS
=
0x02
,
RN_VIAS
=
0x02
,
...
@@ -399,6 +399,14 @@ public:
...
@@ -399,6 +399,14 @@ public:
*/
*/
std
::
list
<
RN_NODE_PTR
>
GetNodes
(
const
BOARD_CONNECTED_ITEM
*
aItem
)
const
;
std
::
list
<
RN_NODE_PTR
>
GetNodes
(
const
BOARD_CONNECTED_ITEM
*
aItem
)
const
;
/**
* Function GetAllNodes()
* Adds all stored items to a list.
* @param aOutput is the list that will have items added.
* @param aType determines the type of added items.
*/
void
GetAllItems
(
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
aType
=
RN_ALL
)
const
;
/**
/**
* Function GetClosestNode()
* Function GetClosestNode()
* Returns a single node that lies in the shortest distance from a specific node.
* Returns a single node that lies in the shortest distance from a specific node.
...
@@ -487,7 +495,7 @@ public:
...
@@ -487,7 +495,7 @@ public:
*/
*/
void
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
void
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
S
aTypes
=
RN_ALL
)
const
;
RN_ITEM_TYPE
aTypes
=
RN_ALL
)
const
;
protected
:
protected
:
///> Validates edge, i.e. modifies source and target nodes for an edge
///> Validates edge, i.e. modifies source and target nodes for an edge
...
@@ -524,6 +532,7 @@ protected:
...
@@ -524,6 +532,7 @@ protected:
typedef
boost
::
unordered_map
<
const
TRACK
*
,
RN_EDGE_MST_PTR
>
TRACK_EDGE_MAP
;
typedef
boost
::
unordered_map
<
const
TRACK
*
,
RN_EDGE_MST_PTR
>
TRACK_EDGE_MAP
;
typedef
boost
::
unordered_map
<
const
ZONE_CONTAINER
*
,
std
::
deque
<
RN_POLY
>
>
ZONE_POLY_MAP
;
typedef
boost
::
unordered_map
<
const
ZONE_CONTAINER
*
,
std
::
deque
<
RN_POLY
>
>
ZONE_POLY_MAP
;
typedef
boost
::
unordered_map
<
const
ZONE_CONTAINER
*
,
std
::
deque
<
RN_EDGE_MST_PTR
>
>
ZONE_EDGE_MAP
;
typedef
boost
::
unordered_map
<
const
ZONE_CONTAINER
*
,
std
::
deque
<
RN_EDGE_MST_PTR
>
>
ZONE_EDGE_MAP
;
///> Map that associates nodes in the ratsnest model to respective nodes.
///> Map that associates nodes in the ratsnest model to respective nodes.
PAD_NODE_MAP
m_pads
;
PAD_NODE_MAP
m_pads
;
...
@@ -661,7 +670,17 @@ public:
...
@@ -661,7 +670,17 @@ public:
*/
*/
void
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
void
GetConnectedItems
(
const
BOARD_CONNECTED_ITEM
*
aItem
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPES
aTypes
=
RN_ALL
)
const
;
RN_ITEM_TYPE
aTypes
=
RN_ALL
)
const
;
/**
* Function GetNetItems()
* Adds all items that belong to a certain net to a list.
* @param aNetCode is the net code.
* @param aOutput is the list that will have items added.
* @param aTypes allows to filter by item types.
*/
void
GetNetItems
(
int
aNetCode
,
std
::
list
<
BOARD_CONNECTED_ITEM
*>&
aOutput
,
RN_ITEM_TYPE
aTypes
=
RN_ALL
)
const
;
/**
/**
* Function AreConnected()
* Function AreConnected()
...
...
pcbnew/tools/common_actions.cpp
View file @
56b692ae
...
@@ -48,6 +48,14 @@ TOOL_ACTION COMMON_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear",
...
@@ -48,6 +48,14 @@ TOOL_ACTION COMMON_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear",
AS_GLOBAL
,
0
,
AS_GLOBAL
,
0
,
""
,
""
);
// No description, it is not supposed to be shown anywhere
""
,
""
);
// No description, it is not supposed to be shown anywhere
TOOL_ACTION
COMMON_ACTIONS
::
selectConnection
(
"pcbnew.InteractiveSelection.SelectConnection"
,
AS_GLOBAL
,
0
,
"copper connection"
,
"Selects whole copper connection."
);
TOOL_ACTION
COMMON_ACTIONS
::
selectNet
(
"pcbnew.InteractiveSelection.SelectNet"
,
AS_GLOBAL
,
0
,
"whole net"
,
"Selects all tracks & vias belonging to the same net."
);
TOOL_ACTION
COMMON_ACTIONS
::
find
(
"pcbnew.InteractiveSelection.Find"
,
TOOL_ACTION
COMMON_ACTIONS
::
find
(
"pcbnew.InteractiveSelection.Find"
,
AS_GLOBAL
,
0
,
// it is handled by wxWidgets hotkey system
AS_GLOBAL
,
0
,
// it is handled by wxWidgets hotkey system
"Find an item"
,
"Searches the document for an item"
);
"Find an item"
,
"Searches the document for an item"
);
...
...
pcbnew/tools/common_actions.h
View file @
56b692ae
...
@@ -56,6 +56,12 @@ public:
...
@@ -56,6 +56,12 @@ public:
/// Unselects an item (specified as the event parameter).
/// Unselects an item (specified as the event parameter).
static
TOOL_ACTION
unselectItem
;
static
TOOL_ACTION
unselectItem
;
/// Selects whole copper connection.
static
TOOL_ACTION
selectConnection
;
/// Selects all connections belonging to a single net.
static
TOOL_ACTION
selectNet
;
// Edit Tool
// Edit Tool
/// Activation of the edit tool
/// Activation of the edit tool
static
TOOL_ACTION
editActivate
;
static
TOOL_ACTION
editActivate
;
...
...
pcbnew/tools/selection_conditions.cpp
View file @
56b692ae
...
@@ -34,6 +34,23 @@ bool SELECTION_CONDITIONS::NotEmpty( const SELECTION& aSelection )
...
@@ -34,6 +34,23 @@ bool SELECTION_CONDITIONS::NotEmpty( const SELECTION& aSelection )
}
}
bool
SELECTION_CONDITIONS
::
OnlyConnectedItems
(
const
SELECTION
&
aSelection
)
{
if
(
aSelection
.
Empty
()
)
return
false
;
for
(
int
i
=
0
;
i
<
aSelection
.
Size
();
++
i
)
{
KICAD_T
type
=
aSelection
.
Item
<
EDA_ITEM
>
(
i
)
->
Type
();
if
(
type
!=
PCB_PAD_T
&&
type
!=
PCB_VIA_T
&&
type
!=
PCB_TRACE_T
&&
type
!=
PCB_ZONE_T
)
return
false
;
}
return
true
;
}
SELECTION_CONDITION
SELECTION_CONDITIONS
::
HasType
(
KICAD_T
aType
)
SELECTION_CONDITION
SELECTION_CONDITIONS
::
HasType
(
KICAD_T
aType
)
{
{
return
boost
::
bind
(
&
SELECTION_CONDITIONS
::
hasTypeFunc
,
_1
,
aType
);
return
boost
::
bind
(
&
SELECTION_CONDITIONS
::
hasTypeFunc
,
_1
,
aType
);
...
...
pcbnew/tools/selection_conditions.h
View file @
56b692ae
...
@@ -65,6 +65,14 @@ public:
...
@@ -65,6 +65,14 @@ public:
*/
*/
static
bool
NotEmpty
(
const
SELECTION
&
aSelection
);
static
bool
NotEmpty
(
const
SELECTION
&
aSelection
);
/**
* Function OnlyConnectedItems
* Tests if selection contains exclusively connected items (pads, tracks, vias, zones).
* @param aSelection is the selection to be tested.
* @return True if there are only connected items connected.
*/
static
bool
OnlyConnectedItems
(
const
SELECTION
&
aSelection
);
/**
/**
* Function HasType
* Function HasType
* Creates functor that tests if among the selected items there is at least one of a given type.
* Creates functor that tests if among the selected items there is at least one of a given type.
...
...
pcbnew/tools/selection_tool.cpp
View file @
56b692ae
...
@@ -28,7 +28,6 @@
...
@@ -28,7 +28,6 @@
#include <boost/bind.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/function.hpp>
#include <class_board.h>
#include <class_board.h>
#include <class_board_item.h>
#include <class_board_item.h>
#include <class_track.h>
#include <class_track.h>
...
@@ -48,12 +47,23 @@
...
@@ -48,12 +47,23 @@
#include <tool/tool_event.h>
#include <tool/tool_event.h>
#include <tool/tool_manager.h>
#include <tool/tool_manager.h>
#include <ratsnest_data.h>
#include "selection_tool.h"
#include "selection_tool.h"
#include "selection_area.h"
#include "selection_area.h"
#include "bright_box.h"
#include "bright_box.h"
#include "common_actions.h"
#include "common_actions.h"
class
SELECT_MENU
:
public
CONTEXT_MENU
{
public
:
SELECT_MENU
()
{
Add
(
COMMON_ACTIONS
::
selectConnection
);
Add
(
COMMON_ACTIONS
::
selectNet
);
}
};
SELECTION_TOOL
::
SELECTION_TOOL
()
:
SELECTION_TOOL
::
SELECTION_TOOL
()
:
TOOL_INTERACTIVE
(
"pcbnew.InteractiveSelection"
),
TOOL_INTERACTIVE
(
"pcbnew.InteractiveSelection"
),
m_frame
(
NULL
),
m_additive
(
false
),
m_multiple
(
false
),
m_frame
(
NULL
),
m_additive
(
false
),
m_multiple
(
false
),
...
@@ -61,6 +71,10 @@ SELECTION_TOOL::SELECTION_TOOL() :
...
@@ -61,6 +71,10 @@ SELECTION_TOOL::SELECTION_TOOL() :
{
{
m_selArea
=
new
SELECTION_AREA
;
m_selArea
=
new
SELECTION_AREA
;
m_selection
.
group
=
new
KIGFX
::
VIEW_GROUP
;
m_selection
.
group
=
new
KIGFX
::
VIEW_GROUP
;
AddSubMenu
(
new
SELECT_MENU
,
"Select..."
,
(
SELECTION_CONDITION
)
SELECTION_CONDITIONS
::
OnlyConnectedItems
&&
SELECTION_CONDITIONS
::
Count
(
1
)
);
}
}
...
@@ -207,6 +221,16 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
...
@@ -207,6 +221,16 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
{
{
clearSelection
();
clearSelection
();
}
}
else
if
(
evt
->
IsAction
(
&
COMMON_ACTIONS
::
selectConnection
)
)
{
selectConnection
(
*
evt
);
}
else
if
(
evt
->
IsAction
(
&
COMMON_ACTIONS
::
selectNet
)
)
{
selectNet
(
*
evt
);
}
}
}
// This tool is supposed to be active forever
// This tool is supposed to be active forever
...
@@ -408,8 +432,11 @@ void SELECTION_TOOL::setTransitions()
...
@@ -408,8 +432,11 @@ void SELECTION_TOOL::setTransitions()
Go
(
&
SELECTION_TOOL
::
ClearSelection
,
COMMON_ACTIONS
::
selectionClear
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
ClearSelection
,
COMMON_ACTIONS
::
selectionClear
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
SelectItem
,
COMMON_ACTIONS
::
selectItem
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
SelectItem
,
COMMON_ACTIONS
::
selectItem
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
UnselectItem
,
COMMON_ACTIONS
::
unselectItem
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
UnselectItem
,
COMMON_ACTIONS
::
unselectItem
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
SelectItem
,
COMMON_ACTIONS
::
selectItem
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
find
,
COMMON_ACTIONS
::
find
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
find
,
COMMON_ACTIONS
::
find
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
findMove
,
COMMON_ACTIONS
::
findMove
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
findMove
,
COMMON_ACTIONS
::
findMove
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
selectConnection
,
COMMON_ACTIONS
::
selectConnection
.
MakeEvent
()
);
Go
(
&
SELECTION_TOOL
::
selectNet
,
COMMON_ACTIONS
::
selectNet
.
MakeEvent
()
);
}
}
...
@@ -512,6 +539,58 @@ int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
...
@@ -512,6 +539,58 @@ int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
return
0
;
return
0
;
}
}
int
SELECTION_TOOL
::
selectConnection
(
const
TOOL_EVENT
&
aEvent
)
{
std
::
list
<
BOARD_CONNECTED_ITEM
*>
itemsList
;
RN_DATA
*
ratsnest
=
getModel
<
BOARD
>
()
->
GetRatsnest
();
BOARD_CONNECTED_ITEM
*
item
=
m_selection
.
Item
<
BOARD_CONNECTED_ITEM
>
(
0
);
clearSelection
();
ratsnest
->
GetConnectedItems
(
item
,
itemsList
,
(
RN_ITEM_TYPE
)(
RN_TRACKS
|
RN_VIAS
)
);
BOOST_FOREACH
(
BOARD_CONNECTED_ITEM
*
i
,
itemsList
)
select
(
i
);
// Inform other potentially interested tools
if
(
itemsList
.
size
()
>
0
)
{
TOOL_EVENT
selectEvent
(
SelectedEvent
);
m_toolMgr
->
ProcessEvent
(
selectEvent
);
}
setTransitions
();
return
0
;
}
int
SELECTION_TOOL
::
selectNet
(
const
TOOL_EVENT
&
aEvent
)
{
std
::
list
<
BOARD_CONNECTED_ITEM
*>
itemsList
;
RN_DATA
*
ratsnest
=
getModel
<
BOARD
>
()
->
GetRatsnest
();
BOARD_CONNECTED_ITEM
*
item
=
m_selection
.
Item
<
BOARD_CONNECTED_ITEM
>
(
0
);
int
netCode
=
item
->
GetNetCode
();
clearSelection
();
ratsnest
->
GetNetItems
(
netCode
,
itemsList
,
(
RN_ITEM_TYPE
)(
RN_TRACKS
|
RN_VIAS
)
);
BOOST_FOREACH
(
BOARD_CONNECTED_ITEM
*
i
,
itemsList
)
select
(
i
);
// Inform other potentially interested tools
if
(
itemsList
.
size
()
>
0
)
{
TOOL_EVENT
selectEvent
(
SelectedEvent
);
m_toolMgr
->
ProcessEvent
(
selectEvent
);
}
setTransitions
();
return
0
;
}
void
SELECTION_TOOL
::
findCallback
(
BOARD_ITEM
*
aItem
)
void
SELECTION_TOOL
::
findCallback
(
BOARD_ITEM
*
aItem
)
{
{
clearSelection
();
clearSelection
();
...
...
pcbnew/tools/selection_tool.h
View file @
56b692ae
...
@@ -206,6 +206,12 @@ private:
...
@@ -206,6 +206,12 @@ private:
*/
*/
bool
selectMultiple
();
bool
selectMultiple
();
///> Selects a continuous copper connection.
int
selectConnection
(
const
TOOL_EVENT
&
aEvent
);
///> Selects all copper connections belonging to a single net.
int
selectNet
(
const
TOOL_EVENT
&
aEvent
);
///> Find dialog callback.
///> Find dialog callback.
void
findCallback
(
BOARD_ITEM
*
aItem
);
void
findCallback
(
BOARD_ITEM
*
aItem
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment