Răsfoiți Sursa

Added CF_TEXT Multi-Selection Paste and fixed large quantity deletion.

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@13 595ec19a-5cb4-439b-94a8-42fb3063c22c
ingenuus 22 ani în urmă
părinte
comite
fca6da48bc
8 a modificat fișierele cu 313 adăugiri și 73 ștergeri
  1. 37 1
      Changes.txt
  2. 2 2
      DatabaseUtilities.cpp
  3. 43 38
      MainTable.cpp
  4. 8 2
      MainTable.h
  5. 7 4
      ProcessCopy.cpp
  6. 167 0
      ProcessPaste.cpp
  7. 4 0
      ProcessPaste.h
  8. 45 26
      QPasteWnd.cpp

+ 37 - 1
Changes.txt

@@ -1,2 +1,38 @@
+8-18-03
+-------
++ Implemented Multi-Selection Paste for CF_TEXT clips by "\r\n" concatenation
+  * CQPasteWnd::OnListSelect()
+  + BOOL CProcessPaste::MultiPaste( int numIDs, int* pIDs, HWND hWnd );
+  + BOOL CProcessPaste::MultiDrag( int numIDs, int* pIDs, HWND hWnd );
+  + CString CProcessPaste::AggregateText(int numIDs, int* pIDs, UINT uiPastType, char* pSeparator);
+  + bool CopyToGlobal( HGLOBAL hGlobal, LPVOID pBuf, ULONG ulBufLen )
+  + HGLOBAL NewGlobal( LPVOID pBuf, ULONG ulBufLen )
+
+* After a delete, position caret at first item deleted.
+  * void CQPasteWnd::DeleteSelectedRows()
+
+! Fixed Error "query too complex" when deleting large number of Clips
+  * void CQPasteWnd::DeleteSelectedRows()
+  + static BOOL CMainTable::DeleteAllClips();
+  + static BOOL CMainTable::DeleteClip( int id );
+  + static BOOL CMainTable::DeleteClips(ARRAY &IDs); (was called DeleteRows)
+
+8-16-03
+-------
+! Perform GlobalUnlock after memcmp.
+  * CDataTable::DataEqual
+
+! Fixed "random" termination.
+  * CMainFrame::OnTimer: added breaks to cases of switch.
+
+
 8-11-03
-Fixed string error in processcopy.cpp
+-------
+! Fixed string error in processcopy.cpp
+
+
+Legend:
++ = added
+- = removed
+* = modified
+! = fixed

+ 2 - 2
DatabaseUtilities.cpp

@@ -302,7 +302,7 @@ BOOL RemoveOldEntries()
 				recset.MovePrev();
 			}
 
-			recset.DeleteRows(IDs);
+			recset.DeleteClips(IDs);
 		}
 	}
 
@@ -328,7 +328,7 @@ BOOL RemoveOldEntries()
 				recset.MoveNext();
 			}
 
-			recset.DeleteRows(IDs);
+			recset.DeleteClips(IDs);
 		}
 	}
 

+ 43 - 38
MainTable.cpp

@@ -73,13 +73,21 @@ void CMainTable::Dump(CDumpContext& dc) const
 }
 #endif //_DEBUG
 
-BOOL CMainTable::DeleteAll()
+// deletes from both Main and Data
+BOOL CMainTable::DeleteClip( int id )
 {
-	BOOL bRet = FALSE;
+CString csMainSQL;
+CString csDataSQL;
+BOOL bRet = FALSE;
+
+	csMainSQL.Format( "DELETE FROM Main WHERE lID = %d", id );
+	csDataSQL.Format( "DELETE FROM Data WHERE lParentID = %d", id );
+
 	try
 	{
 		theApp.EnsureOpenDB();
-		theApp.m_pDatabase->Execute("DELETE * FROM Main", dbFailOnError);
+		theApp.m_pDatabase->Execute(csMainSQL, dbFailOnError);
+		theApp.m_pDatabase->Execute(csDataSQL, dbFailOnError);
 		bRet = TRUE;
 	}
 	catch(CDaoException* e)
@@ -91,59 +99,56 @@ BOOL CMainTable::DeleteAll()
 	return bRet;
 }
 
-BOOL CMainTable::DeleteRows(ARRAY &IDs)
+BOOL CMainTable::DeleteClips(ARRAY &IDs)
 {
-	if(IDs.GetSize() <= 0)
-		return FALSE;
+int count = IDs.GetSize();
 
-	CString csMainSQL = "DELETE FROM Main WHERE";
-	CString csDataSQL = "DELETE FROM Data WHERE";
-	CString csMainFormat;
+	if(count <= 0)
+		return FALSE;
 
-	csMainFormat.Format(" lID = %d", IDs[0]);
-	csMainSQL += csMainFormat;
+BOOL bRet = TRUE;
+	// delete one at a time rather than all in one query
+	//	in order to avoid the "query too large" error for large deletes
+	for( int i=0; i < count && bRet; i++ )
+		bRet = bRet && DeleteClip( IDs[i] );
 
-	csMainFormat.Format(" lParentID = %d", IDs[0]);
-	csDataSQL += csMainFormat;
+	return bRet;
+}
 
-	for(int i = 1; i < IDs.GetSize(); i++)
+BOOL CMainTable::DeleteAllClips()
+{
+BOOL bRet = FALSE;
+	try
 	{
-		csMainFormat.Format(" Or lID = %d", IDs[i]);
-		csMainSQL += csMainFormat;
-
-		csMainFormat.Format(" Or lParentID = %d", IDs[i]);
-		csDataSQL += csMainFormat;
+		theApp.EnsureOpenDB();
+		theApp.m_pDatabase->Execute("DELETE * FROM Main", dbFailOnError);
+		theApp.m_pDatabase->Execute("DELETE * FROM Data", dbFailOnError);
+		bRet = TRUE;
 	}
-		
-	BOOL bRet = TRUE;
+	catch(CDaoException* e)
+	{
+		AfxMessageBox(e->m_pErrorInfo->m_strDescription);
+		e->Delete();
+	}
+
+	return bRet;
+}
 
+BOOL CMainTable::DeleteAll()
+{
+	BOOL bRet = FALSE;
 	try
 	{
 		theApp.EnsureOpenDB();
-		theApp.m_pDatabase->Execute(csMainSQL, dbFailOnError);
+		theApp.m_pDatabase->Execute("DELETE * FROM Main", dbFailOnError);
+		bRet = TRUE;
 	}
 	catch(CDaoException* e)
 	{
 		AfxMessageBox(e->m_pErrorInfo->m_strDescription);
 		e->Delete();
-		bRet = FALSE;
 	}
 
-	if(bRet)
-	{
-		try
-		{
-			theApp.EnsureOpenDB();
-			theApp.m_pDatabase->Execute(csDataSQL, dbFailOnError);
-		}
-		catch(CDaoException* e)
-		{
-			AfxMessageBox(e->m_pErrorInfo->m_strDescription);
-			e->Delete();
-			bRet = FALSE;
-		}
-	}
-	
 	return bRet;
 }
 

+ 8 - 2
MainTable.h

@@ -34,8 +34,14 @@ public:
 	//}}AFX_VIRTUAL
 
 public:
-	BOOL DeleteRows(ARRAY &IDs);
-	BOOL DeleteAll();
+	// deletes from both Main and Data
+	static BOOL DeleteClip( int id );
+	static BOOL DeleteClips(ARRAY &IDs);
+	static BOOL DeleteAllClips();
+
+	// only deletes from Main
+	static BOOL DeleteAll();
+	
 	static HACCEL LoadAcceleratorKeys();
 	void Open(LPCTSTR lpszFormat,...);
 

+ 7 - 4
ProcessCopy.cpp

@@ -330,10 +330,13 @@ BOOL CProcessCopy::GetDescriptionText(CString &csText)
 			if(ulBufLen > LENGTH_OF_TEXT_SNIPET)
 				ulBufLen = LENGTH_OF_TEXT_SNIPET;
 
-			char* buf = csText.GetBuffer(ulBufLen);
-			memcpy(buf, text, ulBufLen);
-			buf[ulBufLen-1] = '\0';
-			csText.ReleaseBuffer();
+			if( ulBufLen > 0 )
+			{
+				char* buf = csText.GetBuffer(ulBufLen);
+				memcpy(buf, text, ulBufLen); // in most cases, last char == null
+				buf[ulBufLen-1] = '\0'; // just in case not null terminated
+				csText.ReleaseBuffer(); // scans for the null
+			}
 					
 			//Unlock the data
 			GlobalUnlock(hgData);

+ 167 - 0
ProcessPaste.cpp

@@ -134,6 +134,173 @@ BOOL CProcessPaste::LoadData(long lID, COleDataSource *pData)
 	return FALSE;
 }
 
+bool CopyToGlobal( HGLOBAL hGlobal, LPVOID pBuf, ULONG ulBufLen )
+{
+	if( hGlobal == 0 || pBuf == 0 || ulBufLen == 0 )
+	{
+		ASSERT(FALSE);
+		return 0;
+	}
+
+	LPVOID pvData = GlobalLock(hGlobal);
+	if(!pvData)
+	{
+		ASSERT(FALSE);
+		return false;
+	}
+
+	ULONG size = GlobalSize(hGlobal);
+
+	if( size < ulBufLen )
+	{
+		ASSERT(FALSE);
+		GlobalUnlock(hGlobal);
+		return false;
+	}
+
+	memcpy(pvData, pBuf, ulBufLen);
+			
+	GlobalUnlock(hGlobal);
+
+	return true;
+}
+
+HGLOBAL NewGlobal( LPVOID pBuf, ULONG ulBufLen )
+{
+	if( pBuf == 0 || ulBufLen == 0 )
+	{
+		ASSERT(FALSE);
+		return 0;
+	}
+	
+	HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE, ulBufLen );
+	if( hGlobal == 0 )
+	{
+		ASSERT(FALSE);
+		return 0;
+	}
+
+	CopyToGlobal( hGlobal, pBuf, ulBufLen );
+
+	return hGlobal;
+}
+
+
+BOOL CProcessPaste::MultiPaste( int numIDs, int* pIDs, HWND hWnd )
+{
+	if( numIDs <= 0 || pIDs == NULL || hWnd == NULL )
+		return FALSE;
+	if( numIDs == 1 )
+		return LoadDataAndPaste( pIDs[0], hWnd );
+
+	CString text = AggregateText(numIDs, pIDs, CF_TEXT, "\r\n");
+	HGLOBAL hGlobal = NewGlobal( (void*)(LPCSTR) text, text.GetLength()+1 );
+	if( !hGlobal )
+		return FALSE;
+	
+	COleDataSource *pOle = new COleDataSource;
+	pOle->CacheGlobalData(CF_TEXT, hGlobal);
+
+	// Should we allow Ditto to add the aggregate to its history list?
+	bool bOldHandleChange = theApp.m_bHandleClipboardDataChange;
+	theApp.m_bHandleClipboardDataChange = false;
+
+//	pOle->FlushClipboard();
+	pOle->SetClipboard();
+		
+	SendPaste(hWnd);
+
+	//Set this back to original value
+	theApp.m_bHandleClipboardDataChange = bOldHandleChange;
+
+	return TRUE;
+}
+
+BOOL CProcessPaste::MultiDrag( int numIDs, int* pIDs, HWND hWnd )
+{
+	if( numIDs <= 0 || pIDs == NULL || hWnd == NULL )
+		return FALSE;
+	if( numIDs == 1 )
+		return LoadDataAndDrag( pIDs[0] );
+
+	CString text = AggregateText(numIDs, pIDs, CF_TEXT, "\r\n");
+	HGLOBAL hGlobal = NewGlobal( (void*)(LPCSTR) text, text.GetLength()+1 );
+	if( !hGlobal )
+		return FALSE;
+	
+	COleDataSource *pOle = new COleDataSource;
+	pOle->CacheGlobalData(CF_TEXT, hGlobal);
+
+	if( pOle->DoDragDrop(DROPEFFECT_COPY) != DROPEFFECT_NONE )
+		return TRUE;
+
+	return FALSE;
+}
+
+
+// This assumes that the given uiPastType is a null terminated text type
+CString CProcessPaste::AggregateText(int numIDs, int* pIDs, UINT uiPastType, char* pSeparator)
+{
+	CString csSQL;
+	CDataTable recset;
+	CString text;
+	char* pData = NULL;
+	DWORD len;
+	DWORD maxLen;
+
+	// maybe we should sum up the "recset.m_ooData.m_dwDataLength" of all IDs first
+	//  in order to determine the max space required???  Or would that be wastefull?
+
+	// allocate a large initial buffer to minimize realloc for concatenations
+	text.GetBuffer(4096);
+	text = "";
+
+	csSQL.Format("SELECT * FROM Data WHERE strClipBoardFormat = \'%s\' AND lParentID = %%d", GetFormatName(uiPastType));
+	try
+	{
+		for( int i=0; i < numIDs; i++ )
+		{
+			recset.Open( csSQL, pIDs[i] );
+			if( !recset.IsEOF() )
+			{
+				maxLen = recset.m_ooData.m_dwDataLength;
+				if( maxLen == 0 )
+					continue;
+				pData = (char*) GlobalLock(recset.m_ooData.m_hData);
+				if(!pData)
+				{
+					ASSERT(FALSE);
+					break;
+				}
+
+				// verify that pData is null terminated 
+				// do a quick check to see if the last character is null
+				if( pData[maxLen-1] != '\0' )
+				{
+					for( len=0; len < maxLen && pData[len] != '\0'; len++ ) {}
+					// if it is not null terminated, skip this item
+					if( len >= maxLen )
+						continue;
+				}
+
+				text += pData;
+				GlobalUnlock(recset.m_ooData.m_hData);
+
+				if( pSeparator )
+					text += pSeparator;
+			}
+			recset.Close();
+		}
+	}
+	catch(CDaoException* e)
+	{
+		ASSERT(FALSE);
+		e->Delete();
+	}
+
+	return text;
+}
+
 void CProcessPaste::SendPaste(HWND hWnd)
 {
 	if(hWnd)

+ 4 - 0
ProcessPaste.h

@@ -18,6 +18,10 @@ public:
 	BOOL LoadDataAndPaste(long lID, HWND hWnd);
 	BOOL LoadDataAndDrag(long lID);
 
+	BOOL MultiPaste( int numIDs, int* pIDs, HWND hWnd );
+	BOOL MultiDrag( int numIDs, int* pIDs, HWND hWnd );
+	CString AggregateText(int numIDs, int* pIDs, UINT uiPastType, char* pSeparator);
+
 protected:
 	void SendPaste(HWND hWnd);
 	BOOL LoadData(long lID, COleDataSource *pData);

+ 45 - 26
QPasteWnd.cpp

@@ -331,21 +331,20 @@ LRESULT CQPasteWnd::OnListSelect(WPARAM wParam, LPARAM lParam)
 	int nCount = (int) wParam;
 	long *pItems = (long*) lParam;
 
-	
-	if(nCount)
-	{			
-		int nID = m_lstHeader.GetItemData(pItems[0]);
+	if(nCount <= 0)
+		return TRUE;
 
-		ShowWindow(SW_HIDE);
+	ARRAY IDs;
+	m_lstHeader.GetSelectionItemData( IDs );
 
-		CProcessPaste past;
-		past.LoadDataAndPaste(nID, m_hWndFocus);
-	}
+	ShowWindow(SW_HIDE);
+
+	CProcessPaste past;
+	past.MultiPaste( IDs.GetCount(), IDs.GetData(), m_hWndFocus );
 
 	return TRUE;
 }
 
-
 LRESULT CQPasteWnd::OnListEnd(WPARAM wParam, LPARAM lParam)
 {
 	HideQPasteWindow();
@@ -714,32 +713,52 @@ LRESULT CQPasteWnd::OnDelete(WPARAM wParam, LPARAM lParam)
 void CQPasteWnd::DeleteSelectedRows()
 {
 	ARRAY IDs;
-	m_lstHeader.GetSelectionItemData(IDs);
+	long lCount = 0;
+
+	if( m_lstHeader.GetSelectedCount() == 0 )
+		return;
+
+	POSITION pos = m_lstHeader.GetFirstSelectedItemPosition();
+	int nFirstSel = m_lstHeader.GetNextSelectedItem( pos );
 
-	if(m_Recset.DeleteRows(IDs))
+	m_Recset.MoveLast();
+	lCount = m_Recset.GetRecordCount();
+
+	if( lCount == m_lstHeader.GetSelectedCount() )
+		m_Recset.DeleteAllClips();
+	else
 	{
-		m_Recset.Requery();
+		m_lstHeader.GetSelectionItemData(IDs);
+		m_Recset.DeleteClips(IDs);
+	}
 
-		long lCount = 0;
-		if(m_Recset.IsEOF() == FALSE)
-		{
-			m_Recset.MoveLast();
-			lCount = m_Recset.GetRecordCount();
-			m_lstHeader.LoadFirstTenHotKeys(m_Recset);
-		}
+	m_Recset.Requery();
 
-		m_lstHeader.SetItemCountEx(lCount);
-		m_lstHeader.Invalidate();
+	// set lCount to current number of records
+	if( m_Recset.IsBOF() && m_Recset.IsEOF() )
+		lCount = 0;
+	else
+	{
+		m_Recset.MoveLast();
+		lCount = m_Recset.GetRecordCount();
+		m_lstHeader.LoadFirstTenHotKeys(m_Recset);
 	}
 
-	int nCurSel = m_lstHeader.GetCaret();
-	if(nCurSel < 0) 
-		nCurSel = 0;
+	m_lstHeader.SetItemCountEx(lCount);
+	m_lstHeader.Invalidate();
 
+//	int nCurSel = m_lstHeader.GetCaret();
 	m_lstHeader.RemoveAllSelection();
 
-	m_lstHeader.SetSelection(nCurSel);
-	m_lstHeader.SetCaret(nCurSel);
+	// adjust new cursor position to the first item we deleted.
+	if( lCount > 0 )
+	{
+		// if there are no items after the one we deleted, then select the last one.
+		if( nFirstSel >= lCount )
+			nFirstSel = lCount - 1;
+		m_lstHeader.SetSelection(nFirstSel);
+		m_lstHeader.SetCaret(nFirstSel);
+	}
 
 	m_lstHeader.RefreshVisibleRows();
 }