Przeglądaj źródła

new files moved from misc.cpp

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@480 595ec19a-5cb4-439b-94a8-42fb3063c22c
sabrogden 16 lat temu
rodzic
commit
f09209e4a4
14 zmienionych plików z 1603 dodań i 0 usunięć
  1. 67 0
      Accels.cpp
  2. 37 0
      Accels.h
  3. 163 0
      AutoSendToClientThread.cpp
  4. 33 0
      AutoSendToClientThread.h
  5. 302 0
      HotKeys.cpp
  6. 73 0
      HotKeys.h
  7. 182 0
      MainFrmThread.cpp
  8. 41 0
      MainFrmThread.h
  9. 238 0
      Popup.cpp
  10. 57 0
      Popup.h
  11. 274 0
      QPasteWndThread.cpp
  12. 57 0
      QPasteWndThread.h
  13. 63 0
      Tokenizer.cpp
  14. 16 0
      Tokenizer.h

+ 67 - 0
Accels.cpp

@@ -0,0 +1,67 @@
+#include "stdafx.h"
+#include "Accels.h"
+
+CAccels::CAccels(){
+
+}
+
+void CAccels::AddAccel(CAccel &a)
+{
+    m_Map.SetAt(a.Key, a.Cmd);
+}
+
+bool CAccels::OnMsg(MSG *pMsg, DWORD &dID)
+{
+    // bit 30 (0x40000000) is 1 if this is NOT the first msg of the key
+    //  i.e. auto-repeat may cause multiple msgs of the same key
+    if((pMsg->lParam &0x40000000) || (pMsg->message != WM_KEYDOWN && pMsg->message != WM_SYSKEYDOWN))
+    {
+        return NULL;
+    }
+
+    if(!pMsg || m_Map.GetCount() <= 0)
+    {
+        return NULL;
+    }
+
+    BYTE vkey = LOBYTE(pMsg->wParam);
+    BYTE mod = GetKeyStateModifiers();
+    DWORD key = ACCEL_MAKEKEY(vkey, mod);
+
+    CString cs;
+    cs.Format(_T("Key: %d, Mod: %d, vkey: %d"), key, mod, vkey);
+    OutputDebugString(cs);
+
+    if(m_Map.Lookup(key, dID))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+BYTE CAccels::GetKeyStateModifiers()
+{
+    BYTE m = 0;
+    if(GetKeyState(VK_SHIFT) &0x8000)
+    {
+        m |= HOTKEYF_SHIFT;
+    }
+    if(GetKeyState(VK_CONTROL) &0x8000)
+    {
+        m |= HOTKEYF_CONTROL;
+    }
+    if(GetKeyState(VK_MENU) &0x8000)
+    {
+        m |= HOTKEYF_ALT;
+    }
+    if(GetKeyState(VK_LWIN) &0x8000)
+    {
+        m |= HOTKEYF_EXT;
+    }
+    if(GetKeyState(VK_RWIN) &0x8000)
+    {
+        m |= HOTKEYF_EXT;
+    }
+    return m;
+}

+ 37 - 0
Accels.h

@@ -0,0 +1,37 @@
+#pragma once
+
+#define ACCEL_VKEY(key)			LOBYTE(key)
+#define ACCEL_MOD(key)			HIBYTE(key)
+#define ACCEL_MAKEKEY(vkey,mod) ((mod << 8) | vkey)
+
+class CAccel
+{
+public:
+    DWORD Key;
+    DWORD Cmd;
+    CAccel(DWORD key = 0, DWORD cmd = 0)
+    {
+        Key = key;
+        Cmd = cmd;
+    }
+};
+
+/*------------------------------------------------------------------*\
+CAccels - Manages a set of CAccel
+\*------------------------------------------------------------------*/
+class CAccels
+{
+public:
+    CAccels();
+
+    CMap < DWORD, DWORD, DWORD, DWORD > m_Map;
+
+    void AddAccel(CAccel &a);
+
+    // handles a key's first WM_KEYDOWN or WM_SYSKEYDOWN message.
+    // it uses GetKeyState to test for modifiers.
+    // returns a pointer to the internal CAccel if it matches the given key or NULL
+    bool OnMsg(MSG *pMsg, DWORD &dID);
+
+    static BYTE GetKeyStateModifiers();
+};

+ 163 - 0
AutoSendToClientThread.cpp

@@ -0,0 +1,163 @@
+#include "stdafx.h"
+#include "AutoSendToClientThread.h"
+#include "Misc.h"
+#include "Options.h"
+#include "CP_Main.h"
+#include "client.h"
+
+CAutoSendToClientThread::CAutoSendToClientThread(void)
+{
+	m_waitTimeout = 30000;
+	for(int eventEnum = 0; eventEnum < ECAUTOSENDTOCLIENTTHREADEVENTS_COUNT; eventEnum++)
+	{
+		AddEvent(eventEnum);
+	}
+}
+
+CAutoSendToClientThread::~CAutoSendToClientThread(void)
+{
+}
+
+void CAutoSendToClientThread::FireSendToClient(CClipList *pClipList)
+{
+	Start();
+
+	ATL::CCritSecLock csLock(m_cs.m_sect);
+	if(m_threadRunning)
+	{
+		Log(_T("Adding clip to send to client in thread"));
+
+		POSITION pos = pClipList->GetHeadPosition();
+		while(pos)
+		{
+			CClip *pClip = pClipList->GetNext(pos);
+		
+			m_saveClips.AddTail(pClip);
+		}
+
+		pClipList->RemoveAll();
+		
+		FireEvent(SEND_TO_CLIENTS);
+	}
+	else
+	{
+		Log(_T("Error creating thread to send to clients"));
+	}
+}
+
+void CAutoSendToClientThread::OnTimeOut(void *param)
+{
+	Stop(-1);
+}
+
+void CAutoSendToClientThread::OnEvent(int eventId, void *param)
+{
+	switch((eCAutoSendToClientThreadEvents)eventId)
+	{
+	case SEND_TO_CLIENTS:
+		OnSendToClient();
+		break;
+	}
+}
+
+void CAutoSendToClientThread::OnSendToClient()
+{
+	CClipList *pLocalClips = new CClipList();
+
+	//Save the clips locally
+	{
+		ATL::CCritSecLock csLock(m_cs.m_sect);
+
+		POSITION pos;
+		CClip* pClip;
+
+		pos = m_saveClips.GetHeadPosition();
+		while(pos)
+		{
+			pClip = m_saveClips.GetNext(pos);
+			pLocalClips->AddTail(pClip);
+		}
+
+		//pLocalClips now own, the clips
+		m_saveClips.RemoveAll();
+	}
+
+	SendToClient(pLocalClips);
+
+	delete pLocalClips;
+	pLocalClips = NULL;
+}
+
+bool CAutoSendToClientThread::SendToClient(CClipList *pClipList)
+{
+	LogSendRecieveInfo("@@@@@@@@@@@@@@@ - START OF SendClientThread - @@@@@@@@@@@@@@@");
+
+	if(pClipList == NULL)
+	{
+		LogSendRecieveInfo("ERROR if(pClipList == NULL)");
+		return FALSE;
+	}
+
+	long lCount = pClipList->GetCount();
+
+	LogSendRecieveInfo(StrF(_T("Start of Send ClientThread Count - %d"), lCount));
+
+	for(int nClient = 0; nClient < MAX_SEND_CLIENTS; nClient++)
+	{
+		if(g_Opt.m_SendClients[nClient].bSendAll && 
+			g_Opt.m_SendClients[nClient].csIP.GetLength() > 0)
+		{
+			CClient client;
+			if(client.OpenConnection(g_Opt.m_SendClients[nClient].csIP) == FALSE)
+			{
+				LogSendRecieveInfo(StrF(_T("ERROR opening connection to %s"), g_Opt.m_SendClients[nClient].csIP));
+
+				if(g_Opt.m_SendClients[nClient].bShownFirstError == FALSE)
+				{
+					CString cs;
+					cs.Format(_T("Error opening connection to %s"), g_Opt.m_SendClients[nClient].csIP);
+					::SendMessage(theApp.m_MainhWnd, WM_SEND_RECIEVE_ERROR, (WPARAM)cs.GetBuffer(cs.GetLength()), 0);
+					cs.ReleaseBuffer();
+
+					g_Opt.m_SendClients[nClient].bShownFirstError = TRUE;
+				}
+
+				continue;
+			}
+
+			//We were connected successfully show an error next time we can't connect
+			g_Opt.m_SendClients[nClient].bShownFirstError = FALSE;
+
+			CClip* pClip;
+			POSITION pos;
+			pos = pClipList->GetHeadPosition();
+			while(pos)
+			{
+				pClip = pClipList->GetNext(pos);
+				if(pClip == NULL)
+				{
+					ASSERT(FALSE);
+					LogSendRecieveInfo("Error in GetNext");
+					break;
+				}
+
+				LogSendRecieveInfo(StrF(_T("Sending clip to %s"), g_Opt.m_SendClients[nClient].csIP));
+
+				if(client.SendItem(pClip) == FALSE)
+				{
+					CString cs;
+					cs.Format(_T("Error sending clip to %s"), g_Opt.m_SendClients[nClient].csIP);
+					::SendMessage(theApp.m_MainhWnd, WM_SEND_RECIEVE_ERROR, (WPARAM)cs.GetBuffer(cs.GetLength()), 0);
+					cs.ReleaseBuffer();
+					break;
+				}
+			}
+
+			client.CloseConnection();
+		}
+	}
+
+	LogSendRecieveInfo("@@@@@@@@@@@@@@@ - END OF SendClientThread - @@@@@@@@@@@@@@@");
+
+	return TRUE;
+}

+ 33 - 0
AutoSendToClientThread.h

@@ -0,0 +1,33 @@
+#pragma once
+
+#include "EventThread.h"
+#include "Clip.h"
+#include <afxmt.h>
+
+
+class CAutoSendToClientThread : public CEventThread
+{
+public:
+	CAutoSendToClientThread(void);
+	~CAutoSendToClientThread(void);
+
+	enum eCAutoSendToClientThreadEvents
+	{ 
+		SEND_TO_CLIENTS,
+
+		ECAUTOSENDTOCLIENTTHREADEVENTS_COUNT  //must be last
+	};
+
+	void FireSendToClient(CClipList *pClipList);
+
+protected:
+	virtual void OnEvent(int eventId, void *param);
+	virtual void OnTimeOut(void *param);
+
+	void OnSendToClient();
+	bool SendToClient(CClipList *pClipList);
+
+	CCriticalSection m_cs;
+	CClipList m_saveClips;
+};
+

+ 302 - 0
HotKeys.cpp

@@ -0,0 +1,302 @@
+#include "stdafx.h"
+#include "HotKeys.h"
+#include "Options.h"
+#include "Misc.h"
+
+CHotKeys g_HotKeys;
+
+
+CHotKey::CHotKey( CString name, DWORD defKey, bool bUnregOnShowDitto ) 
+	: m_Name(name), m_bIsRegistered(false), m_bUnRegisterOnShowDitto(bUnregOnShowDitto)
+{
+	m_Atom = ::GlobalAddAtom( m_Name );
+	ASSERT( m_Atom );
+	m_Key = (DWORD) g_Opt.GetProfileLong( m_Name, (long) defKey );
+	g_HotKeys.Add( this );
+}
+
+CHotKey::~CHotKey()
+{
+	Unregister();
+}
+
+void CHotKey::SetKey( DWORD key, bool bSave )
+{
+	if(m_Key == key)
+	{
+		return;
+	}
+
+	if( m_bIsRegistered )
+		Unregister();
+	m_Key = key;
+	if( bSave )
+		SaveKey();
+}
+
+void CHotKey::LoadKey()
+{
+	SetKey( (DWORD) g_Opt.GetProfileLong( m_Name, 0 ) );
+}
+
+bool CHotKey::SaveKey()
+{
+	return g_Opt.SetProfileLong( m_Name, (long) m_Key ) != FALSE;
+}
+
+BOOL CHotKey::ValidateHotKey(DWORD dwHotKey)
+{
+	ATOM id = ::GlobalAddAtom(_T("HK_VALIDATE"));
+	BOOL bResult = ::RegisterHotKey( g_HotKeys.m_hWnd,
+		id,
+		GetModifier(dwHotKey),
+		LOBYTE(dwHotKey) );
+
+	if(bResult)
+		::UnregisterHotKey(g_HotKeys.m_hWnd, id);
+
+	::GlobalDeleteAtom(id);
+
+	return bResult;
+}
+
+void CHotKey::CopyFromCtrl(CHotKeyCtrl& ctrl, HWND hParent, int nWindowsCBID) 
+{ 
+	long lHotKey = ctrl.GetHotKey();
+
+	short sKeyKode = LOBYTE(lHotKey);
+	short sModifers = HIBYTE(lHotKey);
+
+	if(lHotKey && ::IsDlgButtonChecked(hParent, nWindowsCBID))
+	{
+		sModifers |= HOTKEYF_EXT;
+	}
+
+	SetKey(MAKEWORD(sKeyKode, sModifers)); 
+}
+
+void CHotKey::CopyToCtrl(CHotKeyCtrl& ctrl, HWND hParent, int nWindowsCBID)
+{
+	long lModifiers = HIBYTE(m_Key);
+
+	ctrl.SetHotKey(LOBYTE(m_Key), (WORD)lModifiers); 
+
+	if(lModifiers & HOTKEYF_EXT)
+	{
+		::CheckDlgButton(hParent, nWindowsCBID, BST_CHECKED);
+	}
+}
+
+UINT CHotKey::GetModifier(DWORD dwHotKey)
+{
+	UINT uMod = 0;
+	if( HIBYTE(dwHotKey) & HOTKEYF_SHIFT )   uMod |= MOD_SHIFT;
+	if( HIBYTE(dwHotKey) & HOTKEYF_CONTROL ) uMod |= MOD_CONTROL;
+	if( HIBYTE(dwHotKey) & HOTKEYF_ALT )     uMod |= MOD_ALT;
+	if( HIBYTE(dwHotKey) & HOTKEYF_EXT )     uMod |= MOD_WIN;
+
+	return uMod;
+}
+
+bool CHotKey::Register()
+{
+	if(m_Key)
+	{
+		if(m_bIsRegistered == false)
+		{
+			ASSERT(g_HotKeys.m_hWnd);
+			m_bIsRegistered = ::RegisterHotKey(g_HotKeys.m_hWnd,
+				m_Atom,
+				GetModifier(),
+				LOBYTE(m_Key) ) == TRUE;
+		}
+	}
+	else
+		m_bIsRegistered = true;
+
+	return m_bIsRegistered;
+}
+bool CHotKey::Unregister(bool bOnShowingDitto)
+{
+	if(!m_bIsRegistered)
+		return true;
+
+	if(bOnShowingDitto)
+	{
+		if(m_bUnRegisterOnShowDitto == false)
+			return true;
+	}
+
+	if(m_Key)
+	{
+		ASSERT(g_HotKeys.m_hWnd);
+		if(::UnregisterHotKey( g_HotKeys.m_hWnd, m_Atom))
+		{
+			m_bIsRegistered = false;
+			return true;
+		}
+		else
+		{
+			Log(_T("Unregister FAILED!"));
+			ASSERT(0);
+		}
+	}
+	else
+	{
+		m_bIsRegistered = false;
+		return true;
+	}
+
+	return false;
+}
+
+CHotKeys::CHotKeys() : m_hWnd(NULL) 
+{
+
+}
+
+CHotKeys::~CHotKeys()
+{
+	CHotKey* pHotKey;
+	int count = GetSize();
+	for(int i=0; i < count; i++)
+	{
+		pHotKey = ElementAt(i);
+		if(pHotKey)
+			delete pHotKey;
+	}
+}
+
+int CHotKeys::Find( CHotKey* pHotKey )
+{
+	int count = GetSize();
+	for(int i=0; i < count; i++)
+	{
+		if( pHotKey == ElementAt(i) )
+			return i;
+	}
+	return -1;
+}
+
+bool CHotKeys::Remove( CHotKey* pHotKey )
+{
+	int i = Find(pHotKey);
+	if(i >= 0)
+	{
+		RemoveAt(i);
+		return true;
+	}
+	return false;
+}
+
+void CHotKeys::LoadAllKeys()
+{
+	int count = GetSize();
+	for(int i=0; i < count; i++)
+		ElementAt(i)->LoadKey();
+}
+
+void CHotKeys::SaveAllKeys()
+{
+	int count = GetSize();
+	for(int i=0; i < count; i++)
+		ElementAt(i)->SaveKey();
+}
+
+void CHotKeys::RegisterAll(bool bMsgOnError)
+{
+	CString str;
+	CHotKey* pHotKey;
+	int count = GetSize();
+	for(int i = 0; i < count; i++)
+	{
+		pHotKey = ElementAt(i);
+		if(!pHotKey->Register())
+		{
+			str =  "Error Registering ";
+			str += pHotKey->GetName();
+			Log(str);
+			if(bMsgOnError)
+				AfxMessageBox(str);
+		}
+	}
+}
+
+void CHotKeys::UnregisterAll(bool bMsgOnError, bool bOnShowDitto)
+{
+	CString str;
+	CHotKey* pHotKey;
+	int count = GetSize();
+	for(int i = 0; i < count; i++)
+	{
+		pHotKey = ElementAt(i);
+		if(!pHotKey->Unregister(bOnShowDitto))
+		{
+			str = "Error Unregistering ";
+			str += pHotKey->GetName();
+			Log(str);
+			if(bMsgOnError)
+				AfxMessageBox(str);
+		}
+	}
+}
+
+void CHotKeys::GetKeys(ARRAY& keys)
+{
+	int count = GetSize();
+	keys.SetSize(count);
+	for(int i=0; i < count; i++)
+		keys[i] = ElementAt(i)->GetKey();
+}
+
+// caution! this alters hotkeys based upon corresponding indexes
+void CHotKeys::SetKeys(ARRAY& keys, bool bSave)
+{
+	int count = GetSize();
+	ASSERT(count == keys.GetSize());
+	for(int i=0; i < count; i++)
+		ElementAt(i)->SetKey(keys[i], bSave);
+}
+
+bool CHotKeys::FindFirstConflict(ARRAY& keys, int* pX, int* pY)
+{
+	bool bConflict = false;
+	int i, j;
+	int count = keys.GetSize();
+	DWORD key;
+	for(i = 0; i < count && !bConflict; i++)
+	{
+		key = keys.ElementAt(i);
+		// only check valid keys
+		if(key == 0)
+			continue;
+
+		// scan the array for a duplicate
+		for(j = i+1; j < count; j++ )
+		{
+			if(keys.ElementAt(j) == key)
+			{
+				bConflict = true;
+				break;
+			}
+		}
+	}
+
+	if(bConflict)
+	{
+		if(pX)
+			*pX = i-1;
+		if(pY)
+			*pY = j;
+	}
+
+	return bConflict;
+}
+
+// if true, pX and pY (if valid) are set to the indexes of the conflicting hotkeys.
+bool CHotKeys::FindFirstConflict(int* pX, int* pY)
+{
+	ARRAY keys;
+	GetKeys(keys);
+	return FindFirstConflict(keys, pX, pY);
+}

+ 73 - 0
HotKeys.h

@@ -0,0 +1,73 @@
+#pragma once
+
+#include "ArrayEx.h"
+
+class CHotKey
+{
+public:
+	CString	m_Name;
+	ATOM	m_Atom;
+	DWORD	m_Key; //704 is ctrl-tilda
+	bool	m_bIsRegistered;
+	bool	m_bUnRegisterOnShowDitto;
+	
+	CHotKey( CString name, DWORD defKey = 0, bool bUnregOnShowDitto = false );
+	~CHotKey();
+
+	bool	IsRegistered() { return m_bIsRegistered; }
+	CString GetName()      { return m_Name; }
+	DWORD   GetKey()       { return m_Key; }
+
+	void SetKey( DWORD key, bool bSave = false );
+	// profile
+	void LoadKey();
+	bool SaveKey();
+
+	void CopyFromCtrl(CHotKeyCtrl& ctrl, HWND hParent, int nWindowsCBID);
+	void CopyToCtrl(CHotKeyCtrl& ctrl, HWND hParent, int nWindowsCBID);
+
+//	CString GetKeyAsText();
+//	void SetKeyFromText( CString text );
+
+	static BOOL ValidateHotKey(DWORD dwHotKey);
+	static UINT GetModifier(DWORD dwHotKey);
+	UINT GetModifier() { return GetModifier(m_Key); }
+
+	bool Register();
+	bool Unregister(bool bOnShowingDitto = false);
+};
+
+
+/*------------------------------------------------------------------*\
+	CHotKeys - Manages system-wide hotkeys
+\*------------------------------------------------------------------*/
+
+class CHotKeys : public CArray<CHotKey*,CHotKey*>
+{
+public:
+	HWND	m_hWnd;
+
+	CHotKeys();
+	~CHotKeys();
+
+	void Init( HWND hWnd ) { m_hWnd = hWnd; }
+
+	int Find( CHotKey* pHotKey );
+	bool Remove( CHotKey* pHotKey ); // pHotKey is NOT deleted.
+
+	// profile load / save
+	void LoadAllKeys();
+	void SaveAllKeys();
+
+	void RegisterAll(bool bMsgOnError = false);
+	void UnregisterAll(bool bMsgOnError = false, bool bOnShowDitto = false);
+
+	void GetKeys( ARRAY& keys );
+	void SetKeys( ARRAY& keys, bool bSave = false ); // caution! this alters hotkeys based upon corresponding indexes
+
+	static bool FindFirstConflict( ARRAY& keys, int* pX = NULL, int* pY = NULL );
+	// if true, pX and pY (if valid) are set to the index of the conflicting hotkeys.
+	bool FindFirstConflict( int* pX = NULL, int* pY = NULL );
+};
+
+extern CHotKeys g_HotKeys;

+ 182 - 0
MainFrmThread.cpp

@@ -0,0 +1,182 @@
+#include "stdafx.h"
+#include "MainFrmThread.h"
+#include "DatabaseUtilities.h"
+#include "Options.h"
+#include "Misc.h"
+#include "cp_main.h"
+
+CMainFrmThread::CMainFrmThread(void)
+{
+    for(int eventEnum = 0; eventEnum < ECMAINFRMTHREADEVENTS_COUNT; eventEnum++)
+    {
+        AddEvent(eventEnum);
+    }
+}
+
+CMainFrmThread::~CMainFrmThread(void)
+{
+
+}
+
+void CMainFrmThread::AddClipToSave(CClip *pClip)
+{
+	ATL::CCritSecLock csLock(m_cs.m_sect);
+
+	Log(_T("Adding clip to thread for save to db"));
+	m_saveClips.AddTail(pClip);
+	FireEvent(SAVE_CLIPS);
+}
+
+void CMainFrmThread::AddRemoteClipToSave(CClipList *pClipList)
+{
+	ATL::CCritSecLock csLock(m_cs.m_sect);
+
+	Log(_T("Adding REMOTE clip to thread for save to db"));
+	
+	POSITION pos = pClipList->GetHeadPosition();
+	while(pos)
+	{
+		CClip *pClip = pClipList->GetNext(pos);
+		m_saveRemoteClips.AddTail(pClip);
+	}
+
+	//local cliplist now owns the clip memory
+	pClipList->RemoveAll();
+	
+	FireEvent(SAVE_REMOTE_CLIPS);
+}
+
+void CMainFrmThread::OnEvent(int eventId, void *param)
+{
+    switch((eCMainFrmThreadEvents)eventId)
+    {
+        case DELETE_ENTRIES:
+            OnDeleteEntries();
+            break;
+        case REMOVE_REMOTE_FILES:
+            OnRemoveRemoteFiles();
+            break;
+		case SAVE_CLIPS:
+			OnSaveClips();
+			break;
+		case SAVE_REMOTE_CLIPS:
+			OnSaveRemoteClips();
+			break;
+    }
+}
+
+void CMainFrmThread::OnDeleteEntries()
+{
+    RemoveOldEntries();
+}
+
+void CMainFrmThread::OnRemoveRemoteFiles()
+{
+    CString csDir = CGetSetOptions::GetPath(PATH_REMOTE_FILES);
+    if(FileExists(csDir))
+    {
+        DeleteReceivedFiles(csDir);
+    }
+}
+
+void CMainFrmThread::OnSaveClips()
+{
+	CClipList *pLocalClips = new CClipList();
+
+	//Save the clips locally
+	{
+		ATL::CCritSecLock csLock(m_cs.m_sect);
+
+		POSITION pos;
+		CClip* pClip;
+
+		pos = m_saveClips.GetHeadPosition();
+		while(pos)
+		{
+			pClip = m_saveClips.GetNext(pos);
+			pLocalClips->AddTail(pClip);
+		}
+
+		//pLocalClips now own, the clips
+		m_saveClips.RemoveAll();
+	}
+
+	Log(_T("SaveCopyClips Before AddToDb")); 
+
+	int count = pLocalClips->AddToDB(true);
+
+	Log(StrF(_T("SaveCopyclips After AddToDb, Count: %d"), count));
+
+	if(count > 0)
+	{
+		int Id = pLocalClips->GetTail()->m_ID;
+
+		Log(StrF(_T("SaveCopyclips After AddToDb, Id: %d Before OnCopyCopyCompleted"), Id));
+
+		theApp.OnCopyCompleted(Id, count);
+
+		Log(StrF(_T("SaveCopyclips After AddToDb, Id: %d After OnCopyCopyCompleted"), Id));
+	
+		if(g_Opt.m_lAutoSendClientCount > 0)
+		{
+			m_sendToClientThread.FireSendToClient(pLocalClips);
+		}
+	}
+
+	delete pLocalClips;
+}
+
+void CMainFrmThread::OnSaveRemoteClips()
+{
+	LogSendRecieveInfo("---------Start of OnSaveRemoteClips");
+
+	CClipList *pLocalClips = new CClipList();
+
+	//Save the clips locally
+	{
+		ATL::CCritSecLock csLock(m_cs.m_sect);
+
+		POSITION pos;
+		CClip* pClip;
+
+		pos = m_saveRemoteClips.GetHeadPosition();
+		while(pos)
+		{
+			pClip = m_saveRemoteClips.GetNext(pos);
+			pLocalClips->AddTail(pClip);
+		}
+
+		//pLocalClips now own, the clips
+		m_saveRemoteClips.RemoveAll();
+	}
+
+	LogSendRecieveInfo("---------OnSaveRemoteClips - Before AddToDB");
+
+	int count = pLocalClips->AddToDB(true);
+
+	LogSendRecieveInfo("---------OnSaveRemoteClips - After AddToDB");
+
+	//are we supposed to add this clip to the clipboard
+	CClip *pLastClip = pLocalClips->GetTail();
+	if(pLastClip && pLastClip->m_param1 == TRUE)
+	{
+		theApp.m_FocusID = pLastClip->m_ID;
+
+		LogSendRecieveInfo("---------OnSaveRemoteClips - Before Posting msg to main thread to set clipboard");
+
+		//set the clipboard on the main thread, i was having a problem with setting the clipboard on a thread
+		//guess it needs to be set on the main thread
+		//main window will clear this memory
+		PostMessage(theApp.m_MainhWnd, WM_LOAD_ClIP_ON_CLIPBOARD, (LPARAM)pLastClip, 0);
+
+		LogSendRecieveInfo("---------OnSaveRemoteClips - After Posting msg to main thread to set clipboard");
+
+		pLocalClips->RemoveTail();
+	}
+
+	theApp.RefreshView();
+
+	delete pLocalClips;
+
+	LogSendRecieveInfo("---------End of OnSaveRemoteClips");
+}

+ 41 - 0
MainFrmThread.h

@@ -0,0 +1,41 @@
+#pragma once
+#include "EventThread.h"
+#include "Clip.h"
+#include "AutoSendToClientThread.h"
+#include <afxmt.h>
+
+class CMainFrmThread : public CEventThread
+{
+public:
+    CMainFrmThread(void);
+    ~CMainFrmThread(void);
+
+    enum eCMainFrmThreadEvents
+    {
+        DELETE_ENTRIES, 
+        REMOVE_REMOTE_FILES, 
+		SAVE_CLIPS,
+		SAVE_REMOTE_CLIPS,
+
+        ECMAINFRMTHREADEVENTS_COUNT  //must be last
+    };
+
+    void FireDeleteEntries() { FireEvent(DELETE_ENTRIES); }
+    void FireRemoveRemoteFiles() { FireEvent(REMOVE_REMOTE_FILES); }
+
+	void AddClipToSave(CClip *pClip);
+	void AddRemoteClipToSave(CClipList *pClipList);
+
+protected:
+    virtual void OnEvent(int eventId, void *param);
+
+    void OnDeleteEntries();
+    void OnRemoveRemoteFiles();
+	void OnSaveClips();
+	void OnSaveRemoteClips();
+
+	CCriticalSection m_cs;
+	CClipList m_saveClips;
+	CClipList m_saveRemoteClips;
+	CAutoSendToClientThread m_sendToClientThread;
+};

+ 238 - 0
Popup.cpp

@@ -0,0 +1,238 @@
+#include "stdafx.h"
+#include "Popup.h"
+#include "Misc.h"
+
+void InitToolInfo( TOOLINFO& ti )
+{
+	// INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE
+	ti.cbSize = sizeof(TOOLINFO);
+	ti.uFlags = TTF_ABSOLUTE | TTF_TRACK;
+	ti.hwnd = NULL;
+	ti.hinst = NULL;
+	ti.uId = 0; // CPopup only uses uid 0
+	ti.lpszText = NULL;
+	// ToolTip control will cover the whole window
+	ti.rect.left = 0;
+	ti.rect.top = 0;
+	ti.rect.right = 0;
+	ti.rect.bottom = 0;
+}
+
+/*------------------------------------------------------------------*\
+CPopup - a tooltip that pops up manually (when Show is called).
+- technique learned from codeproject "ToolTipZen" by "Zarembo Maxim"
+\*------------------------------------------------------------------*/
+
+CPopup::CPopup()
+{
+	Init();
+}
+
+// HWND_TOP
+CPopup::CPopup( int x, int y, HWND hWndPosRelativeTo, HWND hWndInsertAfter )
+{
+	Init();
+	m_hWndPosRelativeTo = hWndPosRelativeTo;
+	m_hWndInsertAfter = hWndInsertAfter;
+	SetPos( CPoint(x,y) );
+}
+
+CPopup::~CPopup()
+{
+	Hide();
+	if( m_bOwnTT && ::IsWindow(m_hTTWnd) )
+		::DestroyWindow( m_hTTWnd );
+}
+
+void CPopup::Init()
+{
+	// initialize variables
+	m_bOwnTT = false;
+	m_hTTWnd = NULL;
+	m_bIsShowing = false;
+	m_bAllowShow = true; // used by AllowShow()
+	
+	m_Pos.x = m_Pos.y = 0;
+	m_bTop = true;
+	m_bLeft = true;
+	m_bCenterX = false;
+	m_bCenterY = false;
+	m_hWndPosRelativeTo = NULL;
+	
+	RECT rcScreen;
+	
+	GetMonitorRect(-1, &rcScreen);
+	
+	m_ScreenMaxX = rcScreen.right;
+	m_ScreenMaxY = rcScreen.bottom;
+	
+	m_hWndInsertAfter = HWND_TOP; //HWND_TOPMOST
+	
+	SetTTWnd();
+}
+
+void CPopup::SetTTWnd( HWND hTTWnd, TOOLINFO* pTI )
+{
+	if( pTI )
+		m_TI = *pTI;
+	else
+		InitToolInfo( m_TI );
+	
+	if( m_bOwnTT && ::IsWindow(m_hTTWnd) )
+	{
+		if( !::IsWindow(hTTWnd) )
+			return; // we would have to recreate the one that already exists
+		::DestroyWindow( m_hTTWnd );
+	}
+	
+	m_hTTWnd = hTTWnd;
+	if( ::IsWindow(m_hTTWnd) )
+	{
+		m_bOwnTT = false;
+		// if our uid tooltip already exists, get the data, else add it.
+		if( ! ::SendMessage(m_hTTWnd, TTM_GETTOOLINFO, 0, (LPARAM)(LPTOOLINFO) &m_TI) )
+			::SendMessage(m_hTTWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_TI);
+	}
+	else
+	{
+		m_bOwnTT = true;
+		CreateToolTip();
+	}
+}
+
+void CPopup::CreateToolTip()
+{
+	if( m_hTTWnd != NULL )
+		return;
+	
+	// CREATE A TOOLTIP WINDOW
+	m_hTTWnd = CreateWindowEx(
+		WS_EX_TOPMOST,
+		TOOLTIPS_CLASS,
+		NULL,
+		TTS_NOPREFIX | TTS_ALWAYSTIP,		
+		CW_USEDEFAULT,
+		CW_USEDEFAULT,
+		CW_USEDEFAULT,
+		CW_USEDEFAULT,
+		NULL,
+		NULL,
+		NULL,
+		NULL
+		);
+	m_bOwnTT = true;
+	
+	// SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW
+	::SendMessage(m_hTTWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_TI);
+}
+
+void CPopup::SetTimeout( int timeout )
+{
+	if( m_hTTWnd == NULL )
+		return;
+	::SendMessage(m_hTTWnd, TTM_SETDELAYTIME, TTDT_AUTOMATIC, timeout);
+}
+
+void CPopup::SetPos( CPoint& pos )
+{
+	m_Pos = pos;
+}
+
+void CPopup::SetPosInfo( bool bTop, bool bCenterY, bool bLeft, bool bCenterX )
+{
+	m_bTop = bTop;
+	m_bCenterY = bCenterY;
+	m_bLeft = bLeft;
+	m_bCenterX = bCenterX;
+}
+
+void CPopup::AdjustPos( CPoint& pos )
+{
+	CRect rel(0,0,0,0);
+	CRect rect(0,0,0,0);
+	
+	//	::SendMessage(m_hTTWnd, TTM_ADJUSTRECT, TRUE, (LPARAM)&rect);
+	::GetWindowRect(m_hTTWnd,&rect);
+	
+	if( ::IsWindow(m_hWndPosRelativeTo) )
+		::GetWindowRect(m_hWndPosRelativeTo, &rel);
+	
+	// move the rect to the relative origin
+	rect.bottom = rect.Height() + rel.top;
+	rect.top = rel.top;
+	rect.right = rect.Width() + rel.left;
+	rect.left = rel.left;
+	
+	// adjust the y position
+	rect.OffsetRect( 0, pos.y - (m_bCenterY? rect.Height()/2: (m_bTop? 0: rect.Height())) );
+	if( rect.bottom > m_ScreenMaxY )
+		rect.OffsetRect( 0, m_ScreenMaxY - rect.bottom );
+	
+	// adjust the x position
+	rect.OffsetRect( pos.x - (m_bCenterX? rect.Width()/2: (m_bLeft? 0: rect.Width())), 0 );
+	if( rect.right > m_ScreenMaxX )
+		rect.OffsetRect( m_ScreenMaxX - rect.right, 0 );
+	
+	pos.x = rect.left;
+	pos.y = rect.top;
+}
+
+void CPopup::SendToolTipText( CString text )
+{
+	m_csToolTipText = text;
+	
+	//Replace the tabs with spaces, the tooltip didn't like the \t s
+	text.Replace(_T("\t"), _T("  "));
+	m_TI.lpszText = (LPTSTR) (LPCTSTR) text;
+	
+	// this allows \n and \r to be interpreted correctly
+	::SendMessage(m_hTTWnd, TTM_SETMAXTIPWIDTH, 0, 500);
+	// set the text
+	::SendMessage(m_hTTWnd, TTM_SETTOOLINFO, 0, (LPARAM) (LPTOOLINFO) &m_TI);
+}
+
+void CPopup::Show( CString text, CPoint pos, bool bAdjustPos )
+{
+	if( m_hTTWnd == NULL )
+		return;
+
+	m_csToolTipText = text;
+	
+	if( !m_bIsShowing )
+		::SendMessage(m_hTTWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG(-10000,-10000));
+	
+	SendToolTipText( text );
+	::SendMessage(m_hTTWnd, TTM_TRACKACTIVATE, true, (LPARAM)(LPTOOLINFO) &m_TI);
+	if( bAdjustPos )
+		AdjustPos(pos);
+	// set the position
+	::SendMessage(m_hTTWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG(pos.x,pos.y));
+	
+	// make sure the tooltip will be on top.
+	::SetWindowPos( m_hTTWnd, m_hWndInsertAfter, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE );
+	
+	m_bIsShowing = true;
+}
+
+void CPopup::Show( CString text )
+{ 
+	m_csToolTipText = text;
+	Show( text, m_Pos ); 
+}
+
+void CPopup::AllowShow( CString text )
+{
+	m_csToolTipText = text;
+	
+	if( m_bAllowShow )
+		Show( text, m_Pos );
+}
+
+void CPopup::Hide()
+{
+	if( m_hTTWnd == NULL )
+		return;
+	// deactivate if it is currently activated
+	::SendMessage(m_hTTWnd, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_TI);
+	m_bIsShowing = false;
+}

+ 57 - 0
Popup.h

@@ -0,0 +1,57 @@
+#pragma once
+/*------------------------------------------------------------------*\
+	CPopup - a tooltip that pops up manually (when Show is called).
+	- technique learned from codeproject "ToolTipZen" by "Zarembo Maxim"
+\*------------------------------------------------------------------*/
+
+void InitToolInfo( TOOLINFO& ti ); // initializes toolinfo with uid 0
+
+class CPopup
+{
+public:
+	bool m_bOwnTT;
+
+	HWND m_hTTWnd; // handle to the ToolTip control
+	TOOLINFO m_TI; // struct specifying info about tool in ToolTip control
+
+	bool m_bIsShowing;
+
+	bool m_bTop;  // true if m_Pos.x is the top, false if the bottom
+	bool m_bLeft; // true if m_Pos.y is the left, false if the right
+	bool m_bCenterY; // true if m_Pos is the y center, false if corner
+	bool m_bCenterX; // true if m_Pos is the x center, false if corner
+	HWND m_hWndPosRelativeTo;
+	CPoint m_Pos;
+
+	int m_ScreenMaxX;
+	int m_ScreenMaxY;
+
+	HWND m_hWndInsertAfter;
+
+	bool m_bAllowShow; // used by SafeShow to determine whether to show or not
+
+	CString m_csToolTipText;
+
+	CPopup();
+	CPopup( int x, int y, HWND hWndPosRelativeTo = NULL, HWND hWndInsertAfter = HWND_TOP );
+	~CPopup();
+
+	void Init();
+	void SetTTWnd( HWND hTTWnd = NULL, TOOLINFO* pTI = NULL );
+	void CreateToolTip();
+
+	void SetTimeout( int timeout );
+
+	void AdjustPos( CPoint& pos );
+	void SetPos( CPoint& pos );
+	void SetPosInfo( bool bTop, bool bCenterY, bool bLeft, bool bCenterX );
+
+	void SendToolTipText( CString text );
+
+	void Show( CString text, CPoint pos, bool bAdjustPos = true );
+	void Show( CString text );
+	void AllowShow( CString text ); // only shows if m_bAllowShow is true
+
+	void Hide();
+};
+

+ 274 - 0
QPasteWndThread.cpp

@@ -0,0 +1,274 @@
+#include "stdafx.h"
+#include "QPasteWndThread.h"
+#include "Misc.h"
+#include "Options.h"
+#include "QPasteWnd.h"
+#include "cp_main.h"
+
+CQPasteWndThread::CQPasteWndThread(void)
+{
+    m_waitTimeout = ONE_HOUR * 12;
+
+    m_SearchingEvent = CreateEvent(NULL, TRUE, FALSE, _T(""));
+
+    for(int eventEnum = 0; eventEnum < ECQPASTEWNDTHREADEVENTS_COUNT; eventEnum++)
+    {
+        AddEvent(eventEnum);
+    }
+}
+
+CQPasteWndThread::~CQPasteWndThread(void)
+{
+    CloseHandle(m_SearchingEvent);
+}
+
+void CQPasteWndThread::OnTimeOut(void *param)
+{
+    CloseDatabase();
+}
+
+void CQPasteWndThread::OnEvent(int eventId, void *param)
+{
+    switch((eCQPasteWndThreadEvents)eventId)
+    {
+        case DO_QUERY:
+            OnDoQuery(param);
+            break;
+        case LOAD_ACCELERATORS:
+            OnLoadAccelerators(param);
+            break;
+        case UNLOAD_ACCELERATORS:
+            OnUnloadAccelerators(param);
+            break;
+        case LOAD_ITEMS:
+            OnLoadItems(param);
+            break;
+        case LOAD_EXTRA_DATA:
+            OnLoadExtraData(param);
+            break;
+    }
+}
+
+void CQPasteWndThread::OnDoQuery(void *param)
+{
+    CQPasteWnd *pasteWnd = (CQPasteWnd*)param;
+
+    static CEvent UpdateTimeEvent(TRUE, TRUE, _T("Ditto_Update_Clip_Time"), NULL);
+    //If we pasted then wait for the time on the pasted event to be updated before we query the db
+    DWORD dRet = WaitForSingleObject(UpdateTimeEvent, 2000);
+
+    ResetEvent(m_SearchingEvent);
+    long lTick = GetTickCount();
+
+    pasteWnd->m_CritSection.Lock();
+    pasteWnd->m_bFoundClipToSetFocusTo = false;
+    CString CountSQL(pasteWnd->m_CountSQL);
+    pasteWnd->m_mapCache.clear();
+    pasteWnd->m_loadItems.clear();
+    pasteWnd->m_bStopQuery = false;
+    pasteWnd->m_CritSection.Unlock();
+
+    long lRecordCount = 0;
+
+    try
+    {
+        lRecordCount = m_db.execScalar(CountSQL);
+        ::PostMessage(pasteWnd->m_hWnd, NM_SET_LIST_COUNT, lRecordCount, 0);
+    }
+    CATCH_SQLITE_EXCEPTION 
+
+    SetEvent(m_SearchingEvent);
+
+    Log(StrF(_T("Set list count = %d, time = %d"), lRecordCount, GetTickCount() - lTick));
+}
+
+void CQPasteWndThread::OnLoadItems(void *param)
+{
+    CQPasteWnd *pasteWnd = (CQPasteWnd*)param;
+
+    ResetEvent(m_SearchingEvent);
+
+    long startTick = GetTickCount();
+    int loadItemsIndex = 0;
+    int loadItemsCount = 0;
+    int loadCount = 0;
+    CString localSql;
+    bool clearFirstLoadItem = false;
+
+    pasteWnd->m_CritSection.Lock();
+    if(pasteWnd->m_loadItems.size() > 0)
+    {
+        loadItemsIndex = pasteWnd->m_loadItems[0].x;
+        loadItemsCount = pasteWnd->m_loadItems[0].y - pasteWnd->m_loadItems[0].x;
+        localSql = pasteWnd->m_SQL;
+        pasteWnd->m_bStopQuery = false;
+        clearFirstLoadItem = true;
+    }
+    pasteWnd->m_CritSection.Unlock();
+
+    if(clearFirstLoadItem)
+    {
+        Log(StrF(_T("Load Items start = %d, count = %d"), loadItemsIndex, loadItemsCount));
+
+        CString limit;
+        limit.Format(_T(" LIMIT %d OFFSET %d"), loadItemsCount, loadItemsIndex);
+        localSql += limit;
+
+        CMainTable table;
+
+        CppSQLite3Query q = m_db.execQuery(localSql);
+        while(!q.eof())
+        {
+            pasteWnd->FillMainTable(table, q);
+            table.m_listIndex = loadItemsIndex;
+
+            pasteWnd->m_CritSection.Lock();
+            {
+                pasteWnd->m_mapCache[loadItemsIndex] = table;
+            }
+            pasteWnd->m_CritSection.Unlock();
+
+            if(pasteWnd->m_bStopQuery)
+            {
+                Log(StrF(_T("StopQuery called exiting filling cache count = %d"), loadItemsIndex));
+                break;
+            }
+
+            q.nextRow();
+
+            loadItemsIndex++;
+            loadCount++;
+
+            ::PostMessage(pasteWnd->m_hWnd, NM_REFRESH_ROW, table.m_lID, table.m_listIndex);
+        }
+
+        ::PostMessage(pasteWnd->m_hWnd, NM_REFRESH_ROW,  - 1, 0);
+
+        if(clearFirstLoadItem)
+        {
+            pasteWnd->m_CritSection.Lock();
+            pasteWnd->m_loadItems.erase(pasteWnd->m_loadItems.begin());
+            pasteWnd->m_CritSection.Unlock();
+        }
+
+        Log(StrF(_T("Load items End count = %d, time = %d"), loadCount, GetTickCount() - startTick));
+    }
+
+    SetEvent(m_SearchingEvent);
+}
+
+void CQPasteWndThread::OnLoadExtraData(void *param)
+{
+    ResetEvent(m_SearchingEvent);
+
+    CQPasteWnd *pasteWnd = (CQPasteWnd*)param;
+
+    Log(_T("Start of load extra data, Bitmaps/rtf"));
+
+    std::vector < CClipFormatQListCtrl > localFormats;
+    pasteWnd->m_CritSection.Lock();
+    int count = pasteWnd->m_ExtraDataLoadItems.size();
+    for(int i = 0; i < count; i++)
+    {
+        localFormats.push_back(pasteWnd->m_ExtraDataLoadItems[i]);
+    }
+    pasteWnd->m_ExtraDataLoadItems.clear();
+    pasteWnd->m_CritSection.Unlock();
+
+    count = localFormats.size();
+    for(int i = 0; i < count; i++)
+    {
+        if(theApp.GetClipData(localFormats[i].m_lDBID, localFormats[i]))
+        {
+            Log(StrF(_T("Loaded, extra data for clip %d, type: %d"), localFormats[i].m_lDBID, localFormats[i].m_cfType));
+
+            pasteWnd->m_CritSection.Lock();
+
+            if(localFormats[i].m_cfType == CF_DIB)
+            {
+                pasteWnd->m_cf_dibCache[localFormats[i].m_lDBID] = localFormats[i];
+                //the cache now owns the format data, set it to delete the data in the destructor
+                pasteWnd->m_cf_dibCache[localFormats[i].m_lDBID].m_autoDeleteData = true;
+            }
+            else if(localFormats[i].m_cfType == theApp.m_RTFFormat)
+            {
+                pasteWnd->m_cf_rtfCache[localFormats[i].m_lDBID] = localFormats[i];
+                //the cache now owns the format data, set it to delete the data in the destructor
+                pasteWnd->m_cf_rtfCache[localFormats[i].m_lDBID].m_autoDeleteData = true;
+            }
+
+            pasteWnd->m_CritSection.Unlock();
+
+            ::PostMessage(pasteWnd->m_hWnd, NM_REFRESH_ROW, localFormats[i].m_lDBID, localFormats[i].m_clipRow);
+        }
+        else
+        {
+            pasteWnd->m_CritSection.Lock();
+
+            if(localFormats[i].m_cfType == CF_DIB)
+            {
+                CClipFormatQListCtrl localFormat;
+                pasteWnd->m_cf_dibCache[localFormats[i].m_lDBID] = localFormat;
+            }
+            else if(localFormats[i].m_cfType == theApp.m_RTFFormat)
+            {
+                CClipFormatQListCtrl localFormat;
+                pasteWnd->m_cf_rtfCache[localFormats[i].m_lDBID] = localFormat;
+            }
+
+            pasteWnd->m_CritSection.Unlock();
+        }
+    }
+
+    SetEvent(m_SearchingEvent);
+    Log(_T("End of load extra data, Bitmaps/rtf"));
+}
+
+void CQPasteWndThread::OnLoadAccelerators(void *param)
+{
+    CQPasteWnd *pasteWnd = (CQPasteWnd*)param;
+    OpenDatabase();
+    pasteWnd->m_lstHeader.DestroyAndCreateAccelerator(TRUE, m_db);
+}
+
+void CQPasteWndThread::OnUnloadAccelerators(void *param)
+{
+    CQPasteWnd *pasteWnd = (CQPasteWnd*)param;
+    OpenDatabase();
+    pasteWnd->m_lstHeader.DestroyAndCreateAccelerator(FALSE, m_db);
+}
+
+void CQPasteWndThread::OpenDatabase()
+{
+    try
+    {
+        if(m_dbPath.IsEmpty() == FALSE)
+        {
+            if(m_dbPath != CGetSetOptions::GetDBPath())
+            {
+                CloseDatabase();
+            }
+            else
+            {
+                return ;
+            }
+        }
+
+        m_dbPath = CGetSetOptions::GetDBPath();
+
+        DWORD dStart = GetTickCount();
+        m_db.open(m_dbPath);
+        Log(StrF(_T("Thread RunThread is starting time to open the database - %d"), GetTickCount() - dStart));
+    }
+    CATCH_SQLITE_EXCEPTION
+}
+
+void CQPasteWndThread::CloseDatabase()
+{
+    try
+    {
+        m_dbPath.Empty();
+        m_db.close();
+    }
+    CATCH_SQLITE_EXCEPTION
+}

+ 57 - 0
QPasteWndThread.h

@@ -0,0 +1,57 @@
+#pragma once
+#include "EventThread.h"
+#include "sqlite/CppSQLite3.h"
+
+class CQPasteWndThread: public CEventThread
+{
+public:
+    CQPasteWndThread(void);
+    ~CQPasteWndThread(void);
+
+    enum eCQPasteWndThreadEvents
+    {
+        DO_QUERY, LOAD_ACCELERATORS, UNLOAD_ACCELERATORS, LOAD_ITEMS, LOAD_EXTRA_DATA, 
+
+        ECQPASTEWNDTHREADEVENTS_COUNT  //must be last
+
+    };
+
+    void FireDoQuery()
+    {
+        FireEvent(DO_QUERY);
+    }
+    void FireLoadItems()
+    {
+        FireEvent(LOAD_ITEMS);
+    }
+    void FireLoadExtraData()
+    {
+        FireEvent(LOAD_EXTRA_DATA);
+    }
+    void FireLoadAccelerators()
+    {
+        FireEvent(LOAD_ACCELERATORS);
+    }
+    void FireUnloadAccelerators()
+    {
+        FireEvent(UNLOAD_ACCELERATORS);
+    }
+
+    HANDLE m_SearchingEvent;
+
+protected:
+    virtual void OnEvent(int eventId, void *param);
+    virtual void OnTimeOut(void *param);
+
+    void OnDoQuery(void *param);
+    void OnLoadItems(void *param);
+    void OnLoadExtraData(void *param);
+    void OnLoadAccelerators(void *param);
+    void OnUnloadAccelerators(void *param);
+
+    void OpenDatabase();
+    void CloseDatabase();
+
+    CppSQLite3DB m_db;
+    CString m_dbPath;
+};

+ 63 - 0
Tokenizer.cpp

@@ -0,0 +1,63 @@
+#include "stdafx.h"
+#include "Tokenizer.h"
+
+CTokenizer::CTokenizer(const CString &cs, const CString &csDelim): m_cs(cs), m_nCurPos(0)
+{
+    SetDelimiters(csDelim);
+}
+
+void CTokenizer::SetDelimiters(const CString &csDelim)
+{
+    for(int i = 0; i < csDelim.GetLength(); ++i)
+    {
+        m_delim.Add(csDelim[i]);
+    }
+
+    m_delim.SortAscending();
+}
+
+bool CTokenizer::Next(CString &cs)
+{
+    cs.Empty();
+    int len = m_cs.GetLength();
+
+    while(m_nCurPos < len && m_delim.Find(m_cs[m_nCurPos]))
+    {
+        ++m_nCurPos;
+    }
+
+    if(m_nCurPos >= len)
+    {
+        return false;
+    }
+
+    int nStartPos = m_nCurPos;
+
+    while(m_nCurPos < len && !m_delim.Find(m_cs[m_nCurPos]))
+    {
+        ++m_nCurPos;
+    }
+
+    cs = m_cs.Mid(nStartPos, m_nCurPos - nStartPos);
+
+    return true;
+}
+
+CString CTokenizer::Tail()
+{
+    int len = m_cs.GetLength();
+    int nCurPos = m_nCurPos;
+
+    while(nCurPos < len && m_delim.Find(m_cs[nCurPos]))
+    {
+        ++nCurPos;
+    }
+
+    CString csResult;
+    if(nCurPos < len)
+    {
+        csResult = m_cs.Mid(nCurPos);
+    }
+
+    return csResult;
+}

+ 16 - 0
Tokenizer.h

@@ -0,0 +1,16 @@
+#pragma once
+#include "ArrayEx.h"
+
+class CTokenizer
+{
+public:
+    CString m_cs;
+    CArrayEx < TCHAR > m_delim;
+    int m_nCurPos;
+
+    CTokenizer(const CString &cs, const CString &csDelim);
+    void SetDelimiters(const CString &csDelim);
+
+    bool Next(CString &cs);
+    CString Tail();
+};