847 lines
23 KiB
C++
847 lines
23 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (C) Microsoft Corporation, 1995 - 1999
|
||
|
All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
dragdrop.hxx
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Print queue drag & drop related stuff
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Lazar Ivanov (LazarI) 10-Mar-2000
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.hxx"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "dragdrop.hxx"
|
||
|
|
||
|
#include <initguid.h>
|
||
|
#include "guids.h"
|
||
|
|
||
|
/////////////////////////////////////////////////////
|
||
|
// CPrintQueueDT - print queue drop target impl.
|
||
|
//
|
||
|
QITABLE_DECLARE(CPrintQueueDT)
|
||
|
class CPrintQueueDT: public CUnknownMT<QITABLE_GET(CPrintQueueDT)>, // MT impl. of IUnknown
|
||
|
public IDropTarget,
|
||
|
public IPrintQueueDT,
|
||
|
public CSimpleWndSubclass<CPrintQueueDT>
|
||
|
{
|
||
|
private:
|
||
|
// private members
|
||
|
void DrawVisualFeedBack() const;
|
||
|
void VisualFeedBack(const POINTL &ptl, BOOL bRemove = FALSE);
|
||
|
int GetTargetItem(const POINTL &ptl) const;
|
||
|
HRESULT DropMoveJob(const DragDrop::JOBINFO &job, POINTL ptl);
|
||
|
HRESULT DropPrintFiles(IDataObject *pDataObj);
|
||
|
DWORD RightClickChooseEffect(const POINTL &ptl, DWORD dwEffectIn);
|
||
|
HRESULT GetPrinterName(LPTSTR pszName, UINT nMaxLength);
|
||
|
|
||
|
// static services
|
||
|
static DWORD WINAPI ThreadProc_PrintFiles(LPVOID lpParameter);
|
||
|
|
||
|
public:
|
||
|
// construction/destruction
|
||
|
CPrintQueueDT();
|
||
|
~CPrintQueueDT();
|
||
|
|
||
|
//////////////////
|
||
|
// IUnknown
|
||
|
//
|
||
|
IMPLEMENT_IUNKNOWN()
|
||
|
|
||
|
///////////////////
|
||
|
// IPrintQueueDT
|
||
|
//
|
||
|
STDMETHODIMP RegisterDragDrop(HWND hwndLV, TPrinter *pPrinter);
|
||
|
STDMETHODIMP RevokeDragDrop();
|
||
|
|
||
|
///////////////////
|
||
|
// IDropTarget
|
||
|
//
|
||
|
virtual HRESULT STDMETHODCALLTYPE DragEnter(
|
||
|
/* [unique][in] */ IDataObject *pDataObj,
|
||
|
/* [in] */ DWORD grfKeyState,
|
||
|
/* [in] */ POINTL pt,
|
||
|
/* [out][in] */ DWORD *pdwEffect);
|
||
|
|
||
|
virtual HRESULT STDMETHODCALLTYPE DragOver(
|
||
|
/* [in] */ DWORD grfKeyState,
|
||
|
/* [in] */ POINTL pt,
|
||
|
/* [out][in] */ DWORD *pdwEffect);
|
||
|
|
||
|
virtual HRESULT STDMETHODCALLTYPE DragLeave( void);
|
||
|
|
||
|
virtual HRESULT STDMETHODCALLTYPE Drop(
|
||
|
/* [unique][in] */ IDataObject *pDataObj,
|
||
|
/* [in] */ DWORD grfKeyState,
|
||
|
/* [in] */ POINTL pt,
|
||
|
/* [out][in] */ DWORD *pdwEffect);
|
||
|
|
||
|
// implement CSimpleWndSubclass<CPrintQueueDT>
|
||
|
LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
private:
|
||
|
// private data
|
||
|
LONG m_cRef; // ref count
|
||
|
TPrinter *m_pPrinter; // target print queue
|
||
|
COleComInitializer m_ole2; // make sure OLE2 is initialized.
|
||
|
AUTO_SCROLL_DATA m_asd; // drag & drop autoscroll stuff (declared in shlobjp.h)
|
||
|
DWORD m_grfKeyStateLast; // we need this to implement IDropTarget::Drop correctly
|
||
|
DWORD m_dwEffect; // save the last effect to implement IDropTarget::DragOver
|
||
|
int m_iLastItem; // last item selected (visual feedback)
|
||
|
};
|
||
|
|
||
|
// QueryInterface table
|
||
|
QITABLE_BEGIN(CPrintQueueDT)
|
||
|
QITABENT(CPrintQueueDT, IDropTarget), // IID_IDropTarget
|
||
|
QITABENT(CPrintQueueDT, IPrintQueueDT), // IID_IPrintQueueDT
|
||
|
QITABLE_END()
|
||
|
|
||
|
///////////////////
|
||
|
// CPrintQueueDT
|
||
|
//
|
||
|
void CPrintQueueDT::DrawVisualFeedBack() const
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
int iCount = ListView_GetItemCount(m_hwnd);
|
||
|
ASSERT(iCount && m_iLastItem <= iCount && m_iLastItem >= 0);
|
||
|
|
||
|
RECT rc;
|
||
|
if( m_iLastItem < ListView_GetItemCount(m_hwnd) )
|
||
|
{
|
||
|
ListView_GetItemRect(m_hwnd, m_iLastItem, &rc, LVIR_BOUNDS);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ListView_GetItemRect(m_hwnd, m_iLastItem-1, &rc, LVIR_BOUNDS);
|
||
|
InflateRect(&rc, 0, -abs(rc.bottom - rc.top));
|
||
|
}
|
||
|
|
||
|
// draw a standard XOR line
|
||
|
HDC hDC = GetDC(m_hwnd);
|
||
|
|
||
|
if( hDC )
|
||
|
{
|
||
|
CAutoHandlePen shPen = CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOW));
|
||
|
|
||
|
if( shPen )
|
||
|
{
|
||
|
HGDIOBJ hObjOld = SelectObject(hDC, shPen);
|
||
|
|
||
|
SetROP2(hDC, R2_XORPEN);
|
||
|
MoveToEx(hDC, rc.left+1, rc.top+1, NULL);
|
||
|
LineTo(hDC, rc.right-1, rc.top+1);
|
||
|
|
||
|
SelectObject(hDC, hObjOld);
|
||
|
}
|
||
|
|
||
|
ReleaseDC(m_hwnd, hDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPrintQueueDT::VisualFeedBack(const POINTL &ptl, BOOL bRemove)
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
if( bRemove && -1 != m_iLastItem )
|
||
|
{
|
||
|
// hide the latest visual feedback
|
||
|
DrawVisualFeedBack();
|
||
|
m_iLastItem = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int iItem = GetTargetItem(ptl);
|
||
|
if( iItem != -1 && iItem != m_iLastItem )
|
||
|
{
|
||
|
if( -1 != m_iLastItem )
|
||
|
{
|
||
|
// hide the latest visual feedback
|
||
|
DrawVisualFeedBack();
|
||
|
m_iLastItem = -1;
|
||
|
}
|
||
|
|
||
|
// save the lastest item and draw the new
|
||
|
// visual feedback
|
||
|
m_iLastItem = iItem;
|
||
|
DrawVisualFeedBack();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( -1 == iItem && -1 != m_iLastItem )
|
||
|
{
|
||
|
// hide the latest visual feedback
|
||
|
DrawVisualFeedBack();
|
||
|
m_iLastItem = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int CPrintQueueDT::GetTargetItem(const POINTL &ptl) const
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
LV_HITTESTINFO info = {0};
|
||
|
int iItem = -1;
|
||
|
|
||
|
// find the item on this point
|
||
|
info.pt.x = ptl.x;
|
||
|
info.pt.y = ptl.y;
|
||
|
ScreenToClient(m_hwnd, &info.pt);
|
||
|
iItem = ListView_HitTest(m_hwnd, &info);
|
||
|
|
||
|
if( (info.flags & LVHT_ONITEM) && iItem != -1 )
|
||
|
{
|
||
|
// get the header RECT here
|
||
|
RECT rcHeader;
|
||
|
HWND hwndHeader = ListView_GetHeader(m_hwnd);
|
||
|
ASSERT(hwndHeader);
|
||
|
GetWindowRect(hwndHeader, &rcHeader);
|
||
|
MapWindowPoints(NULL, m_hwnd, reinterpret_cast<LPPOINT>(&rcHeader), 2);
|
||
|
|
||
|
// check if the point is inside the header.
|
||
|
if( PtInRect(&rcHeader, info.pt) )
|
||
|
{
|
||
|
// this item is partially covered from the header -
|
||
|
// not a valid item.
|
||
|
iItem = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RECT rcItem;
|
||
|
ListView_GetItemRect(m_hwnd, iItem, &rcItem, LVIR_BOUNDS);
|
||
|
if( abs(rcItem.top - info.pt.y) > (abs(rcItem.bottom - rcItem.top)/2) )
|
||
|
{
|
||
|
// this point is in the lower half of the item rect -
|
||
|
// go to the next item (not a problem if iItem == count
|
||
|
iItem ++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return iItem;
|
||
|
}
|
||
|
|
||
|
HRESULT CPrintQueueDT::DropMoveJob(const DragDrop::JOBINFO &job, POINTL ptl)
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
IDENT jobID = -1;
|
||
|
int iItem = GetTargetItem(ptl);
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
// get the position of the job we are mving in front of
|
||
|
if( iItem < ListView_GetItemCount(m_hwnd) )
|
||
|
{
|
||
|
LVITEM lvi = {0};
|
||
|
lvi.iItem = iItem;
|
||
|
lvi.iSubItem = 0;
|
||
|
lvi.mask = LVIF_PARAM;
|
||
|
|
||
|
if( ListView_GetItem(m_hwnd, &lvi) )
|
||
|
{
|
||
|
jobID = m_pPrinter->pData()->GetId((HITEM)lvi.lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// calculate the new & old position of the job moved
|
||
|
NATURAL_INDEX uNewPos = (-1 == jobID) ? iItem : m_pPrinter->pData()->GetNaturalIndex(jobID, NULL);
|
||
|
NATURAL_INDEX uOldPos = (-1 == job.dwJobID) ? job.iItem : m_pPrinter->pData()->GetNaturalIndex(job.dwJobID, NULL);
|
||
|
|
||
|
if( uNewPos > uOldPos )
|
||
|
{
|
||
|
uNewPos--;
|
||
|
}
|
||
|
|
||
|
if( uNewPos != uOldPos )
|
||
|
{
|
||
|
CAutoHandlePrinter shPrinter = NULL;
|
||
|
DWORD dwAccess = 0;
|
||
|
|
||
|
if( ERROR_SUCCESS == TPrinter::sOpenPrinter(job.szPrinterName, &dwAccess, &shPrinter) )
|
||
|
{
|
||
|
DWORD cbJob2 = 0;
|
||
|
CAutoPtrSpl<JOB_INFO_2> spJob2 = NULL;
|
||
|
|
||
|
if( VDataRefresh::bGetJob(shPrinter, job.dwJobID, 2, spJob2.GetPPV(), &cbJob2) )
|
||
|
{
|
||
|
// set the new position and call SetJob to reorder.
|
||
|
spJob2->Position = uNewPos+1;
|
||
|
if( !SetJob(shPrinter, job.dwJobID, 2, spJob2.GetPtrAs<LPBYTE>(), 0) )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||
|
MessageBeep((UINT)-1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CPrintQueueDT::DropPrintFiles(IDataObject *pDataObj)
|
||
|
{
|
||
|
HRESULT hr = E_UNEXPECTED;
|
||
|
CRefPtrCOM<IStream> spStream;
|
||
|
|
||
|
// we create a background therad which will attempt to print
|
||
|
// documents in pDataObj
|
||
|
hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObj, &spStream);
|
||
|
|
||
|
if( SUCCEEDED(hr) )
|
||
|
{
|
||
|
CAutoPtr<CCookiesHolder> spThreadInfo = new CCookiesHolder;
|
||
|
if( spThreadInfo && spThreadInfo->SetCount(2) )
|
||
|
{
|
||
|
// as we pass pointer to ourselves to the thread proc
|
||
|
// we need to AddRef ourselves here
|
||
|
AddRef();
|
||
|
|
||
|
// setup the cookie holder
|
||
|
spThreadInfo->SetCookie<IStream*>(0, spStream);
|
||
|
spThreadInfo->SetCookie<CPrintQueueDT*>(1, this);
|
||
|
|
||
|
// spin the background thread here
|
||
|
DWORD dwThreadId;
|
||
|
CAutoHandleNT shThread = TSafeThread::Create(NULL, 0,
|
||
|
(LPTHREAD_START_ROUTINE)CPrintQueueDT::ThreadProc_PrintFiles,
|
||
|
spThreadInfo, 0, &dwThreadId);
|
||
|
|
||
|
if( shThread )
|
||
|
{
|
||
|
// the thread will take care to free this memory
|
||
|
spThreadInfo.Detach();
|
||
|
spStream.Detach();
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// thread creation failed. setup appropriate HRESULT from
|
||
|
// last error
|
||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||
|
|
||
|
// compensate the AddRef above
|
||
|
Release();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// new CCookiesHolder has failed
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CPrintQueueDT::GetPrinterName(LPTSTR pszName, UINT nMaxLength)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
TCHAR szCurrentPrinterName[kPrinterBufMax];
|
||
|
|
||
|
if( pszName )
|
||
|
{
|
||
|
if( m_pPrinter )
|
||
|
{
|
||
|
m_pPrinter->pszPrinterName(szCurrentPrinterName);
|
||
|
lstrcpyn(pszName, szCurrentPrinterName, nMaxLength);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_UNEXPECTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI CPrintQueueDT::ThreadProc_PrintFiles(LPVOID lpParameter)
|
||
|
{
|
||
|
TCHAR szPrinterName[kPrinterBufMax];
|
||
|
HRESULT hr = E_UNEXPECTED;
|
||
|
CAutoPtr<CCookiesHolder> spThreadInfo = (CCookiesHolder *)lpParameter;
|
||
|
|
||
|
if( spThreadInfo )
|
||
|
{
|
||
|
// make sure OLE2 is initialized.
|
||
|
COleComInitializer ole2(TRUE);
|
||
|
|
||
|
// get the cookies
|
||
|
CAutoPtrCOM<IStream> spStream = spThreadInfo->GetCookie<IStream*>(0);
|
||
|
CAutoPtrCOM<CPrintQueueDT> spPrintQueueDT = spThreadInfo->GetCookie<CPrintQueueDT*>(1);
|
||
|
|
||
|
if( ole2 && spStream && spPrintQueueDT )
|
||
|
{
|
||
|
UINT uCount = 0;
|
||
|
CAutoPtrPIDL pidlPrinter;
|
||
|
CRefPtrCOM<IShellFolder> spLocalPrnFolder;
|
||
|
CRefPtrCOM<IDropTarget> spPrinterDT;
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
// attempt to get IDropTarget for the printer name
|
||
|
|
||
|
if( SUCCEEDED(hr = spPrintQueueDT->GetPrinterName(szPrinterName, ARRAYSIZE(szPrinterName))) &&
|
||
|
SUCCEEDED(hr = ShellServices::CreatePrinterPIDL(NULL, szPrinterName,
|
||
|
&spLocalPrnFolder, &pidlPrinter)) &&
|
||
|
SUCCEEDED(hr = spLocalPrnFolder->GetUIObjectOf(NULL, 1, pidlPrinter.GetPPCT(),
|
||
|
IID_IDropTarget, &uCount, spPrinterDT.GetPPV())) )
|
||
|
{
|
||
|
// unmarshal the data object from the stream
|
||
|
CRefPtrCOM<IDataObject> spDataObj;
|
||
|
hr = CoGetInterfaceAndReleaseStream(spStream, IID_IDataObject, spDataObj.GetPPV());
|
||
|
|
||
|
if( SUCCEEDED(hr) )
|
||
|
{
|
||
|
// CoGetInterfaceAndReleaseStream relases the stream -
|
||
|
// make sure we don't release twice
|
||
|
spStream.Detach();
|
||
|
|
||
|
// simulate drag & drop over the printer object here
|
||
|
DWORD dwEffect = DROPEFFECT_COPY;
|
||
|
hr = SHSimulateDrop(spPrinterDT, spDataObj, MK_LBUTTON, NULL, &dwEffect);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( FAILED(hr) && ERROR_INVALID_PRINTER_NAME == SCODE_CODE(GetScode(hr)) )
|
||
|
{
|
||
|
// this printer is probably a remote printer which the user is not connected to.
|
||
|
// he must be connected to the printer in order to be able to print. show up
|
||
|
// appropriate message to inform the user about this case and ask if he wants to
|
||
|
// connect to this printer and then print.
|
||
|
if( IDYES == iMessage(NULL, IDS_PRINTERS_TITLE, IDS_PRINT_NOTCONNECTED,
|
||
|
MB_YESNO|MB_ICONQUESTION, kMsgNone, NULL) )
|
||
|
{
|
||
|
// make a printer connection here.
|
||
|
UINT uLen = COUNTOF(szPrinterName);
|
||
|
|
||
|
// attempt to connect to this printer. if this fails it shows up the
|
||
|
// appropriate error message, so don't bother to show up UI here.
|
||
|
if( bPrinterSetup(NULL, MSP_NETPRINTER, uLen, szPrinterName, &uLen, NULL) )
|
||
|
{
|
||
|
// release/reset the smart pointers and try to print again.
|
||
|
spLocalPrnFolder = NULL;
|
||
|
spPrinterDT = NULL;
|
||
|
pidlPrinter = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// setup the return value here
|
||
|
if( SUCCEEDED(hr) )
|
||
|
{
|
||
|
return ERROR_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DWORD dwErr = HRESULT_CODE(hr);
|
||
|
SetLastError(dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// implement CSimpleWndSubclass<CPrintQueueDT>
|
||
|
LRESULT CPrintQueueDT::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch( uMsg )
|
||
|
{
|
||
|
case WM_VSCROLL:
|
||
|
case WM_HSCROLL:
|
||
|
{
|
||
|
// make sure the feedback is hidden when scrolling
|
||
|
POINTL ptl = {0};
|
||
|
VisualFeedBack(ptl, TRUE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// allways call the default processing
|
||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
DWORD CPrintQueueDT::RightClickChooseEffect(const POINTL &ptl, DWORD dwEffectIn)
|
||
|
{
|
||
|
HMENU hMenu = NULL;
|
||
|
|
||
|
// pop up a menu to choose from.
|
||
|
if( dwEffectIn == DROPEFFECT_MOVE )
|
||
|
{
|
||
|
// we're moving a job
|
||
|
hMenu = ShellServices::LoadPopupMenu(ghInst, POPUP_DRAGDROP_MOVE);
|
||
|
SetMenuDefaultItem(hMenu, POPUP_DRAGDROP_MOVE, MF_BYCOMMAND);
|
||
|
}
|
||
|
else if( dwEffectIn == DROPEFFECT_COPY )
|
||
|
{
|
||
|
// we are printing files
|
||
|
hMenu = ShellServices::LoadPopupMenu(ghInst, POPUP_DRAGDROP_PRINT);
|
||
|
SetMenuDefaultItem(hMenu, POPUP_DRAGDROP_PRINT, MF_BYCOMMAND);
|
||
|
}
|
||
|
|
||
|
if( hMenu )
|
||
|
{
|
||
|
// show up the context menu
|
||
|
BOOL bReturn = TrackPopupMenu(hMenu,
|
||
|
TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
|
||
|
ptl.x, ptl.y, 0, m_hwnd, NULL);
|
||
|
|
||
|
// modify the dwEffectIn according the user choise
|
||
|
switch( bReturn )
|
||
|
{
|
||
|
case IDM_DRAGDROP_PRINT:
|
||
|
dwEffectIn = DROPEFFECT_COPY;
|
||
|
break;
|
||
|
|
||
|
case IDM_DRAGDROP_MOVE:
|
||
|
dwEffectIn = DROPEFFECT_MOVE;
|
||
|
break;
|
||
|
|
||
|
case IDM_DRAGDROP_CANCEL:
|
||
|
dwEffectIn = DROPEFFECT_NONE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
dwEffectIn = DROPEFFECT_NONE;
|
||
|
}
|
||
|
|
||
|
DestroyMenu(hMenu);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// if hMenu is NULL (i.e. LoadMenu has falied) then
|
||
|
// cancel the whole operation
|
||
|
dwEffectIn = DROPEFFECT_NONE;
|
||
|
}
|
||
|
|
||
|
// return modified dwEffectIn.
|
||
|
return dwEffectIn;
|
||
|
}
|
||
|
|
||
|
CPrintQueueDT::CPrintQueueDT()
|
||
|
: m_cRef(1),
|
||
|
m_pPrinter(NULL),
|
||
|
m_ole2(TRUE),
|
||
|
m_grfKeyStateLast(0),
|
||
|
m_dwEffect(0),
|
||
|
m_iLastItem(-1)
|
||
|
{
|
||
|
// nothing
|
||
|
}
|
||
|
|
||
|
CPrintQueueDT::~CPrintQueueDT()
|
||
|
{
|
||
|
if( IsAttached() )
|
||
|
{
|
||
|
RevokeDragDrop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////
|
||
|
// IPrintQueueDT members
|
||
|
//
|
||
|
STDMETHODIMP CPrintQueueDT::RegisterDragDrop(HWND hwndLV, TPrinter *pPrinter)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
if( !IsAttached() && m_ole2 && hwndLV && pPrinter )
|
||
|
{
|
||
|
// register this window for OLE2 drag & drop
|
||
|
hr = ::RegisterDragDrop(GetParent(hwndLV), static_cast<IDropTarget*>(this));
|
||
|
|
||
|
if( SUCCEEDED(hr) && Attach(hwndLV) )
|
||
|
{
|
||
|
// make sure shell icon cache is initialized, so
|
||
|
// DAD_* functions behave correctly.
|
||
|
FileIconInit(FALSE);
|
||
|
|
||
|
m_pPrinter = pPrinter;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CPrintQueueDT::RevokeDragDrop()
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
if( IsAttached() && m_ole2 && m_pPrinter )
|
||
|
{
|
||
|
// unregister this window for OLE2 drag & drop
|
||
|
hr = ::RevokeDragDrop(GetParent(m_hwnd));
|
||
|
|
||
|
Detach();
|
||
|
m_pPrinter = NULL;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// this API is supposed to be declared in shlobjp.h, and exported from shell32.dll,
|
||
|
// but for some reason it isn't. duplicate the code here.
|
||
|
STDAPI_(BOOL) DAD_DragEnterEx3(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtobj)
|
||
|
{
|
||
|
RECT rc;
|
||
|
GetWindowRect(hwndTarget, &rc);
|
||
|
|
||
|
// If hwndTarget is RTL mirrored, then measure the
|
||
|
// the client point from the visual right edge
|
||
|
// (near edge in RTL mirrored windows). [samera]
|
||
|
POINT pt;
|
||
|
if( GetWindowLong(hwndTarget, GWL_EXSTYLE) & WS_EX_LAYOUTRTL )
|
||
|
pt.x = rc.right - ptStart.x;
|
||
|
else
|
||
|
pt.x = ptStart.x - rc.left;
|
||
|
|
||
|
pt.y = ptStart.y - rc.top;
|
||
|
return DAD_DragEnterEx2(hwndTarget, pt, pdtobj);
|
||
|
}
|
||
|
|
||
|
///////////////////
|
||
|
// IDropTarget
|
||
|
//
|
||
|
HRESULT STDMETHODCALLTYPE CPrintQueueDT::DragEnter(
|
||
|
/* [unique][in] */ IDataObject *pDataObj,
|
||
|
/* [in] */ DWORD grfKeyState,
|
||
|
/* [in] */ POINTL pt,
|
||
|
/* [out][in] */ DWORD *pdwEffect)
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
|
||
|
// save the last key state
|
||
|
m_grfKeyStateLast = grfKeyState;
|
||
|
|
||
|
// by default - none
|
||
|
*pdwEffect = DROPEFFECT_NONE;
|
||
|
|
||
|
STGMEDIUM medium;
|
||
|
FORMATETC fmte = {DragDrop::g_cfPrintJob, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
|
||
|
// try to get DragDrop::g_cfPrintJob data first.
|
||
|
if( SUCCEEDED(pDataObj->GetData(&fmte, &medium)) )
|
||
|
{
|
||
|
// aquire the JOBINFO
|
||
|
DragDrop::JOBINFO *pJobInfo = (DragDrop::JOBINFO *)GlobalLock(medium.hGlobal);
|
||
|
|
||
|
if( pJobInfo && pJobInfo->hwndLV == m_hwnd )
|
||
|
{
|
||
|
// this is a print job from our window, accept this
|
||
|
*pdwEffect = DROPEFFECT_MOVE;
|
||
|
}
|
||
|
|
||
|
// release medium
|
||
|
GlobalUnlock(medium.hGlobal);
|
||
|
ReleaseStgMedium(&medium);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// try to get CF_HDROP data.
|
||
|
FORMATETC fmte2 = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
if( SUCCEEDED(pDataObj->QueryGetData(&fmte2)) )
|
||
|
{
|
||
|
// we also accept files for printing
|
||
|
*pdwEffect = DROPEFFECT_COPY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// store dwEffect for DragOver
|
||
|
m_dwEffect = *pdwEffect;
|
||
|
|
||
|
// invoke shell to do the standard stuff
|
||
|
DAD_DragEnterEx3(m_hwnd, pt, pDataObj);
|
||
|
DAD_InitScrollData(&m_asd);
|
||
|
|
||
|
return S_OK;
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CPrintQueueDT::DragOver(
|
||
|
/* [in] */ DWORD grfKeyState,
|
||
|
/* [in] */ POINTL pt,
|
||
|
/* [out][in] */ DWORD *pdwEffect)
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
|
||
|
// effect was stored in DragEnter
|
||
|
*pdwEffect = m_dwEffect;
|
||
|
|
||
|
// convert to local coords
|
||
|
POINT pts = {pt.x, pt.y};
|
||
|
ScreenToClient(m_hwnd, &pts);
|
||
|
|
||
|
// assume coords of our window match listview
|
||
|
if( DAD_AutoScroll(m_hwnd, &m_asd, &pts) )
|
||
|
{
|
||
|
*pdwEffect |= DROPEFFECT_SCROLL;
|
||
|
}
|
||
|
|
||
|
if( DROPEFFECT_MOVE & (*pdwEffect) )
|
||
|
{
|
||
|
// draw visual feedback if necessary
|
||
|
VisualFeedBack(pt, FALSE);
|
||
|
}
|
||
|
|
||
|
// invoke shell to do the standard stuff
|
||
|
DAD_DragMove(pts);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CPrintQueueDT::DragLeave( void)
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
|
||
|
// remove the visual feedback if any
|
||
|
POINTL ptl = {0};
|
||
|
VisualFeedBack(ptl, TRUE);
|
||
|
|
||
|
// invoke shell to do the standard stuff
|
||
|
DAD_DragLeave();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CPrintQueueDT::Drop(
|
||
|
/* [unique][in] */ IDataObject *pDataObj,
|
||
|
/* [in] */ DWORD grfKeyState,
|
||
|
/* [in] */ POINTL pt,
|
||
|
/* [out][in] */ DWORD *pdwEffect)
|
||
|
{
|
||
|
ASSERT(IsAttached());
|
||
|
HRESULT hr = E_UNEXPECTED;
|
||
|
|
||
|
if( m_grfKeyStateLast & MK_LBUTTON )
|
||
|
{
|
||
|
*pdwEffect = m_dwEffect;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pdwEffect = RightClickChooseEffect(pt, m_dwEffect);
|
||
|
}
|
||
|
|
||
|
// do the drop here
|
||
|
|
||
|
if( DROPEFFECT_COPY == *pdwEffect )
|
||
|
{
|
||
|
// we're printing the file(s) pDataObj to the printer
|
||
|
DropPrintFiles(pDataObj);
|
||
|
}
|
||
|
else if( DROPEFFECT_MOVE == *pdwEffect )
|
||
|
{
|
||
|
// we are reordering jobs withing this print queue
|
||
|
STGMEDIUM medium = {0};
|
||
|
FORMATETC fmte = {DragDrop::g_cfPrintJob, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
hr = pDataObj->GetData(&fmte, &medium);
|
||
|
|
||
|
if( SUCCEEDED(hr) )
|
||
|
{
|
||
|
DragDrop::JOBINFO *pJobInfo = (DragDrop::JOBINFO *)GlobalLock(medium.hGlobal);
|
||
|
|
||
|
if( pJobInfo )
|
||
|
{
|
||
|
hr = DropMoveJob(*pJobInfo, pt);
|
||
|
GlobalUnlock(medium.hGlobal);
|
||
|
}
|
||
|
|
||
|
ReleaseStgMedium(&medium);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// make sure to unlock the window for updating
|
||
|
DragLeave();
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////
|
||
|
// common drag & drop APIs & data structures
|
||
|
//
|
||
|
namespace DragDrop
|
||
|
{
|
||
|
|
||
|
// print job clipboard format (JOBINFO)
|
||
|
CLIPFORMAT g_cfPrintJob = 0;
|
||
|
|
||
|
// registers the clipboard format for a print job (JOBINFO)
|
||
|
void RegisterPrintJobClipboardFormat()
|
||
|
{
|
||
|
if( !g_cfPrintJob )
|
||
|
{
|
||
|
g_cfPrintJob = static_cast<CLIPFORMAT>(
|
||
|
RegisterClipboardFormat(TEXT("PrintJob32")));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// creates IDataObject & IDropSource for a printer job objec
|
||
|
HRESULT CreatePrintJobObject(const JOBINFO &jobInfo, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
|
||
|
if( ppv )
|
||
|
{
|
||
|
*ppv = NULL; // reset this
|
||
|
|
||
|
// make sure print job data type is registered first
|
||
|
RegisterPrintJobClipboardFormat();
|
||
|
|
||
|
// instantiate CSimpleDataObjImpl template for JOBINFO
|
||
|
CAutoPtrCOM< CDataObj<> > spDataObj = new CDataObj<>;
|
||
|
CAutoPtrCOM< CSimpleDataObjImpl<JOBINFO> > spJobObj = new CSimpleDataObjImpl<JOBINFO>(jobInfo, g_cfPrintJob, spDataObj);
|
||
|
|
||
|
if( spDataObj && spJobObj )
|
||
|
{
|
||
|
hr = spJobObj->QueryInterface(riid, ppv);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// instantiate a IPrintQueueDT implementation
|
||
|
HRESULT CreatePrintQueueDT(REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
|
||
|
if( ppv )
|
||
|
{
|
||
|
*ppv = NULL; // reset this
|
||
|
|
||
|
// instantiate CPrintQueueDT
|
||
|
CAutoPtrCOM<CPrintQueueDT> spObj = new CPrintQueueDT;
|
||
|
|
||
|
if( spObj )
|
||
|
{
|
||
|
hr = spObj->QueryInterface(riid, ppv);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
} // namespace DragDrop
|