Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Valentin Platzgummer
qgroundcontrol
Commits
c37b4af7
Commit
c37b4af7
authored
Jul 16, 2011
by
lm
Browse files
Merged, removed MAVLink generator from main codebase, still present as included external app
parents
f338b5ef
0f28dcbf
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/ui/QGCRemoteControlView.cc
View file @
c37b4af7
...
...
@@ -80,9 +80,8 @@ void QGCRemoteControlView::setUASId(int id)
// The UAS exists, disconnect any existing connections
disconnect
(
uas
,
SIGNAL
(
remoteControlChannelRawChanged
(
int
,
float
,
float
)),
this
,
SLOT
(
setChannel
(
int
,
float
,
float
)));
disconnect
(
uas
,
SIGNAL
(
remoteControlRSSIChanged
(
float
)),
this
,
SLOT
(
setRemoteRSSI
(
float
)));
disconnect
(
uas
,
SIGNAL
(
radioCalibrationRawReceived
(
const
QPointer
<
RadioCalibrationData
>
)),
calibrationWindow
,
SLOT
(
receive
(
const
QPointer
<
RadioCalibrationData
>&
)));
disconnect
(
uas
,
SIGNAL
(
remoteControlChannelRawChanged
(
int
,
float
)),
calibrationWindow
,
SLOT
(
setChannelRaw
(
int
,
float
)));
disconnect
(
uas
,
SIGNAL
(
remoteControlChannelScaledChanged
(
int
,
float
,
float
)),
calibrationWindow
,
SLOT
(
setChannelScaled
(
int
,
float
)));
disconnect
(
uas
,
SIGNAL
(
radioCalibrationRawReceived
(
const
QPointer
<
RadioCalibrationData
>&
)),
calibrationWindow
,
SLOT
(
receive
(
const
QPointer
<
RadioCalibrationData
>&
)));
disconnect
(
uas
,
SIGNAL
(
remoteControlChannelRawChanged
(
int
,
float
)),
calibrationWindow
,
SLOT
(
setChannel
(
int
,
float
)));
}
}
...
...
@@ -97,6 +96,9 @@ void QGCRemoteControlView::setUASId(int id)
connect
(
newUAS
,
SIGNAL
(
remoteControlRSSIChanged
(
float
)),
this
,
SLOT
(
setRemoteRSSI
(
float
)));
connect
(
newUAS
,
SIGNAL
(
remoteControlChannelRawChanged
(
int
,
float
)),
this
,
SLOT
(
setChannelRaw
(
int
,
float
)));
connect
(
newUAS
,
SIGNAL
(
remoteControlChannelScaledChanged
(
int
,
float
)),
this
,
SLOT
(
setChannelScaled
(
int
,
float
)));
// only connect raw channels to calibration window widget
connect
(
newUAS
,
SIGNAL
(
remoteControlChannelRawChanged
(
int
,
float
)),
calibrationWindow
,
SLOT
(
setChannel
(
int
,
float
)));
}
}
...
...
src/ui/RadioCalibration/RadioCalibrationWindow.cc
View file @
c37b4af7
...
...
@@ -52,68 +52,7 @@ RadioCalibrationWindow::RadioCalibrationWindow(QWidget *parent) :
setUASId
(
0
);
}
//void RadioCalibrationWindow::setChannelRaw(int ch, float raw)
//{
// /** this expects a particular channel to function mapping
// \todo allow run-time channel mapping
// */
// switch (ch)
// {
// case 0:
// aileron->channelChanged(raw);
// break;
// case 1:
// elevator->channelChanged(raw);
// break;
// case 2:
// throttle->channelChanged(raw);
// break;
// case 3:
// rudder->channelChanged(raw);
// break;
// case 4:
// gyro->channelChanged(raw);
// break;
// case 5:
// pitch->channelChanged(raw);
// break;
// }
//}
//void RadioCalibrationWindow::setChannelScaled(int ch, float normalized)
//{
// FIXME James
// FIXME Bryan
// /** this expects a particular channel to function mapping
// \todo allow run-time channel mapping
// */
// switch (ch)
// {
// case 0:
// aileron->channelChanged(raw);
// break;
// case 1:
// elevator->channelChanged(raw);
// break;
// case 2:
// throttle->channelChanged(raw);
// break;
// case 3:
// rudder->channelChanged(raw);
// break;
// case 4:
// gyro->channelChanged(raw);
// break;
// case 5:
// pitch->channelChanged(raw);
// break;
// }
//}
void
RadioCalibrationWindow
::
setChannel
(
int
ch
,
float
raw
)
{
...
...
@@ -313,7 +252,7 @@ void RadioCalibrationWindow::parseSetpoint(const QDomElement &setpoint, const QP
void
RadioCalibrationWindow
::
send
()
{
qDebug
()
<<
__FILE__
<<
__LINE__
<<
"uasId = "
<<
uasId
;
#ifdef MAVLINK_ENABLED_UALBERTA
_MESSAGES
#ifdef MAVLINK_ENABLED_UALBERTA
UAS
*
uas
=
dynamic_cast
<
UAS
*>
(
UASManager
::
instance
()
->
getUASForId
(
uasId
));
if
(
uas
)
{
mavlink_message_t
msg
;
...
...
src/ui/RadioCalibration/RadioCalibrationWindow.h
View file @
c37b4af7
...
...
@@ -67,9 +67,6 @@ public:
public
slots
:
void
setChannel
(
int
ch
,
float
raw
);
// @todo remove these functions if they are not needed - were added by lm on dec 14, 2010
// void setChannelRaw(int ch, float raw);
// void setChannelScaled(int ch, float normalized);
void
loadFile
();
void
saveFile
();
void
send
();
...
...
src/ui/mavlink/QGCMAVLinkTextEdit.cc
deleted
100644 → 0
View file @
f338b5ef
#include
"QGCMAVLinkTextEdit.h"
#include
<QMessageBox>
#include
<QMenu>
#include
<QEvent>
#include
<QPainter>
#include
<QScrollBar>
#include
<QTextLayout>
QGCMAVLinkTextEdit
::
QGCMAVLinkTextEdit
(
QWidget
*
parent
)
:
QTextEdit
(
parent
)
{
setViewportMargins
(
50
,
0
,
0
,
0
);
highlight
=
new
XmlHighlighter
(
document
());
setLineWrapMode
(
QTextEdit
::
NoWrap
);
setAcceptRichText
(
false
);
connect
(
verticalScrollBar
(),
SIGNAL
(
valueChanged
(
int
)),
this
,
SLOT
(
update
()));
connect
(
this
,
SIGNAL
(
textChanged
()),
this
,
SLOT
(
update
()));
}
bool
QGCMAVLinkTextEdit
::
Conform
()
{
QString
errorStr
;
int
errorLine
,
errorColumn
;
QDomDocument
doc
;
return
doc
.
setContent
(
text
(),
false
,
&
errorStr
,
&
errorLine
,
&
errorColumn
);
}
QDomDocument
QGCMAVLinkTextEdit
::
xml_document
()
{
QString
errorStr
;
int
errorLine
,
errorColumn
;
QDomDocument
doc
;
doc
.
setContent
(
text
(),
false
,
&
errorStr
,
&
errorLine
,
&
errorColumn
);
return
doc
;
}
void
QGCMAVLinkTextEdit
::
setPlainText
(
const
QString
txt
)
{
QString
errorStr
;
int
errorLine
,
errorColumn
;
QDomDocument
doc
;
if
(
!
doc
.
setContent
(
txt
,
false
,
&
errorStr
,
&
errorLine
,
&
errorColumn
))
{
QTextEdit
::
setPlainText
(
txt
);
}
else
{
QTextEdit
::
setPlainText
(
doc
.
toString
(
5
));
}
}
bool
QGCMAVLinkTextEdit
::
syntaxcheck
()
{
bool
error
=
false
;
if
(
text
().
size
()
>
0
)
{
QString
errorStr
;
int
errorLine
,
errorColumn
;
QDomDocument
doc
;
if
(
!
doc
.
setContent
(
text
(),
false
,
&
errorStr
,
&
errorLine
,
&
errorColumn
))
{
//////return doc.toString(5);
QMessageBox
::
information
(
0
,
tr
(
"Found xml error"
),
tr
(
"Check line %1 column %2 on string
\"
%3
\"
!"
)
.
arg
(
errorLine
-
1
)
.
arg
(
errorColumn
-
1
)
.
arg
(
errorStr
));
error
=
true
;
// FIXME Mark line
if
(
errorLine
>=
0
)
{
}
}
else
{
QMessageBox
::
information
(
0
,
tr
(
"XML valid."
),
tr
(
"All tag are valid size %1."
).
arg
(
text
().
size
()));
setPlainText
(
doc
.
toString
(
5
));
}
}
else
{
QMessageBox
::
information
(
0
,
tr
(
"XML not found!"
),
tr
(
"Null size xml document!"
));
error
=
true
;
}
return
error
;
}
void
QGCMAVLinkTextEdit
::
contextMenuEvent
(
QContextMenuEvent
*
e
)
{
Q_UNUSED
(
e
);
QMenu
*
RContext
=
createOwnStandardContextMenu
();
RContext
->
exec
(
QCursor
::
pos
());
delete
RContext
;
}
QMenu
*
QGCMAVLinkTextEdit
::
createOwnStandardContextMenu
()
{
QMenu
*
TContext
=
createStandardContextMenu
();
TContext
->
addAction
(
QIcon
(
QString
::
fromUtf8
(
":/img/zoomin.png"
)),
tr
(
"Zoom In"
),
this
,
SLOT
(
zoomIn
()
)
);
TContext
->
addAction
(
QIcon
(
QString
::
fromUtf8
(
":/img/zoomout.png"
)),
tr
(
"Zoom Out"
),
this
,
SLOT
(
zoomOut
()
)
);
TContext
->
addAction
(
tr
(
"Check xml syntax"
),
this
,
SLOT
(
syntaxcheck
()
)
);
return
TContext
;
}
bool
QGCMAVLinkTextEdit
::
event
(
QEvent
*
event
)
{
if
(
event
->
type
()
==
QEvent
::
Paint
)
{
QPainter
p
(
this
);
p
.
fillRect
(
0
,
0
,
50
,
height
(),
QColor
(
"#636363"
));
QFont
workfont
(
font
());
QPen
pen
(
QColor
(
"#ffffff"
),
1
);
p
.
setPen
(
pen
);
p
.
setFont
(
workfont
);
int
contentsY
=
verticalScrollBar
()
->
value
();
qreal
pageBottom
=
contentsY
+
viewport
()
->
height
();
int
m_lineNumber
(
1
);
const
QFontMetrics
fm
=
fontMetrics
();
const
int
ascent
=
fontMetrics
().
ascent
()
+
1
;
for
(
QTextBlock
block
=
document
()
->
begin
();
block
.
isValid
();
block
=
block
.
next
(),
m_lineNumber
++
)
{
QTextLayout
*
layout
=
block
.
layout
();
const
QRectF
boundingRect
=
layout
->
boundingRect
();
QPointF
position
=
layout
->
position
();
if
(
position
.
y
()
+
boundingRect
.
height
()
<
contentsY
)
{
continue
;
}
if
(
position
.
y
()
>
pageBottom
)
{
break
;
}
const
QString
txt
=
QString
::
number
(
m_lineNumber
);
p
.
drawText
(
50
-
fm
.
width
(
txt
)
-
2
,
qRound
(
position
.
y
())
-
contentsY
+
ascent
,
txt
);
}
p
.
setPen
(
QPen
(
Qt
::
NoPen
));
}
else
if
(
event
->
type
()
==
QEvent
::
KeyPress
)
{
QKeyEvent
*
ke
=
static_cast
<
QKeyEvent
*>
(
event
);
if
((
ke
->
modifiers
()
&
Qt
::
ControlModifier
)
&&
ke
->
key
()
==
Qt
::
Key_Minus
)
{
QTextEdit
::
zoomOut
();
return
true
;
}
if
((
ke
->
modifiers
()
&
Qt
::
ControlModifier
)
&&
ke
->
key
()
==
Qt
::
Key_Plus
)
{
QTextEdit
::
zoomIn
();
return
true
;
}
}
return
QTextEdit
::
event
(
event
);
}
static
const
QColor
DEFAULT_SYNTAX_CHAR
=
Qt
::
blue
;
static
const
QColor
DEFAULT_ELEMENT_NAME
=
Qt
::
darkRed
;
static
const
QColor
DEFAULT_COMMENT
=
Qt
::
darkGreen
;
static
const
QColor
DEFAULT_ATTRIBUTE_NAME
=
Qt
::
red
;
static
const
QColor
DEFAULT_ATTRIBUTE_VALUE
=
Qt
::
darkGreen
;
static
const
QColor
DEFAULT_ERROR
=
Qt
::
darkMagenta
;
static
const
QColor
DEFAULT_OTHER
=
Qt
::
black
;
// Regular expressions for parsing XML borrowed from:
// http://www.cs.sfu.ca/~cameron/REX.html
static
const
QString
EXPR_COMMENT
=
"<!--[^-]*-([^-][^-]*-)*->"
;
static
const
QString
EXPR_COMMENT_BEGIN
=
"<!--"
;
static
const
QString
EXPR_COMMENT_END
=
"[^-]*-([^-][^-]*-)*->"
;
static
const
QString
EXPR_ATTRIBUTE_VALUE
=
"
\"
[^<
\"
]*
\"
|'[^<']*'"
;
static
const
QString
EXPR_NAME
=
"([A-Za-z_:]|[^
\\
x00-
\\
x7F])([A-Za-z0-9_:.-]|[^
\\
x00-
\\
x7F])*"
;
XmlHighlighter
::
XmlHighlighter
(
QObject
*
parent
)
:
QSyntaxHighlighter
(
parent
)
{
init
();
}
XmlHighlighter
::
XmlHighlighter
(
QTextDocument
*
parent
)
:
QSyntaxHighlighter
(
parent
)
{
init
();
}
XmlHighlighter
::
XmlHighlighter
(
QTextEdit
*
parent
)
:
QSyntaxHighlighter
(
parent
)
{
init
();
}
XmlHighlighter
::~
XmlHighlighter
()
{
}
void
XmlHighlighter
::
init
()
{
fmtSyntaxChar
.
setForeground
(
DEFAULT_SYNTAX_CHAR
);
fmtElementName
.
setForeground
(
DEFAULT_ELEMENT_NAME
);
fmtComment
.
setForeground
(
DEFAULT_COMMENT
);
fmtAttributeName
.
setForeground
(
DEFAULT_ATTRIBUTE_NAME
);
fmtAttributeValue
.
setForeground
(
DEFAULT_ATTRIBUTE_VALUE
);
fmtError
.
setForeground
(
DEFAULT_ERROR
);
fmtOther
.
setForeground
(
DEFAULT_OTHER
);
}
void
XmlHighlighter
::
setHighlightColor
(
HighlightType
type
,
QColor
color
,
bool
foreground
)
{
QTextCharFormat
format
;
if
(
foreground
)
format
.
setForeground
(
color
);
else
format
.
setBackground
(
color
);
setHighlightFormat
(
type
,
format
);
}
void
XmlHighlighter
::
setHighlightFormat
(
HighlightType
type
,
QTextCharFormat
format
)
{
switch
(
type
)
{
case
SyntaxChar
:
fmtSyntaxChar
=
format
;
break
;
case
ElementName
:
fmtElementName
=
format
;
break
;
case
Comment
:
fmtComment
=
format
;
break
;
case
AttributeName
:
fmtAttributeName
=
format
;
break
;
case
AttributeValue
:
fmtAttributeValue
=
format
;
break
;
case
Error
:
fmtError
=
format
;
break
;
case
Other
:
fmtOther
=
format
;
break
;
}
rehighlight
();
}
void
XmlHighlighter
::
highlightBlock
(
const
QString
&
text
)
{
int
i
=
0
;
int
pos
=
0
;
int
brackets
=
0
;
state
=
(
previousBlockState
()
==
InElement
?
ExpectAttributeOrEndOfElement
:
NoState
);
if
(
previousBlockState
()
==
InComment
)
{
// search for the end of the comment
QRegExp
expression
(
EXPR_COMMENT_END
);
pos
=
expression
.
indexIn
(
text
,
i
);
if
(
pos
>=
0
)
{
// end comment found
const
int
iLength
=
expression
.
matchedLength
();
setFormat
(
0
,
iLength
-
3
,
fmtComment
);
setFormat
(
iLength
-
3
,
3
,
fmtSyntaxChar
);
i
+=
iLength
;
// skip comment
}
else
{
// in comment
setFormat
(
0
,
text
.
length
(),
fmtComment
);
setCurrentBlockState
(
InComment
);
return
;
}
}
for
(;
i
<
text
.
length
();
i
++
)
{
switch
(
text
.
at
(
i
).
toAscii
())
{
case
'<'
:
brackets
++
;
if
(
brackets
==
1
)
{
setFormat
(
i
,
1
,
fmtSyntaxChar
);
state
=
ExpectElementNameOrSlash
;
}
else
{
// wrong bracket nesting
setFormat
(
i
,
1
,
fmtError
);
}
break
;
case
'>'
:
brackets
--
;
if
(
brackets
==
0
)
{
setFormat
(
i
,
1
,
fmtSyntaxChar
);
}
else
{
// wrong bracket nesting
setFormat
(
i
,
1
,
fmtError
);
}
state
=
NoState
;
break
;
case
'/'
:
if
(
state
==
ExpectElementNameOrSlash
)
{
state
=
ExpectElementName
;
setFormat
(
i
,
1
,
fmtSyntaxChar
);
}
else
{
if
(
state
==
ExpectAttributeOrEndOfElement
)
{
setFormat
(
i
,
1
,
fmtSyntaxChar
);
}
else
{
processDefaultText
(
i
,
text
);
}
}
break
;
case
'='
:
if
(
state
==
ExpectEqual
)
{
state
=
ExpectAttributeValue
;
setFormat
(
i
,
1
,
fmtOther
);
}
else
{
processDefaultText
(
i
,
text
);
}
break
;
case
'\''
:
case
'\"'
:
if
(
state
==
ExpectAttributeValue
)
{
// search attribute value
QRegExp
expression
(
EXPR_ATTRIBUTE_VALUE
);
pos
=
expression
.
indexIn
(
text
,
i
);
if
(
pos
==
i
)
// attribute value found ?
{
const
int
iLength
=
expression
.
matchedLength
();
setFormat
(
i
,
1
,
fmtOther
);
setFormat
(
i
+
1
,
iLength
-
2
,
fmtAttributeValue
);
setFormat
(
i
+
iLength
-
1
,
1
,
fmtOther
);
i
+=
iLength
-
1
;
// skip attribute value
state
=
ExpectAttributeOrEndOfElement
;
}
else
{
processDefaultText
(
i
,
text
);
}
}
else
{
processDefaultText
(
i
,
text
);
}
break
;
case
'!'
:
if
(
state
==
ExpectElementNameOrSlash
)
{
// search comment
QRegExp
expression
(
EXPR_COMMENT
);
pos
=
expression
.
indexIn
(
text
,
i
-
1
);
if
(
pos
==
i
-
1
)
// comment found ?
{
const
int
iLength
=
expression
.
matchedLength
();
setFormat
(
pos
,
4
,
fmtSyntaxChar
);
setFormat
(
pos
+
4
,
iLength
-
7
,
fmtComment
);
setFormat
(
iLength
-
3
,
3
,
fmtSyntaxChar
);
i
+=
iLength
-
2
;
// skip comment
state
=
NoState
;
brackets
--
;
}
else
{
// Try find multiline comment
QRegExp
expression
(
EXPR_COMMENT_BEGIN
);
// search comment start
pos
=
expression
.
indexIn
(
text
,
i
-
1
);
//if (pos == i - 1) // comment found ?
if
(
pos
>=
i
-
1
)
{
setFormat
(
i
,
3
,
fmtSyntaxChar
);
setFormat
(
i
+
3
,
text
.
length
()
-
i
-
3
,
fmtComment
);
setCurrentBlockState
(
InComment
);
return
;
}
else
{
processDefaultText
(
i
,
text
);
}
}
}
else
{
processDefaultText
(
i
,
text
);
}
break
;
default:
const
int
iLength
=
processDefaultText
(
i
,
text
);
if
(
iLength
>
0
)
i
+=
iLength
-
1
;
break
;
}
}
if
(
state
==
ExpectAttributeOrEndOfElement
)
{
setCurrentBlockState
(
InElement
);
}
}
int
XmlHighlighter
::
processDefaultText
(
int
i
,
const
QString
&
text
)
{
// length of matched text
int
iLength
=
0
;
switch
(
state
)
{
case
ExpectElementNameOrSlash
:
case
ExpectElementName
:
{
// search element name
QRegExp
expression
(
EXPR_NAME
);
const
int
pos
=
expression
.
indexIn
(
text
,
i
);
if
(
pos
==
i
)
// found ?
{
iLength
=
expression
.
matchedLength
();
setFormat
(
pos
,
iLength
,
fmtElementName
);
state
=
ExpectAttributeOrEndOfElement
;
}
else
{
setFormat
(
i
,
1
,
fmtOther
);
}
}
break
;
case
ExpectAttributeOrEndOfElement
:
{
// search attribute name
QRegExp
expression
(
EXPR_NAME
);
const
int
pos
=
expression
.
indexIn
(
text
,
i
);
if
(
pos
==
i
)
// found ?
{
iLength
=
expression
.
matchedLength
();
setFormat
(
pos
,
iLength
,
fmtAttributeName
);
state
=
ExpectEqual
;
}
else
{
setFormat
(
i
,
1
,
fmtOther
);
}
}
break
;
default:
setFormat
(
i
,
1
,
fmtOther
);
break
;
}
return
iLength
;
}
src/ui/mavlink/QGCMAVLinkTextEdit.h
deleted
100644 → 0
View file @
f338b5ef
// Based on: Syntax highlighting from:
// http://code.google.com/p/fop-miniscribus/
// (GPL v2) thanks!
#ifndef QGCMAVLINKTEXTEDIT_H
#define QGCMAVLINKTEXTEDIT_H
#include
<QSyntaxHighlighter>
#include
<QTextCharFormat>
#include
<QColor>
#include
<QDomDocument>
#include
<QTextEdit>
//class QGCMAVLinkTextEdit : public QTextEdit
//{
//public:
// QGCMAVLinkTextEdit();
//};
class
XmlHighlighter
:
public
QSyntaxHighlighter
{
public:
XmlHighlighter
(
QObject
*
parent
);
XmlHighlighter
(
QTextDocument
*
parent
);
XmlHighlighter
(
QTextEdit
*
parent
);
~
XmlHighlighter
();
enum
HighlightType
{
SyntaxChar
,
ElementName
,
Comment
,
AttributeName
,
AttributeValue
,
Error
,
Other
};
void
setHighlightColor
(
HighlightType
type
,
QColor
color
,
bool
foreground
=
true
);
void
setHighlightFormat
(
HighlightType
type
,
QTextCharFormat
format
);
protected:
void
highlightBlock
(
const
QString
&
rstrText
);
int
processDefaultText
(
int
i
,
const
QString
&
rstrText
);
private:
void
init
();
QTextCharFormat
fmtSyntaxChar
;
QTextCharFormat
fmtElementName
;
QTextCharFormat
fmtComment
;
QTextCharFormat
fmtAttributeName
;
QTextCharFormat
fmtAttributeValue
;
QTextCharFormat
fmtError
;
QTextCharFormat
fmtOther
;
enum
ParsingState
{
NoState
=
0
,
ExpectElementNameOrSlash
,
ExpectElementName
,
ExpectAttributeOrEndOfElement
,
ExpectEqual
,
ExpectAttributeValue
};
enum
BlockState
{
NoBlock
=
-
1
,
InComment
,
InElement
};
ParsingState
state
;
};
class
QGCMAVLinkTextEdit
:
public
QTextEdit
{
Q_OBJECT
//
public:
QGCMAVLinkTextEdit
(
QWidget
*
parent
=
0
);
bool
Conform
();
QDomDocument
xml_document
();
inline
QString
text
()
const
{
return
QTextEdit
::
toPlainText
();
}
QMenu
*
createOwnStandardContextMenu
();
protected:
void
contextMenuEvent
(
QContextMenuEvent
*
e
);
bool
event
(
QEvent
*
event
);
private:
XmlHighlighter
*
highlight
;
signals:
public
slots
:
bool
syntaxcheck
();
void
setPlainText
(
const
QString
txt
);
};
#endif // QGCMAVLINKTEXTEDIT_H
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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