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
163e9c5b
Commit
163e9c5b
authored
May 25, 2022
by
Connor McAdams
Committed by
Alexandre Julliard
Jun 14, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
uiautomationcore: Implement NavigateDirection_{Previous/Next}Sibling for MSAA providers.
Signed-off-by:
Connor McAdams
<
cmcadams@codeweavers.com
>
parent
66b6786e
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
194 additions
and
77 deletions
+194
-77
uiautomation.c
dlls/uiautomationcore/tests/uiautomation.c
+62
-70
uia_provider.c
dlls/uiautomationcore/uia_provider.c
+132
-7
No files found.
dlls/uiautomationcore/tests/uiautomation.c
View file @
163e9c5b
...
...
@@ -1112,13 +1112,13 @@ static void test_uia_prov_from_acc_navigation(void)
elfrag2
=
(
void
*
)
0xdeadbeef
;
hr
=
IRawElementProviderFragment_Navigate
(
elfrag
,
NavigateDirection_NextSibling
,
&
elfrag2
);
ok
(
Accessible
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!
elfrag2
,
"elfrag2 != NULL
\n
"
);
elfrag2
=
(
void
*
)
0xdeadbeef
;
hr
=
IRawElementProviderFragment_Navigate
(
elfrag
,
NavigateDirection_PreviousSibling
,
&
elfrag2
);
ok
(
Accessible
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!
elfrag2
,
"elfrag2 != NULL
\n
"
);
/*
...
...
@@ -1149,18 +1149,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT
(
Accessible_get_accChild
);
SET_EXPECT
(
Accessible_get_accState
);
hr
=
IRawElementProviderFragment_Navigate
(
elfrag2
,
NavigateDirection_NextSibling
,
&
elfrag3
);
todo_wine
ok
(
Accessible
.
ref
==
5
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
todo_wine
ok
(
!!
elfrag3
,
"elfrag2 == NULL
\n
"
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChildCount
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChild
);
todo_wine
CHECK_CALLED
(
Accessible_get_accState
);
if
(
elfrag3
)
{
check_fragment_acc
(
elfrag3
,
&
Accessible
.
IAccessible_iface
,
3
);
IRawElementProviderFragment_Release
(
elfrag3
);
ok
(
Accessible
.
ref
==
3
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
}
ok
(
Accessible
.
ref
==
5
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!!
elfrag3
,
"elfrag2 == NULL
\n
"
);
CHECK_CALLED
(
Accessible_get_accChildCount
);
CHECK_CALLED
(
Accessible_get_accChild
);
CHECK_CALLED
(
Accessible_get_accState
);
check_fragment_acc
(
elfrag3
,
&
Accessible
.
IAccessible_iface
,
3
);
IRawElementProviderFragment_Release
(
elfrag3
);
ok
(
Accessible
.
ref
==
3
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
IRawElementProviderFragment_Release
(
elfrag2
);
ok
(
Accessible_child
.
ref
==
1
,
"Unexpected refcnt %ld
\n
"
,
Accessible_child
.
ref
);
ok
(
Accessible
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
...
...
@@ -1206,19 +1204,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT
(
Accessible_get_accChild
);
SET_EXPECT
(
Accessible_get_accState
);
hr
=
IRawElementProviderFragment_Navigate
(
elfrag2
,
NavigateDirection_PreviousSibling
,
&
elfrag3
);
todo_wine
ok
(
Accessible
.
ref
==
5
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
todo_wine
ok
(
!!
elfrag3
,
"elfrag2 == NULL
\n
"
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChildCount
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChild
);
todo_wine
CHECK_CALLED
(
Accessible_get_accState
);
if
(
elfrag3
)
{
check_fragment_acc
(
elfrag3
,
&
Accessible
.
IAccessible_iface
,
3
);
IRawElementProviderFragment_Release
(
elfrag3
);
ok
(
Accessible
.
ref
==
3
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
}
ok
(
Accessible
.
ref
==
5
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!!
elfrag3
,
"elfrag2 == NULL
\n
"
);
CHECK_CALLED
(
Accessible_get_accChildCount
);
CHECK_CALLED
(
Accessible_get_accChild
);
CHECK_CALLED
(
Accessible_get_accState
);
check_fragment_acc
(
elfrag3
,
&
Accessible
.
IAccessible_iface
,
3
);
IRawElementProviderFragment_Release
(
elfrag3
);
ok
(
Accessible
.
ref
==
3
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
IRawElementProviderFragment_Release
(
elfrag2
);
ok
(
Accessible_child2
.
ref
==
1
,
"Unexpected refcnt %ld
\n
"
,
Accessible_child2
.
ref
);
ok
(
Accessible
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
...
...
@@ -1338,21 +1333,21 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT
(
Accessible_child2_accLocation
);
SET_EXPECT
(
Accessible_child2_get_accName
);
hr
=
IRawElementProviderFragment_Navigate
(
elfrag
,
NavigateDirection_NextSibling
,
&
elfrag2
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!
elfrag2
,
"elfrag2 != NULL
\n
"
);
todo_wine
CHECK_CALLED_MULTI
(
Accessible_get_accChildCount
,
5
);
todo_wine
CHECK_CALLED_MULTI
(
Accessible_get_accChild
,
4
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accParent
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accRole
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accState
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accChildCount
);
todo_wine
CHECK_CALLED
(
Accessible_child_accLocation
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accName
);
todo_wine
CHECK_CALLED
(
Accessible_child2_get_accRole
);
todo_wine
CHECK_CALLED
(
Accessible_child2_get_accState
);
todo_wine
CHECK_CALLED
(
Accessible_child2_get_accChildCount
);
todo_wine
CHECK_CALLED
(
Accessible_child2_accLocation
);
todo_wine
CHECK_CALLED
(
Accessible_child2_get_accName
);
CHECK_CALLED_MULTI
(
Accessible_get_accChildCount
,
5
);
CHECK_CALLED_MULTI
(
Accessible_get_accChild
,
4
);
CHECK_CALLED
(
Accessible_child_get_accParent
);
CHECK_CALLED
(
Accessible_child_get_accRole
);
CHECK_CALLED
(
Accessible_child_get_accState
);
CHECK_CALLED
(
Accessible_child_get_accChildCount
);
CHECK_CALLED
(
Accessible_child_accLocation
);
CHECK_CALLED
(
Accessible_child_get_accName
);
CHECK_CALLED
(
Accessible_child2_get_accRole
);
CHECK_CALLED
(
Accessible_child2_get_accState
);
CHECK_CALLED
(
Accessible_child2_get_accChildCount
);
CHECK_CALLED
(
Accessible_child2_accLocation
);
CHECK_CALLED
(
Accessible_child2_get_accName
);
/* Now they have a role mismatch, we can determine our position. */
set_accessible_props
(
&
Accessible_child2
,
ROLE_SYSTEM_DOCUMENT
,
STATE_SYSTEM_FOCUSABLE
,
1
,
...
...
@@ -1369,27 +1364,27 @@ static void test_uia_prov_from_acc_navigation(void)
* Even though we didn't get a new fragment, now that we know our
* position, a reference is added to the parent IAccessible.
*/
todo_wine
ok
(
Accessible
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
Accessible
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!
elfrag2
,
"elfrag2 != NULL
\n
"
);
todo_wine
CHECK_CALLED_MULTI
(
Accessible_get_accChildCount
,
6
);
todo_wine
CHECK_CALLED_MULTI
(
Accessible_get_accChild
,
5
);
todo_wine
CHECK_CALLED
(
Accessible_get_accState
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accParent
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accRole
);
todo_wine
CHECK_CALLED
(
Accessible_child2_get_accRole
);
CHECK_CALLED_MULTI
(
Accessible_get_accChildCount
,
6
);
CHECK_CALLED_MULTI
(
Accessible_get_accChild
,
5
);
CHECK_CALLED
(
Accessible_get_accState
);
CHECK_CALLED
(
Accessible_child_get_accParent
);
CHECK_CALLED
(
Accessible_child_get_accRole
);
CHECK_CALLED
(
Accessible_child2_get_accRole
);
/* Now that we know our position, no extra nav work. */
SET_EXPECT
(
Accessible_get_accChildCount
);
SET_EXPECT
(
Accessible_get_accChild
);
SET_EXPECT
(
Accessible_get_accState
);
hr
=
IRawElementProviderFragment_Navigate
(
elfrag
,
NavigateDirection_NextSibling
,
&
elfrag2
);
todo_wine
ok
(
Accessible
.
ref
==
4
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
todo_wine
ok
(
!!
elfrag2
,
"elfrag2 == NULL
\n
"
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChildCount
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChild
);
todo_wine
CHECK_CALLED
(
Accessible_get_accState
);
ok
(
Accessible
.
ref
==
4
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!!
elfrag2
,
"elfrag2 == NULL
\n
"
);
CHECK_CALLED
(
Accessible_get_accChildCount
);
CHECK_CALLED
(
Accessible_get_accChild
);
CHECK_CALLED
(
Accessible_get_accState
);
if
(
elfrag2
)
{
check_fragment_acc
(
elfrag2
,
&
Accessible
.
IAccessible_iface
,
3
);
...
...
@@ -1449,23 +1444,20 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT
(
Accessible_child_accNavigate
);
SET_EXPECT
(
Accessible_child_get_accParent
);
hr
=
IRawElementProviderFragment_Navigate
(
elfrag
,
NavigateDirection_NextSibling
,
&
elfrag2
);
todo_wine
ok
(
Accessible_child
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible_child
.
ref
);
todo_wine
ok
(
Accessible
.
ref
==
4
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
todo_wine
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
todo_wine
ok
(
!!
elfrag2
,
"elfrag2 == NULL
\n
"
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChildCount
);
todo_wine
CHECK_CALLED
(
Accessible_get_accChild
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accState
);
todo_wine
CHECK_CALLED
(
Accessible_child_accNavigate
);
todo_wine
CHECK_CALLED
(
Accessible_child_get_accParent
);
if
(
elfrag2
)
{
check_fragment_acc
(
elfrag2
,
&
Accessible_child
.
IAccessible_iface
,
CHILDID_SELF
);
IRawElementProviderFragment_Release
(
elfrag2
);
ok
(
Accessible_child
.
ref
==
1
,
"Unexpected refcnt %ld
\n
"
,
Accessible_child
.
ref
);
ok
(
Accessible
.
ref
==
3
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
}
ok
(
Accessible_child
.
ref
==
2
,
"Unexpected refcnt %ld
\n
"
,
Accessible_child
.
ref
);
ok
(
Accessible
.
ref
==
4
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
ok
(
hr
==
S_OK
,
"Unexpected hr %#lx.
\n
"
,
hr
);
ok
(
!!
elfrag2
,
"elfrag2 == NULL
\n
"
);
CHECK_CALLED
(
Accessible_get_accChildCount
);
CHECK_CALLED
(
Accessible_get_accChild
);
CHECK_CALLED
(
Accessible_child_get_accState
);
CHECK_CALLED
(
Accessible_child_accNavigate
);
CHECK_CALLED
(
Accessible_child_get_accParent
);
check_fragment_acc
(
elfrag2
,
&
Accessible_child
.
IAccessible_iface
,
CHILDID_SELF
);
IRawElementProviderFragment_Release
(
elfrag2
);
ok
(
Accessible_child
.
ref
==
1
,
"Unexpected refcnt %ld
\n
"
,
Accessible_child
.
ref
);
ok
(
Accessible
.
ref
==
3
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
IRawElementProviderFragment_Release
(
elfrag
);
IRawElementProviderSimple_Release
(
elprov
);
ok
(
Accessible
.
ref
==
1
,
"Unexpected refcnt %ld
\n
"
,
Accessible
.
ref
);
...
...
dlls/uiautomationcore/uia_provider.c
View file @
163e9c5b
...
...
@@ -223,7 +223,7 @@ static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
#define DIR_FORWARD 0
#define DIR_REVERSE 1
static
HRESULT
msaa_acc_get_next_child
(
IAccessible
*
acc
,
LONG
start_pos
,
LONG
direction
,
IAccessible
**
child
,
LONG
*
child_id
,
LONG
*
child_pos
)
IAccessible
**
child
,
LONG
*
child_id
,
LONG
*
child_pos
,
BOOL
check_visible
)
{
LONG
child_count
,
cur_pos
;
IDispatch
*
disp
;
...
...
@@ -246,7 +246,7 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
if
(
hr
==
S_FALSE
)
{
if
(
!
msaa_check_acc_state
(
acc
,
cid
,
STATE_SYSTEM_INVISIBLE
))
if
(
!
check_visible
||
!
msaa_check_acc_state
(
acc
,
cid
,
STATE_SYSTEM_INVISIBLE
))
{
*
child
=
acc
;
*
child_id
=
*
child_pos
=
cur_pos
;
...
...
@@ -264,7 +264,7 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
break
;
variant_init_i4
(
&
cid
,
CHILDID_SELF
);
if
(
!
msaa_check_acc_state
(
acc_child
,
cid
,
STATE_SYSTEM_INVISIBLE
))
if
(
!
check_visible
||
!
msaa_check_acc_state
(
acc_child
,
cid
,
STATE_SYSTEM_INVISIBLE
))
{
*
child
=
acc_child
;
*
child_id
=
CHILDID_SELF
;
...
...
@@ -287,6 +287,86 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
return
hr
;
}
static
HRESULT
msaa_acc_get_child_pos
(
IAccessible
*
acc
,
IAccessible
**
out_parent
,
LONG
*
out_child_pos
)
{
LONG
child_count
,
child_id
,
child_pos
,
match_pos
;
IAccessible
*
child
,
*
parent
,
*
match
,
**
children
;
HRESULT
hr
;
int
i
;
*
out_parent
=
NULL
;
*
out_child_pos
=
0
;
hr
=
msaa_acc_get_parent
(
acc
,
&
parent
);
if
(
FAILED
(
hr
)
||
!
parent
)
return
hr
;
hr
=
IAccessible_get_accChildCount
(
parent
,
&
child_count
);
if
(
FAILED
(
hr
)
||
!
child_count
)
{
IAccessible_Release
(
parent
);
return
hr
;
}
children
=
heap_alloc_zero
(
sizeof
(
*
children
)
*
child_count
);
if
(
!
children
)
return
E_OUTOFMEMORY
;
match
=
NULL
;
for
(
i
=
0
;
i
<
child_count
;
i
++
)
{
hr
=
msaa_acc_get_next_child
(
parent
,
i
+
1
,
DIR_FORWARD
,
&
child
,
&
child_id
,
&
child_pos
,
FALSE
);
if
(
FAILED
(
hr
)
||
!
child
)
goto
exit
;
if
(
child
!=
parent
)
children
[
i
]
=
child
;
else
IAccessible_Release
(
child
);
}
for
(
i
=
0
;
i
<
child_count
;
i
++
)
{
if
(
!
children
[
i
])
continue
;
if
(
msaa_acc_compare
(
acc
,
children
[
i
]))
{
if
(
!
match
)
{
match
=
children
[
i
];
match_pos
=
i
+
1
;
}
/* Can't have more than one IAccessible match. */
else
{
match
=
NULL
;
match_pos
=
0
;
break
;
}
}
}
exit:
if
(
match
)
{
*
out_parent
=
parent
;
*
out_child_pos
=
match_pos
;
}
else
IAccessible_Release
(
parent
);
for
(
i
=
0
;
i
<
child_count
;
i
++
)
{
if
(
children
[
i
])
IAccessible_Release
(
children
[
i
]);
}
heap_free
(
children
);
return
hr
;
}
static
LONG
msaa_role_to_uia_control_type
(
LONG
role
)
{
switch
(
role
)
...
...
@@ -649,10 +729,10 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
if
(
direction
==
NavigateDirection_FirstChild
)
hr
=
msaa_acc_get_next_child
(
msaa_prov
->
acc
,
1
,
DIR_FORWARD
,
&
acc
,
&
child_id
,
&
child_pos
);
&
child_pos
,
TRUE
);
else
hr
=
msaa_acc_get_next_child
(
msaa_prov
->
acc
,
child_count
,
DIR_REVERSE
,
&
acc
,
&
child_id
,
&
child_pos
);
&
child_pos
,
TRUE
);
if
(
FAILED
(
hr
)
||
!
acc
)
break
;
...
...
@@ -676,8 +756,53 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
case
NavigateDirection_NextSibling
:
case
NavigateDirection_PreviousSibling
:
FIXME
(
"Unimplemented NavigateDirection %d
\n
"
,
direction
);
return
E_NOTIMPL
;
if
(
msaa_check_root_acc
(
msaa_prov
))
break
;
if
(
!
msaa_prov
->
parent
)
{
if
(
V_I4
(
&
msaa_prov
->
cid
)
!=
CHILDID_SELF
)
{
msaa_prov
->
parent
=
msaa_prov
->
acc
;
IAccessible_AddRef
(
msaa_prov
->
acc
);
msaa_prov
->
child_pos
=
V_I4
(
&
msaa_prov
->
cid
);
}
else
{
hr
=
msaa_acc_get_child_pos
(
msaa_prov
->
acc
,
&
acc
,
&
child_pos
);
if
(
FAILED
(
hr
)
||
!
acc
)
break
;
msaa_prov
->
parent
=
acc
;
msaa_prov
->
child_pos
=
child_pos
;
}
}
if
(
direction
==
NavigateDirection_NextSibling
)
hr
=
msaa_acc_get_next_child
(
msaa_prov
->
parent
,
msaa_prov
->
child_pos
+
1
,
DIR_FORWARD
,
&
acc
,
&
child_id
,
&
child_pos
,
TRUE
);
else
hr
=
msaa_acc_get_next_child
(
msaa_prov
->
parent
,
msaa_prov
->
child_pos
-
1
,
DIR_REVERSE
,
&
acc
,
&
child_id
,
&
child_pos
,
TRUE
);
if
(
FAILED
(
hr
)
||
!
acc
)
break
;
hr
=
UiaProviderFromIAccessible
(
acc
,
child_id
,
0
,
&
elprov
);
if
(
SUCCEEDED
(
hr
))
{
struct
msaa_provider
*
prov
=
impl_from_msaa_provider
(
elprov
);
*
ret_val
=
&
prov
->
IRawElementProviderFragment_iface
;
prov
->
parent
=
msaa_prov
->
parent
;
IAccessible_AddRef
(
msaa_prov
->
parent
);
if
(
acc
!=
msaa_prov
->
acc
)
prov
->
child_pos
=
child_pos
;
else
prov
->
child_pos
=
child_id
;
}
IAccessible_Release
(
acc
);
break
;
default:
FIXME
(
"Invalid NavigateDirection %d
\n
"
,
direction
);
...
...
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