// OXMulticlipboardDlg.cpp : implementation file // // Version: 9.3 #include "stdafx.h" #include "OXMulticlipboardDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifndef __AFXOLE_H__ #error "You must #include in stdafx.h Make sure you have called AfxOleInit() in InitInstance()" #endif TCHAR szTips[15][32]= { _T("CF_NONE"), _T("CF_TEXT"), _T("CF_BITMAP"), _T("CF_METAFILEPICT"), _T("CF_SYLK"), _T("CF_DIF"), _T("CF_OEMTEXT"), _T("CF_DIB"), _T("CF_PALETTE"), _T("CF_PENDATA"), _T("CF_RIFF"), _T("CF_WAVE"), _T("CF_UNICODETEXT"), _T("CF_ENHMETAFILE"), _T("CF_CUSTOMFORMAT") }; ///////////////////////////////////////////////////////////////////////////// // CMulticlipboardDlg dialog IMPLEMENT_DYNCREATE(COXMulticlipboardDlg,CDialog); COXMulticlipboardDlg::COXMulticlipboardDlg(CWnd* pParent /*=NULL*/) : CDialog(COXMulticlipboardDlg::IDD, pParent), m_nIconSpace(OX_CLIP_ICON_SPACE), m_hWndNext(NULL), m_nMaxPocketsCount(OX_CLIP_MAX_POCKETS), m_nPocketsInRow(OX_CLIP_POCKETS_IN_ROW), m_bReplacePockets(TRUE), m_nCurrentPocket(-1), m_nIconHeight(OX_CLIP_ICON_WIDTH), m_nIconWidth(OX_CLIP_ICON_WIDTH), m_nEdgeOffset(OX_CLIP_EDGE_OFFSET), m_nStaticDimension(OX_CLIP_STATIC_DIMENSION), m_bPaste(FALSE) { } void COXMulticlipboardDlg::DoDataExchange(CDataExchange* pDX) { //{{AFX_DATA_MAP(COXMulticlipboardDlg) //}}AFX_DATA_MAP(COXMulticlipboardDlg) CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(COXMulticlipboardDlg, CDialog) //{{AFX_MSG_MAP(COXMulticlipboardDlg) ON_WM_DESTROY() ON_WM_CHANGECBCHAIN() ON_WM_DRAWCLIPBOARD() ON_WM_CREATE() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMulticlipboardDlg message handlers BOOL COXMulticlipboardDlg::OnInitDialog() { CDialog::OnInitDialog(); //register COXMulticlipboardDlg in order //to receive notifications messages m_hWndNext=::SetClipboardViewer(m_hWnd); return TRUE; } void COXMulticlipboardDlg::OnDestroy() { //clear collection of the pockets for (int n=0;nDestroyWindow(); delete pPocket; } m_pPockets.RemoveAll(); //remove 'this' from the chain of clipboard vieweres ::ChangeClipboardChain(m_hWnd, m_hWndNext); //call parent CDialog::OnDestroy(); } void COXMulticlipboardDlg::OnChangeCbChain(HWND hWndRemove, HWND hWndAfter) { CDialog::OnChangeCbChain(hWndRemove, hWndAfter); //if the window we currently passing //notification messages changes, update //our handle if (hWndRemove==m_hWndNext) m_hWndNext=hWndRemove; //pass message to the next window if (m_hWndNext) ::SendMessage(m_hWndNext,WM_DRAWCLIPBOARD, NULL, NULL); } void COXMulticlipboardDlg::OnDrawClipboard() { //we've got notification CDialog::OnDrawClipboard(); //if we does put the data to the clipboard, //m_bPaste will be TRUE // do nothing in this case if (!m_bPaste) { //get a pocket for the data COXClipPocket* pPocket=GetNextPocket(); if (pPocket) { //put data to the pocket and call virtual //function OnClipInfoChanged() ClipToPocket(pPocket); OnClipInfoChanged(GetCurrentPocket()); } } //pass to the next window if (m_hWndNext) ::SendMessage(m_hWndNext,WM_DRAWCLIPBOARD, NULL, NULL); } int COXMulticlipboardDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; //try to create tooltip control if (!m_tip.Create(this)) { TRACE0("COXMulticlipboardDlg::OnCreate() failed to create COXToolTipCtrl\r\n"); } //load icons for different data types if (!LoadImageList()) { TRACE0("COXMulticlipboardDlg::OnCreate() failed to create image list\r\n"); return -1; } return 0; } COXClipPocket* COXMulticlipboardDlg::GetNextPocket() { //this function does much work - //it returns pocket to put clipboard info //in case no pockets available it will add //one row of pockets and return first available COXClipPocket* pPocket=NULL; m_nCurrentPocket++; //we've reached max count of pockets //avalables, so go to the first one //if m_bReplacePockets permit us to do it if (m_nCurrentPocket==m_nMaxPocketsCount) { if (m_bReplacePockets && m_pPockets.GetSize()) { m_nCurrentPocket=0; pPocket=(COXClipPocket*) m_pPockets.GetAt(m_nCurrentPocket); ASSERT(::IsWindow(pPocket->GetSafeHwnd())); } return pPocket; } //determine existence of the requested pocket //if exists, return this pocket if (m_nCurrentPocketGetSafeHwnd())); return pPocket; } //no luck to find pocket, have to create //new pockets row //get number of last pocket we will create int nLastPocket=(m_nCurrentPocket/m_nPocketsInRow+1)*4-1; nLastPocket=min(m_nMaxPocketsCount-1,nLastPocket); //calculate rect //if there is no place to create new pockets return NULL int nMaxPockets=AdjustSize(nLastPocket); if (!nMaxPockets) return NULL; nLastPocket=nMaxPockets-1; if (m_nCurrentPocket>nLastPocket) { if (m_bReplacePockets) { m_nCurrentPocket=0; pPocket=(COXClipPocket*) m_pPockets.GetAt(m_nCurrentPocket); ASSERT(::IsWindow(pPocket->GetSafeHwnd())); } else m_nCurrentPocket=nLastPocket; return pPocket; } if (m_pPockets.GetSize() && m_pPockets.GetUpperBound()>=m_nCurrentPocket-1) pPocket=(COXClipPocket*) m_pPockets.GetAt(m_nCurrentPocket-1); for (int n=m_nCurrentPocket;n<=nLastPocket;n++) { CRect rect; COXClipPocket* pPocketsRow; //no more pockets can be created VERIFY(GetProposedRect(n, rect)); pPocketsRow=new COXClipPocket(this); m_pPockets.Add(pPocketsRow); VERIFY(pPocketsRow->Create(rect.TopLeft(), m_nCurrentPocket+OX_CLIP_FIRST_ID)); //this pocket (the first in the row) we have to return if (n==m_nCurrentPocket) pPocket=pPocketsRow; } return pPocket; } BOOL COXMulticlipboardDlg::GetProposedRect(UINT nNumber, CRect &rect) { CRect rectDialog; CRect rectClient; //the function calculates the placement for the //specified pocket //this placement does not included edge offset //(borders of the pocket), just size of the icon //because COXClipPocket::Create() will increase //this rect on edge offset itself. GetClientRect(&rectClient); rect.left=GetIconSpace()+GetStaticDimension()+ GetEdgeOffset()+(GetIconWidth()+2*GetEdgeOffset()+ GetIconSpace())*(nNumber%m_nPocketsInRow); rect.right=rect.left+GetIconWidth(); rect.top=GetButtonsHeight()+GetIconSpace()+GetStaticDimension()+ +GetEdgeOffset()+ (GetIconSpace()+GetIconHeight()+GetEdgeOffset()*2)* (nNumber/m_nPocketsInRow); rect.bottom=rect.top+GetIconHeight(); return TRUE; } BOOL COXMulticlipboardDlg::CanResize() { //override it and return FALSE //in case of docked non-sizeable state return TRUE; } int COXMulticlipboardDlg::AdjustSize(UINT nLast) { CRect rectDialog; CRect rectClient; CRect rectDesired; CRect rectStatic; CRect rect; GetClientRect(&rectClient); GetWindowRect(&rectDialog); //calculate desired rect for specified number //of the pockets CalculateDesiredRect(nLast+1, rectDesired); if (rectDesired==rectClient) { //it has the same size, //just make sure the pockets are //placed properly MovePockets(nLast); MoveStatic(); GetClientRect(&rect); InvalidateRect(&rect); return nLast+1; } //well, desired size is different from what we have //can we resize the dialog? if (CanResize()) { CRect rectOffset; rectOffset=rectDesired; rectOffset-=rectClient; rectDialog+=rectOffset; //move dialog to approved position MoveWindow(&rectDialog); //calculate and move static to a new position MoveStatic(); //move buttons to new positions MovePockets(nLast); GetClientRect(&rect); InvalidateRect(&rect); return nLast+1; } //no, cannot resize MoveStatic(); //calculate number of the pockets currently can be placed //on this dialog int n=0; for (n=nLast;n>=0;n--) { GetProposedRect(n,rect); rect.InflateRect(GetEdgeOffset(),GetEdgeOffset()); if (rectClient.PtInRect(rect.BottomRight()) && rectClient.PtInRect(rect.TopLeft())) break; } MovePockets(nLast); GetClientRect(&rect); InvalidateRect(&rect); return n+1; } UINT COXMulticlipboardDlg::GetButtonsHeight() { //override this function and provide //height of your buttons if you have placed //any at the top of the dialog return NULL; } void COXMulticlipboardDlg::OnClipInfoChanged(UINT nPocketNumber) { //this function sets the tips for the pockets //and caption text for the dialog //you can override it ASSERT(nPocketNumber<(UINT) m_pPockets.GetSize()); COXClipPocket* pPocket=(COXClipPocket*) m_pPockets.GetAt(nPocketNumber); ASSERT(pPocket); UINT nFormat=pPocket->GetDataType(); //get clipboard format name TCHAR szBuffer[_MAX_PATH]; if (nFormat>=OX_CLIP_CUSTOM_FORMAT) ::GetClipboardFormatName(nFormat,(LPTSTR) &szBuffer,_MAX_PATH-1); else lstrcpy(szBuffer,szTips[nFormat]); if (::IsWindow(m_tip.m_hWnd)) { m_tip.DelTool(pPocket); VERIFY(m_tip.AddTool(pPocket,szBuffer)); } //set caption text CString sText; sText.Format(IDD_OX_MULTICLIPBOARDDLG_POCKET, m_nCurrentPocket+1,m_nMaxPocketsCount); SetWindowText((LPCTSTR) sText); } BOOL COXMulticlipboardDlg::PreTranslateMessage(MSG* pMsg) { //handle tips m_tip.RelayEvent(pMsg); return CDialog::PreTranslateMessage(pMsg); } BOOL COXMulticlipboardDlg::LoadImageList() { //virtual function - override it to provide your //own image list return m_imgList.Create(IDB_OX_BITMAP_LIST,GetIconWidth(),2,(COLORREF) 0xFF00FF); } void COXMulticlipboardDlg::CalculateDesiredRect(int nPockets, CRect &rect) { //the function calculates desired rect //for certain number of the pockets nPockets=nPockets?nPockets:1; UINT nRows=nPockets/m_nPocketsInRow; nRows+=(nPockets%m_nPocketsInRow)?1:0; UINT nCols=min(m_nPocketsInRow,nPockets); rect.left=rect.top=0; rect.right=GetIconSpace()+2*GetStaticDimension()+ nCols*(GetIconWidth()+ GetIconSpace()+2*GetEdgeOffset()); rect.bottom=GetButtonsHeight()+GetIconSpace()+ 2*GetStaticDimension()+nRows* (GetIconHeight()+GetIconSpace()+2*GetEdgeOffset()); } void COXMulticlipboardDlg::RemovePocket(int nPocket) { //the function remove pocket from //the collection, actually destroying //the window and deleting COXClipPocket object if (nPocket>m_pPockets.GetUpperBound()) return; COXClipPocket* pPocket=(COXClipPocket*) m_pPockets.GetAt(nPocket); if (m_pPockets.GetUpperBound()) { pPocket->DestroyWindow(); delete pPocket; m_pPockets.RemoveAt(nPocket); if (m_nCurrentPocket>=nPocket) m_nCurrentPocket--; } else pPocket->SetData(NULL,NULL); //one pocket less - we have to adjust ths size AdjustSize(PtrToUint(m_pPockets.GetUpperBound())); } void COXMulticlipboardDlg::MovePockets(int nLast) { CRect rect; CRect rectDesired; //calculate proper position for //the pockets and move them if nessesary for (int n=0;n<= nLast;n++) { if (m_pPockets.GetUpperBound()GetSafeHwnd())) continue; pPocket->GetWindowRect(&rect); ScreenToClient(&rect); rect.DeflateRect(GetEdgeOffset(),GetEdgeOffset()); GetProposedRect(n,rectDesired); if (rectDesired!=rect) { rectDesired.InflateRect(GetEdgeOffset(),GetEdgeOffset()); pPocket->MoveWindow(&rectDesired); } } } BOOL COXMulticlipboardDlg::OnCommand(WPARAM wParam, LPARAM lParam) { if (HIWORD(wParam)==BN_CLICKED) { HWND hWnd=(HWND) lParam; CWnd* pWnd=CWnd::FromHandle(hWnd); if (pWnd && pWnd->IsKindOf(RUNTIME_CLASS(COXClipPocket))) { //call PocketToClip() function to copy data from //pocket to the clipboard COXClipPocket* pPocket=(COXClipPocket*) pWnd; if (pPocket) { m_bPaste=TRUE; PocketToClip(pPocket); } } } m_bPaste=FALSE; return CDialog::OnCommand(wParam, lParam); } void COXMulticlipboardDlg::MoveStatic() { //this function calculates and moves //static control to proper position CRect rectStatic; GetClientRect(&rectStatic); rectStatic.DeflateRect(GetStaticDimension(), GetStaticDimension()); rectStatic.top+=GetButtonsHeight(); CWnd* pWnd=GetDlgItem(IDC_OX_STATIC_EDGE); pWnd->MoveWindow(&rectStatic); } void COXMulticlipboardDlg::ClipToPocket(COXClipPocket *pPocket) { //the function opens clipboard and //calls virtual ClipObjectToPocket() //to make copy of the clipboard data ASSERT(pPocket); COleDataObject clipobj; if (!clipobj.AttachClipboard()) { TRACE0("CMulticlipboardDlg::ClipToPocket : Failed to attach to clipboard\r\n"); return; } ClipObjectToPocket(pPocket, &clipobj); } BOOL COXMulticlipboardDlg::ClipObjectToPocket(COXClipPocket *pPocket, COleDataObject *pObject) { //this function actually does coping //clipboard data to the pocket //Clipboard data usually may be retrieved in //more than one format. The best matching format is //that that first will be retrieved by //GetNextFormat() function. ASSERT(pPocket); //pObject must be attached to the clipboard already ASSERT(pObject); pObject->BeginEnumFormats(); FORMATETC Format; STGMEDIUM Stgmed; BOOL bRet; //try to do loop retrieving the best available format do { bRet=pObject->GetNextFormat(&Format); if (!bRet) return FALSE; //by default it does not save info in custom registered formats //to do it you have to define OX_CLIP_DO_CUSTOM_FORMATS #ifndef OX_CLIP_DO_CUSTOM_FORMATS if (Format.cfFormat>=OX_CLIP_CUSTOM_FORMAT) continue; #endif if (pObject->IsDataAvailable(Format.cfFormat,&Format)) { BOOL bData; //trying to get data bData=pObject->GetData(Format.cfFormat, &Stgmed); if (!bData) { //no luck to get data //try to get it with global memory handle Stgmed.hGlobal=pObject->GetGlobalData(Format.cfFormat); if (!Stgmed.hGlobal) { //cannot access this format, will try another one TRACE1("Unable to access data from clipboard. Format=0x%08x\r\n",Format.cfFormat); continue; } //we have it in global memory Stgmed.pUnkForRelease=NULL; Stgmed.tymed=TYMED_HGLOBAL; } // put it in the pocket and return pPocket->SetData(Format.cfFormat, &Stgmed); return TRUE; } }while(bRet); return FALSE; } void COXMulticlipboardDlg::PocketToClip(COXClipPocket *pPocket) { //the function actually does copy from //the pocket to the clipboard tOXData* pData; pData=pPocket->GetData(); ASSERT(pData); COleDataSource* pSource=new COleDataSource; STGMEDIUM Stgmed; //copy data from the pocket HRESULT hRslt=::CopyStgMedium(&pData->Stgmed,&Stgmed); if (hRslt==ERROR_SUCCESS) { if (Stgmed.tymed==TYMED_HGLOBAL) { pSource->CacheGlobalData((unsigned short) pData->nFormat, Stgmed.hGlobal); } else pSource->CacheData((unsigned short) pData->nFormat,&Stgmed); pSource->SetClipboard(); } } void COXMulticlipboardDlg::EmptyPocket(int nPocket) { if (nPocket=NULL) { COXClipPocket* pPocket=(COXClipPocket*) m_pPockets.GetAt(nPocket); EmptyPocket(pPocket); } } void COXMulticlipboardDlg::EmptyPocket(COXClipPocket* pPocket) { ASSERT(pPocket); tOXData* pData=pPocket->GetData(); pData->Clear(); }