Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
magicseteditor
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
magicseteditor
Commits
cfd59072
Commit
cfd59072
authored
Dec 19, 2006
by
twanvl
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
improved cursor handling in text editor
parent
54659c21
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
247 additions
and
96 deletions
+247
-96
data/en.mse-locale/locale
data/en.mse-locale/locale
+5
-0
src/gui/set/window.cpp
src/gui/set/window.cpp
+2
-2
src/gui/symbol/window.cpp
src/gui/symbol/window.cpp
+1
-1
src/gui/update_checker.cpp
src/gui/update_checker.cpp
+2
-2
src/gui/value/text.cpp
src/gui/value/text.cpp
+106
-74
src/gui/value/text.hpp
src/gui/value/text.hpp
+17
-13
src/gui/welcome_window.cpp
src/gui/welcome_window.cpp
+1
-1
src/render/text/element.hpp
src/render/text/element.hpp
+15
-2
src/render/text/viewer.hpp
src/render/text/viewer.hpp
+1
-1
src/util/tagged_string.cpp
src/util/tagged_string.cpp
+74
-0
src/util/tagged_string.hpp
src/util/tagged_string.hpp
+23
-0
No files found.
data/en.mse-locale/locale
View file @
cfd59072
...
@@ -195,15 +195,20 @@ button:
...
@@ -195,15 +195,20 @@ button:
#
Card
select
#
Card
select
select
all
:
Select
&
All
select
all
:
Select
&
All
select
none
:
Select
&
None
select
none
:
Select
&
None
#
Update
checker
close
:
&
Close
##############################################################
Titles
in
the
GUI
##############################################################
Titles
in
the
GUI
title
:
title
:
magic
set
editor
:
Magic
Set
Editor
magic
set
editor
:
Magic
Set
Editor
about
:
About
Magic
Set
Editor
about
:
About
Magic
Set
Editor
symbol
editor
:
Symbol
Editor
#
dialogs
#
dialogs
open
set
:
Open
Set
open
set
:
Open
Set
save
set
:
Save
Set
As
save
set
:
Save
Set
As
save
image
:
Save
Image
save
image
:
Save
Image
updates
availible
:
Updates
Availible
#
preferences
#
preferences
preferences
:
Preferences
preferences
:
Preferences
display
:
Display
display
:
Display
...
...
src/gui/set/window.cpp
View file @
cfd59072
...
@@ -94,11 +94,11 @@ SetWindow::SetWindow(Window* parent, const SetP& set)
...
@@ -94,11 +94,11 @@ SetWindow::SetWindow(Window* parent, const SetP& set)
menuBar
->
Append
(
menuHelp
,
_MENU_
(
"help"
));
menuBar
->
Append
(
menuHelp
,
_MENU_
(
"help"
));
SetMenuBar
(
menuBar
);
SetMenuBar
(
menuBar
);
// status bar
// status bar
CreateStatusBar
();
CreateStatusBar
();
SetStatusText
(
_
(
"Welcome to Magic Set Editor"
));
SetStatusText
(
_
(
"Welcome to Magic Set Editor"
));
// tool bar
// tool bar
wxToolBar
*
tb
=
CreateToolBar
(
wxTB_FLAT
|
wxNO_BORDER
|
wxTB_HORIZONTAL
);
wxToolBar
*
tb
=
CreateToolBar
(
wxTB_FLAT
|
wxNO_BORDER
|
wxTB_HORIZONTAL
);
tb
->
SetToolBitmapSize
(
wxSize
(
18
,
18
));
tb
->
SetToolBitmapSize
(
wxSize
(
18
,
18
));
...
...
src/gui/symbol/window.cpp
View file @
cfd59072
...
@@ -48,7 +48,7 @@ SymbolWindow::SymbolWindow(Window* parent, const SymbolValueP& value, const SetP
...
@@ -48,7 +48,7 @@ SymbolWindow::SymbolWindow(Window* parent, const SymbolValueP& value, const SetP
}
}
void
SymbolWindow
::
init
(
Window
*
parent
,
SymbolP
symbol
)
{
void
SymbolWindow
::
init
(
Window
*
parent
,
SymbolP
symbol
)
{
Create
(
parent
,
wxID_ANY
,
_
(
"Symbol E
ditor"
),
wxDefaultPosition
,
wxSize
(
600
,
600
),
wxDEFAULT_FRAME_STYLE
|
wxNO_FULL_REPAINT_ON_RESIZE
);
Create
(
parent
,
wxID_ANY
,
_
TITLE_
(
"symbol e
ditor"
),
wxDefaultPosition
,
wxSize
(
600
,
600
),
wxDEFAULT_FRAME_STYLE
|
wxNO_FULL_REPAINT_ON_RESIZE
);
inSelectionEvent
=
false
;
inSelectionEvent
=
false
;
// Menu bar
// Menu bar
...
...
src/gui/update_checker.cpp
View file @
cfd59072
...
@@ -120,11 +120,11 @@ struct HtmlWindowToBrowser : public wxHtmlWindow {
...
@@ -120,11 +120,11 @@ struct HtmlWindowToBrowser : public wxHtmlWindow {
void
show_update_dialog
(
Window
*
parent
)
{
void
show_update_dialog
(
Window
*
parent
)
{
if
(
!
update_available
())
return
;
// we already have the latest version
if
(
!
update_available
())
return
;
// we already have the latest version
// Show update dialog
// Show update dialog
wxDialog
*
dlg
=
new
wxDialog
(
parent
,
wxID_ANY
,
_
(
"U
pdates availible"
),
wxDefaultPosition
);
wxDialog
*
dlg
=
new
wxDialog
(
parent
,
wxID_ANY
,
_
TITLE_
(
"u
pdates availible"
),
wxDefaultPosition
);
// controls
// controls
wxHtmlWindow
*
html
=
new
HtmlWindowToBrowser
(
dlg
,
wxID_ANY
,
wxDefaultPosition
,
wxDefaultSize
,
wxHW_SCROLLBAR_AUTO
|
wxSUNKEN_BORDER
);
wxHtmlWindow
*
html
=
new
HtmlWindowToBrowser
(
dlg
,
wxID_ANY
,
wxDefaultPosition
,
wxDefaultSize
,
wxHW_SCROLLBAR_AUTO
|
wxSUNKEN_BORDER
);
html
->
SetPage
(
update_version_data
->
description
);
html
->
SetPage
(
update_version_data
->
description
);
wxButton
*
close
=
new
wxButton
(
dlg
,
wxID_OK
,
_
(
"&C
lose"
));
wxButton
*
close
=
new
wxButton
(
dlg
,
wxID_OK
,
_
BUTTON_
(
"c
lose"
));
close
->
SetDefault
();
close
->
SetDefault
();
// layout
// layout
wxSizer
*
s
=
new
wxBoxSizer
(
wxVERTICAL
);
wxSizer
*
s
=
new
wxBoxSizer
(
wxVERTICAL
);
...
...
src/gui/value/text.cpp
View file @
cfd59072
This diff is collapsed.
Click to expand it.
src/gui/value/text.hpp
View file @
cfd59072
...
@@ -10,6 +10,7 @@
...
@@ -10,6 +10,7 @@
// ----------------------------------------------------------------------------- : Includes
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/prec.hpp>
#include <util/tagged_string.hpp> // for Movement
#include <gui/value/editor.hpp>
#include <gui/value/editor.hpp>
#include <render/value/text.hpp>
#include <render/value/text.hpp>
...
@@ -17,11 +18,9 @@ class TextValueEditorScrollBar;
...
@@ -17,11 +18,9 @@ class TextValueEditorScrollBar;
// ----------------------------------------------------------------------------- : TextValueEditor
// ----------------------------------------------------------------------------- : TextValueEditor
/// Directions of cursor movement
enum
IndexType
enum
Movement
{
TYPE_CURSOR
///< Positions are cursor positions
{
MOVE_LEFT
///< Always move the cursor to the left
,
TYPE_INDEX
///< Positions are character indices
,
MOVE_MID
///< Move in whichever direction the distance to move is shorter (TODO: define shorter)
,
MOVE_RIGHT
///< Always move the cursor to the right
};
};
/// An editor 'control' for editing TextValues
/// An editor 'control' for editing TextValues
...
@@ -83,16 +82,19 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
...
@@ -83,16 +82,19 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
// --------------------------------------------------- : Data
// --------------------------------------------------- : Data
private:
private:
size_t
selection_start
,
selection_end
;
///< Cursor position/selection (if any)
size_t
selection_start
,
selection_end
;
///< Cursor position/selection (if any), cursor positions
size_t
selection_start_i
,
selection_end_i
;
///< Cursor position/selection, character indices
TextValueEditorScrollBar
*
scrollbar
;
///< Scrollbar for multiline fields in native look
TextValueEditorScrollBar
*
scrollbar
;
///< Scrollbar for multiline fields in native look
bool
select_words
;
///< Select whole words when dragging the mouse?
bool
select_words
;
///< Select whole words when dragging the mouse?
// --------------------------------------------------- : Selection / movement
// --------------------------------------------------- : Selection / movement
/// Move the selection to a new location, clears the previously drawn selection
/// Move the selection to a new location, clears the previously drawn selection.
void
moveSelection
(
size_t
new_end
,
bool
also_move_start
=
true
,
Movement
dir
=
MOVE_MID
);
/** t specifies what kind of position new_end is */
/// Move the selection to a new location, but does not redraw
void
moveSelection
(
IndexType
t
,
size_t
new_end
,
bool
also_move_start
=
true
,
Movement
dir
=
MOVE_MID
);
void
moveSelectionNoRedraw
(
size_t
new_end
,
bool
also_move_start
=
true
,
Movement
dir
=
MOVE_MID
);
/// Move the selection to a new location, but does not redraw.
/** t specifies what kind of position new_end is */
void
moveSelectionNoRedraw
(
IndexType
t
,
size_t
new_end
,
bool
also_move_start
=
true
,
Movement
dir
=
MOVE_MID
);
/// Replace the current selection with 'replacement', name the action
/// Replace the current selection with 'replacement', name the action
void
replaceSelection
(
const
String
&
replacement
,
const
String
&
name
);
void
replaceSelection
(
const
String
&
replacement
,
const
String
&
name
);
...
@@ -104,7 +106,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
...
@@ -104,7 +106,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
*
*
* When correcting the selection, move in the given direction
* When correcting the selection, move in the given direction
*/
*/
void
fixSelection
(
Movement
dir
=
MOVE_MID
);
void
fixSelection
(
IndexType
t
=
TYPE_CURSOR
,
Movement
dir
=
MOVE_MID
);
/// Return a position resulting from moving pos outside the range [start...end), in the direction dir
/// Return a position resulting from moving pos outside the range [start...end), in the direction dir
static
size_t
move
(
size_t
pos
,
size_t
start
,
size_t
end
,
Movement
dir
);
static
size_t
move
(
size_t
pos
,
size_t
start
,
size_t
end
,
Movement
dir
);
...
@@ -113,11 +115,13 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
...
@@ -113,11 +115,13 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
void
showCaret
();
void
showCaret
();
/// Position of previous visible & selectable character
/// Position of previous visible & selectable character
/** Uses cursor positions */
size_t
prevCharBoundry
(
size_t
pos
)
const
;
size_t
prevCharBoundry
(
size_t
pos
)
const
;
size_t
nextCharBoundry
(
size_t
pos
)
const
;
size_t
nextCharBoundry
(
size_t
pos
)
const
;
/// Front of previous word, used witch Ctrl+Left/right
/// Front of previous word, used witch Ctrl+Left/right
size_t
prevWordBoundry
(
size_t
pos
)
const
;
/** Uses character indices */
size_t
nextWordBoundry
(
size_t
pos
)
const
;
size_t
prevWordBoundry
(
size_t
pos_i
)
const
;
size_t
nextWordBoundry
(
size_t
pos_i
)
const
;
// --------------------------------------------------- : Scrolling
// --------------------------------------------------- : Scrolling
...
...
src/gui/welcome_window.cpp
View file @
cfd59072
...
@@ -64,7 +64,7 @@ void WelcomeWindow::draw(DC& dc) {
...
@@ -64,7 +64,7 @@ void WelcomeWindow::draw(DC& dc) {
}
}
void
WelcomeWindow
::
onOpenSet
(
wxCommandEvent
&
)
{
void
WelcomeWindow
::
onOpenSet
(
wxCommandEvent
&
)
{
wxFileDialog
dlg
(
this
,
_
(
"Open a
set"
),
wxEmptyString
,
wxEmptyString
,
import_formats
(),
wxOPEN
);
wxFileDialog
dlg
(
this
,
_
TITLE_
(
"open
set"
),
wxEmptyString
,
wxEmptyString
,
import_formats
(),
wxOPEN
);
if
(
dlg
.
ShowModal
()
==
wxID_OK
)
{
if
(
dlg
.
ShowModal
()
==
wxID_OK
)
{
close
(
import_set
(
dlg
.
GetPath
()));
close
(
import_set
(
dlg
.
GetPath
()));
}
}
...
...
src/render/text/element.hpp
View file @
cfd59072
...
@@ -130,7 +130,7 @@ class FontTextElement : public SimpleTextElement {
...
@@ -130,7 +130,7 @@ class FontTextElement : public SimpleTextElement {
:
SimpleTextElement
(
text
,
start
,
end
)
:
SimpleTextElement
(
text
,
start
,
end
)
,
font
(
font
)
,
font
(
font
)
{}
{}
virtual
void
draw
(
RotatedDC
&
dc
,
double
scale
,
const
RealRect
&
rect
,
const
double
*
xs
,
DrawWhat
what
,
size_t
start
,
size_t
end
)
const
;
virtual
void
draw
(
RotatedDC
&
dc
,
double
scale
,
const
RealRect
&
rect
,
const
double
*
xs
,
DrawWhat
what
,
size_t
start
,
size_t
end
)
const
;
virtual
void
getCharInfo
(
RotatedDC
&
dc
,
double
scale
,
vector
<
CharInfo
>&
out
)
const
;
virtual
void
getCharInfo
(
RotatedDC
&
dc
,
double
scale
,
vector
<
CharInfo
>&
out
)
const
;
virtual
double
minScale
()
const
;
virtual
double
minScale
()
const
;
...
@@ -146,7 +146,7 @@ class SymbolTextElement : public SimpleTextElement {
...
@@ -146,7 +146,7 @@ class SymbolTextElement : public SimpleTextElement {
:
SimpleTextElement
(
text
,
start
,
end
)
:
SimpleTextElement
(
text
,
start
,
end
)
,
font
(
font
),
ctx
(
*
ctx
)
,
font
(
font
),
ctx
(
*
ctx
)
{}
{}
virtual
void
draw
(
RotatedDC
&
dc
,
double
scale
,
const
RealRect
&
rect
,
const
double
*
xs
,
DrawWhat
what
,
size_t
start
,
size_t
end
)
const
;
virtual
void
draw
(
RotatedDC
&
dc
,
double
scale
,
const
RealRect
&
rect
,
const
double
*
xs
,
DrawWhat
what
,
size_t
start
,
size_t
end
)
const
;
virtual
void
getCharInfo
(
RotatedDC
&
dc
,
double
scale
,
vector
<
CharInfo
>&
out
)
const
;
virtual
void
getCharInfo
(
RotatedDC
&
dc
,
double
scale
,
vector
<
CharInfo
>&
out
)
const
;
virtual
double
minScale
()
const
;
virtual
double
minScale
()
const
;
...
@@ -162,9 +162,22 @@ class CompoundTextElement : public TextElement {
...
@@ -162,9 +162,22 @@ class CompoundTextElement : public TextElement {
public:
public:
CompoundTextElement
(
const
String
&
text
,
size_t
start
,
size_t
end
)
:
TextElement
(
text
,
start
,
end
)
{}
CompoundTextElement
(
const
String
&
text
,
size_t
start
,
size_t
end
)
:
TextElement
(
text
,
start
,
end
)
{}
virtual
void
draw
(
RotatedDC
&
dc
,
double
scale
,
const
RealRect
&
rect
,
const
double
*
xs
,
DrawWhat
what
,
size_t
start
,
size_t
end
)
const
;
virtual
void
getCharInfo
(
RotatedDC
&
dc
,
double
scale
,
vector
<
CharInfo
>&
out
)
const
;
virtual
double
minScale
()
const
;
TextElements
elements
;
///< the elements
TextElements
elements
;
///< the elements
};
};
/// A TextElement drawn using a grey background
class
AtomTextElement
:
public
CompoundTextElement
{
public:
AtomTextElement
(
const
String
&
text
,
size_t
start
,
size_t
end
)
:
CompoundTextElement
(
text
,
start
,
end
)
{}
virtual
void
draw
(
RotatedDC
&
dc
,
double
scale
,
const
RealRect
&
rect
,
const
double
*
xs
,
DrawWhat
what
,
size_t
start
,
size_t
end
)
const
;
};
// ----------------------------------------------------------------------------- : Other text elements
// ----------------------------------------------------------------------------- : Other text elements
/// A text element that displays a horizontal separator line
/// A text element that displays a horizontal separator line
...
...
src/render/text/viewer.hpp
View file @
cfd59072
...
@@ -81,7 +81,7 @@ class TextViewer {
...
@@ -81,7 +81,7 @@ class TextViewer {
/// Is the character at the given index visible?
/// Is the character at the given index visible?
bool
isVisible
(
size_t
index
)
const
;
bool
isVisible
(
size_t
index
)
const
;
/// Find the first character index that is at/before/after the given index, and which has a nonzero width
/// Find the first character index that is at/before/after the given index, and which has a nonzero width
/** More precisely: it returns a position so that
no character after it has
zero width
/** More precisely: it returns a position so that
the character after it in the direction delta has non
zero width
*/
*/
size_t
firstVisibleChar
(
size_t
index
,
int
delta
)
const
;
size_t
firstVisibleChar
(
size_t
index
,
int
delta
)
const
;
...
...
src/util/tagged_string.cpp
View file @
cfd59072
...
@@ -159,6 +159,80 @@ String anti_tag(const String& tag) {
...
@@ -159,6 +159,80 @@ String anti_tag(const String& tag) {
else
return
_
(
"</"
)
+
tag
+
_
(
">"
);
else
return
_
(
"</"
)
+
tag
+
_
(
">"
);
}
}
// ----------------------------------------------------------------------------- : Cursor position
size_t
index_to_cursor
(
const
String
&
str
,
size_t
index
,
Movement
dir
)
{
size_t
cursor
=
0
;
size_t
start
=
0
,
end
=
0
;
index
=
min
(
index
,
str
.
size
());
// find the range [start...end) with the same cursor value, that contains index
// after the loop, cursor corresponds to index end
for
(
size_t
i
=
0
;
i
<
str
.
size
()
;
)
{
Char
c
=
str
.
GetChar
(
i
);
if
(
c
==
_
(
'<'
))
{
// a tag
if
(
is_substr
(
str
,
i
,
_
(
"<atom"
))
||
is_substr
(
str
,
i
,
_
(
"<sep"
)))
{
// skip tag contents, tag counts as a single 'character'
i
=
skip_tag
(
str
,
match_close_tag
(
str
,
i
));
cursor
++
;
start
=
end
;
end
=
i
;
if
(
end
>
index
)
break
;
}
else
{
i
=
skip_tag
(
str
,
i
);
end
=
i
;
}
}
else
{
cursor
++
;
i
++
;
start
=
end
;
end
=
i
;
if
(
end
>
index
)
break
;
}
}
if
(
cursor
==
0
)
return
0
;
if
(
i
==
str
.
size
())
return
cursor
;
if
(
dir
==
MOVE_LEFT
)
return
cursor
-
1
;
if
(
dir
==
MOVE_RIGHT
)
return
cursor
-
(
start
==
index
);
// which is nearer? start or end?
return
cursor
-
((
int
)(
index
-
start
)
<=
(
int
)(
end
-
index
));
}
void
cursor_to_index_range
(
const
String
&
str
,
size_t
cursor
,
size_t
&
start
,
size_t
&
end
)
{
start
=
end
=
0
;
size_t
cur
=
0
;
size_t
i
=
0
;
while
(
cur
<=
cursor
&&
i
<
str
.
size
())
{
Char
c
=
str
.
GetChar
(
i
);
if
(
c
==
_
(
'<'
))
{
// a tag
if
(
is_substr
(
str
,
i
,
_
(
"<atom"
))
||
is_substr
(
str
,
i
,
_
(
"<sep"
)))
{
// skip tag contents, tag counts as a single 'character'
i
=
skip_tag
(
str
,
match_close_tag
(
str
,
i
));
cur
++
;
if
(
cur
==
cursor
)
start
=
i
;
}
else
{
i
=
skip_tag
(
str
,
i
);
}
}
else
{
cur
++
;
i
++
;
if
(
cur
==
cursor
)
start
=
i
;
}
}
end
=
min
(
i
,
str
.
size
());
if
(
cur
<
cursor
)
start
=
end
=
str
.
size
();
}
size_t
cursor_to_index
(
const
String
&
str
,
size_t
cursor
)
{
size_t
start
,
end
;
cursor_to_index_range
(
str
,
cursor
,
start
,
end
);
// TODO: If at i there is <tag></tag> return a position inside the tags
// This allows formating to be enabled without a selection
return
start
;
}
// ----------------------------------------------------------------------------- : Global operations
// ----------------------------------------------------------------------------- : Global operations
String
remove_tag
(
const
String
&
str
,
const
String
&
tag
)
{
String
remove_tag
(
const
String
&
str
,
const
String
&
tag
)
{
...
...
src/util/tagged_string.hpp
View file @
cfd59072
...
@@ -80,6 +80,29 @@ String close_tag(const String& tag);
...
@@ -80,6 +80,29 @@ String close_tag(const String& tag);
/// The matching close tag for an open tag and vice versa
/// The matching close tag for an open tag and vice versa
String
anti_tag
(
const
String
&
tag
);
String
anti_tag
(
const
String
&
tag
);
// ----------------------------------------------------------------------------- : Cursor position
/// Directions of cursor movement
enum
Movement
{
MOVE_LEFT
=
-
1
///< Always move the cursor to the left
,
MOVE_MID
=
0
///< Move in whichever direction the distance to move is shorter (TODO: define shorter)
,
MOVE_RIGHT
=
1
///< Always move the cursor to the right
};
/// Find the cursor position corresponding to the given character index.
/** A cursor position always corresponds to a valid place to type text.
* The cursor position is rounded to the direction dir.
*/
size_t
index_to_cursor
(
const
String
&
str
,
size_t
index
,
Movement
dir
=
MOVE_MID
);
/// Find the range of character indeces corresponding to the given cursor position
/** The output parameters will correspond to the range [start...end) which are all valid character indices.
*/
void
cursor_to_index_range
(
const
String
&
str
,
size_t
cursor
,
size_t
&
begin
,
size_t
&
end
);
/// Find the character index corresponding to the given cursor position
size_t
cursor_to_index
(
const
String
&
str
,
size_t
cursor
);
// ----------------------------------------------------------------------------- : Global operations
// ----------------------------------------------------------------------------- : Global operations
/// Remove all instances of a tag and its close tag, but keep the contents.
/// Remove all instances of a tag and its close tag, but keep the contents.
...
...
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