Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
wine
Manage
Activity
Members
Labels
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Terraform modules
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
catsout
wine
Commits
9bffcfc5
Commit
9bffcfc5
authored
25 years ago
by
Juergen Schmied
Committed by
Alexandre Julliard
25 years ago
Browse files
Options
Downloads
Patches
Plain Diff
New interface: DropSource.
parent
3539693b
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
dlls/shell32/shlview.c
+183
-72
183 additions, 72 deletions
dlls/shell32/shlview.c
with
183 additions
and
72 deletions
dlls/shell32/shlview.c
+
183
−
72
View file @
9bffcfc5
/*
* ShellView
*
* Copyright 1998 <juergen.schmied@metronet.de>
* Copyright 1998
,1999
<juergen.schmied@metronet.de>
*
* FIXME: when the ShellView_WndProc gets a WM_NCDESTROY should we do a
* Release() ???
...
...
@@ -19,7 +19,8 @@
#include
"wine/obj_shellbrowser.h"
#include
"wine/obj_dockingwindowframe.h"
#include
"wine/obj_extracticon.h"
#include
"wine/obj_dragdrop.h"
#include
"wine/undocshell.h"
#include
"shresdef.h"
#include
"spy.h"
#include
"debugtools.h"
...
...
@@ -36,6 +37,7 @@ typedef struct
DWORD
ref
;
ICOM_VTABLE
(
IOleCommandTarget
)
*
lpvtblOleCommandTarget
;
ICOM_VTABLE
(
IDropTarget
)
*
lpvtblDropTarget
;
ICOM_VTABLE
(
IDropSource
)
*
lpvtblDropSource
;
ICOM_VTABLE
(
IViewObject
)
*
lpvtblViewObject
;
IShellFolder
*
pSFParent
;
IShellBrowser
*
pShellBrowser
;
...
...
@@ -60,6 +62,10 @@ static struct ICOM_VTABLE(IDropTarget) dtvt;
#define _IDropTarget_Offset ((int)(&(((IShellViewImpl*)0)->lpvtblDropTarget)))
#define _ICOM_THIS_From_IDropTarget(class, name) class* This = (class*)(((char*)name)-_IDropTarget_Offset);
static
struct
ICOM_VTABLE
(
IDropSource
)
dsvt
;
#define _IDropSource_Offset ((int)(&(((IShellViewImpl*)0)->lpvtblDropSource)))
#define _ICOM_THIS_From_IDropSource(class, name) class* This = (class*)(((char*)name)-_IDropSource_Offset);
static
struct
ICOM_VTABLE
(
IViewObject
)
vovt
;
#define _IViewObject_Offset ((int)(&(((IShellViewImpl*)0)->lpvtblViewObject)))
#define _ICOM_THIS_From_IViewObject(class, name) class* This = (class*)(((char*)name)-_IViewObject_Offset);
...
...
@@ -81,25 +87,27 @@ static struct ICOM_VTABLE(IViewObject) vovt;
extern
void
WINAPI
_InsertMenuItem
(
HMENU
hmenu
,
UINT
indexMenu
,
BOOL
fByPosition
,
UINT
wID
,
UINT
fType
,
LPSTR
dwTypeData
,
UINT
fState
);
/*
Items merged into the toolbar and and the filemenu
*/
typedef
struct
{
int
idCommand
;
int
iImage
;
int
idButtonString
;
int
idMenuString
;
int
nStringOffset
;
BYTE
bState
;
BYTE
bStyle
;
}
MYTOOLINFO
,
*
LPMYTOOLINFO
;
MYTOOLINFO
g_Tools
[]
=
{
{
IDM_VIEW_FILES
,
0
,
IDS_TB_VIEW_FILES
,
IDS_MI_VIEW_FILES
,
0
,
TBSTATE_ENABLED
,
TBSTYLE_BUTTON
},
{
-
1
,
0
,
0
,
0
,
0
,
0
,
0
}
MYTOOLINFO
Tools
[]
=
{
{
FCIDM_SHVIEW_BIGICON
,
0
,
0
,
IDS_VIEW_LARGE
,
TBSTATE_ENABLED
,
TBSTYLE_BUTTON
},
{
FCIDM_SHVIEW_SMALLICON
,
0
,
0
,
IDS_VIEW_SMALL
,
TBSTATE_ENABLED
,
TBSTYLE_BUTTON
},
{
FCIDM_SHVIEW_LISTVIEW
,
0
,
0
,
IDS_VIEW_LIST
,
TBSTATE_ENABLED
,
TBSTYLE_BUTTON
},
{
FCIDM_SHVIEW_REPORTVIEW
,
0
,
0
,
IDS_VIEW_DETAILS
,
TBSTATE_ENABLED
,
TBSTYLE_BUTTON
},
{
-
1
,
0
,
0
,
0
,
0
,
0
}
};
/*
BOOL g_bViewKeys;
BOOL g_bShowIDW;
*/
typedef
void
(
CALLBACK
*
PFNSHGETSETTINGSPROC
)(
LPSHELLFLAGSTATE
lpsfs
,
DWORD
dwMask
);
/**********************************************************
...
...
@@ -112,6 +120,7 @@ IShellView * IShellView_Constructor( IShellFolder * pFolder)
sv
->
lpvtbl
=&
svvt
;
sv
->
lpvtblOleCommandTarget
=&
ctvt
;
sv
->
lpvtblDropTarget
=&
dtvt
;
sv
->
lpvtblDropSource
=&
dsvt
;
sv
->
lpvtblViewObject
=&
vovt
;
sv
->
pSFParent
=
pFolder
;
...
...
@@ -251,11 +260,6 @@ static BOOL ShellView_CreateList (IShellViewImpl * This)
/**********************************************************
* ShellView_InitList()
*/
int
nColumn1
=
120
;
/* width of column */
int
nColumn2
=
80
;
int
nColumn3
=
170
;
int
nColumn4
=
60
;
static
BOOL
ShellView_InitList
(
IShellViewImpl
*
This
)
{
LVCOLUMNA
lvColumn
;
...
...
@@ -270,21 +274,20 @@ static BOOL ShellView_InitList(IShellViewImpl * This)
lvColumn
.
fmt
=
LVCFMT_LEFT
;
lvColumn
.
pszText
=
szString
;
lvColumn
.
cx
=
nColumn1
;
strcpy
(
szString
,
"File"
);
/*LoadStringA(shell32_hInstance, IDS_COLUMN1, szString, sizeof(szString));*/
lvColumn
.
cx
=
120
;
LoadStringA
(
shell32_hInstance
,
IDS_SHV_COLUMN1
,
szString
,
sizeof
(
szString
));
ListView_InsertColumnA
(
This
->
hWndList
,
0
,
&
lvColumn
);
lvColumn
.
cx
=
nColumn2
;
strcpy
(
szString
,
"S
ize
"
);
lvColumn
.
cx
=
80
;
LoadStringA
(
shell32_hInstance
,
IDS_SHV_COLUMN2
,
szString
,
s
ize
of
(
szString
)
);
ListView_InsertColumnA
(
This
->
hWndList
,
1
,
&
lvColumn
);
lvColumn
.
cx
=
nColumn3
;
strcpy
(
szString
,
"Type"
);
lvColumn
.
cx
=
170
;
LoadStringA
(
shell32_hInstance
,
IDS_SHV_COLUMN3
,
szString
,
sizeof
(
szString
)
);
ListView_InsertColumnA
(
This
->
hWndList
,
2
,
&
lvColumn
);
lvColumn
.
cx
=
nColumn4
;
strcpy
(
szString
,
"Modified"
);
lvColumn
.
cx
=
60
;
LoadStringA
(
shell32_hInstance
,
IDS_SHV_COLUMN4
,
szString
,
sizeof
(
szString
)
);
ListView_InsertColumnA
(
This
->
hWndList
,
3
,
&
lvColumn
);
ListView_SetImageList
(
This
->
hWndList
,
ShellSmallIconList
,
LVSIL_SMALL
);
...
...
@@ -393,6 +396,9 @@ static HRESULT ShellView_FillList(IShellViewImpl * This)
*/
static
LRESULT
ShellView_OnCreate
(
IShellViewImpl
*
This
)
{
#if 0
IDropTarget* pdt;
#endif
TRACE
(
"%p
\n
"
,
This
);
if
(
ShellView_CreateList
(
This
))
...
...
@@ -402,6 +408,18 @@ static LRESULT ShellView_OnCreate(IShellViewImpl * This)
ShellView_FillList
(
This
);
}
}
#if 0
/* This makes a chrash since we havn't called OleInititialize. But if we
do this call in DllMain it breaks some apps. The native shell32 does not
call OleInitialize and not even depend on ole32.dll.
But for some apps it works...*/
if (SUCCEEDED(IShellFolder_CreateViewObject(This->pSFParent, This->hWnd, &IID_IDropTarget, (LPVOID*)&pdt)))
{
pRegisterDragDrop(This->hWnd, pdt);
IDropTarget_Release(pdt);
}
#endif
return
S_OK
;
}
...
...
@@ -418,29 +436,32 @@ static HMENU ShellView_BuildFileMenu(IShellViewImpl * This)
int
nTools
,
i
;
HMENU
hSubMenu
;
TRACE
(
"(%p)
semi-stub
\n
"
,
This
);
TRACE
(
"(%p)
\n
"
,
This
);
hSubMenu
=
CreatePopupMenu
();
if
(
hSubMenu
)
{
/*get the number of items in our global array*/
for
(
nTools
=
0
;
g_
Tools
[
nTools
].
idCommand
!=
-
1
;
nTools
++
){}
for
(
nTools
=
0
;
Tools
[
nTools
].
idCommand
!=
-
1
;
nTools
++
){}
/*add the menu items*/
for
(
i
=
0
;
i
<
nTools
;
i
++
)
{
strcpy
(
szText
,
"dummy BuildFileMenu"
);
{
LoadStringA
(
shell32_hInstance
,
Tools
[
i
].
idMenuString
,
szText
,
MAX_PATH
);
ZeroMemory
(
&
mii
,
sizeof
(
mii
));
mii
.
cbSize
=
sizeof
(
mii
);
mii
.
fMask
=
MIIM_TYPE
|
MIIM_ID
|
MIIM_STATE
;
if
(
TBSTYLE_SEP
!=
g_Tools
[
i
].
bStyle
)
/* no seperator*/
{
mii
.
fType
=
MFT_STRING
;
if
(
TBSTYLE_SEP
!=
Tools
[
i
].
bStyle
)
/* no seperator*/
{
mii
.
fType
=
MFT_STRING
;
mii
.
fState
=
MFS_ENABLED
;
mii
.
dwTypeData
=
szText
;
mii
.
wID
=
g_
Tools
[
i
].
idCommand
;
mii
.
wID
=
Tools
[
i
].
idCommand
;
}
else
{
mii
.
fType
=
MFT_SEPARATOR
;
{
mii
.
fType
=
MFT_SEPARATOR
;
}
/* tack This item onto the end of the menu */
InsertMenuItemA
(
hSubMenu
,
(
UINT
)
-
1
,
TRUE
,
&
mii
);
...
...
@@ -617,7 +638,7 @@ static void ShellView_DoContextMenu(IShellViewImpl * This, WORD x, WORD y, BOOL
uCommand
=
TrackPopupMenu
(
hMenu
,
TPM_LEFTALIGN
|
TPM_RETURNCMD
,
x
,
y
,
0
,
This
->
hWnd
,
NULL
);
DestroyMenu
(
hMenu
);
FIXM
E
(
"-- (%p)->(uCommand=0x%08x )
\n
"
,
This
,
uCommand
);
TRAC
E
(
"-- (%p)->(uCommand=0x%08x )
\n
"
,
This
,
uCommand
);
ZeroMemory
(
&
cmi
,
sizeof
(
cmi
));
cmi
.
cbSize
=
sizeof
(
cmi
);
...
...
@@ -823,7 +844,6 @@ static LRESULT ShellView_OnNotify(IShellViewImpl * This, UINT CtlID, LPNMHDR lpn
{
NM_LISTVIEW
*
lpnmlv
=
(
NM_LISTVIEW
*
)
lpnmh
;
NMLVDISPINFOA
*
lpdi
=
(
NMLVDISPINFOA
*
)
lpnmh
;
LPITEMIDLIST
pidl
;
DWORD
dwCursor
;
STRRET
str
;
TRACE
(
"%p CtlID=%u lpnmh->code=%x
\n
"
,
This
,
CtlID
,
lpnmh
->
code
);
...
...
@@ -857,12 +877,6 @@ static LRESULT ShellView_OnNotify(IShellViewImpl * This, UINT CtlID, LPNMHDR lpn
ShellView_DoContextMenu
(
This
,
0
,
0
,
TRUE
);
break
;
case
NM_RCLICK
:
TRACE
(
"-- NM_RCLICK %p
\n
"
,
This
);
dwCursor
=
GetMessagePos
();
ShellView_DoContextMenu
(
This
,
LOWORD
(
dwCursor
),
HIWORD
(
dwCursor
),
FALSE
);
break
;
case
LVN_GETDISPINFOA
:
TRACE
(
"-- LVN_GETDISPINFOA %p
\n
"
,
This
);
pidl
=
(
LPITEMIDLIST
)
lpdi
->
item
.
lParam
;
...
...
@@ -878,8 +892,8 @@ static LRESULT ShellView_OnNotify(IShellViewImpl * This, UINT CtlID, LPNMHDR lpn
case
2
:
/* extension */
{
char
sTemp
[
64
];
if
(
_ILGetExtension
(
pidl
,
sTemp
,
64
))
{
if
(
!
(
HCR_MapTypeToValue
(
sTemp
,
sTemp
,
64
)
&&
HCR_MapTypeToValue
(
sTemp
,
lpdi
->
item
.
pszText
,
lpdi
->
item
.
cchTextMax
)))
{
if
(
!
(
HCR_MapTypeToValue
(
sTemp
,
sTemp
,
64
,
TRUE
)
&&
HCR_MapTypeToValue
(
sTemp
,
lpdi
->
item
.
pszText
,
lpdi
->
item
.
cchTextMax
,
FALSE
)))
{
lstrcpynA
(
lpdi
->
item
.
pszText
,
sTemp
,
lpdi
->
item
.
cchTextMax
);
strncat
(
lpdi
->
item
.
pszText
,
"-file"
,
lpdi
->
item
.
cchTextMax
);
}
...
...
@@ -911,34 +925,65 @@ static LRESULT ShellView_OnNotify(IShellViewImpl * This, UINT CtlID, LPNMHDR lpn
}
}
else
/*the item text is being requested*/
{
if
(
lpdi
->
item
.
mask
&
LVIF_TEXT
)
/*is the text being requested?*/
{
if
(
SUCCEEDED
(
IShellFolder_GetDisplayNameOf
(
This
->
pSFParent
,
pidl
,
SHGDN_NORMAL
|
SHGDN_INFOLDER
,
&
str
)))
{
if
(
STRRET_WSTR
==
str
.
uType
)
{
WideCharToLocal
(
lpdi
->
item
.
pszText
,
str
.
u
.
pOleStr
,
lpdi
->
item
.
cchTextMax
);
SHFree
(
str
.
u
.
pOleStr
);
}
else
if
(
STRRET_CSTRA
==
str
.
uType
)
{
lstrcpynA
(
lpdi
->
item
.
pszText
,
str
.
u
.
cStr
,
lpdi
->
item
.
cchTextMax
);
}
else
{
FIXME
(
"type wrong
\n
"
);
}
{
if
(
lpdi
->
item
.
mask
&
LVIF_TEXT
)
/*is the text being requested?*/
{
if
(
SUCCEEDED
(
IShellFolder_GetDisplayNameOf
(
This
->
pSFParent
,
pidl
,
SHGDN_NORMAL
|
SHGDN_INFOLDER
,
&
str
)))
{
StrRetToStrNA
(
lpdi
->
item
.
pszText
,
lpdi
->
item
.
cchTextMax
,
&
str
,
pidl
);
}
TRACE
(
"-- text=%s
\n
"
,
lpdi
->
item
.
pszText
);
}
if
(
lpdi
->
item
.
mask
&
LVIF_IMAGE
)
/*is the image being requested?*/
{
lpdi
->
item
.
iImage
=
SHMapPIDLToSystemImageListIndex
(
This
->
pSFParent
,
pidl
,
0
);
{
lpdi
->
item
.
iImage
=
SHMapPIDLToSystemImageListIndex
(
This
->
pSFParent
,
pidl
,
0
);
}
}
break
;
case
LVN_ITEMCHANGED
:
TRACE
(
"-- LVN_ITEMCHANGED %p
\n
"
,
This
);
/* ShellView_GetSelections(This); */
OnStateChange
(
This
,
CDBOSC_SELCHANGE
);
/* the browser will get the IDataObject now */
break
;
case
LVN_BEGINDRAG
:
case
LVN_BEGINRDRAG
:
if
(
ShellView_GetSelections
(
This
))
{
IDataObject
*
pda
;
DWORD
dwAttributes
;
DWORD
dwEffect
=
DROPEFFECT_COPY
|
DROPEFFECT_MOVE
;
if
(
SUCCEEDED
(
IShellFolder_GetUIObjectOf
(
This
->
pSFParent
,
This
->
hWnd
,
This
->
cidl
,
This
->
apidl
,
&
IID_IDataObject
,
0
,(
LPVOID
*
)
&
pda
)))
{
IDropSource
*
pds
=
(
IDropSource
*
)
&
(
This
->
lpvtblDropSource
);
/* own DropSource interface */
if
(
SUCCEEDED
(
IShellFolder_GetAttributesOf
(
This
->
pSFParent
,
This
->
cidl
,
This
->
apidl
,
&
dwAttributes
)))
{
if
(
dwAttributes
&
SFGAO_CANLINK
)
{
dwEffect
|=
DROPEFFECT_LINK
;
}
}
if
(
pds
)
{
DWORD
dwEffect
;
pDoDragDrop
(
pda
,
pds
,
dwEffect
,
&
dwEffect
);
}
IDataObject_Release
(
pda
);
}
}
break
;
case
LVN_BEGINLABELEDITA
:
case
LVN_ENDLABELEDITA
:
FIXME
(
"labeledit
\n
"
);
break
;
default:
TRACE
(
"-- %p WM_COMMAND %s unhandled
\n
"
,
This
,
SPY_GetMsgName
(
lpnmh
->
code
));
break
;;
...
...
@@ -954,7 +999,6 @@ static LRESULT CALLBACK ShellView_WndProc(HWND hWnd, UINT uMessage, WPARAM wPara
{
IShellViewImpl
*
pThis
=
(
IShellViewImpl
*
)
GetWindowLongA
(
hWnd
,
GWL_USERDATA
);
LPCREATESTRUCTA
lpcs
;
DWORD
dwCursor
;
TRACE
(
"(hwnd=%x msg=%x wparm=%x lparm=%lx)
\n
"
,
hWnd
,
uMessage
,
wParam
,
lParam
);
...
...
@@ -978,23 +1022,14 @@ static LRESULT CALLBACK ShellView_WndProc(HWND hWnd, UINT uMessage, WPARAM wPara
GET_WM_COMMAND_CMD
(
wParam
,
lParam
),
GET_WM_COMMAND_HWND
(
wParam
,
lParam
));
case
WM_CONTEXTMENU
:
ShellView_DoContextMenu
(
pThis
,
LOWORD
(
lParam
),
HIWORD
(
lParam
),
FALSE
);
return
0
;
case
WM_SHOWWINDOW
:
UpdateWindow
(
pThis
->
hWndList
);
break
;
case
WM_PARENTNOTIFY
:
/* fixme: should not be handled here*/
TRACE
(
"-- WM_PARENTNOTIFY
\n
"
);
if
(
LOWORD
(
wParam
)
==
WM_RBUTTONDOWN
)
{
dwCursor
=
GetMessagePos
();
ShellView_DoContextMenu
(
pThis
,
LOWORD
(
dwCursor
),
HIWORD
(
dwCursor
),
FALSE
);
return
TRUE
;
}
break
;
default:
TRACE
(
"-- message %s unhandled
\n
"
,
SPY_GetMsgName
(
uMessage
));
break
;
case
WM_DESTROY
:
pRevokeDragDrop
(
pThis
->
hWnd
);
break
;
}
return
DefWindowProcA
(
hWnd
,
uMessage
,
wParam
,
lParam
);
...
...
@@ -1034,6 +1069,10 @@ static HRESULT WINAPI IShellView_fnQueryInterface(IShellView * iface,REFIID riid
{
*
ppvObj
=
(
IDropTarget
*
)
&
(
This
->
lpvtblDropTarget
);
}
else
if
(
IsEqualIID
(
riid
,
&
IID_IDropSource
))
{
*
ppvObj
=
(
IDropSource
*
)
&
(
This
->
lpvtblDropSource
);
}
else
if
(
IsEqualIID
(
riid
,
&
IID_IViewObject
))
{
*
ppvObj
=
(
IViewObject
*
)
&
(
This
->
lpvtblViewObject
);
...
...
@@ -1564,6 +1603,78 @@ static struct ICOM_VTABLE(IDropTarget) dtvt =
ISVDropTarget_Drop
};
/**********************************************************
* ISVDropSource implementation
*/
static
HRESULT
WINAPI
ISVDropSource_QueryInterface
(
IDropSource
*
iface
,
REFIID
riid
,
LPVOID
*
ppvObj
)
{
char
xriid
[
50
];
_ICOM_THIS_From_IDropSource
(
IShellViewImpl
,
iface
);
WINE_StringFromCLSID
((
LPCLSID
)
riid
,
xriid
);
TRACE
(
"(%p)->(
\n\t
IID:
\t
%s,%p)
\n
"
,
This
,
xriid
,
ppvObj
);
return
IShellFolder_QueryInterface
((
IShellFolder
*
)
This
,
riid
,
ppvObj
);
}
static
ULONG
WINAPI
ISVDropSource_AddRef
(
IDropSource
*
iface
)
{
_ICOM_THIS_From_IDropSource
(
IShellViewImpl
,
iface
);
TRACE
(
"(%p)->(count=%lu)
\n
"
,
This
,
This
->
ref
);
return
IShellFolder_AddRef
((
IShellFolder
*
)
This
);
}
static
ULONG
WINAPI
ISVDropSource_Release
(
IDropSource
*
iface
)
{
_ICOM_THIS_From_IDropSource
(
IShellViewImpl
,
iface
);
TRACE
(
"(%p)->(count=%lu)
\n
"
,
This
,
This
->
ref
);
return
IShellFolder_Release
((
IShellFolder
*
)
This
);
}
static
HRESULT
WINAPI
ISVDropSource_QueryContinueDrag
(
IDropSource
*
iface
,
BOOL
fEscapePressed
,
DWORD
grfKeyState
)
{
_ICOM_THIS_From_IDropSource
(
IShellViewImpl
,
iface
);
TRACE
(
"(%p)
\n
"
,
This
);
if
(
fEscapePressed
)
return
DRAGDROP_S_CANCEL
;
else
if
(
!
(
grfKeyState
&
MK_LBUTTON
)
&&
!
(
grfKeyState
&
MK_RBUTTON
))
return
DRAGDROP_S_DROP
;
else
return
NOERROR
;
}
static
HRESULT
WINAPI
ISVDropSource_GiveFeedback
(
IDropSource
*
iface
,
DWORD
dwEffect
)
{
_ICOM_THIS_From_IDropSource
(
IShellViewImpl
,
iface
);
TRACE
(
"(%p)
\n
"
,
This
);
return
DRAGDROP_S_USEDEFAULTCURSORS
;
}
static
struct
ICOM_VTABLE
(
IDropSource
)
dsvt
=
{
ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
ISVDropSource_QueryInterface
,
ISVDropSource_AddRef
,
ISVDropSource_Release
,
ISVDropSource_QueryContinueDrag
,
ISVDropSource_GiveFeedback
};
/**********************************************************
* ISVViewObject implementation
*/
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
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!
Save comment
Cancel
Please
register
or
sign in
to comment