Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
592b53d0
Commit
592b53d0
authored
Mar 22, 2007
by
Dmitry Timoshkov
Committed by
Alexandre Julliard
Mar 22, 2007
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
riched20: Split unicode and ansi window procs, handle messages depending on the unicode flag.
Add a hack for WM_GETTEXT in RichEdit20W class when running in win9x emulation mode.
parent
b9eb76de
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
89 additions
and
65 deletions
+89
-65
editor.c
dlls/riched20/editor.c
+60
-31
editor.h
dlls/riched20/editor.h
+2
-4
string.c
dlls/riched20/string.c
+4
-23
editor.c
dlls/riched20/tests/editor.c
+23
-7
No files found.
dlls/riched20/editor.c
View file @
592b53d0
...
...
@@ -1413,17 +1413,16 @@ get_msg_name(UINT msg)
return
""
;
}
/******************************************************************
* RichEditANSIWndProc (RICHED20.10)
*/
LRESULT
WINAPI
RichEditANSIWndProc
(
HWND
hWnd
,
UINT
msg
,
WPARAM
wParam
,
LPARAM
lParam
)
{
static
LRESULT
RichEditWndProc_common
(
HWND
hWnd
,
UINT
msg
,
WPARAM
wParam
,
LPARAM
lParam
,
BOOL
unicode
)
{
ME_TextEditor
*
editor
=
(
ME_TextEditor
*
)
GetWindowLongPtrW
(
hWnd
,
0
);
TRACE
(
"h
Wnd %p msg %04x (%s) %08x %08lx
\n
"
,
hWnd
,
msg
,
get_msg_name
(
msg
),
wParam
,
lParam
);
TRACE
(
"h
wnd %p msg %04x (%s) %x %lx, unicode %d
\n
"
,
hWnd
,
msg
,
get_msg_name
(
msg
),
wParam
,
lParam
,
unicode
);
if
(
!
editor
&&
msg
!=
WM_NCCREATE
&&
msg
!=
WM_NCDESTROY
)
{
ERR
(
"
RichEditANSIWndProc
called with invalid hWnd %p - application bug?
\n
"
,
hWnd
);
ERR
(
"called with invalid hWnd %p - application bug?
\n
"
,
hWnd
);
return
0
;
}
...
...
@@ -1490,7 +1489,6 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
TRACE
(
"WM_NCCREATE: style 0x%08x
\n
"
,
pcs
->
style
);
editor
=
ME_MakeEditor
(
hWnd
);
SetWindowLongPtrW
(
hWnd
,
0
,
(
LONG_PTR
)
editor
);
pcs
=
0
;
/* ignore */
return
TRUE
;
}
case
EM_EMPTYUNDOBUFFER
:
...
...
@@ -1817,7 +1815,7 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
{
int
from
,
to
;
ME_Style
*
style
;
LPWSTR
wszText
=
ME_ToUnicode
(
hWnd
,
(
void
*
)
lParam
);
LPWSTR
wszText
=
ME_ToUnicode
(
unicode
,
(
void
*
)
lParam
);
size_t
len
=
lstrlenW
(
wszText
);
TRACE
(
"EM_REPLACESEL - %s
\n
"
,
debugstr_w
(
wszText
));
...
...
@@ -1833,7 +1831,7 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
*/
if
(
len
>
0
&&
wszText
[
len
-
1
]
==
'\n'
)
ME_ClearTempStyle
(
editor
);
ME_EndToUnicode
(
hWnd
,
wszText
);
ME_EndToUnicode
(
unicode
,
wszText
);
ME_CommitUndo
(
editor
);
if
(
!
wParam
)
ME_EmptyUndoStack
(
editor
);
...
...
@@ -1885,21 +1883,21 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
if
(
lParam
)
{
TRACE
(
"WM_SETTEXT lParam==%lx
\n
"
,
lParam
);
if
(
!
IsWindowUnicode
(
hWnd
)
&&
!
strncmp
((
char
*
)
lParam
,
"{
\\
rtf"
,
5
))
if
(
!
unicode
&&
!
strncmp
((
char
*
)
lParam
,
"{
\\
rtf"
,
5
))
{
/* Undocumented: WM_SETTEXT supports RTF text */
ME_StreamInRTFString
(
editor
,
0
,
(
char
*
)
lParam
);
}
else
{
LPWSTR
wszText
=
ME_ToUnicode
(
hWnd
,
(
void
*
)
lParam
);
LPWSTR
wszText
=
ME_ToUnicode
(
unicode
,
(
void
*
)
lParam
);
TRACE
(
"WM_SETTEXT - %s
\n
"
,
debugstr_w
(
wszText
));
/* debugstr_w() */
if
(
lstrlenW
(
wszText
)
>
0
)
{
/* uses default style! */
ME_InsertTextFromCursor
(
editor
,
0
,
wszText
,
-
1
,
editor
->
pBuffer
->
pDefaultStyle
);
}
ME_EndToUnicode
(
hWnd
,
wszText
);
ME_EndToUnicode
(
unicode
,
wszText
);
}
}
else
...
...
@@ -1980,9 +1978,9 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
{
TEXTRANGEW
tr
;
/* W and A differ only by rng->lpstrText */
tr
.
chrg
.
cpMin
=
0
;
tr
.
chrg
.
cpMax
=
wParam
-
1
;
tr
.
chrg
.
cpMax
=
wParam
?
(
wParam
-
1
)
:
0
;
tr
.
lpstrText
=
(
WCHAR
*
)
lParam
;
return
RichEdit
ANSIWndProc
(
hWnd
,
EM_GETTEXTRANGE
,
0
,
(
LPARAM
)
&
tr
);
return
RichEdit
WndProc_common
(
hWnd
,
EM_GETTEXTRANGE
,
0
,
(
LPARAM
)
&
tr
,
unicode
);
}
case
EM_GETTEXTEX
:
{
...
...
@@ -2003,7 +2001,7 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
nStart
=
0
;
nCount
=
ex
->
cb
-
1
;
}
if
(
ex
->
codepage
==
1200
||
IsWindowUnicode
(
hWnd
)
)
if
(
ex
->
codepage
==
1200
||
unicode
)
{
nCount
=
min
(
nCount
,
ex
->
cb
/
sizeof
(
WCHAR
)
-
1
);
return
ME_GetTextW
(
editor
,
(
LPWSTR
)
lParam
,
nStart
,
nCount
,
ex
->
flags
&
GT_USECRLF
);
...
...
@@ -2033,7 +2031,7 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
tr
.
chrg
.
cpMin
=
from
;
tr
.
chrg
.
cpMax
=
to
;
tr
.
lpstrText
=
(
WCHAR
*
)
lParam
;
return
RichEdit
ANSIWndProc
(
hWnd
,
EM_GETTEXTRANGE
,
0
,
(
LPARAM
)
&
tr
);
return
RichEdit
WndProc_common
(
hWnd
,
EM_GETTEXTRANGE
,
0
,
(
LPARAM
)
&
tr
,
unicode
);
}
case
EM_GETSCROLLPOS
:
{
...
...
@@ -2046,9 +2044,9 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
{
TEXTRANGEW
*
rng
=
(
TEXTRANGEW
*
)
lParam
;
TRACE
(
"EM_GETTEXTRANGE min=%d max=%d unicode=%d emul1.0=%d length=%d
\n
"
,
rng
->
chrg
.
cpMin
,
rng
->
chrg
.
cpMax
,
IsWindowUnicode
(
hWnd
),
rng
->
chrg
.
cpMin
,
rng
->
chrg
.
cpMax
,
unicode
,
editor
->
bEmulateVersion10
,
ME_GetTextLength
(
editor
));
if
(
IsWindowUnicode
(
hWnd
)
)
if
(
unicode
)
return
ME_GetTextW
(
editor
,
rng
->
lpstrText
,
rng
->
chrg
.
cpMin
,
rng
->
chrg
.
cpMax
-
rng
->
chrg
.
cpMin
,
editor
->
bEmulateVersion10
);
else
{
...
...
@@ -2066,7 +2064,6 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
case
EM_GETLINE
:
{
ME_DisplayItem
*
run
;
const
BOOL
bUnicode
=
IsWindowUnicode
(
hWnd
);
const
unsigned
int
nMaxChars
=
*
(
WORD
*
)
lParam
;
unsigned
int
nEndChars
,
nCharsLeft
=
nMaxChars
;
char
*
dest
=
(
char
*
)
lParam
;
...
...
@@ -2075,7 +2072,7 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
int
nLF
=
editor
->
bEmulateVersion10
;
TRACE
(
"EM_GETLINE: row=%d, nMaxChars=%d (%s)
\n
"
,
(
int
)
wParam
,
nMaxChars
,
bU
nicode
?
"Unicode"
:
"Ansi"
);
u
nicode
?
"Unicode"
:
"Ansi"
);
run
=
ME_FindRowWithNumber
(
editor
,
wParam
);
if
(
run
==
NULL
)
...
...
@@ -2091,19 +2088,19 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
strText
=
run
->
member
.
run
.
strText
;
nCopy
=
min
(
nCharsLeft
,
strText
->
nLen
);
if
(
bU
nicode
)
if
(
u
nicode
)
lstrcpynW
((
LPWSTR
)
dest
,
strText
->
szData
,
nCopy
);
else
nCopy
=
WideCharToMultiByte
(
CP_ACP
,
0
,
strText
->
szData
,
nCopy
,
dest
,
nCharsLeft
,
NULL
,
NULL
);
dest
+=
nCopy
*
(
bU
nicode
?
sizeof
(
WCHAR
)
:
1
);
dest
+=
nCopy
*
(
u
nicode
?
sizeof
(
WCHAR
)
:
1
);
nCharsLeft
-=
nCopy
;
}
/* append \r\0 (or \r\n\0 in 1.0), space allowing */
nEndChars
=
min
(
nCharsLeft
,
2
+
nLF
);
nCharsLeft
-=
nEndChars
;
if
(
bU
nicode
)
if
(
u
nicode
)
{
const
WCHAR
src
[]
=
{
'\r'
,
'\0'
};
const
WCHAR
src10
[]
=
{
'\r'
,
'\n'
,
'\0'
};
...
...
@@ -2362,7 +2359,15 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
goto
do_default
;
case
WM_CHAR
:
{
WCHAR
wstr
=
LOWORD
(
wParam
);
WCHAR
wstr
;
if
(
unicode
)
wstr
=
(
WCHAR
)
wParam
;
else
{
CHAR
charA
=
wParam
;
MultiByteToWideChar
(
CP_ACP
,
0
,
&
charA
,
1
,
&
wstr
,
1
);
}
if
(
editor
->
AutoURLDetect_bEnable
)
ME_AutoURLDetect
(
editor
,
wstr
);
...
...
@@ -2532,7 +2537,8 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
LRESULT
ret
;
int
mask
=
0
;
int
changes
=
0
;
if
((
ret
=
RichEditANSIWndProc
(
hWnd
,
WM_GETTEXTLENGTH
,
0
,
0
))
==
0
)
ret
=
RichEditWndProc_common
(
hWnd
,
WM_GETTEXTLENGTH
,
0
,
0
,
unicode
);
if
(
!
ret
)
{
/*Check for valid wParam*/
if
((((
wParam
&
TM_RICHTEXT
)
&&
((
wParam
&
TM_PLAINTEXT
)
<<
1
)))
||
...
...
@@ -2566,6 +2572,29 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
return
0L
;
}
static
LRESULT
WINAPI
RichEditWndProcW
(
HWND
hWnd
,
UINT
msg
,
WPARAM
wParam
,
LPARAM
lParam
)
{
BOOL
unicode
=
TRUE
;
/* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
if
(
msg
==
WM_GETTEXT
&&
(
GetVersion
()
&
0x80000000
))
unicode
=
FALSE
;
return
RichEditWndProc_common
(
hWnd
,
msg
,
wParam
,
lParam
,
unicode
);
}
static
LRESULT
WINAPI
RichEditWndProcA
(
HWND
hWnd
,
UINT
msg
,
WPARAM
wParam
,
LPARAM
lParam
)
{
return
RichEditWndProc_common
(
hWnd
,
msg
,
wParam
,
lParam
,
FALSE
);
}
/******************************************************************
* RichEditANSIWndProc (RICHED20.10)
*/
LRESULT
WINAPI
RichEditANSIWndProc
(
HWND
hWnd
,
UINT
msg
,
WPARAM
wParam
,
LPARAM
lParam
)
{
return
RichEditWndProcA
(
hWnd
,
msg
,
wParam
,
lParam
);
}
/******************************************************************
* RichEdit10ANSIWndProc (RICHED20.9)
...
...
@@ -2648,7 +2677,7 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in
WCHAR
*
pStart
=
buffer
;
if
(
!
item
)
{
*
buffer
=
L'\0'
;
*
buffer
=
0
;
return
0
;
}
...
...
@@ -2700,12 +2729,12 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in
if
(
!
nChars
)
{
TRACE
(
"nWritten=%d, actual=%d
\n
"
,
nWritten
,
buffer
-
pStart
);
*
buffer
=
L'\0'
;
*
buffer
=
0
;
return
nWritten
;
}
item
=
ME_FindItemFwd
(
item
,
diRun
);
}
*
buffer
=
L'\0'
;
*
buffer
=
0
;
TRACE
(
"nWritten=%d, actual=%d
\n
"
,
nWritten
,
buffer
-
pStart
);
return
nWritten
;
}
...
...
@@ -2716,7 +2745,7 @@ static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
WNDCLASSA
wcA
;
wcW
.
style
=
CS_DBLCLKS
|
CS_HREDRAW
|
CS_VREDRAW
|
CS_GLOBALCLASS
;
wcW
.
lpfnWndProc
=
RichEdit
ANSIWndProc
;
wcW
.
lpfnWndProc
=
RichEdit
WndProcW
;
wcW
.
cbClsExtra
=
0
;
wcW
.
cbWndExtra
=
sizeof
(
ME_TextEditor
*
);
wcW
.
hInstance
=
NULL
;
/* hInstance would register DLL-local class */
...
...
@@ -2742,7 +2771,7 @@ static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
}
wcA
.
style
=
CS_DBLCLKS
|
CS_HREDRAW
|
CS_VREDRAW
|
CS_GLOBALCLASS
;
wcA
.
lpfnWndProc
=
RichEdit
ANSIWndProc
;
wcA
.
lpfnWndProc
=
RichEdit
WndProcA
;
wcA
.
cbClsExtra
=
0
;
wcA
.
cbWndExtra
=
sizeof
(
ME_TextEditor
*
);
wcA
.
hInstance
=
NULL
;
/* hInstance would register DLL-local class */
...
...
dlls/riched20/editor.h
View file @
592b53d0
...
...
@@ -115,10 +115,8 @@ int ME_VPosToPos(ME_String *s, int nVPos);
int
ME_PosToVPos
(
ME_String
*
s
,
int
nPos
);
void
ME_StrDeleteV
(
ME_String
*
s
,
int
nVChar
,
int
nChars
);
/* smart helpers for A<->W conversions, they reserve/free memory and call MultiByte<->WideChar functions */
LPWSTR
ME_ToUnicode
(
HWND
hWnd
,
LPVOID
psz
);
void
ME_EndToUnicode
(
HWND
hWnd
,
LPVOID
psz
);
LPSTR
ME_ToAnsi
(
HWND
hWnd
,
LPVOID
psz
);
void
ME_EndToAnsi
(
HWND
hWnd
,
LPVOID
psz
);
LPWSTR
ME_ToUnicode
(
BOOL
unicode
,
LPVOID
psz
);
void
ME_EndToUnicode
(
BOOL
unicode
,
LPVOID
psz
);
static
inline
int
ME_IsWSpace
(
WCHAR
ch
)
{
...
...
dlls/riched20/string.c
View file @
592b53d0
...
...
@@ -345,9 +345,9 @@ ME_CallWordBreakProc(ME_TextEditor *editor, ME_String *str, INT start, INT code)
return
editor
->
pfnWordBreak
(
str
->
szData
,
start
,
str
->
nLen
,
code
);
}
LPWSTR
ME_ToUnicode
(
HWND
hWnd
,
LPVOID
psz
)
LPWSTR
ME_ToUnicode
(
BOOL
unicode
,
LPVOID
psz
)
{
if
(
IsWindowUnicode
(
hWnd
)
)
if
(
unicode
)
return
(
LPWSTR
)
psz
;
else
{
WCHAR
*
tmp
;
...
...
@@ -358,27 +358,8 @@ LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz)
}
}
void
ME_EndToUnicode
(
HWND
hWnd
,
LPVOID
psz
)
void
ME_EndToUnicode
(
BOOL
unicode
,
LPVOID
psz
)
{
if
(
!
IsWindowUnicode
(
hWnd
))
FREE_OBJ
(
psz
);
}
LPSTR
ME_ToAnsi
(
HWND
hWnd
,
LPVOID
psz
)
{
if
(
!
IsWindowUnicode
(
hWnd
))
return
(
LPSTR
)
psz
;
else
{
char
*
tmp
;
int
nChars
=
WideCharToMultiByte
(
CP_ACP
,
0
,
(
WCHAR
*
)
psz
,
-
1
,
NULL
,
0
,
NULL
,
NULL
);
if
((
tmp
=
ALLOC_N_OBJ
(
char
,
nChars
))
!=
NULL
)
WideCharToMultiByte
(
CP_ACP
,
0
,
(
WCHAR
*
)
psz
,
-
1
,
tmp
,
nChars
,
NULL
,
NULL
);
return
tmp
;
}
}
void
ME_EndToAnsi
(
HWND
hWnd
,
LPVOID
psz
)
{
if
(
!
IsWindowUnicode
(
hWnd
))
if
(
!
unicode
)
FREE_OBJ
(
psz
);
}
dlls/riched20/tests/editor.c
View file @
592b53d0
...
...
@@ -1578,15 +1578,12 @@ static void test_unicode_conversions(void)
ret = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)txt); \
ok(ret, "SendMessageA(WM_SETTEXT) error %u\n", GetLastError()); \
} while(0)
#define expect_textA(hwnd, txt
, todo
) \
#define expect_textA(hwnd, txt) \
do { \
memset(bufA, 0xAA, sizeof(bufA)); \
ret = SendMessageA(hwnd, WM_GETTEXT, 64, (LPARAM)bufA); \
ok(ret, "SendMessageA(WM_GETTEXT) error %u\n", GetLastError()); \
ret = lstrcmpA(bufA, txt); \
if (todo) \
todo_wine ok(!ret, "strings not match: expected %s got %s\n", txt, bufA); \
else \
ok(!ret, "strings not match: expected %s got %s\n", txt, bufA); \
} while(0)
...
...
@@ -1601,7 +1598,7 @@ static void test_unicode_conversions(void)
ret = SendMessageW(hwnd, WM_GETTEXT, 64, (LPARAM)bufW); \
ok(ret, "SendMessageW(WM_GETTEXT) error %u\n", GetLastError()); \
ret = lstrcmpW(bufW, txt); \
ok(!ret, "strings not match
\n"
); \
ok(!ret, "strings not match
expected[0] %x got[0] %x\n", txt[0], bufW[0]
); \
} while(0)
hwnd
=
CreateWindowExA
(
0
,
"RichEdit20W"
,
NULL
,
WS_POPUP
,
...
...
@@ -1614,11 +1611,30 @@ static void test_unicode_conversions(void)
else
ok
(
ret
,
"RichEdit20W should be unicode under NT
\n
"
);
memset
(
bufA
,
0xAA
,
sizeof
(
bufA
));
ret
=
SendMessageA
(
hwnd
,
WM_GETTEXT
,
64
,
(
LPARAM
)
bufA
);
ok
(
!
ret
,
"empty richedit should return 0, got %d
\n
"
,
ret
);
ok
(
!*
bufA
,
"empty richedit should return empty string, got %s
\n
"
,
bufA
);
ret
=
SendMessageA
(
hwnd
,
WM_CHAR
,
(
WPARAM
)
textW
[
0
],
0
);
ok
(
!
ret
,
"SendMessageA(WM_CHAR) should return 0, got %d
\n
"
,
ret
);
expect_textA
(
hwnd
,
"t"
);
ret
=
SendMessageA
(
hwnd
,
WM_CHAR
,
(
WPARAM
)
textA
[
1
],
0
);
ok
(
!
ret
,
"SendMessageA(WM_CHAR) should return 0, got %d
\n
"
,
ret
);
expect_textA
(
hwnd
,
"te"
);
set_textA
(
hwnd
,
NULL
);
memset
(
bufA
,
0xAA
,
sizeof
(
bufA
));
ret
=
SendMessageA
(
hwnd
,
WM_GETTEXT
,
64
,
(
LPARAM
)
bufA
);
ok
(
!
ret
,
"empty richedit should return 0, got %d
\n
"
,
ret
);
ok
(
!*
bufA
,
"empty richedit should return empty string, got %s
\n
"
,
bufA
);
if
(
is_win9x
)
set_textA
(
hwnd
,
textW
);
else
set_textA
(
hwnd
,
textA
);
expect_textA
(
hwnd
,
textA
,
is_win9x
);
expect_textA
(
hwnd
,
textA
);
if
(
!
is_win9x
)
{
...
...
@@ -1635,7 +1651,7 @@ static void test_unicode_conversions(void)
ok
(
!
ret
,
"RichEdit20A should NOT be unicode
\n
"
);
set_textA
(
hwnd
,
textA
);
expect_textA
(
hwnd
,
textA
,
FALSE
);
expect_textA
(
hwnd
,
textA
);
if
(
!
is_win9x
)
{
...
...
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