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
c50efb48
Commit
c50efb48
authored
Jul 06, 2011
by
jean-pierre charras
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
pcbnew: better code to read/modify/delete footprints in module libraries.
parent
ec7475f3
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
463 additions
and
292 deletions
+463
-292
wxWidgets_patch_notes.txt
Documentation/wxWidgets_patch_notes.txt
+0
-5
confirm.cpp
common/confirm.cpp
+14
-72
CMakeLists.txt
pcbnew/CMakeLists.txt
+1
-0
class_footprint_library.cpp
pcbnew/class_footprint_library.cpp
+239
-0
class_footprint_library.h
pcbnew/class_footprint_library.h
+94
-0
librairi.cpp
pcbnew/librairi.cpp
+73
-155
loadcmp.cpp
pcbnew/loadcmp.cpp
+42
-60
No files found.
Documentation/wxWidgets_patch_notes.txt
View file @
c50efb48
...
@@ -45,11 +45,6 @@ parameter is a double)
...
@@ -45,11 +45,6 @@ parameter is a double)
Workaround:
Workaround:
Use a version > 2.9.1
Use a version > 2.9.1
Currently ( 2011, april 12 ) the 2.9.2 is not yet finalized
(and can be found only on the wxWidgets snv server)
can be fixed by replacing the file <wxWidgets-2.9.1>/src/common/xlocale.cpp
by the corresponding file from the 2.9.2 version (from wxWidgets svn server)
*************************************************************************************
*************************************************************************************
wxGTK version: All
wxGTK version: All
...
...
common/confirm.cpp
View file @
c50efb48
/************************/
/*
/* Menu "CONFIRMATION" */
* confirm.cpp
/* Function get_Message */
* utilities to display some error, warning and info short messges
/* Test requires ESC */
*/
/************************/
#include "fctsys.h"
#include "fctsys.h"
#include "common.h"
#include "common.h"
enum
id_dialog
{
ID_TIMOUT
=
1500
};
/* Class for displaying messages, similar to wxMessageDialog,
* but can be erased after a time out expires.
*
* @note - Do not use the time feature. It is broken by design because
* the dialog is shown as modal and wxWidgets will assert when
* compiled in the debug mode. This is because the dialog steals
* the event queue when dialog is modal so the timer event never
* gets to the dialog event handle. Using dialogs to display
* transient is brain dead anyway. Use the message panel or some
* other method.
*/
class
WinEDA_MessageDialog
:
public
wxMessageDialog
{
private
:
int
m_LifeTime
;
wxTimer
m_Timer
;
public
:
WinEDA_MessageDialog
(
wxWindow
*
parent
,
const
wxString
&
msg
,
const
wxString
&
title
,
int
style
,
int
lifetime
);
~
WinEDA_MessageDialog
()
{
};
void
OnTimeOut
(
wxTimerEvent
&
event
);
DECLARE_EVENT_TABLE
()
};
BEGIN_EVENT_TABLE
(
WinEDA_MessageDialog
,
wxMessageDialog
)
EVT_TIMER
(
ID_TIMOUT
,
WinEDA_MessageDialog
::
OnTimeOut
)
END_EVENT_TABLE
()
WinEDA_MessageDialog
::
WinEDA_MessageDialog
(
wxWindow
*
parent
,
const
wxString
&
msg
,
const
wxString
&
title
,
int
style
,
int
lifetime
)
:
wxMessageDialog
(
parent
,
msg
,
title
,
style
)
{
m_LifeTime
=
lifetime
;
m_Timer
.
SetOwner
(
this
,
ID_TIMOUT
);
if
(
m_LifeTime
>
0
)
m_Timer
.
Start
(
100
*
m_LifeTime
,
wxTIMER_ONE_SHOT
);
}
void
WinEDA_MessageDialog
::
OnTimeOut
(
wxTimerEvent
&
event
)
{
m_Timer
.
Stop
();
EndModal
(
wxID_YES
);
/* Does not work, I do not know why (this
* function is correctly called after time out).
* See not above as to why this doesn't work. */
}
/* Display an error or warning message.
/* Display an error or warning message.
* If display time > 0 the dialog disappears after displayTime 0.1 seconds
* TODO:
* If display time > 0 the dialog disappears after displayTime ( in 0.1 second )
*
*
*/
*/
void
DisplayError
(
wxWindow
*
parent
,
const
wxString
&
text
,
int
displaytime
)
void
DisplayError
(
wxWindow
*
parent
,
const
wxString
&
text
,
int
displaytime
)
...
@@ -76,12 +17,11 @@ void DisplayError( wxWindow* parent, const wxString& text, int displaytime )
...
@@ -76,12 +17,11 @@ void DisplayError( wxWindow* parent, const wxString& text, int displaytime )
wxMessageDialog
*
dialog
;
wxMessageDialog
*
dialog
;
if
(
displaytime
>
0
)
if
(
displaytime
>
0
)
dialog
=
new
WinEDA_MessageDialog
(
parent
,
text
,
_
(
"Warning"
),
dialog
=
new
wxMessageDialog
(
parent
,
text
,
_
(
"Warning"
),
wxOK
|
wxICON_INFORMATION
,
wxOK
|
wxCENTRE
|
wxICON_INFORMATION
);
displaytime
);
else
else
dialog
=
new
WinEDA_
MessageDialog
(
parent
,
text
,
_
(
"Error"
),
dialog
=
new
wx
MessageDialog
(
parent
,
text
,
_
(
"Error"
),
wxOK
|
wx
ICON_ERROR
,
0
);
wxOK
|
wx
CENTRE
|
wxICON_ERROR
);
dialog
->
ShowModal
();
dialog
->
ShowModal
();
dialog
->
Destroy
();
dialog
->
Destroy
();
...
@@ -89,14 +29,16 @@ void DisplayError( wxWindow* parent, const wxString& text, int displaytime )
...
@@ -89,14 +29,16 @@ void DisplayError( wxWindow* parent, const wxString& text, int displaytime )
/* Display an informational message.
/* Display an informational message.
* TODO:
* If display time > 0 the message disappears after displayTime (in 0.1 second )
*/
*/
void
DisplayInfoMessage
(
wxWindow
*
parent
,
const
wxString
&
text
,
void
DisplayInfoMessage
(
wxWindow
*
parent
,
const
wxString
&
text
,
int
displaytime
)
int
displaytime
)
{
{
wxMessageDialog
*
dialog
;
wxMessageDialog
*
dialog
;
dialog
=
new
WinEDA_
MessageDialog
(
parent
,
text
,
_
(
"Info:"
),
dialog
=
new
wx
MessageDialog
(
parent
,
text
,
_
(
"Info:"
),
wxOK
|
wx
ICON_INFORMATION
,
displaytime
);
wxOK
|
wx
CENTRE
|
wxICON_INFORMATION
);
dialog
->
ShowModal
();
dialog
->
ShowModal
();
dialog
->
Destroy
();
dialog
->
Destroy
();
...
...
pcbnew/CMakeLists.txt
View file @
c50efb48
...
@@ -93,6 +93,7 @@ set(PCBNEW_SRCS
...
@@ -93,6 +93,7 @@ set(PCBNEW_SRCS
block.cpp
block.cpp
block_module_editor.cpp
block_module_editor.cpp
build_BOM_from_board.cpp
build_BOM_from_board.cpp
class_footprint_library.cpp
class_pcb_layer_widget.cpp
class_pcb_layer_widget.cpp
clean.cpp
clean.cpp
connect.cpp
connect.cpp
...
...
pcbnew/class_footprint_library.cpp
0 → 100644
View file @
c50efb48
/**
* @file class_footprint_library.cpp
* Helper class to read/write footprint libraries.
*/
#include "fctsys.h"
#include "kicad_string.h"
#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "richio.h"
#include "filter_reader.h"
#include "class_footprint_library.h"
/*
* Module library header format:
* LIBRARY HEADER-datetime
* $INDEX
* List of modules names (1 name per line)
* $EndIndex
* List of descriptions of Modules
* $EndLIBRARY
*/
FOOTPRINT_LIBRARY
::
FOOTPRINT_LIBRARY
(
FILE
*
aFile
,
FILTER_READER
*
aReader
)
{
wxASSERT
(
m_reader
||
m_file
);
m_file
=
aFile
;
m_reader
=
aReader
;
m_LineNum
=
0
;
}
/* function IsLibrary
* Read the library file Header
* return > 0 if this file is a footprint lib
* (currentlu return 1 but could be a value > 1 for future file formats
*/
int
FOOTPRINT_LIBRARY
::
IsLibrary
(
)
{
char
*
line
;
char
buffer
[
1024
];
if
(
m_reader
)
{
m_reader
->
ReadLine
();
line
=
m_reader
->
Line
();
}
else
{
line
=
buffer
;
GetLine
(
m_file
,
line
,
&
m_LineNum
);
}
StrPurge
(
line
);
if
(
strnicmp
(
line
,
ENTETE_LIBRAIRIE
,
L_ENTETE_LIB
)
==
0
)
return
1
;
return
0
;
}
/*
* function RebuildIndex
* Read the full library file and build the list od footprints found
* and do not use the $INDEX ... $EndINDEX section
*/
bool
FOOTPRINT_LIBRARY
::
RebuildIndex
()
{
m_List
.
Clear
();
char
name
[
1024
];
if
(
m_reader
)
{
while
(
m_reader
->
ReadLine
()
)
{
char
*
line
=
m_reader
->
Line
();
StrPurge
(
line
);
if
(
strnicmp
(
line
,
"$MODULE"
,
7
)
==
0
)
{
sscanf
(
line
+
7
,
" %s"
,
name
);
m_List
.
Add
(
FROM_UTF8
(
name
)
);
}
}
}
else
{
char
line
[
1024
];
while
(
GetLine
(
m_file
,
line
,
&
m_LineNum
)
)
{
if
(
strnicmp
(
line
,
"$MODULE"
,
7
)
==
0
)
{
sscanf
(
line
+
7
,
" %s"
,
name
);
m_List
.
Add
(
FROM_UTF8
(
name
)
);
}
}
}
return
true
;
}
/* function ReadSectionIndex
* Read the $INDEX ... $EndINDEX section
* list of footprints is stored in m_List
*/
bool
FOOTPRINT_LIBRARY
::
ReadSectionIndex
()
{
// Some broken INDEX sections have more than one section
// So we must read the next line after $EndINDEX tag,
// to see if this is not a new $INDEX tag.
bool
exit
=
false
;
if
(
m_reader
)
{
while
(
m_reader
->
ReadLine
()
)
{
char
*
line
=
m_reader
->
Line
();
StrPurge
(
line
);
if
(
strnicmp
(
line
,
"$INDEX"
,
6
)
==
0
)
{
exit
=
false
;
while
(
m_reader
->
ReadLine
()
)
{
StrPurge
(
line
);
m_List
.
Add
(
FROM_UTF8
(
line
)
);
if
(
strnicmp
(
line
,
"$EndINDEX"
,
9
)
==
0
)
{
exit
=
true
;
break
;
}
}
}
else
if
(
exit
)
break
;
}
}
else
{
char
line
[
1024
];
while
(
GetLine
(
m_file
,
line
,
&
m_LineNum
)
)
{
if
(
strnicmp
(
line
,
"$INDEX"
,
6
)
==
0
)
{
exit
=
false
;
while
(
GetLine
(
m_file
,
line
,
&
m_LineNum
)
)
{
StrPurge
(
line
);
m_List
.
Add
(
FROM_UTF8
(
line
)
);
if
(
strnicmp
(
line
,
"$EndINDEX"
,
9
)
==
0
)
{
exit
=
true
;
break
;
}
}
}
else
if
(
exit
)
break
;
}
}
return
true
;
}
/* Function WriteHeader
* Write the library header
*/
bool
FOOTPRINT_LIBRARY
::
WriteHeader
()
{
char
line
[
256
];
fprintf
(
m_file
,
"%s %s
\n
"
,
ENTETE_LIBRAIRIE
,
DateAndTime
(
line
)
);
fprintf
(
m_file
,
"# encoding utf-8
\n
"
);
return
true
;
}
/* Function WriteSectionIndex
* Write the $INDEX ... $EndINDEX section.
* This section is filled by names in m_List
*/
bool
FOOTPRINT_LIBRARY
::
WriteSectionIndex
()
{
fputs
(
"$INDEX
\n
"
,
m_file
);
for
(
unsigned
ii
=
0
;
ii
<
m_List
.
GetCount
();
ii
++
)
{
fprintf
(
m_file
,
"%s
\n
"
,
TO_UTF8
(
m_List
[
ii
]
)
);
}
fputs
(
"$EndINDEX
\n
"
,
m_file
);
return
true
;
}
/* Function WriteEndOfFile
* Write the last line section.
*/
bool
FOOTPRINT_LIBRARY
::
WriteEndOfFile
()
{
fputs
(
"$EndLIBRARY
\n
"
,
m_file
);
return
true
;
}
/*
* Function FindInList
* Search for aName int m_List and return true if found
*/
bool
FOOTPRINT_LIBRARY
::
FindInList
(
const
wxString
&
aName
)
{
for
(
unsigned
ii
=
0
;
ii
<
m_List
.
GetCount
();
ii
++
)
{
if
(
m_List
[
ii
].
CmpNoCase
(
aName
)
==
0
)
return
true
;
}
return
false
;
}
/**
* Function RemoveFromList
* Search for aName int m_List and remove it
* @return true if found and removed
*/
bool
FOOTPRINT_LIBRARY
::
RemoveFromList
(
const
wxString
&
aName
)
{
for
(
unsigned
ii
=
0
;
ii
<
m_List
.
GetCount
();
ii
++
)
{
if
(
m_List
[
ii
].
CmpNoCase
(
aName
)
==
0
)
{
m_List
.
RemoveAt
(
ii
);
return
true
;
}
}
return
false
;
}
/**
* Function SortList
* Sort m_List in alphabetic order
*/
void
FOOTPRINT_LIBRARY
::
SortList
()
{
m_List
.
Sort
();
}
pcbnew/class_footprint_library.h
0 → 100644
View file @
c50efb48
/* Helper class to read/write footprints libraries
*/
#ifndef _FOOTPRINT_LIBRARY_H_
#define _FOOTPRINT_LIBRARY_H_
#include "filter_reader.h"
class
FOOTPRINT_LIBRARY
{
public
:
wxArrayString
m_List
;
// list of footprints, used to read/write INDEX section
wxString
m_LibraryName
;
// the full library name
int
m_LineNum
;
// the line count
private
:
FILTER_READER
*
m_reader
;
// FILTER_READER to read file. id NULL, use m_file
FILE
*
m_file
;
// footprint file to read/write.
public
:
/**
* ctor
* @param aFile = a FILE * pointer used for write operations,
* and read operations when aReader = NULL
* @param aReader = a FILTER_READER pointer used for read operations
* If NULL, a direct aFILE read is used
*/
FOOTPRINT_LIBRARY
(
FILE
*
aFile
,
FILTER_READER
*
aReader
=
NULL
);
~
FOOTPRINT_LIBRARY
()
{
}
/**
* function IsLibrary
* Read the library file Header
* return > 0 if this file is a footprint lib
* (currentlu return 1 but could be a value > 1 for future file formats
*/
int
IsLibrary
(
);
/**
* function RebuildIndex
* Read the full library file and build the list od footprints found
* Do not use the $INDEX ... $EndINDEX section
*/
bool
RebuildIndex
();
/**
* function ReadSectionIndex
* Read the $INDEX ... $EndINDEX section
* list of footprints is stored in m_List
*/
bool
ReadSectionIndex
();
/**
* Function WriteHeader
* Write the library header
*/
bool
WriteHeader
();
/**
* Function WriteSectionIndex
* Write the $INDEX ... $EndINDEX section.
* This section is filled by names in m_List
*/
bool
WriteSectionIndex
();
/**
* Function WriteEndOfFile
* Write the last line section.
*/
bool
WriteEndOfFile
();
/**
* Function FindInList
* Search for aName int m_List
* @return true if found
*/
bool
FindInList
(
const
wxString
&
aName
);
/**
* Function RemoveFromList
* Search for aName int m_List and remove it
* @return true if found and removed
*/
bool
RemoveFromList
(
const
wxString
&
aName
);
/**
* Function SortList
* Sort m_List in alphabetic order
*/
void
SortList
();
};
#endif
pcbnew/librairi.cpp
View file @
c50efb48
This diff is collapsed.
Click to expand it.
pcbnew/loadcmp.cpp
View file @
c50efb48
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
#include "dialog_helpers.h"
#include "dialog_helpers.h"
#include "filter_reader.h"
#include "filter_reader.h"
#include "footprint_info.h"
#include "footprint_info.h"
#include "class_footprint_library.h"
static
void
DisplayCmpDoc
(
wxString
&
Name
);
static
void
DisplayCmpDoc
(
wxString
&
Name
);
...
@@ -194,9 +195,7 @@ MODULE* PCB_BASE_FRAME::Get_Librairie_Module( const wxString& aLibraryFullFilena
...
@@ -194,9 +195,7 @@ MODULE* PCB_BASE_FRAME::Get_Librairie_Module( const wxString& aLibraryFullFilena
const
wxString
&
aModuleName
,
const
wxString
&
aModuleName
,
bool
aDisplayMessageError
)
bool
aDisplayMessageError
)
{
{
int
found
=
0
;
wxFileName
fn
;
wxFileName
fn
;
char
*
Line
;
wxString
Name
;
wxString
Name
;
wxString
msg
,
tmp
;
wxString
msg
,
tmp
;
MODULE
*
NewModule
;
MODULE
*
NewModule
;
...
@@ -244,11 +243,9 @@ MODULE* PCB_BASE_FRAME::Get_Librairie_Module( const wxString& aLibraryFullFilena
...
@@ -244,11 +243,9 @@ MODULE* PCB_BASE_FRAME::Get_Librairie_Module( const wxString& aLibraryFullFilena
msg
.
Printf
(
_
(
"Scan Lib: %s"
),
GetChars
(
tmp
)
);
msg
.
Printf
(
_
(
"Scan Lib: %s"
),
GetChars
(
tmp
)
);
SetStatusText
(
msg
);
SetStatusText
(
msg
);
/* Reading header ENTETE_LIBRAIRIE */
FOOTPRINT_LIBRARY
curr_lib
(
file
,
&
reader
);
reader
.
ReadLine
();
Line
=
reader
.
Line
();
if
(
!
curr_lib
.
IsLibrary
()
)
StrPurge
(
Line
);
if
(
strnicmp
(
Line
,
ENTETE_LIBRAIRIE
,
L_ENTETE_LIB
)
!=
0
)
{
{
msg
.
Printf
(
_
(
"<%s> is not a valid Kicad PCB footprint library file."
),
msg
.
Printf
(
_
(
"<%s> is not a valid Kicad PCB footprint library file."
),
GetChars
(
tmp
)
);
GetChars
(
tmp
)
);
...
@@ -258,60 +255,37 @@ MODULE* PCB_BASE_FRAME::Get_Librairie_Module( const wxString& aLibraryFullFilena
...
@@ -258,60 +255,37 @@ MODULE* PCB_BASE_FRAME::Get_Librairie_Module( const wxString& aLibraryFullFilena
}
}
/* Reading the list of modules in the library. */
/* Reading the list of modules in the library. */
found
=
0
;
curr_lib
.
ReadSectionIndex
();
while
(
!
found
&&
reader
.
ReadLine
()
)
bool
found
=
curr_lib
.
FindInList
(
aModuleName
);
{
Line
=
reader
.
Line
();
if
(
strnicmp
(
Line
,
"$MODULE"
,
6
)
==
0
)
break
;
if
(
strnicmp
(
Line
,
"$INDEX"
,
6
)
==
0
)
{
while
(
reader
.
ReadLine
()
)
{
Line
=
reader
.
Line
();
if
(
strnicmp
(
Line
,
"$EndINDEX"
,
9
)
==
0
)
break
;
StrPurge
(
Line
);
msg
=
FROM_UTF8
(
Line
);
if
(
msg
.
CmpNoCase
(
aModuleName
)
==
0
)
{
found
=
1
;
break
;
/* found! */
}
}
}
}
/* Read library. */
/* Read library. */
while
(
found
&&
reader
.
ReadLine
()
)
if
(
found
)
{
{
Line
=
reader
.
Line
();
fileReader
.
Rewind
();
if
(
Line
[
0
]
!=
'$'
)
while
(
reader
.
ReadLine
()
)
continue
;
{
char
*
line
=
reader
.
Line
();
if
(
Line
[
1
]
!=
'M'
)
StrPurge
(
line
+
8
);
continue
;
if
(
strnicmp
(
Line
,
"$MODULE"
,
7
)
!=
0
)
continue
;
StrPurge
(
Line
+
8
);
if
(
strnicmp
(
line
,
"$MODULE"
,
7
)
!=
0
)
continue
;
// Read module name.
// Read module name.
Name
=
FROM_UTF8
(
L
ine
+
8
);
Name
=
FROM_UTF8
(
l
ine
+
8
);
if
(
Name
.
CmpNoCase
(
aModuleName
)
==
0
)
if
(
Name
.
CmpNoCase
(
aModuleName
)
==
0
)
{
{
NewModule
=
new
MODULE
(
GetBoard
()
);
NewModule
=
new
MODULE
(
GetBoard
()
);
// Switch the locale to standard C (needed to print
// Switch the locale to standard C (needed to print
// floating point numbers like 1.3)
// floating point numbers like 1.3)
SetLocaleTo_C_standard
();
SetLocaleTo_C_standard
();
NewModule
->
ReadDescr
(
&
reader
);
NewModule
->
ReadDescr
(
&
reader
);
SetLocaleTo_Default
();
// revert to the current locale
SetLocaleTo_Default
();
// revert to the current locale
GetBoard
()
->
Add
(
NewModule
,
ADD_APPEND
);
GetBoard
()
->
Add
(
NewModule
,
ADD_APPEND
);
SetStatusText
(
wxEmptyString
);
SetStatusText
(
wxEmptyString
);
return
NewModule
;
return
NewModule
;
}
}
}
}
}
...
@@ -384,14 +358,22 @@ wxString PCB_BASE_FRAME::Select_1_Module_From_List( EDA_DRAW_FRAME* aWindow,
...
@@ -384,14 +358,22 @@ wxString PCB_BASE_FRAME::Select_1_Module_From_List( EDA_DRAW_FRAME* aWindow,
for
(
unsigned
ii
=
0
;
ii
<
MList
.
GetCount
();
ii
++
)
for
(
unsigned
ii
=
0
;
ii
<
MList
.
GetCount
();
ii
++
)
footprint_names_list
.
Add
(
MList
.
GetItem
(
ii
).
m_Module
);
footprint_names_list
.
Add
(
MList
.
GetItem
(
ii
).
m_Module
);
msg
.
Printf
(
_
(
"Modules [%d items]"
),
footprint_names_list
.
GetCount
()
);
if
(
footprint_names_list
.
GetCount
()
)
WinEDAListBox
dlg
(
aWindow
,
msg
,
footprint_names_list
,
OldName
,
{
DisplayCmpDoc
,
GetComponentDialogPosition
()
);
msg
.
Printf
(
_
(
"Modules [%d items]"
),
footprint_names_list
.
GetCount
()
);
WinEDAListBox
dlg
(
aWindow
,
msg
,
footprint_names_list
,
OldName
,
DisplayCmpDoc
,
GetComponentDialogPosition
()
);
if
(
dlg
.
ShowModal
()
==
wxID_OK
)
if
(
dlg
.
ShowModal
()
==
wxID_OK
)
CmpName
=
dlg
.
GetTextSelection
();
CmpName
=
dlg
.
GetTextSelection
();
else
CmpName
.
Empty
();
}
else
else
{
DisplayError
(
aWindow
,
_
(
"No footprint found"
)
);
CmpName
.
Empty
();
CmpName
.
Empty
();
}
if
(
CmpName
!=
wxEmptyString
)
if
(
CmpName
!=
wxEmptyString
)
OldName
=
CmpName
;
OldName
=
CmpName
;
...
...
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