Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vdt-plugin
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
vdt-plugin
Commits
0ecf21d0
Commit
0ecf21d0
authored
Jul 03, 2016
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
changed hadling settings to tolerate toool configs editing
parent
c9ce9dff
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
163 additions
and
37 deletions
+163
-37
Option.java
src/com/elphel/vdt/core/options/Option.java
+21
-1
OptionsCore.java
src/com/elphel/vdt/core/options/OptionsCore.java
+36
-14
OptionsUtils.java
src/com/elphel/vdt/core/options/OptionsUtils.java
+72
-8
ValueBasedListOption.java
src/com/elphel/vdt/core/options/ValueBasedListOption.java
+2
-2
ParamNodeReader.java
...com/elphel/vdt/core/tools/config/xml/ParamNodeReader.java
+11
-3
XMLConfig.java
src/com/elphel/vdt/core/tools/config/xml/XMLConfig.java
+3
-2
Context.java
src/com/elphel/vdt/core/tools/contexts/Context.java
+5
-5
ContextOptionsDialog.java
src/com/elphel/vdt/ui/options/ContextOptionsDialog.java
+13
-2
No files found.
src/com/elphel/vdt/core/options/Option.java
View file @
0ecf21d0
...
@@ -26,6 +26,8 @@
...
@@ -26,6 +26,8 @@
*******************************************************************************/
*******************************************************************************/
package
com
.
elphel
.
vdt
.
core
.
options
;
package
com
.
elphel
.
vdt
.
core
.
options
;
import
java.util.List
;
import
org.eclipse.jface.preference.IPreferenceStore
;
import
org.eclipse.jface.preference.IPreferenceStore
;
/**
/**
...
@@ -97,6 +99,24 @@ public abstract class Option {
...
@@ -97,6 +99,24 @@ public abstract class Option {
value
=
store
.
getString
(
key
);
value
=
store
.
getString
(
key
);
setValue
(
value
);
setValue
(
value
);
}
else
{
}
else
{
// Trying to get with different index
//See TODO: 07/01/2016 in OptionUtils
// public static List<String> getStoreContext( final String contextID
// if (contextID.equals("cocotb")) {
if
(!
key
.
startsWith
(
OptionsUtils
.
KEY_CONTENT
)){
// Otherwise will be infinite loop
List
<
String
>
context
=
OptionsUtils
.
getStoreContext
(
contextID
,
store
);
String
patt
=
contextID
+
"_[^_]*"
+
key
.
substring
(
key
.
indexOf
(
"_"
,
contextID
.
length
()+
1
));
for
(
String
skey:
context
){
if
(
skey
.
matches
(
patt
)){
value
=
store
.
getString
(
skey
);
setValue
(
value
);
System
.
out
.
println
(
"Option.doLoad(): in context "
+
contextID
+
" - found replacement for "
+
key
+
": "
+
skey
);
return
value
;
}
}
}
value
=
doLoadDefault
();
value
=
doLoadDefault
();
}
}
return
value
;
return
value
;
...
...
src/com/elphel/vdt/core/options/OptionsCore.java
View file @
0ecf21d0
...
@@ -186,6 +186,7 @@ public class OptionsCore {
...
@@ -186,6 +186,7 @@ public class OptionsCore {
}
}
public
static
void
doLoadContextOptions
(
Context
context
,
IPreferenceStore
store
)
{
public
static
void
doLoadContextOptions
(
Context
context
,
IPreferenceStore
store
)
{
// System.out.println("doLoadContextOptions("+context.getName()+")");
List
<
Parameter
>
list
=
context
.
getParams
();
List
<
Parameter
>
list
=
context
.
getParams
();
if
(
list
.
isEmpty
())
if
(
list
.
isEmpty
())
return
;
return
;
...
@@ -198,6 +199,7 @@ public class OptionsCore {
...
@@ -198,6 +199,7 @@ public class OptionsCore {
option
=
new
ParamBasedListOption
(
param
);
option
=
new
ParamBasedListOption
(
param
);
else
else
option
=
new
ParamBasedOption
(
param
);
option
=
new
ParamBasedOption
(
param
);
option
.
setPreferenceStore
(
store
);
option
.
setPreferenceStore
(
store
);
option
.
doLoad
();
option
.
doLoad
();
}
}
...
@@ -217,16 +219,26 @@ public class OptionsCore {
...
@@ -217,16 +219,26 @@ public class OptionsCore {
}
}
public
static
void
doStoreContextOptions
(
Context
context
,
IProject
project
)
{
public
static
void
doStoreContextOptions
(
Context
context
,
IProject
project
)
{
if
(
"cocotb"
.
equals
(
context
.
getName
())){
System
.
out
.
println
(
"doStoreContextOptions('cocotb'), project="
+
project
);
}
IPreferenceStore
store
=
getPreferenceStore
(
context
,
project
);
IPreferenceStore
store
=
getPreferenceStore
(
context
,
project
);
doStoreContextOptions
(
context
,
store
);
doStoreContextOptions
(
context
,
store
);
}
}
public
static
void
doStoreContextOptions
(
Context
context
)
{
public
static
void
doStoreContextOptions
(
Context
context
)
{
if
(
"cocotb"
.
equals
(
context
.
getName
())){
System
.
out
.
println
(
"doStoreContextOptions('cocotb')"
);
}
IPreferenceStore
store
=
VerilogPlugin
.
getDefault
().
getPreferenceStore
();
IPreferenceStore
store
=
VerilogPlugin
.
getDefault
().
getPreferenceStore
();
doStoreContextOptions
(
context
,
store
);
doStoreContextOptions
(
context
,
store
);
}
}
public
static
void
doStoreContextOptions
(
Context
context
,
IPreferenceStore
store
)
{
public
static
void
doStoreContextOptions
(
Context
context
,
IPreferenceStore
store
)
{
if
(
"cocotb"
.
equals
(
context
.
getName
())){
System
.
out
.
println
(
"static doStoreContextOptions('cocotb')"
);
}
List
<
Parameter
>
list
=
context
.
getParams
();
List
<
Parameter
>
list
=
context
.
getParams
();
if
(
list
.
isEmpty
())
if
(
list
.
isEmpty
())
return
;
return
;
...
@@ -240,7 +252,7 @@ public class OptionsCore {
...
@@ -240,7 +252,7 @@ public class OptionsCore {
option
.
setPreferenceStore
(
store
);
option
.
setPreferenceStore
(
store
);
option
.
doStore
();
option
.
doStore
();
}
}
OptionsUtils
.
setStoreVersion
(
context
.
getVersion
(),
context
.
getName
(),
store
);
OptionsUtils
.
setStoreVersion
(
context
.
get
Context
Version
(),
context
.
getName
(),
store
);
finalizeDoStore
(
store
);
finalizeDoStore
(
store
);
}
}
...
@@ -250,6 +262,7 @@ public class OptionsCore {
...
@@ -250,6 +262,7 @@ public class OptionsCore {
if
(
store
.
needsSaving
())
if
(
store
.
needsSaving
())
((
ScopedPreferenceStore
)
store
).
save
();
((
ScopedPreferenceStore
)
store
).
save
();
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
System
.
out
.
println
(
"finalizeDoStore() - out of sync (edited manually settings file?"
);
// Nothing do do, we don't need to bother the user
// Nothing do do, we don't need to bother the user
}
}
}
}
...
@@ -284,20 +297,29 @@ public class OptionsCore {
...
@@ -284,20 +297,29 @@ public class OptionsCore {
}
}
private
static
void
checkVersionCompatibility
(
Context
context
,
IPreferenceStore
store
)
{
private
static
void
checkVersionCompatibility
(
Context
context
,
IPreferenceStore
store
)
{
if
(
OptionsUtils
.
isVersionCompatible
(
context
.
getVersion
(),
context
.
getName
(),
store
))
// Context Version is a version of the file, like <vdt-project version = "0.8">
if
(
OptionsUtils
.
isVersionCompatible
(
context
.
getContextVersion
(),
context
.
getName
(),
store
))
return
;
return
;
Shell
shell
=
VerilogPlugin
.
getActiveWorkbenchShell
();
Shell
shell
=
VerilogPlugin
.
getActiveWorkbenchShell
();
String
message
=
"Version of TSL description has been changed.\nDo you wish to delete stored values?"
;
String
message
=
"Version of TSL description for context '"
+
context
.
getName
()+
"' has been changed.\n"
+
"What do you wish to do with the stored values?\n"
+
"'Delete' resets all context values to defaults\n"
+
"'Update' tries to convert settings to newer format\n"
+
"'Cancel' does nothing, keeping existing files (until saved)."
;
MessageDialog
messageBox
=
new
MessageDialog
(
shell
,
"Warning"
,
null
MessageDialog
messageBox
=
new
MessageDialog
(
shell
,
"Warning"
,
null
,
message
,
message
,
MessageDialog
.
WARNING
,
MessageDialog
.
WARNING
,
new
String
[]{
"
Yes"
,
"No
"
}
,
new
String
[]{
"
Delete"
,
"Update"
,
"Cancel
"
}
,
1
);
,
1
);
messageBox
.
open
();
messageBox
.
open
();
if
(
messageBox
.
getReturnCode
()
==
0
)
{
int
returnCode
=
messageBox
.
getReturnCode
();
if
(
returnCode
==
0
)
{
OptionsUtils
.
clearStore
(
context
.
getName
(),
store
);
OptionsUtils
.
clearStore
(
context
.
getName
(),
store
);
finalizeDoStore
(
store
);
finalizeDoStore
(
store
);
}
else
if
(
returnCode
==
1
)
{
OptionsUtils
.
updateStore
(
context
.
getName
(),
store
);
finalizeDoStore
(
store
);
}
}
}
}
...
...
src/com/elphel/vdt/core/options/OptionsUtils.java
View file @
0ecf21d0
...
@@ -28,8 +28,12 @@ package com.elphel.vdt.core.options;
...
@@ -28,8 +28,12 @@ package com.elphel.vdt.core.options;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.Iterator
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
org.eclipse.jface.preference.IPreferenceStore
;
import
org.eclipse.jface.preference.IPreferenceStore
;
...
@@ -51,6 +55,7 @@ class OptionsUtils {
...
@@ -51,6 +55,7 @@ class OptionsUtils {
public
static
final
String
KEY_VERSION
=
"com.elphel.store.version."
;
public
static
final
String
KEY_VERSION
=
"com.elphel.store.version."
;
private
static
final
String
SEPARATOR
=
"<-@##@->"
;
private
static
final
String
SEPARATOR
=
"<-@##@->"
;
private
static
final
String
CONTEXT_SEPARATOR
=
"_@_"
;
public
static
String
convertListToString
(
List
<
String
>
list
)
{
public
static
String
convertListToString
(
List
<
String
>
list
)
{
String
value
=
""
;
String
value
=
""
;
...
@@ -94,9 +99,21 @@ class OptionsUtils {
...
@@ -94,9 +99,21 @@ class OptionsUtils {
}
}
public
static
String
toKey
(
Parameter
param
)
{
public
static
String
toKey
(
Parameter
param
)
{
Context
context
=
param
.
getContext
();
// Context context = param.getContext();
int
index
=
context
.
getParams
().
indexOf
(
param
);
// int index = context.getParams().indexOf(param);
return
""
+
param
.
getContext
().
getName
()
+
"_"
+
index
+
"_"
+
param
.
getID
();
// return "" + param.getContext().getName() + "_" + index + "_" + param.getID();
return
""
+
param
.
getContext
().
getName
()
+
CONTEXT_SEPARATOR
+
param
.
getID
();
/* TODO: 07/02/2016: Disabled using parameter index in the key - that was causing VDT to forget all settings
* when number of tool parameters was modified.
* ContextOptionsDialog */
//TODO: 07/01/2016: Understand why index is needed? It changes when xml files are edited (added/deleted parameters
// But each parameter ID + context should already be unique
// So meanwhile I'll add searching for same context/parameter with other indices if the exact match is not found
}
}
...
@@ -172,6 +189,49 @@ class OptionsUtils {
...
@@ -172,6 +189,49 @@ class OptionsUtils {
store
.
setToDefault
(
getVersionKey
(
contextID
));
store
.
setToDefault
(
getVersionKey
(
contextID
));
}
}
public
static
void
updateStore
(
final
String
contextID
,
IPreferenceStore
store
)
{
List
<
String
>
contextList
=
getStoreContext
(
contextID
,
store
);
Map
<
String
,
String
>
newKeyValues
=
new
HashMap
<
String
,
String
>();
Pattern
patt_new
=
Pattern
.
compile
(
"[a-zA-Z0-9_]*"
+
CONTEXT_SEPARATOR
);
Pattern
patt_indx
=
Pattern
.
compile
(
"([a-zA-Z0-9_]*)_[0-9]+_(.*)"
);
for
(
String
key
:
contextList
)
{
if
(
patt_new
.
matcher
(
key
).
lookingAt
()){
newKeyValues
.
put
(
key
,
store
.
getString
(
key
));
// Found new <context>_@_<parameter>
}
else
{
Matcher
matcher
=
patt_indx
.
matcher
(
key
);
if
(
matcher
.
lookingAt
())
{
String
newKey
=
matcher
.
group
(
1
)+
CONTEXT_SEPARATOR
+
matcher
.
group
(
2
);
if
(!
newKeyValues
.
containsKey
(
newKey
)){
// Only if it is not yet set - not to overwrite the new value
newKeyValues
.
put
(
newKey
,
store
.
getString
(
key
));
// Found new <context>_@_<parameter>
}
}
}
store
.
setToDefault
(
key
);
}
// Save converted/filtered values, if they are not empty (will not be able to overwrite non-empty default)
for
(
String
key
:
newKeyValues
.
keySet
()){
store
.
putValue
(
key
,
newKeyValues
.
get
(
key
));
}
// now regenerate context string
//Rebuild the list, getting rid of indexed entries
contextList
=
new
ArrayList
<
String
>();
for
(
String
key
:
newKeyValues
.
keySet
()){
contextList
.
add
(
key
);
}
final
String
contentKey
=
getContentKey
(
contextID
);
ValueBasedListOption
option
=
new
ValueBasedListOption
(
contentKey
,
contextID
,
contextList
);
option
.
setPreferenceStore
(
store
);
option
.
doStore
(
contextList
);
// store.setToDefault(getContentKey(contextID));
store
.
setToDefault
(
getVersionKey
(
contextID
));
}
public
static
void
setStoreVersion
(
final
String
version
public
static
void
setStoreVersion
(
final
String
version
,
final
String
contextID
,
final
String
contextID
,
IPreferenceStore
store
)
,
IPreferenceStore
store
)
...
@@ -190,6 +250,9 @@ class OptionsUtils {
...
@@ -190,6 +250,9 @@ class OptionsUtils {
{
{
boolean
compatible
;
boolean
compatible
;
final
String
versionKey
=
getVersionKey
(
contextID
);
final
String
versionKey
=
getVersionKey
(
contextID
);
if
(
versionKey
.
endsWith
(
"ocotb"
)){
System
.
out
.
println
(
"isVersionCompatible(cocotb)"
);
}
if
(
version
==
null
)
{
if
(
version
==
null
)
{
compatible
=
!
store
.
contains
(
versionKey
);
compatible
=
!
store
.
contains
(
versionKey
);
}
else
if
(
store
.
contains
(
versionKey
))
{
}
else
if
(
store
.
contains
(
versionKey
))
{
...
@@ -200,7 +263,8 @@ class OptionsUtils {
...
@@ -200,7 +263,8 @@ class OptionsUtils {
return
compatible
;
return
compatible
;
}
}
private
static
List
<
String
>
getStoreContext
(
final
String
contextID
public
static
List
<
String
>
getStoreContext
(
final
String
contextID
// private static List<String> getStoreContext( final String contextID
,
IPreferenceStore
store
)
{
,
IPreferenceStore
store
)
{
List
<
String
>
context
=
new
ArrayList
<
String
>();
List
<
String
>
context
=
new
ArrayList
<
String
>();
ValueBasedListOption
option
=
new
ValueBasedListOption
(
getContentKey
(
contextID
),
contextID
,
context
);
ValueBasedListOption
option
=
new
ValueBasedListOption
(
getContentKey
(
contextID
),
contextID
,
context
);
...
...
src/com/elphel/vdt/core/options/ValueBasedListOption.java
View file @
0ecf21d0
...
@@ -94,7 +94,7 @@ public class ValueBasedListOption extends Option {
...
@@ -94,7 +94,7 @@ public class ValueBasedListOption extends Option {
/**
/**
* Load the default option value from persistent storage
* Load the default option value from persistent storage
*
*
* @return the sequnce of the list items separated by SEPARATOR.
* @return the sequ
e
nce of the list items separated by SEPARATOR.
*/
*/
public
String
doLoadDefault
()
{
public
String
doLoadDefault
()
{
List
<
String
>
list
=
doLoadDefaultList
();
List
<
String
>
list
=
doLoadDefaultList
();
...
@@ -110,7 +110,7 @@ public class ValueBasedListOption extends Option {
...
@@ -110,7 +110,7 @@ public class ValueBasedListOption extends Option {
/**
/**
* Save value to persistent storage
* Save value to persistent storage
*
*
* @param value the sequnce of the list items separated by SEPARATOR.
* @param value the sequ
e
nce of the list items separated by SEPARATOR.
*/
*/
public
boolean
doStore
(
String
value
)
{
public
boolean
doStore
(
String
value
)
{
List
<
String
>
list
=
OptionsUtils
.
convertStringToList
(
value
);
List
<
String
>
list
=
OptionsUtils
.
convertStringToList
(
value
);
...
...
src/com/elphel/vdt/core/tools/config/xml/ParamNodeReader.java
View file @
0ecf21d0
...
@@ -38,6 +38,7 @@ import com.elphel.vdt.core.tools.contexts.Context;
...
@@ -38,6 +38,7 @@ import com.elphel.vdt.core.tools.contexts.Context;
public
class
ParamNodeReader
extends
AbstractConditionNodeReader
{
public
class
ParamNodeReader
extends
AbstractConditionNodeReader
{
private
List
<
Parameter
>
paramList
=
new
ArrayList
<
Parameter
>();
private
List
<
Parameter
>
paramList
=
new
ArrayList
<
Parameter
>();
private
List
<
String
>
paramIdList
=
new
ArrayList
<
String
>();
public
ParamNodeReader
(
XMLConfig
config
,
Context
context
)
{
public
ParamNodeReader
(
XMLConfig
config
,
Context
context
)
{
super
(
config
,
context
);
super
(
config
,
context
);
...
@@ -46,7 +47,14 @@ public class ParamNodeReader extends AbstractConditionNodeReader {
...
@@ -46,7 +47,14 @@ public class ParamNodeReader extends AbstractConditionNodeReader {
public
void
readNode
(
Node
node
,
Condition
condition
)
throws
ConfigException
{
public
void
readNode
(
Node
node
,
Condition
condition
)
throws
ConfigException
{
if
(
XMLConfig
.
isElemNode
(
node
,
XMLConfig
.
PARAMETER_TAG
))
{
if
(
XMLConfig
.
isElemNode
(
node
,
XMLConfig
.
PARAMETER_TAG
))
{
try
{
try
{
paramList
.
add
(
readParam
(
node
,
condition
));
Parameter
param
=
readParam
(
node
,
condition
);
String
id
=
param
.
getID
();
if
(
paramIdList
.
contains
(
id
)){
System
.
out
.
println
(
"Warning: duplicate parameter ('"
+
id
+
"') in context '"
+
context
+
"' defined in "
+
param
.
getSourceXML
());
// throw new ConfigException("Duplicate parameter ('" + id + "') in context '" + context + "' defined in "+param.getSourceXML());
}
paramIdList
.
add
(
id
);
paramList
.
add
(
param
);
}
catch
(
ConfigException
e
)
{
}
catch
(
ConfigException
e
)
{
config
.
logError
(
e
);
config
.
logError
(
e
);
}
}
...
...
src/com/elphel/vdt/core/tools/config/xml/XMLConfig.java
View file @
0ecf21d0
...
@@ -774,7 +774,7 @@ public class XMLConfig extends Config {
...
@@ -774,7 +774,7 @@ public class XMLConfig extends Config {
context
.
setInputDialogLabel
(
contextInputDefinition
.
getLabel
());
context
.
setInputDialogLabel
(
contextInputDefinition
.
getLabel
());
context
.
setCommandLinesBlocks
(
contextCommandLinesBlocks
);
context
.
setCommandLinesBlocks
(
contextCommandLinesBlocks
);
context
.
setVersion
(
currentFileVersion
);
context
.
set
Context
Version
(
currentFileVersion
);
return
context
;
return
context
;
}
}
...
@@ -1363,6 +1363,7 @@ public class XMLConfig extends Config {
...
@@ -1363,6 +1363,7 @@ public class XMLConfig extends Config {
rootNode
=
node
;
rootNode
=
node
;
currentFileVersion
=
getAttributeValue
(
node
,
ROOT_VERSION_ATTR
);
currentFileVersion
=
getAttributeValue
(
node
,
ROOT_VERSION_ATTR
);
}
}
}
}
...
...
src/com/elphel/vdt/core/tools/contexts/Context.java
View file @
0ecf21d0
...
@@ -72,7 +72,7 @@ public abstract class Context {
...
@@ -72,7 +72,7 @@ public abstract class Context {
private
List
<
String
>
createdControlFiles
=
new
ArrayList
<
String
>();
private
List
<
String
>
createdControlFiles
=
new
ArrayList
<
String
>();
private
boolean
initialized
=
false
;
private
boolean
initialized
=
false
;
private
String
workingDirectory
;
private
String
workingDirectory
;
private
String
v
ersion
;
private
String
contextV
ersion
;
// private Context context=null;
// private Context context=null;
private
int
currentHash
;
// calculated during buildparam from non-parser command blocks and command files.
private
int
currentHash
;
// calculated during buildparam from non-parser command blocks and command files.
protected
Context
(
String
name
,
protected
Context
(
String
name
,
...
@@ -127,12 +127,12 @@ public abstract class Context {
...
@@ -127,12 +127,12 @@ public abstract class Context {
initialized
=
true
;
initialized
=
true
;
}
}
public
void
setVersion
(
String
version
)
{
public
void
set
Context
Version
(
String
version
)
{
this
.
v
ersion
=
version
;
this
.
contextV
ersion
=
version
;
}
}
public
String
getVersion
()
{
public
String
get
Context
Version
()
{
return
v
ersion
;
return
contextV
ersion
;
}
}
public
void
setParams
(
List
<
Parameter
>
params
)
throws
ConfigException
{
public
void
setParams
(
List
<
Parameter
>
params
)
throws
ConfigException
{
...
...
src/com/elphel/vdt/ui/options/ContextOptionsDialog.java
View file @
0ecf21d0
...
@@ -86,6 +86,17 @@ public class ContextOptionsDialog extends Dialog {
...
@@ -86,6 +86,17 @@ public class ContextOptionsDialog extends Dialog {
}
}
protected
void
okPressed
()
{
protected
void
okPressed
()
{
/* Currently multiple same-named parameters in the same context have warnings.
* What happens is that after parameters are changed in the dialog, the new value is
* applied only to the first entry, then the saecond (unmodified) entry is processed
* and overwrites the modified value (effectively disabling modification. Another
* option would be to partially restore name+index in the preference store - just
* instead of the index of all parameters, use index among those with the same ID.
* In that case rare use of multiple same-name parameters (as was used in the demo
* tools.xml - same name parameters where inside <if></if>) will not cause troubles
* as it was when every change of the number of tool parameters made VDT to forget
* tool settings */
optionsBlock
.
performApply
();
optionsBlock
.
performApply
();
OptionsCore
.
doStoreContextOptions
(
context
,
store
);
OptionsCore
.
doStoreContextOptions
(
context
,
store
);
context
.
setWorkingDirectory
(
location
);
context
.
setWorkingDirectory
(
location
);
...
...
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