Browse Source

Add some options with some refactoring (#588)

* remove Edge special action because of Edge changed

* refactor some code about paste

* refactor about image process

* renamed confused variable

* add Support all types feature with some refactoring

* Enable PNG format by default if no formats are defined

* change GetAvailableTypes API

* fix LoadFromClipboard to ensure getting types to accept

* fix GetAvailableTypes error
Minha, Jeong 1 year ago
parent
commit
7641c03964
22 changed files with 2505 additions and 2664 deletions
  1. 24 41
      AddType.cpp
  2. 1 1
      AddType.h
  3. 49 178
      AdvGeneral.cpp
  4. 62 70
      BitmapHelper.cpp
  5. 1 0
      CP_Main.cpp
  6. 2 0
      CP_Main.vcxproj
  7. 2 0
      CP_Main.vcxproj.filters
  8. 2054 2154
      Clip.cpp
  9. 3 1
      Clip.h
  10. 7 31
      CopyThread.cpp
  11. 13 14
      ExternalWindowTracker.cpp
  12. 20 0
      ImageHelper.cpp
  13. 63 0
      ImageHelper.h
  14. 30 0
      Misc.cpp
  15. 2 0
      Misc.h
  16. 74 81
      OleClipSource.cpp
  17. 11 5
      Options.cpp
  18. 4 0
      Options.h
  19. 1 0
      OptionsTypes.cpp
  20. 34 37
      ProcessPaste.cpp
  21. 24 25
      QPasteWnd.cpp
  22. 24 26
      SendKeys.cpp

+ 24 - 41
AddType.cpp

@@ -3,6 +3,7 @@
 
 #include "stdafx.h"
 #include "cp_main.h"
+#include "Misc.h"
 #include "AddType.h"
 
 #ifdef _DEBUG
@@ -28,7 +29,7 @@ void CAddType::DoDataExchange(CDataExchange* pDX)
 {
 	CDialog::DoDataExchange(pDX);
 	//{{AFX_DATA_MAP(CAddType)
-	DDX_Control(pDX, IDC_LIST1, m_lbDefaultTypes);
+	DDX_Control(pDX, IDC_LIST1, m_lbCandidateTypes);
 	DDX_Text(pDX, IDC_EDIT1, m_eCustomType);
 	DDV_MaxChars(pDX, m_eCustomType, 50);
 	//}}AFX_DATA_MAP
@@ -54,7 +55,7 @@ BOOL CAddType::OnInitDialog()
 	::CheckDlgButton(m_hWnd, IDC_RADIO_CURRENT_TYPES, BST_CHECKED);
 	OnBnClickedRadioCurrentTypes();
 	
-	m_lbDefaultTypes.SetFocus();
+	m_lbCandidateTypes.SetFocus();
 
 	theApp.m_Language.UpdateOptionSupportedTypesAdd(this);
 	return FALSE;
@@ -62,7 +63,7 @@ BOOL CAddType::OnInitDialog()
 
 void CAddType::AddCurrentClipboardTypes()
 {
-	m_lbDefaultTypes.ResetContent();
+	m_lbCandidateTypes.ResetContent();
 
 	COleDataObject oleData;
 
@@ -76,7 +77,7 @@ void CAddType::AddCurrentClipboardTypes()
 	while (oleData.GetNextFormat(&test))
 	{
 		BOOL b = oleData.IsDataAvailable(test.cfFormat);
-		m_lbDefaultTypes.AddString(GetFormatName(test.cfFormat));
+		m_lbCandidateTypes.AddString(GetFormatName(test.cfFormat));
 	}
 
 	oleData.Release();
@@ -84,32 +85,14 @@ void CAddType::AddCurrentClipboardTypes()
 
 void CAddType::AddCommonTypes()
 {
-	m_lbDefaultTypes.ResetContent();
-	m_lbDefaultTypes.AddString(_T("CF_TEXT"));
-	m_lbDefaultTypes.AddString(_T("CF_BITMAP"));
-	m_lbDefaultTypes.AddString(_T("CF_METAFILEPICT"));
-	m_lbDefaultTypes.AddString(_T("CF_SYLK"));
-	m_lbDefaultTypes.AddString(_T("CF_DIF"));
-	m_lbDefaultTypes.AddString(_T("CF_TIFF"));
-	m_lbDefaultTypes.AddString(_T("CF_OEMTEXT"));
-	m_lbDefaultTypes.AddString(_T("CF_DIB"));
-	m_lbDefaultTypes.AddString(_T("CF_PALETTE"));
-	m_lbDefaultTypes.AddString(_T("CF_PENDATA"));
-	m_lbDefaultTypes.AddString(_T("CF_RIFF"));
-	m_lbDefaultTypes.AddString(_T("CF_WAVE"));
-	m_lbDefaultTypes.AddString(_T("CF_UNICODETEXT"));
-	m_lbDefaultTypes.AddString(_T("CF_ENHMETAFILE"));
-	m_lbDefaultTypes.AddString(_T("CF_HDROP"));
-	m_lbDefaultTypes.AddString(_T("CF_LOCALE"));
-	m_lbDefaultTypes.AddString(_T("CF_OWNERDISPLAY"));
-	m_lbDefaultTypes.AddString(_T("CF_DSPTEXT"));
-	m_lbDefaultTypes.AddString(_T("CF_DSPBITMAP"));
-	m_lbDefaultTypes.AddString(_T("CF_DSPMETAFILEPICT"));
-	m_lbDefaultTypes.AddString(_T("CF_DSPENHMETAFILE"));
-	m_lbDefaultTypes.AddString(GetFormatName(RegisterClipboardFormat(CF_RTF)));
-	m_lbDefaultTypes.AddString(GetFormatName(RegisterClipboardFormat(CF_RTFNOOBJS)));
-	m_lbDefaultTypes.AddString(GetFormatName(RegisterClipboardFormat(CF_RETEXTOBJ)));
-	m_lbDefaultTypes.AddString(GetFormatName(RegisterClipboardFormat(_T("HTML Format"))));
+	m_lbCandidateTypes.ResetContent();
+	for (auto systemClipFormat : GetSystemClipFormats()) {
+		m_lbCandidateTypes.AddString(GetFormatName(systemClipFormat));
+	}
+	m_lbCandidateTypes.AddString(GetFormatName(RegisterClipboardFormat(CF_RTF)));
+	m_lbCandidateTypes.AddString(GetFormatName(RegisterClipboardFormat(CF_RTFNOOBJS)));
+	m_lbCandidateTypes.AddString(GetFormatName(RegisterClipboardFormat(CF_RETEXTOBJ)));
+	m_lbCandidateTypes.AddString(GetFormatName(RegisterClipboardFormat(_T("HTML Format"))));
 }
 
 void CAddType::OnBnClickedRadioPrimaryTypes()
@@ -117,11 +100,11 @@ void CAddType::OnBnClickedRadioPrimaryTypes()
 	AddCommonTypes();
 	::ShowWindow(::GetDlgItem(m_hWnd, IDC_LIST1), SW_SHOW);
 	::ShowWindow(::GetDlgItem(m_hWnd, IDC_EDIT1), SW_HIDE);
-	m_lbDefaultTypes.SetFocus();
-	if (m_lbDefaultTypes.GetCount() > 0)
+	m_lbCandidateTypes.SetFocus();
+	if (m_lbCandidateTypes.GetCount() > 0)
 	{
-		m_lbDefaultTypes.SetCurSel(0);
-		m_lbDefaultTypes.SetSel(0);
+		m_lbCandidateTypes.SetCurSel(0);
+		m_lbCandidateTypes.SetSel(0);
 	}
 }
 
@@ -130,11 +113,11 @@ void CAddType::OnBnClickedRadioCurrentTypes()
 	AddCurrentClipboardTypes();
 	::ShowWindow(::GetDlgItem(m_hWnd, IDC_LIST1), SW_SHOW);
 	::ShowWindow(::GetDlgItem(m_hWnd, IDC_EDIT1), SW_HIDE);
-	m_lbDefaultTypes.SetFocus();
-	if (m_lbDefaultTypes.GetCount() > 0)
+	m_lbCandidateTypes.SetFocus();
+	if (m_lbCandidateTypes.GetCount() > 0)
 	{
-		m_lbDefaultTypes.SetCurSel(0);
-		m_lbDefaultTypes.SetSel(0);
+		m_lbCandidateTypes.SetCurSel(0);
+		m_lbCandidateTypes.SetSel(0);
 
 	}
 }
@@ -153,17 +136,17 @@ void CAddType::OnBnClickedAdd()
 	if (IsDlgButtonChecked(IDC_RADIO_PRIMARY_TYPES) == BST_CHECKED ||
 		IsDlgButtonChecked(IDC_RADIO_CURRENT_TYPES) == BST_CHECKED)
 	{
-		int nCount = m_lbDefaultTypes.GetSelCount();
+		int nCount = m_lbCandidateTypes.GetSelCount();
 		if (nCount)
 		{
 			CString cs;
 			CArray<int, int> items;
 			items.SetSize(nCount);
-			m_lbDefaultTypes.GetSelItems(nCount, items.GetData());
+			m_lbCandidateTypes.GetSelItems(nCount, items.GetData());
 
 			for (int i = 0; i < nCount; i++)
 			{
-				m_lbDefaultTypes.GetText(items[i], cs);
+				m_lbCandidateTypes.GetText(items[i], cs);
 				m_csSelectedTypes.Add(cs);
 			}
 		}

+ 1 - 1
AddType.h

@@ -19,7 +19,7 @@ public:
 // Dialog Data
 	//{{AFX_DATA(CAddType)
 	enum { IDD = IDD_ADD_TYPE };
-	CListBox	m_lbDefaultTypes;
+	CListBox	m_lbCandidateTypes;
 	CString	m_eCustomType;
 	//}}AFX_DATA
 

+ 49 - 178
AdvGeneral.cpp

@@ -141,6 +141,7 @@ END_MESSAGE_MAP()
 #define SETTING_SLUGIFY_SEPARATOR 90
 #define SETTING_FAST_THUMBNAIL_MODE 91
 #define SETTING_CLIPBOARD_RESTORE_AFTER_COPY_BUFFER_DELAY 92
+#define SETTING_SUPPORT_ALL_TYPES 93
 
 BOOL CAdvGeneral::OnInitDialog()
 {
@@ -180,6 +181,8 @@ BOOL CAdvGeneral::OnInitDialog()
 	AddTrueFalse(pGroupTest, _T("Always show scroll bar"), CGetSetOptions::GetShowScrollBar(), SETTING_ALWAYS_SHOW_SCROLL_BAR);
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Amount of text to save for description"), g_Opt.m_bDescTextSize, _T(""), SETTING_DESC_SIZE));
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Copy and save clipboard delay (ms)"), (long)CGetSetOptions::GetCopyAndSveDelay(), _T(""), SETTING_COPY_SAVE_DELAY));
+	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Clipboard restore delay after copy buffer sent paste (ms, default: 750))"), (long)(CGetSetOptions::GetDittoRestoreClipboardDelay()), _T(""), SETTING_CLIPBOARD_RESTORE_AFTER_COPY_BUFFER_DELAY));
+
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Default paste string"), CGetSetOptions::GetDefaultPasteString(), _T(""), SETTING_DEFAULT_PASTE_STRING));
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Default copy string"), CGetSetOptions::GetDefaultCopyString(), _T(""), SETTING_DEFAULT_COPY_STRING));
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Default cut string"), CGetSetOptions::GetDefaultCutString(), _T(""), SETTING_DEFAULT_CUT_STRING));
@@ -237,8 +240,6 @@ BOOL CAdvGeneral::OnInitDialog()
 
 	AddTrueFalse(pGroupTest, _T("Refresh view after paste"), CGetSetOptions::GetRefreshViewAfterPasting(), SETTING_REFRESH_VIEW_AFTER_PASTE);
 
-	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("clipboard restore after copy buffer sent paste delay (ms, default: 750))"), (long)(CGetSetOptions::GetDittoRestoreClipboardDelay()), _T(""), SETTING_CLIPBOARD_RESTORE_AFTER_COPY_BUFFER_DELAY));
-
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Save clipboard delay (ms, default: 100))"), (long)(CGetSetOptions::GetProcessDrawClipboardDelay()), _T(""), SETTING_CLIPBOARD_SAVE_DELAY));
 
 	AddTrueFalse(pGroupTest, _T("Save multi-pastes"), CGetSetOptions::GetSaveMultiPaste(), SETTING_SAVE_MULTI_PASTE);
@@ -258,10 +259,12 @@ BOOL CAdvGeneral::OnInitDialog()
 	AddTrueFalse(pGroupTest, _T("Show startup tooltip message"), CGetSetOptions::GetShowStartupMessage(), SETTING_SHOW_STARTUP_MESSAGE);
 
 	AddTrueFalse(pGroupTest, _T("Show text for first ten copy hot keys"), CGetSetOptions::GetShowTextForFirstTenHotKeys(), SETTING_TEXT_FIRST_TEN);
-	AddTrueFalse(pGroupTest, _T("Show thumbnails(for CF_DIB types) (could increase memory usage and display speed)"), CGetSetOptions::GetDrawThumbnail(), SETTING_DRAW_THUMBNAILS);
+	AddTrueFalse(pGroupTest, _T("Show thumbnails(for CF_DIB and PNG types) (could increase memory usage and display speed)"), CGetSetOptions::GetDrawThumbnail(), SETTING_DRAW_THUMBNAILS);
 	
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Slugify Separator (default: -)"), CGetSetOptions::GetSlugifySeparator(), _T(""), SETTING_SLUGIFY_SEPARATOR));
 
+	AddTrueFalse(pGroupTest, _T("Support all types ignoring supported type list (default: false))"), CGetSetOptions::GetSupportAllTypes(), SETTING_SUPPORT_ALL_TYPES);
+
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Text lines per clip"), CGetSetOptions::GetLinesPerRow(), _T(""), SETTING_LINES_PER_ROW));
 
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Tooltip display time(ms) max of 32000 (-1 default (5 seconds), 0 to turn off)"), g_Opt.m_tooltipTimeout, _T(""), SETTING_TOOLTIP_TIMEOUT));
@@ -372,44 +375,28 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_SHOW_TASKBAR_ICON:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowIconInSysTray(val);
 				}
 				break;
 			case SETTING_SAVE_MULTI_PASTE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetSaveMultiPaste(val);
 				}
 				break;
 			case SETTING_HIDE_ON_HOTKEY_IF_VISIBLE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetHideDittoOnHotKeyIfAlreadyShown(val);
 				}
 				break;
 			case SETTING_PASTE_IN_ACTIVE_WINDOW:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetSendPasteAfterSelection(val);
 				}
 				break;
@@ -429,11 +416,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_ENSURE_CONNECTED:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetEnsureConnectToClipboard(val);
 				}
 				break;
@@ -446,22 +429,14 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_TEXT_FIRST_TEN:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowTextForFirstTenHotKeys(val);
 				}
 				break;
 			case SETTING_SHOW_LEADING_WHITESPACE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetDescShowLeadingWhiteSpace(val);
 				}
 				break;
@@ -474,11 +449,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_ENABLE_TRANSPARENCY:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetEnableTransparency(val);
 				}
 				break;
@@ -497,132 +468,84 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_DRAW_THUMBNAILS:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetDrawThumbnail(val);
 				}
 				break;
 			case SETTING_FAST_THUMBNAIL_MODE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetFastThumbnailMode(val);
 				}
 				break;
 			case SETTING_DRAW_RTF:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetDrawRTF(val);
 				}
 				break;
 			case SETTING_FIND_AS_TYPE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetFindAsYouType(val);
 				}
 				break;
 			case SETTING_ENSURE_WINDOW_IS_VISIBLE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetEnsureEntireWindowCanBeSeen(val);
 				}
 				break;
 			case SETTING_SHOW_GROUP_CLIPS_IN_LIST:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowAllClipsInMainList(val);
 				}
 				break;
 			case SETTING_PROMPT_ON_DELETE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetPromptWhenDeletingClips(val);
 				}
 				break;
 			case SETTING_ALWAYS_SHOW_SCROLL_BAR:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowScrollBar(val);
 				}
 				break;
 			case SETTING_PASTE_AS_ADMIN:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetPasteAsAdmin(val);
 				}
 				break;
 			case SETTTING_SHOW_IN_TASKBAR:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowInTaskBar(val);
 				}
 				break;
 			case SETTING_SHOW_CLIP_PASTED:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowIfClipWasPasted(val);
 				}
 				break;
 			case SETTING_SHOW_MSG_WHEN_RECEIVING_MANUAL_SENT_CLIP:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowMsgWhenReceivingManualSentClip(val);
 				}
 				break;
@@ -635,55 +558,35 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_UPDATE_ORDER_ON_PASTE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetUpdateTimeOnPaste(val);
 				}
 				break;
 			case SETTING_UPDATE_ORDER_ON_CTRL_C:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetUpdateClipOrderOnCtrlC(val);
 				}
 				break;
 			case SETTING_MULTIPASTE_REVERSE_ORDER:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetMultiPasteReverse(val);
 				}
 				break;
 			case SETTING_ALLOW_DUPLICATES:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetAllowDuplicates(val);
 				}
 				break;
 			case SETTING_ALOW_BACK_TO_BACK_DUPLICATES:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetAllowBackToBackDuplicates(val);
 				}
 				break;
@@ -731,11 +634,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_SHOW_STARTUP_MESSAGE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetShowStartupMessage(val);
 				}
 				break;
@@ -788,11 +687,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_REVERT_TO_TOP_LEVEL_GROUP:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetRevertToTopLevelGroup(val);
 				}
 				break;
@@ -835,11 +730,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_OPEN_TO_GROUP_AS_ACTIVE_EXE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetOpenToGroupByActiveExe(val);
 				}
 				break;
@@ -847,11 +738,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_ADD_CF_HDROP_ON_DRAG:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetAddCFHDROP_OnDrag(val);
 				}
 				break;
@@ -870,22 +757,14 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_MOVE_SELECTION_ON_OPEN_HOTKEY:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetMoveSelectionOnOpenHotkey(val);
 				}
 				break;
 			case SETTING_MAINTAIN_SEARCH_VIEW:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetMaintainSearchView(val);
 				}
 				break;
@@ -898,22 +777,14 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_DEBUG_TO_FILE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetEnableDebugLogging(val);
 				}
 				break;
 			case SETTING_DEBUG_TO_OUTPUT_STRING:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetEnableOutputDebugStringLogging(val);
 				}
 				break;
@@ -926,11 +797,7 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_DISABLE_FRIENDS:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = true;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = false;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetAllowFriends(val);
 				}
 				break;
@@ -943,14 +810,18 @@ void CAdvGeneral::OnBnClickedOk()
 			case SETTING_REFRESH_VIEW_AFTER_PASTE:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{
-					BOOL val = false;
-					if (wcscmp(pNewValue->bstrVal, L"True") == 0)
-					{
-						val = true;
-					}
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
 					CGetSetOptions::SetRefreshViewAfterPasting(val);
 				}
 				break;
+			case SETTING_SUPPORT_ALL_TYPES:
+				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
+				{
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
+					CGetSetOptions::SetSupportAllTypes(val);
+				}
+				break;
+
 			}
 		}
 	}

+ 62 - 70
BitmapHelper.cpp

@@ -101,6 +101,8 @@ BOOL CBitmapHelper::GetCBitmap(void	*pClip2, CDC *pDC, CBitmap *pBitMap, int nMa
 BOOL CBitmapHelper::GetCBitmap(CClipFormats &clips, CDC* pDC, CBitmap* pBitMap, BOOL horizontal)
 {
 	BOOL bRet = FALSE;
+	if (!pBitMap)
+		return bRet;
 
 	int count = clips.GetCount();
 	int width = 0;
@@ -123,7 +125,6 @@ BOOL CBitmapHelper::GetCBitmap(CClipFormats &clips, CDC* pDC, CBitmap* pBitMap,
 			height += (int)gdipBitmap->GetHeight();
 		}
 	
-
 		delete gdipBitmap;
 	}
 
@@ -148,37 +149,33 @@ BOOL CBitmapHelper::GetCBitmap(CClipFormats &clips, CDC* pDC, CBitmap* pBitMap,
 	{
 		CClipFormat clip = clips[i];
 
-		if (clip.m_cfType == CF_DIB ||
-			clip.m_cfType == theApp.m_PNG_Format)
+		if (clip.m_cfType != CF_DIB &&
+			clip.m_cfType != theApp.m_PNG_Format)
+			continue;
+	
+		Gdiplus::Bitmap* gdipBitmap = clip.CreateGdiplusBitmap();
+		if (gdipBitmap == NULL)
+			continue;
+
+		const UINT gdipHeight = gdipBitmap->GetHeight();
+		const UINT gdipWidth = gdipBitmap->GetWidth();
+		if (gdipHeight == 0 || gdipWidth == 0) 
 		{
-			if (pBitMap)
-			{
-				Gdiplus::Bitmap* gdipBitmap = clip.CreateGdiplusBitmap();
-
-				if (gdipBitmap != NULL &&
-					gdipBitmap->GetHeight() > 0 &&
-					gdipBitmap->GetWidth() > 0)
-				{
-					Gdiplus::ImageAttributes attrs;
-					Gdiplus::Rect dest(destX, destY, gdipBitmap->GetWidth(), gdipBitmap->GetHeight());
-
-					graphics.DrawImage(gdipBitmap, dest, 0, 0, gdipBitmap->GetWidth(), gdipBitmap->GetHeight(), Gdiplus::UnitPixel, &attrs);
-
-					if (horizontal)
-					{
-						destX += gdipBitmap->GetWidth();
-					}
-					else
-					{
-						destY += gdipBitmap->GetHeight();
-					}
-
-					delete gdipBitmap;
-
-					bRet = TRUE;
-				}
-			}
+			delete gdipBitmap;
+			continue;
 		}
+
+		Gdiplus::Rect dest(destX, destY, gdipBitmap->GetWidth(), gdipBitmap->GetHeight());
+		Gdiplus::ImageAttributes attrs;
+		graphics.DrawImage(gdipBitmap, dest, 0, 0, gdipBitmap->GetWidth(), gdipBitmap->GetHeight(), Gdiplus::UnitPixel, &attrs);
+
+		if (horizontal)
+			destX += gdipBitmap->GetWidth();
+		else
+			destY += gdipBitmap->GetHeight();
+
+		delete gdipBitmap;
+		bRet = TRUE;
 	}
 
 	MemDc2.SelectObject(oldBitmap2);
@@ -280,16 +277,16 @@ HANDLE CBitmapHelper::hBitmapToDIB(HBITMAP hBitmap, DWORD dwCompression, HPALETT
 
     // We need a device context to get the DIB from
     hDC = GetDC(NULL);
-    hPal = SelectPalette(hDC,hPal,FALSE);
+    hPal = SelectPalette(hDC, hPal, FALSE);
     (void)RealizePalette(hDC);
 
     // Allocate enough memory to hold bitmapinfoheader and color table
-    hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
+    hDIB = GlobalAlloc(GMEM_FIXED, dwLen);
 
     if (!hDIB)
 	{
-        (void)SelectPalette(hDC,hPal,FALSE);
-        ReleaseDC(NULL,hDC);
+        (void)SelectPalette(hDC, hPal, FALSE);
+        ReleaseDC(NULL, hDC);
         return NULL;
     }
 
@@ -320,25 +317,23 @@ HANDLE CBitmapHelper::hBitmapToDIB(HBITMAP hBitmap, DWORD dwCompression, HPALETT
     // Realloc the buffer so that it can hold all the bits
     dwLen += bi.biSizeImage;
 	handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE);
-    if(handle)
-	{
-		hDIB = handle;
-	}
-    else
+    if(!handle)
 	{
 		GlobalFree(hDIB);
 
 		// Reselect the original palette
-		(void)SelectPalette(hDC,hPal,FALSE);
-		ReleaseDC(NULL,hDC);
+		(void)SelectPalette(hDC, hPal, FALSE);
+		ReleaseDC(NULL, hDC);
 		return NULL;
-    }
+	}
+	
+	hDIB = handle;
 
     // Get the bitmap bits
     lpbi = (LPBITMAPINFOHEADER)hDIB;
 
     // FINALLY get the DIB
-    BOOL bGotBits = GetDIBits( hDC, hBitmap,
+    BOOL bGotBits = GetDIBits(hDC, hBitmap,
                             0L,                             // Start scan line
                             (DWORD)bi.biHeight,             // # of scan lines
                             (LPBYTE)lpbi                    // address for bitmap bits
@@ -350,13 +345,13 @@ HANDLE CBitmapHelper::hBitmapToDIB(HBITMAP hBitmap, DWORD dwCompression, HPALETT
     {
         GlobalFree(hDIB);
         
-        (void)SelectPalette(hDC,hPal,FALSE);
+        (void)SelectPalette(hDC, hPal, FALSE);
         ReleaseDC(NULL,hDC);
         return NULL;
     }
 
-    (void)SelectPalette(hDC,hPal,FALSE);
-    ReleaseDC(NULL,hDC);
+    (void)SelectPalette(hDC, hPal, FALSE);
+    ReleaseDC(NULL, hDC);
     return hDIB;
 }
 
@@ -365,36 +360,33 @@ bool CBitmapHelper::DrawDIB(CDC *pDC, HANDLE hData, int nLeft, int nRight, int &
 {
 	LPBITMAPINFO	lpBI ;
 	void*           pDIBBits;
-	bool bRet = false;
 	
 	lpBI = (LPBITMAPINFO)GlobalLock(hData);
-	if(lpBI)
-	{
-		int nColors = lpBI->bmiHeader.biClrUsed ? lpBI->bmiHeader.biClrUsed : 1 << lpBI->bmiHeader.biBitCount;
+	if (!lpBI)
+		return false;
 
-		if( lpBI->bmiHeader.biBitCount > 8 )
-		{
-			pDIBBits = (LPVOID)((LPDWORD)(lpBI->bmiColors + lpBI->bmiHeader.biClrUsed) + 
-				((lpBI->bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
-		}
-		else
-		{
-			pDIBBits = (LPVOID)(lpBI->bmiColors + nColors);
-		}
+	int nColors = lpBI->bmiHeader.biClrUsed ? lpBI->bmiHeader.biClrUsed : 1 << lpBI->bmiHeader.biBitCount;
 
-		::StretchDIBits(pDC->m_hDC,
-					nLeft, nRight, 
-					lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
-					0, 0, lpBI->bmiHeader.biWidth, 
-					lpBI->bmiHeader.biHeight,
-					pDIBBits, lpBI, DIB_PAL_COLORS, SRCCOPY);
+	if( lpBI->bmiHeader.biBitCount > 8 )
+	{
+		pDIBBits = (LPVOID)((LPDWORD)(lpBI->bmiColors + lpBI->bmiHeader.biClrUsed) + 
+			((lpBI->bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
+	}
+	else
+	{
+		pDIBBits = (LPVOID)(lpBI->bmiColors + nColors);
+	}
 
-		nWidth = lpBI->bmiHeader.biWidth;
+	::StretchDIBits(pDC->m_hDC,
+				nLeft, nRight, 
+				lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
+				0, 0, lpBI->bmiHeader.biWidth, 
+				lpBI->bmiHeader.biHeight,
+				pDIBBits, lpBI, DIB_PAL_COLORS, SRCCOPY);
 
-		GlobalUnlock(hData) ;
+	nWidth = lpBI->bmiHeader.biWidth;
 
-		bRet = true;
-	}
+	GlobalUnlock(hData);
 
-	return bRet;
+	return true;
 }

+ 1 - 0
CP_Main.cpp

@@ -671,6 +671,7 @@ CClipTypes* CCP_MainApp::LoadTypesFromDB()
 		pTypes->Add(CF_HDROP);
 		pTypes->Add(CF_DIB);
 		pTypes->Add(GetFormatID(_T("HTML Format")));
+		pTypes->Add(GetFormatID(_T("PNG")));
 	}
 
 	return pTypes;

+ 2 - 0
CP_Main.vcxproj

@@ -759,6 +759,7 @@
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <ClCompile Include="ICU_String.cpp" />
+    <ClCompile Include="ImageHelper.cpp" />
     <ClCompile Include="NoDbFrameWnd.cpp" />
     <ClCompile Include="CopyProperties.cpp">
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
@@ -2905,6 +2906,7 @@
     <ClInclude Include="ClipCompare.h" />
     <ClInclude Include="ClipFormatQListCtrl.h" />
     <ClInclude Include="ICU_String.h" />
+    <ClInclude Include="ImageHelper.h" />
     <ClInclude Include="NoDbFrameWnd.h" />
     <ClInclude Include="CreateQRCodeImage.h" />
     <ClInclude Include="CustomFriendsHelper.h" />

+ 2 - 0
CP_Main.vcxproj.filters

@@ -471,6 +471,7 @@
       <Filter>source</Filter>
     </ClCompile>
     <ClCompile Include="ImageFormatAggregator.cpp" />
+    <ClCompile Include="ImageHelper.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="sqlite\CppSQLite3.h">
@@ -979,6 +980,7 @@
       <Filter>header</Filter>
     </ClInclude>
     <ClInclude Include="ImageFormatAggregator.h" />
+    <ClInclude Include="ImageHelper.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="CP_Main.rc">

+ 2054 - 2154
Clip.cpp

@@ -1,2154 +1,2054 @@
-// ProcessCopy.cpp: implementation of the CProcessCopy class.
-//
-//////////////////////////////////////////////////////////////////////
-
-#include "stdafx.h"
-#include "CP_Main.h"
-#include "Clip.h"
-#include "DatabaseUtilities.h"
-#include "Crc32Dynamic.h"
-#include "sqlite\CppSQLite3.h"
-#include "shared/TextConvert.h"
-#include "zlib/zlib.h"
-#include "Misc.h"
-#include "Md5.h"
-#include "ChaiScriptOnCopy.h"
-#include "DittoChaiScript.h"
-
-#include <Mmsystem.h>
-
-#include "Path.h"
-
-#ifdef _DEBUG
-#undef THIS_FILE
-static char THIS_FILE[]=__FILE__;
-#define new DEBUG_NEW
-#endif
-
-
-/*----------------------------------------------------------------------------*\
-COleDataObjectEx
-\*----------------------------------------------------------------------------*/
-
-HGLOBAL COleDataObjectEx::GetGlobalData(CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
-{
-    HGLOBAL hGlobal = COleDataObject::GetGlobalData(cfFormat, lpFormatEtc);
-	if(hGlobal)
-	{
-		if(!::IsValid(hGlobal))
-		{
-			Log( StrF(
-				_T("COleDataObjectEx::GetGlobalData(\"%s\"): ERROR: Invalid (NULL) data returned."),
-				GetFormatName(cfFormat) ) );
-			::GlobalFree( hGlobal );
-			hGlobal = NULL;
-		}
-		return hGlobal;
-	}
-	
-	// The data isn't in global memory, so try getting an IStream interface to it.
-	STGMEDIUM stg;
-	
-	if(!GetData(cfFormat, &stg))
-	{
-		return 0;
-	}
-	
-	switch(stg.tymed)
-	{
-	case TYMED_HGLOBAL:
-		hGlobal = stg.hGlobal;
-		break;
-		
-	case TYMED_ISTREAM:
-		{
-			UINT            uDataSize;
-			LARGE_INTEGER	li;
-			ULARGE_INTEGER	uli;
-			
-			li.HighPart = li.LowPart = 0;
-			
-			if ( SUCCEEDED( stg.pstm->Seek ( li, STREAM_SEEK_END, &uli )))
-			{
-				hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, uli.LowPart );
-				
-				void* pv = GlobalLock(hGlobal);
-				stg.pstm->Seek(li, STREAM_SEEK_SET, NULL);
-				HRESULT result = stg.pstm->Read(pv, uli.LowPart, (PULONG)&uDataSize);
-				GlobalUnlock(hGlobal);
-				
-				if( FAILED(result) )
-					hGlobal = GlobalFree(hGlobal);
-			}
-			break;  // case TYMED_ISTREAM
-		}
-	} // end switch
-	
-	ReleaseStgMedium(&stg);
-	
-	if(hGlobal && !::IsValid(hGlobal))
-	{
-		Log( StrF(
-			_T("COleDataObjectEx::GetGlobalData(\"%s\"): ERROR: Invalid (NULL) data returned."),
-			GetFormatName(cfFormat)));
-		::GlobalFree(hGlobal);
-		hGlobal = NULL;
-	}
-	
-	return hGlobal;
-}
-
-/*----------------------------------------------------------------------------*\
-CClipFormat - holds the data of one clip format.
-\*----------------------------------------------------------------------------*/
-CClipFormat::CClipFormat(CLIPFORMAT cfType, HGLOBAL hgData, int parentId)
-{
-	m_cfType = cfType;
-	m_hgData = hgData;
-	m_autoDeleteData = true;
-	m_parentId = parentId;
-}
-
-CClipFormat::~CClipFormat() 
-{ 
-	Free(); 
-}
-
-void CClipFormat::Clear()
-{
-	m_cfType = 0;
-	m_hgData = 0;
-	m_dataId = -1;
-	m_parentId = -1;
-}
-
-void CClipFormat::Free()
-{
-	if(m_autoDeleteData)
-	{
-		if(m_hgData)
-		{
-			m_hgData = ::GlobalFree( m_hgData );
-			m_hgData = NULL;
-		}
-	}
-}
-
-Gdiplus::Bitmap *CClipFormat::CreateGdiplusBitmap()
-{
-	Gdiplus::Bitmap *gdipBitmap = NULL;
-	IStream* pIStream = NULL;
-
-	if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream) == S_OK)
-	{
-		if (this->m_cfType == CF_DIB)
-		{
-			LPVOID pvData = GlobalLock(this->m_hgData);
-			ULONG size = (ULONG)GlobalSize(this->m_hgData);
-
-			BITMAPINFO *lpBI = (BITMAPINFO *)pvData;
-
-			int nPaletteEntries = 1 << lpBI->bmiHeader.biBitCount;
-			if (lpBI->bmiHeader.biBitCount > 8)
-				nPaletteEntries = 0;
-			else if (lpBI->bmiHeader.biClrUsed != 0)
-				nPaletteEntries = lpBI->bmiHeader.biClrUsed;
-
-			BITMAPFILEHEADER BFH;
-			memset(&BFH, 0, sizeof(BITMAPFILEHEADER));
-			BFH.bfType = 'MB';
-			BFH.bfSize = sizeof(BITMAPFILEHEADER) + size;
-			BFH.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + nPaletteEntries * sizeof(RGBQUAD);
-
-			pIStream->Write(&BFH, sizeof(BITMAPFILEHEADER), NULL);
-			pIStream->Write(pvData, size, NULL);
-
-			GlobalUnlock(this->m_hgData);
-
-			gdipBitmap = Gdiplus::Bitmap::FromStream(pIStream);
-		}
-		else if (this->m_cfType == theApp.m_PNG_Format)
-		{
-			LPVOID pvData = GlobalLock(this->m_hgData);
-			ULONG size = (ULONG)GlobalSize(this->m_hgData);
-			pIStream->Write(pvData, size, NULL);
-
-			GlobalUnlock(this->m_hgData);
-
-			gdipBitmap = Gdiplus::Bitmap::FromStream(pIStream);
-		}
-
-		pIStream->Release();
-	}
-
-	return gdipBitmap;
-}
-
-
-
-/*----------------------------------------------------------------------------*\
-CClipFormats - holds an array of CClipFormat
-\*----------------------------------------------------------------------------*/
-// returns a pointer to the CClipFormat in this array which matches the given type
-//  or NULL if that type doesn't exist in this array.
-CClipFormat* CClipFormats::FindFormat(UINT cfType)
-{
-	CClipFormat* pCF;
-	INT_PTR count = GetSize();
-
-	for(int i=0; i < count; i++)
-	{
-		pCF = &ElementAt(i);
-		if(pCF->m_cfType == cfType)
-			return pCF;
-	}
-	return NULL;
-}
-
-bool CClipFormats::RemoveFormat(CLIPFORMAT cfType)
-{
-	bool removed = false;
-	CClipFormat* pCF;
-	INT_PTR count = GetSize();
-
-	for (int i = 0; i < count; i++)
-	{
-		pCF = &ElementAt(i);
-		if (pCF->m_cfType == cfType)
-		{
-			this->RemoveAt(i);
-			removed = true;
-			break;
-		}
-	}
-	return removed;
-}
-
-
-
-
-/*----------------------------------------------------------------------------*\
-CClip - holds multiple CClipFormats and CopyClipboard() statistics
-\*----------------------------------------------------------------------------*/
-
-DWORD CClip::m_LastAddedCRC = 0;
-int CClip::m_lastAddedID = -1;
-
-CClip::CClip() : 
-	m_id(0), 
-	m_CRC(0),
-	m_parentId(-1),
-	m_dontAutoDelete(FALSE),
-	m_shortCut(0),
-	m_bIsGroup(FALSE),
-	m_param1(0),
-	m_clipOrder(0),
-	m_stickyClipOrder(INVALID_STICKY),
-	m_stickyClipGroupOrder(INVALID_STICKY),
-	m_clipGroupOrder(0),
-	m_globalShortCut(FALSE),
-	m_moveToGroupShortCut(0),
-	m_globalMoveToGroupShortCut(FALSE)
-{
-	m_copyReason = CopyReasonEnum::COPY_TO_UNKOWN;
-	m_addToDbStickyEnum = AddToDbStickyEnum::INVALID;
-}
-
-CClip::~CClip()
-{
-	EmptyFormats();
-}
-
-void CClip::Clear()
-{
-	m_id = -1;
-	m_Time = 0;
-	m_Desc = "";
-	m_CRC = 0;
-	m_parentId = -1;
-	m_dontAutoDelete = FALSE;
-	m_shortCut = 0;
-	m_bIsGroup = FALSE;
-	m_csQuickPaste = "";
-	m_param1 = 0;
-	m_globalShortCut = FALSE;
-	m_moveToGroupShortCut = 0;
-	m_globalMoveToGroupShortCut = 0;
-	
-	EmptyFormats();
-}
-
-const CClip& CClip::operator=(const CClip &clip)
-{
-	const CClipFormat* pCF;
-
-	m_id = clip.m_id;
-	m_Time = clip.m_Time;
-	m_lastPasteDate = clip.m_lastPasteDate;
-	m_CRC = clip.m_CRC;
-	m_parentId = clip.m_parentId;
-	m_dontAutoDelete = clip.m_dontAutoDelete;
-	m_shortCut = clip.m_shortCut;
-	m_bIsGroup = clip.m_bIsGroup;
-	m_csQuickPaste = clip.m_csQuickPaste;
-	m_moveToGroupShortCut = clip.m_moveToGroupShortCut;
-	m_globalMoveToGroupShortCut = clip.m_globalMoveToGroupShortCut;
-
-	INT_PTR nCount = clip.m_Formats.GetSize();
-	
-	for(int i = 0; i < nCount; i++)
-	{
-		pCF = &clip.m_Formats.GetData()[i];
-
-		LPVOID pvData = GlobalLock(pCF->m_hgData);
-		if(pvData)
-		{
-			AddFormat(pCF->m_cfType, pvData, (UINT)GlobalSize(pCF->m_hgData));
-		}
-		GlobalUnlock(pCF->m_hgData);
-	}
-
-	//Set this after since in could get the wrong description in AddFormat
-	m_Desc = clip.m_Desc;
-
-	return *this;
-}
-
-void CClip::EmptyFormats()
-{
-	// free global memory in m_Formats
-	for(INT_PTR i = m_Formats.GetSize()-1; i >= 0; i--)
-	{
-		m_Formats[i].Free();
-		m_Formats.RemoveAt(i);
-	}
-}
-
-// Adds a new Format to this Clip by copying the given data.
-bool CClip::AddFormat(CLIPFORMAT cfType, void* pData, UINT nLen, bool setDesc)
-{
-	ASSERT(pData && nLen);
-	HGLOBAL hGlobal = ::NewGlobalP(pData, nLen);
-	ASSERT(hGlobal);
-
-	// update the Clip statistics
-	m_Time = m_Time.GetCurrentTime();
-
-	if (setDesc)
-	{
-		if (cfType != CF_UNICODETEXT || !SetDescFromText(hGlobal, true))
-			SetDescFromType();
-	}
-	
-	CClipFormat format(cfType,hGlobal);
-	CClipFormat *pFormat;
-	
-	pFormat = m_Formats.FindFormat(cfType);
-	// if the format type already exists as part of this clip, replace the data
-	if(pFormat)
-	{
-		pFormat->Free();
-		pFormat->m_hgData = format.m_hgData;
-	}
-	else
-	{
-		m_Formats.Add(format);
-	}
-	
-	format.m_hgData = 0; // now owned by m_Formats
-	return true;
-}
-
-// Fills this CClip with the contents of the clipboard.
-int CClip::LoadFromClipboard(CClipTypes* pClipTypes, bool checkClipboardIgnore, CString activeApp, CString activeAppTitle)
-{
-	COleDataObjectEx oleData;
-	CClipTypes defaultTypes;
-	CClipTypes* pTypes = pClipTypes;
-
-	// m_Formats should be empty when this is called.
-	ASSERT(m_Formats.GetSize() == 0);
-	
-	// If the data is supposed to be private, then return
-	if(::IsClipboardFormatAvailable(theApp.m_cfIgnoreClipboard))
-	{
-		Log(_T("Clipboard ignore type is on the clipboard, skipping this clipboard change"));
-		return FALSE;
-	}
-
-	//If we are saving a multi paste then delay us connecting to the clipboard
-	//to allow the ctrl-v to do a paste
-	if(::IsClipboardFormatAvailable(theApp.m_cfDelaySavingData))
-	{
-		Log(_T("Delay clipboard type is on the clipboard, delaying 1500 ms to allow ctrl-v to work"));
-		Sleep(1500);
-	}
-		
-	//Attach to the clipboard
-	if(!oleData.AttachClipboard())
-	{
-		Log(_T("failed to attache to clipboard, skipping this clipboard change"));
-		ASSERT(0); // does this ever happen?
-		return FALSE;
-	}
-	
-	oleData.EnsureClipboardObject();
-	
-	// if no types were given, get only the first (most important) type.
-	//  (subsequent types could be synthetic due to automatic type conversions)
-	if(pTypes == NULL || pTypes->GetSize() == 0)
-	{
-		ASSERT(0); // this feature is not currently used... it is an error if it is.
-		
-		FORMATETC formatEtc;
-		oleData.BeginEnumFormats();
-		oleData.GetNextFormat(&formatEtc);
-		defaultTypes.Add(formatEtc.cfFormat);
-		pTypes = &defaultTypes;
-	}
-	
-	m_Desc = "[Ditto Error] BAD DESCRIPTION";
-	
-	// Get Description String
-	// NOTE: We make sure that the description always corresponds to the
-	//  data saved by using the exact same globalmem instance as the source
-	//  for both... i.e. we only fetch the description format type once.
-	CClipFormat cfDesc;
-	bool bIsDescSet = false;
-
-	cfDesc.m_cfType = CF_UNICODETEXT;	
-	if(oleData.IsDataAvailable(cfDesc.m_cfType))
-	{
-		for (int i = 0; i < 10; i++)
-		{
-			cfDesc.m_hgData = oleData.GetGlobalData(cfDesc.m_cfType);
-			if (cfDesc.m_hgData == NULL)
-			{
-				Log(StrF(_T("Tried to set description from cf_unicode, data is NULL, try: %d"), i+1));
-			}
-			else
-			{
-				break;
-			}
-			Sleep(10);
-		}
-		bIsDescSet = SetDescFromText(cfDesc.m_hgData, true);
-
-		Log(StrF(_T("Tried to set description from cf_unicode text, Set: %d, Desc: [%s]"), bIsDescSet, m_Desc.Left(30)));
-	}
-
-	if(bIsDescSet == false)
-	{
-		cfDesc.m_cfType = CF_TEXT;	
-		if(oleData.IsDataAvailable(cfDesc.m_cfType))
-		{
-			for (int i = 0; i < 10; i++)
-			{
-				cfDesc.m_hgData = oleData.GetGlobalData(cfDesc.m_cfType);
-				if (cfDesc.m_hgData == NULL)
-				{
-					Log(StrF(_T("Tried to set description from cf_text, data is NULL, try: %d"), i + 1));
-				}
-				else
-				{
-					break;
-				}
-				Sleep(10);
-			}
-
-			bIsDescSet = SetDescFromText(cfDesc.m_hgData, false);
-
-			Log(StrF(_T("Tried to set description from cf_text text, Set: %d, Desc: [%s]"), bIsDescSet, m_Desc.Left(30)));
-		}
-	}
-
-	INT_PTR nSize;
-	CClipFormat cf;
-	INT_PTR numTypes = pTypes->GetSize();
-
-	Log(StrF(_T("Begin enumerating over supported types, Count: %d"), numTypes));
-
-	for(int i = 0; i < numTypes; i++)
-	{
-		cf.m_cfType = pTypes->ElementAt(i);
-
-		if (cf.m_cfType == CF_DIB &&
-			g_Opt.m_excludeCF_DIBInExcel &&
-			activeApp.MakeLower() == _T("excel.exe"))
-		{
-			continue;
-		}
-
-		BOOL bSuccess = false;
-		Log(StrF(_T("Begin try and load type %s"), GetFormatName(cf.m_cfType)));
-		
-		// is this the description we already fetched?
-		if(cf.m_cfType == cfDesc.m_cfType)
-		{
-			cf = cfDesc;
-			cfDesc.m_hgData = 0; // cf owns it now (to go into m_Formats)
-		}
-		else if(!oleData.IsDataAvailable(cf.m_cfType))
-		{
-			Log(StrF(_T("End of load - Data is not available for type %s"), GetFormatName(cf.m_cfType)));
-			continue;
-		}
-		else
-		{
-			for (int i = 0; i < 2; i++)
-			{
-				cf.m_hgData = oleData.GetGlobalData(cf.m_cfType);
-				if (cf.m_hgData != NULL)
-				{
-					break;
-				}
-
-				Log(StrF(_T("Tried to get data for type: %s, data is NULL, try: %d"), GetFormatName(cf.m_cfType), i + 1));
-				Sleep(5);
-			}
-		}
-		
-		if(cf.m_hgData)
-		{
-			nSize = GlobalSize(cf.m_hgData);
-			if(nSize > 0)
-			{
-				if(g_Opt.m_lMaxClipSizeInBytes > 0 && (int)nSize > g_Opt.m_lMaxClipSizeInBytes)
-				{
-					CString cs;
-					cs.Format(_T("Maximum clip size reached max size = %d, clip size = %d"), g_Opt.m_lMaxClipSizeInBytes, nSize);
-					Log(cs);
-
-					oleData.Release();
-					return -1;
-				}
-
-				ASSERT(::IsValid(cf.m_hgData));
-				
-				m_Formats.Add(cf);
-				bSuccess = true;
-			}
-			else
-			{
-				ASSERT(FALSE); // a valid GlobalMem with 0 size is strange
-				cf.Free();
-				Log(StrF(_T("Data length is 0 for type %s"), GetFormatName(cf.m_cfType)));
-			}
-			cf.m_hgData = 0; // m_Formats owns it now
-		}
-
-		Log(StrF(_T("End of load - type %s, Success: %d"), GetFormatName(cf.m_cfType), bSuccess));
-	}
-
-	Log(StrF(_T("End enumerating over supported types, Count: %d"), numTypes));
-	
-	m_Time = CTime::GetCurrentTime();
-	
-	if(!bIsDescSet)
-	{
-		SetDescFromType();
-
-		Log(StrF(_T("Setting description from type, Desc: [%s]"), m_Desc.Left(30)));
-	}
-	
-	// if the description was in a type that is not supported,
-	//we have to free it since it wasn't added to m_Formats
-	if(cfDesc.m_hgData)
-	{
-		cfDesc.Free();
-	}
-	
-	oleData.Release();
-	
-	if(m_Formats.GetSize() == 0)
-	{
-		Log(_T("No clip types were in supported types array"));
-		return FALSE;
-	}
-
-	bool calledOnCopyScript = false;
-	try
-	{
-		for (auto & listItem : g_Opt.m_copyScripts.m_list)
-		{
-			if (listItem.m_active)
-			{
-				Log(StrF(_T("Start of process copy name: %s, script: %s"), listItem.m_name, listItem.m_script));
-
-				ChaiScriptOnCopy onCopy;
-				CDittoChaiScript clipData(this, (LPCSTR)CTextConvert::UnicodeToAnsi(activeApp), (LPCSTR)CTextConvert::UnicodeToAnsi(activeAppTitle));
-				if (onCopy.ProcessScript(clipData, (LPCSTR)CTextConvert::UnicodeToAnsi(listItem.m_script)) == false)
-				{
-					Log(StrF(_T("End of process copy name: %s, returned false, not saving this copy to Ditto, last Error: %s"), listItem.m_name, onCopy.m_lastError));
-
-					return -1;
-				}
-
-				calledOnCopyScript = true;
-
-				Log(StrF(_T("End of process copy name: %s, returned true, last Error: %s"), listItem.m_name, onCopy.m_lastError));
-			}
-			else
-			{
-				Log(StrF(_T("Script is not active, not processing name: %s, script: %s"), listItem.m_name, listItem.m_script));
-			}
-		}
-	}
-	catch (CException *ex)
-	{
-		TCHAR szCause[255];
-		ex->GetErrorMessage(szCause, 255);
-		CString cs;
-		cs.Format(_T("save copy exception: %s"), szCause);
-		Log(cs);
-	}
-	catch (...)
-	{
-		Log(_T("save copy exception 2"));	
-	}
-
-	//copy script could have changed the data, make sure the description matches
-	if (calledOnCopyScript)
-	{
-		auto uString = this->GetUnicodeTextFormat();
-		if (uString != _T(""))
-		{
-			if (uString.GetLength() > g_Opt.m_bDescTextSize)
-			{
-				m_Desc = uString.Left(g_Opt.m_bDescTextSize);
-			}
-			else
-			{
-				m_Desc = uString;
-			}
-		}
-		else
-		{
-			auto aString = this->GetCFTextTextFormat();
-			if (aString != "")
-			{
-				if (aString.GetLength() > g_Opt.m_bDescTextSize)
-				{
-					m_Desc = aString.Left(g_Opt.m_bDescTextSize);
-				}
-				else
-				{
-					m_Desc = aString;
-				}
-			}
-			else
-			{
-				SetDescFromType();
-			}
-		}
-
-		Log(StrF(_T("Called on copy script, this could change the description, regenerated desc: %s"), m_Desc));
-	}
-
-	if (this->m_Desc != _T(""))
-	{
-		std::wstring stringData(this->m_Desc);
-		if (g_Opt.m_regexHelper.TextMatchFilters(activeApp, stringData))
-		{
-			return -1;
-		}
-	}
-
-	return TRUE;
-}
-
-bool CClip::SetDescFromText(HGLOBAL hgData, bool unicode)
-{
-	if(hgData == 0)
-		return false;
-	
-	bool bRet = false;
-	INT_PTR bufLen = 0;
-
-	if(unicode)
-	{
-		TCHAR* text = (TCHAR *) GlobalLock(hgData);
-		bufLen = GlobalSize(hgData);
-
-		m_Desc = CString(text, bufLen/(sizeof(wchar_t)));
-		bRet = true;
-	}
-	else
-	{
-		char* text = (char *) GlobalLock(hgData);
-		bufLen = GlobalSize(hgData);
-	
-		m_Desc = CString(text, bufLen);
-		bRet = true;
-	}
-		
-	if(bufLen > g_Opt.m_bDescTextSize)
-	{
-		m_Desc = m_Desc.Left(g_Opt.m_bDescTextSize);
-	}
-	
-	//Unlock the data
-	GlobalUnlock(hgData);
-	
-	return bRet;
-}
-
-bool CClip::SetDescFromType()
-{
-	INT_PTR size = m_Formats.GetSize();
-	if(size <= 0)
-	{
-		return false;
-	}
-
-	int nCF_HDROPIndex = -1;
-	for(int i = 0; i < size; i++)
-	{
-		if(m_Formats[i].m_cfType == CF_HDROP)
-		{
-			nCF_HDROPIndex = i;
-		}
-	}
-
-	if(nCF_HDROPIndex >= 0)
-	{
-		using namespace nsPath;
-
-		HDROP drop = (HDROP)GlobalLock(m_Formats[nCF_HDROPIndex].m_hgData);
-		int nNumFiles = min(5, DragQueryFile(drop, -1, NULL, 0));
-
-		if(nNumFiles > 1)
-			m_Desc = "Copied Files - ";
-		else
-			m_Desc = "Copied File - ";
-
-		TCHAR file[MAX_PATH];
-		
-		for(int nFile = 0; nFile < nNumFiles; nFile++)
-		{
-			if(DragQueryFile(drop, nFile, file, sizeof(file)) > 0)
-			{
-				CPath path(file);
-				m_Desc += path.GetName();
-				m_Desc += " - ";
-				m_Desc += file;
-				m_Desc += "\n";
-			}
-		}
-		
-		GlobalUnlock(m_Formats[nCF_HDROPIndex].m_hgData);
-	}
-	else
-	{
-		m_Desc = GetFormatName(m_Formats[0].m_cfType);
-	}
-
-	return m_Desc.GetLength() > 0;
-}
-
-bool CClip::AddToDB(bool bCheckForDuplicates)
-{
-	bool bResult;
-	try
-	{
-		m_Time = CTime::GetCurrentTime().GetTime();
-
-		m_CRC = GenerateCRC();
-
-		if(bCheckForDuplicates &&
-			m_parentId < 0)
-		{	
-			int nID = FindDuplicate();
-			if(nID >= 0)
-			{
-				MakeLatestOrder();
-				MakeLatestGroupOrder();
-
-				CString sql;
-				
-				sql.Format(_T("UPDATE Main SET clipOrder = %f where lID = %d;"), 
-								m_clipOrder, nID);
-
-				int ret = theApp.m_db.execDML(sql);
-
-				int groupRet = -1;
-
-				if(m_parentId > -1)
-				{
-					sql.Format(_T("UPDATE Main SET clipGroupOrder = %f where lID = %d;"), 
-						m_clipGroupOrder, nID);
-
-					groupRet = theApp.m_db.execDML(sql);
-				}
-
-
-				m_id = nID;
-
-				Log(StrF(_T("Found duplicate clip in db, Id: %d, ParentId: %d crc: %d, NewOrder: %f, GroupOrder %f, Ret: %d, GroupRet: %d, SQL: %s"), 
-										nID, m_parentId, m_CRC, m_clipOrder, m_clipGroupOrder, ret, groupRet, sql));
-
-				return true;
-			}
-		}
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
-
-	int removeStickySettingClipId = -1;
-
-	if (m_addToDbStickyEnum == AddToDbStickyEnum::MAKE_TOP_STICKY)
-	{
-		m_stickyClipOrder = this->GetNewTopSticky(m_parentId, -1);
-	}
-	else if (m_addToDbStickyEnum == AddToDbStickyEnum::MAKE_LAST_STICKY)
-	{
-		m_stickyClipOrder = this->GetNewLastSticky(m_parentId, -1);
-	}
-	else if (m_addToDbStickyEnum == AddToDbStickyEnum::REPLACE_TOP_STICKY)
-	{
-		m_stickyClipOrder = this->GetNewTopSticky(m_parentId, -1);
-		removeStickySettingClipId = GetExistingTopStickyClipId(m_parentId);
-	}
-	
-	bResult = false;
-	if(AddToMainTable())
-	{		
-		bResult = AddToDataTable();
-	}
-
-	if(bResult)
-	{
-		if(g_Opt.m_csPlaySoundOnCopy.IsEmpty() == FALSE)
-			PlaySound(g_Opt.m_csPlaySoundOnCopy, NULL, SND_FILENAME|SND_ASYNC);
-
-		if (removeStickySettingClipId > 0)
-		{
-			RemoveStickySetting(removeStickySettingClipId, m_parentId);
-		}
-	}
-	
-	// should be emptied by AddToDataTable
-	//ASSERT(m_Formats.GetSize() == 0);
-	
-	return bResult;
-}
-
-// if a duplicate exists, set recset to the duplicate and return true
-int CClip::FindDuplicate()
-{
-	try
-	{
-		//If they are allowing duplicates still check 
-		//the last copied item
-		if(g_Opt.m_bAllowDuplicates)
-		{
-			if (g_Opt.m_allowBackToBackDuplicates == FALSE)
-			{
-				if (m_CRC == m_LastAddedCRC)
-					return m_lastAddedID;
-			}
-		}
-		else
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Main WHERE CRC = %d"), m_CRC);
-				
-			if(q.eof() == false)
-			{
-				return q.getIntField(_T("lID"));
-			}
-		}
-	}
-	CATCH_SQLITE_EXCEPTION
-		
-	return -1;
-}
-
-
-
-DWORD CClip::GenerateCRC()
-{
-	CClipFormat* pCF;
-	DWORD dwCRC = 0xFFFFFFFF;
-
-	CCrc32Dynamic *pCrc32 = new CCrc32Dynamic;
-	if(pCrc32)
-	{
-		//Generate a CRC value for all copied data
-
-		INT_PTR size = m_Formats.GetSize();
-		for(int i = 0; i < size ; i++)
-		{
-			pCF = & m_Formats.ElementAt(i);
-			
-			const unsigned char *Data = (const unsigned char *)GlobalLock(pCF->m_hgData);
-			if(Data)
-			{
-				if (CGetSetOptions::GetAdjustClipsForCRC())
-				{
-					//Try and remove known things that change in rtf (word and outlook)
-					if (pCF->m_cfType == theApp.m_RTFFormat)
-					{
-						CStringA CStringData((char*)Data);
-
-						//In word and outlook I was finding that data in the \\datastore section was always changing, remove this for the crc check
-						RemoveRTFSection(CStringData, "{\\*\\datastore");
-
-						//In word and outlook rsid values are always changing, remove these for the crc check
-						DeleteParamFromRTF(CStringData, "\\rsid", true);
-						DeleteParamFromRTF(CStringData, "\\insrsid", true);
-						DeleteParamFromRTF(CStringData, "\\mdispDef1", false);
-
-						pCrc32->GenerateCrc32((const LPBYTE)CStringData.GetBuffer(), (DWORD)CStringData.GetLength(), dwCRC);
-					}
-					else
-					{
-						//i've seen examble where the text size was 10 but the data size was 20, leading to random crc values
-						//try and only check the crc for the actual text
-						int dataLength = (int)GlobalSize(pCF->m_hgData);
-						if (pCF->m_cfType == CF_TEXT)
-						{
-							dataLength = min(dataLength, ((int)strlen((char*)Data) + 1));
-						}
-						else if (pCF->m_cfType == CF_UNICODETEXT)
-						{
-							dataLength = min(dataLength, (((int)wcslen((wchar_t*)Data) + 1) * 2));
-						}
-						pCrc32->GenerateCrc32((const LPBYTE)Data, (DWORD)dataLength, dwCRC);
-					}
-				}
-				else
-				{
-					pCrc32->GenerateCrc32((const LPBYTE)Data, (DWORD)GlobalSize(pCF->m_hgData), dwCRC);
-				}
-			}
-			GlobalUnlock(pCF->m_hgData);
-		}
-
-		dwCRC = ~dwCRC;
-
-		delete pCrc32;
-	}
-
-	return dwCRC;
-}
-
-// assigns m_ID
-bool CClip::AddToMainTable()
-{
-	try
-	{
-		m_Desc.Replace(_T("'"), _T("''"));
-		m_csQuickPaste.Replace(_T("'"), _T("''"));
-
-		CString cs;
-		cs.Format(_T("INSERT into Main (lDate, mText, lShortCut, lDontAutoDelete, CRC, bIsGroup, lParentID, QuickPasteText, clipOrder, clipGroupOrder, globalShortCut, lastPasteDate, stickyClipOrder, stickyClipGroupOrder, MoveToGroupShortCut, GlobalMoveToGroupShortCut) ")
-						_T("values(%d, '%s', %d, %d, %d, %d, %d, '%s', %f, %f, %d, %d, %f, %f, %d, %d);"),
-							(int)m_Time.GetTime(),
-							m_Desc,
-							m_shortCut,
-							m_dontAutoDelete,
-							m_CRC,
-							m_bIsGroup,
-							m_parentId,
-							m_csQuickPaste,
-							m_clipOrder,
-							m_clipGroupOrder,
-							m_globalShortCut,
-							(int)CTime::GetCurrentTime().GetTime(),
-							m_stickyClipOrder,
-							m_stickyClipGroupOrder,
-							m_moveToGroupShortCut,
-							m_globalMoveToGroupShortCut);
-
-		theApp.m_db.execDML(cs);
-
-		m_id = (long)theApp.m_db.lastRowId();
-
-		Log(StrF(_T("Added clip to main table, Id: %d, ParentId: %d Desc: %s, Order: %f, GroupOrder: %f"), m_id, m_parentId, m_Desc, m_clipOrder, m_clipGroupOrder));
-
-		m_LastAddedCRC = m_CRC;
-		m_lastAddedID = m_id;
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
-	
-	return true;
-}
-
-bool CClip::ModifyMainTable()
-{
-	bool bRet = false;
-	try
-	{
-		m_Desc.Replace(_T("'"), _T("''"));
-		m_csQuickPaste.Replace(_T("'"), _T("''"));
-
-		theApp.m_db.execDMLEx(_T("UPDATE Main SET lShortCut = %d, ")
-			_T("mText = '%s', ")
-			_T("lParentID = %d, ")
-			_T("lDontAutoDelete = %d, ")
-			_T("QuickPasteText = '%s', ")
-			_T("clipOrder = %f, ")
-			_T("clipGroupOrder = %f, ")
-			_T("globalShortCut = %d, ")
-			_T("stickyClipOrder = %f, ")
-			_T("stickyClipGroupOrder = %f, ")
-			_T("MoveToGroupShortCut = %d, ")
-			_T("GlobalMoveToGroupShortCut = %d ")
-			_T("WHERE lID = %d;"), 
-			m_shortCut, 
-			m_Desc, 
-			m_parentId, 
-			m_dontAutoDelete, 
-			m_csQuickPaste,
-			m_clipOrder,
-			m_clipGroupOrder,
-			m_globalShortCut,
-			m_stickyClipOrder,
-			m_stickyClipGroupOrder,
-			m_moveToGroupShortCut,
-			m_globalMoveToGroupShortCut,
-			m_id);
-
-		bRet = true;
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
-
-	return bRet;
-}
-
-bool CClip::ModifyDescription()
-{
-	bool bRet = false;
-	try
-	{
-		m_Desc.Replace(_T("'"), _T("''"));
-
-		theApp.m_db.execDMLEx(_T("UPDATE Main SET mText = '%s' ")
-			_T("WHERE lID = %d;"),
-			m_Desc,
-			m_id);
-
-		bRet = true;
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
-
-		return bRet;
-}
-
-// Empties m_Formats as it saves them to the Data Table.
-bool CClip::AddToDataTable()
-{
-	CClipFormat* pCF;
-
-	try
-	{
-		CppSQLite3Statement stmt = theApp.m_db.compileStatement(_T("insert into Data values (NULL, ?, ?, ?);"));
-		
-		for(INT_PTR i = m_Formats.GetSize()-1; i >= 0 ; i--)
-		{
-			pCF = &m_Formats.ElementAt(i);
-
-			CString formatName = GetFormatName(pCF->m_cfType);
-			int clipSize = 0;
-			
-			stmt.bind(1, m_id);
-			stmt.bind(2, formatName);
-
-			const unsigned char *Data = (const unsigned char *)GlobalLock(pCF->m_hgData);
-			if(Data)
-			{
-				clipSize = (int)GlobalSize(pCF->m_hgData);
-				stmt.bind(3, Data, clipSize);
-			}
-			GlobalUnlock(pCF->m_hgData);
-			
-			stmt.execDML();
-			stmt.reset();
-
-			pCF->m_dataId = (long)theApp.m_db.lastRowId();
-
-			Log(StrF(_T("Added ClipData to DB, Id: %d, ParentId: %d Type: %s, size: %d"), pCF->m_dataId, m_id, formatName, clipSize));
-		}
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
-		
-	return true;
-}
-
-void CClip::MoveUp(int parentId)
-{
-	try
-	{
-		//In a group, not a sticky
-		if(parentId > -1 && m_stickyClipGroupOrder == INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder == -(2147483647) AND clipGroupOrder > %f ORDER BY clipGroupOrder ASC LIMIT 1"), parentId, m_clipGroupOrder);
-			if (q.eof() == false)
-			{
-				int idAbove = q.getIntField(_T("lID"));
-				double orderAbove = q.getFloatField(_T("clipGroupOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND clipGroupOrder > %f AND stickyClipGroupOrder == -(2147483647) ORDER BY clipGroupOrder ASC LIMIT 1"), parentId, orderAbove);
-				if (q2.eof() == false)
-				{ 
-					int idTwoAbove = q2.getIntField(_T("lID"));
-					double orderTwoAbove = q2.getFloatField(_T("clipGroupOrder"));
-
-					m_clipGroupOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
-				}
-				else
-				{
-					m_clipGroupOrder = orderAbove + 1;
-				}
-			}
-		}
-		// main group, not a sticky
-		else if(parentId <= -1 && m_stickyClipOrder == INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder > %f AND stickyClipOrder == -(2147483647) ORDER BY clipOrder ASC LIMIT 1"), m_clipOrder);
-			if (q.eof() == false)
-			{
-				int idAbove = q.getIntField(_T("lID"));
-				double orderAbove = q.getFloatField(_T("clipOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder > %f AND stickyClipOrder == -(2147483647) ORDER BY clipOrder ASC LIMIT 1"), orderAbove);
-				if (q2.eof() == false)
-				{ 
-					int idTwoAbove = q2.getIntField(_T("lID"));
-					double orderTwoAbove = q2.getFloatField(_T("clipOrder"));
-
-					m_clipOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
-				}
-				else
-				{
-					m_clipOrder = orderAbove + 1;
-				}
-			}
-		}
-		//In a group, a sticky clip
-		else if(parentId > -1 && m_stickyClipGroupOrder != INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder > %f ORDER BY stickyClipGroupOrder ASC LIMIT 1"), parentId, m_stickyClipGroupOrder);
-			if (q.eof() == false)
-			{
-				int idAbove = q.getIntField(_T("lID"));
-				double orderAbove = q.getFloatField(_T("stickyClipGroupOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder > %f ORDER BY stickyClipGroupOrder ASC LIMIT 1"), parentId, orderAbove);
-				if (q2.eof() == false)
-				{
-					int idTwoAbove = q2.getIntField(_T("lID"));
-					double orderTwoAbove = q2.getFloatField(_T("stickyClipGroupOrder"));
-
-					m_stickyClipGroupOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
-				}
-				else
-				{
-					m_stickyClipGroupOrder = orderAbove + 1;
-				}
-			}
-		}
-		//not in a group, a sticky clip
-		else if(parentId <= -1 && m_stickyClipOrder != INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder > %f ORDER BY stickyClipOrder ASC LIMIT 1"), m_stickyClipOrder);
-			if (q.eof() == false)
-			{
-				int idAbove = q.getIntField(_T("lID"));
-				double orderAbove = q.getFloatField(_T("stickyClipOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder > %f ORDER BY stickyClipOrder ASC LIMIT 1"), orderAbove);
-				if (q2.eof() == false)
-				{
-					int idTwoAbove = q2.getIntField(_T("lID"));
-					double orderTwoAbove = q2.getFloatField(_T("stickyClipOrder"));
-
-					m_stickyClipOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
-				}
-				else
-				{
-					m_stickyClipOrder = orderAbove + 1;
-				}
-			}
-		}
-	}
-	CATCH_SQLITE_EXCEPTION
-}
-
-void CClip::MoveDown(int parentId)
-{
-	try
-	{
-		//In a group, not a sticky
-		if(parentId > -1 && m_stickyClipGroupOrder == INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND clipGroupOrder < %f AND stickyClipGroupOrder = -(2147483647) ORDER BY clipGroupOrder DESC LIMIT 1"), parentId, m_clipGroupOrder);
-			if (q.eof() == false)
-			{
-				int idBelow = q.getIntField(_T("lID"));
-				double orderBelow = q.getFloatField(_T("clipGroupOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND clipGroupOrder < %f AND stickyClipGroupOrder = -(2147483647) ORDER BY clipGroupOrder DESC LIMIT 1"), parentId, orderBelow);
-				if (q2.eof() == false)
-				{ 
-					int idTwoBelow = q2.getIntField(_T("lID"));
-					double orderTwoBelow = q2.getFloatField(_T("clipGroupOrder"));
-
-					m_clipGroupOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
-				}
-				else
-				{
-					m_clipGroupOrder = orderBelow - 1;
-				}
-			}
-		}
-		// main group, not a sticky
-		else if(parentId <= -1 && m_stickyClipOrder == INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder < %f AND stickyClipOrder = -(2147483647) ORDER BY clipOrder DESC LIMIT 1"), m_clipOrder);
-			if (q.eof() == false)
-			{
-				int idBelow = q.getIntField(_T("lID"));
-				double orderBelow = q.getFloatField(_T("clipOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder < %f AND stickyClipOrder = -(2147483647) ORDER BY clipOrder DESC LIMIT 1"), orderBelow);
-				if (q2.eof() == false)
-				{ 
-					int idTwoBelow = q2.getIntField(_T("lID"));
-					double orderTwoBelow = q2.getFloatField(_T("clipOrder"));
-
-					m_clipOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
-				}
-				else
-				{
-					m_clipOrder = orderBelow - 1;
-				}
-			}
-		}
-		//In a group, a sticky clip
-		else if(parentId > -1 && m_stickyClipGroupOrder != INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder < %f ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId, m_stickyClipGroupOrder);
-			if (q.eof() == false)
-			{
-				int idBelow = q.getIntField(_T("lID"));
-				double orderBelow = q.getFloatField(_T("stickyClipGroupOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder < %f ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId, orderBelow);
-				if (q2.eof() == false)
-				{
-					int idTwoBelow = q2.getIntField(_T("lID"));
-					double orderTwoBelow = q2.getFloatField(_T("stickyClipGroupOrder"));
-
-					m_stickyClipGroupOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
-				}
-				else
-				{
-					m_stickyClipGroupOrder = orderBelow - 1;
-				}
-			}
-		}
-		//not in a group, a sticky clip
-		else if(parentId <= -1 && m_stickyClipOrder != INVALID_STICKY)
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder < %f ORDER BY stickyClipOrder DESC LIMIT 1"), m_stickyClipOrder);
-			if (q.eof() == false)
-			{
-				int idBelow = q.getIntField(_T("lID"));
-				double orderBelow = q.getFloatField(_T("stickyClipOrder"));
-
-				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder < %f ORDER BY stickyClipOrder DESC LIMIT 1"), orderBelow);
-				if (q2.eof() == false)
-				{
-					int idTwoBelow = q2.getIntField(_T("lID"));
-					double orderTwoBelow = q2.getFloatField(_T("stickyClipOrder"));
-
-					m_stickyClipOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
-				}
-				else
-				{
-					m_stickyClipOrder = orderBelow - 1;
-				}
-			}
-		}
-	}
-	CATCH_SQLITE_EXCEPTION
-}
-
-void CClip::MakeStickyTop(int parentId)
-{
-	if (parentId < 0)
-	{
-		m_stickyClipOrder = GetNewTopSticky(parentId, m_id);
-	}
-	else
-	{
-		m_stickyClipGroupOrder = GetNewTopSticky(parentId, m_id);
-	}
-}
-
-void CClip::MakeStickyLast(int parentId)
-{
-	if (parentId < 0)
-	{
-		m_stickyClipOrder = GetNewLastSticky(parentId, m_id);
-	}
-	else
-	{
-		m_stickyClipGroupOrder = GetNewLastSticky(parentId, m_id);
-	}
-}
-
-bool CClip::RemoveStickySetting(int parentId)
-{
-	bool reset = false;
-	if (parentId < 0)
-	{
-		if (m_stickyClipOrder != INVALID_STICKY)
-		{
-			m_stickyClipOrder = INVALID_STICKY;
-			reset = true;
-		}
-	}
-	else
-	{
-		if (m_stickyClipGroupOrder != INVALID_STICKY)
-		{
-			m_stickyClipGroupOrder = INVALID_STICKY;
-			reset = true;
-		}
-	}
-
-	return reset;
-}
-
-bool CClip::RemoveStickySetting(int clipId, int parentId)
-{
-	bool reset = false;
-	if (parentId < 0)
-	{
-		int c = theApp.m_db.execDMLEx(_T("UPDATE Main SET stickyClipOrder = %f WHERE lID = %d"), (double)INVALID_STICKY, clipId);
-		int y = 0;
-	}
-	else
-	{
-		int c = theApp.m_db.execDMLEx(_T("UPDATE Main SET stickyClipGroupOrder = %f WHERE lID = %d"), (double)INVALID_STICKY, clipId);
-		int y = 0;
-	}
-
-	return reset;
-}
-
-double CClip::GetExistingTopStickyClipId(int parentId)
-{
-	int existingTopClipId = -1;
-
-	try
-	{
-		if (parentId < 0)
-		{
-			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT lID FROM Main WHERE stickyClipOrder <> -(2147483647) ORDER BY stickyClipOrder DESC LIMIT 1"));
-			if (q.eof() == false)
-			{
-				existingTopClipId = q.getIntField(_T("lID"));
-			}
-		}
-		else
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Main WHERE lParentID = %d AND stickyClipGroupOrder <> -(2147483647) ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId);
-			if (q.eof() == false)
-			{
-				existingTopClipId = q.getIntField(_T("lID"));
-			}
-		}
-
-	}
-	CATCH_SQLITE_EXCEPTION
-
-	return existingTopClipId;
-}
-
-double CClip::GetNewTopSticky(int parentId, int clipId)
-{
-	double newOrder = 1;
-	double existingMaxOrder = 0;
-	CString existingDesc = _T("");
-
-	try
-	{
-		if (parentId < 0)
-		{
-			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT lID, stickyClipOrder, mText FROM Main WHERE stickyClipOrder <> -(2147483647) ORDER BY stickyClipOrder DESC LIMIT 1"));
-			if (q.eof() == false)
-			{
-				existingMaxOrder = q.getFloatField(_T("stickyClipOrder"));
-				existingDesc = q.getStringField(_T("mText"));
-				newOrder = existingMaxOrder + 1;
-			}
-		}
-		else
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder, mText FROM Main WHERE lParentID = %d AND stickyClipGroupOrder <> -(2147483647) ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId);
-			if (q.eof() == false)
-			{
-				existingMaxOrder = q.getFloatField(_T("stickyClipGroupOrder"));
-				newOrder = existingMaxOrder + 1;
-			}
-		}
-
-		if (newOrder == 0.0)
-			newOrder += 1;
-
-		Log(StrF(_T("GetNewTopSticky, Id: %d, parentId: %d, CurrentMax: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMaxOrder, existingDesc, newOrder));
-	}
-	CATCH_SQLITE_EXCEPTION
-
-	return newOrder;
-}
-
-double CClip::GetNewLastSticky(int parentId, int clipId)
-{
-	double newOrder = 1;
-	double existingMaxOrder = 0;
-	CString existingDesc = _T("");
-
-	try
-	{
-		if (parentId < 0)
-		{
-			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT stickyClipOrder, mText FROM Main WHERE stickyClipOrder <> -(2147483647) ORDER BY stickyClipOrder LIMIT 1"));
-			if (q.eof() == false)
-			{
-				existingMaxOrder = q.getFloatField(_T("stickyClipOrder"));
-				existingDesc = q.getStringField(_T("mText"));
-				newOrder = existingMaxOrder - 1;
-			}
-		}
-		else
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT stickyClipGroupOrder, mText FROM Main WHERE lParentID = %d AND stickyClipGroupOrder <> -(2147483647) ORDER BY stickyClipGroupOrder LIMIT 1"), parentId);
-			if (q.eof() == false)
-			{
-				existingMaxOrder = q.getFloatField(_T("stickyClipGroupOrder"));
-				newOrder = existingMaxOrder - 1;
-			}
-		}
-
-		if (newOrder == 0.0)
-			newOrder -= 1;
-
-		Log(StrF(_T("GetNewLastSticky, Id: %d, parentId: %d, CurrentMax: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMaxOrder, existingDesc, newOrder));
-	}
-	CATCH_SQLITE_EXCEPTION
-
-		return newOrder;
-}
-
-void CClip::MakeLatestOrder()
-{
-	m_clipOrder = GetNewOrder(-1, m_id);
-}
-
-void CClip::MakeLatestGroupOrder()
-{
-	if(m_parentId > -1)
-	{
-		m_clipGroupOrder = GetNewOrder(m_parentId, m_id);
-	}
-}
-
-void CClip::MakeLastOrder()
-{
-	m_clipOrder = GetNewLastOrder(-1, m_id);
-}
-
-void CClip::MakeLastGroupOrder()
-{
-	if (m_parentId > -1)
-	{
-		m_clipGroupOrder = GetNewLastOrder(m_parentId, m_id);
-	}
-}
-
-double CClip::GetNewOrder(int parentId, int clipId)
-{
-	double newOrder = 0;
-	double existingMaxOrder = 0;
-	CString existingDesc = _T("");
-
-	try
-	{
-		if(parentId < 0)
-		{
-			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT clipOrder, mText FROM Main ORDER BY clipOrder DESC LIMIT 1"));			
-			if(q.eof() == false)
-			{
-				existingMaxOrder = q.getFloatField(_T("clipOrder"));
-				existingDesc = q.getStringField(_T("mText"));
-				newOrder = existingMaxOrder + 1;
-			}
-		}
-		else
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT clipGroupOrder, mText FROM Main WHERE lParentID = %d ORDER BY clipGroupOrder DESC LIMIT 1"), parentId);			
-			if(q.eof() == false)
-			{
-				existingMaxOrder = q.getFloatField(_T("clipGroupOrder"));
-				newOrder = existingMaxOrder + 1;
-			}
-		}
-
-		Log(StrF(_T("GetNewOrder, Id: %d, parentId: %d, CurrentMax: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMaxOrder, existingDesc, newOrder));
-	}
-	CATCH_SQLITE_EXCEPTION
-
-	return newOrder;
-}
-
-double CClip::GetNewLastOrder(int parentId, int clipId)
-{
-	double newOrder = 0;
-	double existingMinOrder = 0;
-	CString existingDesc = _T("");
-
-	try
-	{
-		if (parentId < 0)
-		{
-			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT clipOrder, mText FROM Main where clipOrder notnull ORDER BY clipOrder ASC LIMIT 1"));
-			if (q.eof() == false)
-			{
-				existingMinOrder = q.getFloatField(_T("clipOrder"));
-				existingDesc = q.getStringField(_T("mText"));
-				newOrder = existingMinOrder - 1;
-			}
-		}
-		else
-		{
-			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT clipGroupOrder, mText FROM Main WHERE lParentID = %d AND clipGroupOrder notnull ORDER BY clipGroupOrder ASC LIMIT 1"), parentId);
-			if (q.eof() == false)
-			{
-				existingMinOrder = q.getFloatField(_T("clipGroupOrder"));
-				newOrder = existingMinOrder - 1;
-			}
-		}
-
-		Log(StrF(_T("GetLastOrder, Id: %d, parentId: %d, CurrentMin: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMinOrder, existingDesc, newOrder));
-	}
-	CATCH_SQLITE_EXCEPTION
-
-		return newOrder;
-}
-
-BOOL CClip::LoadMainTable(int id)
-{
-	bool bRet = false;
-	try
-	{
-		CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT * FROM Main WHERE lID = %d"), id);
-
-		if(q.eof() == false)
-		{
-			m_Time = q.getIntField(_T("lDate"));
-			m_Desc = q.getStringField(_T("mText"));
-			m_CRC = q.getIntField(_T("CRC"));
-			m_parentId = q.getIntField(_T("lParentID"));
-			m_dontAutoDelete = q.getIntField(_T("lDontAutoDelete"));
-			m_shortCut = q.getIntField(_T("lShortCut"));
-			m_bIsGroup = q.getIntField(_T("bIsGroup"));
-			m_csQuickPaste = q.getStringField(_T("QuickPasteText"));
-			m_clipOrder = q.getFloatField(_T("clipOrder"));
-			m_clipGroupOrder = q.getFloatField(_T("clipGroupOrder"));
-			m_globalShortCut = q.getIntField(_T("globalShortCut"));
-			m_lastPasteDate = q.getIntField(_T("lastPasteDate"));
-			m_stickyClipOrder = q.getFloatField(_T("stickyClipOrder"));
-			m_stickyClipGroupOrder = q.getFloatField(_T("stickyClipGroupOrder"));
-			m_moveToGroupShortCut = q.getIntField(_T("MoveToGroupShortCut"));
-			m_globalMoveToGroupShortCut = q.getIntField(_T("GlobalMoveToGroupShortCut"));
-
-			m_id = id;
-
-			bRet = true;
-		}
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(FALSE)
-
-	return bRet;
-}
-
-// STATICS
-
-// Allocates a Global containing the requested Clip Format Data
-HGLOBAL CClip::LoadFormat(int id, UINT cfType)
-{
-	HGLOBAL hGlobal = 0;
-	try
-	{
-		CString csSQL;
-		
-		csSQL.Format(
-			_T("SELECT Data.ooData FROM Data ")
-			_T("INNER JOIN Main ON Main.lID = Data.lParentID ")
-			_T("WHERE Main.lID = %d ")
-			_T("AND Data.strClipBoardFormat = \'%s\'"),
-			id,
-			GetFormatName(cfType));
-
-		CppSQLite3Query q = theApp.m_db.execQuery(csSQL);
-
-		if(q.eof() == false)
-		{
-			int nDataLen = 0;
-			const unsigned char *cData = q.getBlobField(0, nDataLen);
-			if(cData == NULL)
-			{
-				return false;
-			}
-
-			hGlobal = NewGlobalP((LPVOID)cData, nDataLen);
-		}
-	}
-	CATCH_SQLITE_EXCEPTION
-		
-	return hGlobal;
-}
-
-bool CClip::LoadFormats(int id, bool bOnlyLoad_CF_TEXT, bool includeRichTextForTextOnly)
-{
-	DWORD startTick = GetTickCount();
-	CClipFormat cf;
-	HGLOBAL hGlobal = 0;
-	m_Formats.RemoveAll();
-
-	try
-	{	
-		//Open the data table for all that have the parent id
-
-		//Order by Data.lID so that when generating CRC it's always in the same order as the first time
-		//we generated it
-		CString csSQL;
-
-		CString textFilter = _T("");
-		if(bOnlyLoad_CF_TEXT)
-		{
-			textFilter = _T("(strClipBoardFormat = 'CF_TEXT' OR strClipBoardFormat = 'CF_UNICODETEXT' OR strClipBoardFormat = 'CF_HDROP'");
-
-			if(includeRichTextForTextOnly)
-			{
-				textFilter = textFilter + _T(" OR strClipBoardFormat = 'Rich Text Format') AND ");
-			}
-			else
-			{
-				textFilter = textFilter + _T(") AND ");
-			}
-		}
-
-		csSQL.Format(
-			_T("SELECT lID, lParentID, strClipBoardFormat, ooData FROM Data ")
-			_T("WHERE %s lParentID = %d ORDER BY Data.lID desc"), textFilter, id);
-
-		CppSQLite3Query q = theApp.m_db.execQuery(csSQL);
-
-		while(q.eof() == false)
-		{
-			cf.m_dataId = q.getIntField(_T("lID"));
-			cf.m_parentId = q.getIntField(_T("lParentID"));
-			cf.m_cfType = GetFormatID(q.getStringField(_T("strClipBoardFormat")));
-			
-			if(bOnlyLoad_CF_TEXT)
-			{
-				if(cf.m_cfType != CF_TEXT && 
-					cf.m_cfType != CF_UNICODETEXT &&
-					cf.m_cfType != CF_HDROP &&
-					(cf.m_cfType != theApp.m_RTFFormat && !includeRichTextForTextOnly))
-				{
-					q.nextRow();
-					continue;
-				}
-			}
-
-			int nDataLen = 0;
-			const unsigned char *cData = q.getBlobField(_T("ooData"), nDataLen);
-			if(cData != NULL)
-			{
-				hGlobal = NewGlobalP((LPVOID)cData, nDataLen);
-			}
-			
-			cf.m_hgData = hGlobal;
-			m_Formats.Add(cf);
-
-			q.nextRow();
-		}
-
-		// formats owns all the data
-		cf.m_hgData = 0;
-	}
-	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
-		
-	DWORD endTick = GetTickCount();
-	if((endTick-startTick) > 150)
-		Log(StrF(_T("Paste Timing LoadFormats: %d, ClipId: %d"), endTick-startTick, id));
-
-	return m_Formats.GetSize() > 0;
-}
-
-void CClip::LoadTypes(int id, CClipTypes& types)
-{
-	types.RemoveAll();
-	try
-	{
-		CString csSQL;
-		// get formats for Clip "lID" (Main.lID) using the corresponding Main.lDataID
-		
-		//Order by Data.lID so that when generating CRC it's always in the same order as the first time
-		//we generated it
-		csSQL.Format(
-			_T("SELECT strClipBoardFormat FROM Data ")
-			_T("INNER JOIN Main ON Main.lID = Data.lParentID ")
-			_T("WHERE Main.lID = %d ORDER BY Data.lID desc"), id);
-
-		CppSQLite3Query q = theApp.m_db.execQuery(csSQL);			
-
-		while(q.eof() == false)
-		{		
-			types.Add(GetFormatID(q.getStringField(0)));
-			q.nextRow();
-		}
-	}
-	CATCH_SQLITE_EXCEPTION
-}
-
-bool CClip::SaveFromEditWnd(BOOL bUpdateDesc)
-{
-	bool bRet = false;
-
-	try
-	{
-		theApp.m_db.execDMLEx(_T("DELETE FROM Data WHERE lParentID = %d;"), m_id);
-
-		DWORD CRC = GenerateCRC();
-
-		AddToDataTable();
-
-		theApp.m_db.execDMLEx(_T("UPDATE Main SET CRC = %d WHERE lID = %d"), CRC, m_id);
-		
-		if(bUpdateDesc)
-		{
-			m_Desc.Replace(_T("'"), _T("''"));
-			theApp.m_db.execDMLEx(_T("UPDATE Main SET mText = '%s' WHERE lID = %d"), m_Desc, m_id);
-		}
-
-		bRet = true;
-	}
-	CATCH_SQLITE_EXCEPTION
-
-	return bRet;
-}
-
-CStringW CClip::GetUnicodeTextFormat()
-{
-	IClipFormat *pFormat = this->Clips()->FindFormatEx(CF_UNICODETEXT);
-	if(pFormat != NULL)
-	{
-		return pFormat->GetAsCString();
-	}
-
-	return _T("");
-}
-
-CStringA CClip::GetCFTextTextFormat()
-{
-	IClipFormat *pFormat = this->Clips()->FindFormatEx(CF_TEXT);
-	if(pFormat != NULL)
-	{
-		return pFormat->GetAsCStringA();
-	}
-
-	return _T("");
-}
-
-BOOL CClip::WriteTextToFile(CString path, BOOL unicode, BOOL asci, BOOL utf8)
-{
-	BOOL ret = false;
-
-	CFile f;
-	if(f.Open(path, CFile::modeWrite|CFile::modeCreate))
-	{
-		CStringW w = GetUnicodeTextFormat();
-		CStringA a = GetCFTextTextFormat();
-		
-		if(utf8 && w != _T(""))
-		{
-			CStringA convToUtf8 = CTextConvert::UnicodeToUTF8(w);
-			std::byte header[2];
-			header[0] = (std::byte)0xEF;
-			header[1] = (std::byte)0xBB;
-			f.Write(&header, 2);
-			f.Write(convToUtf8.GetBuffer(), convToUtf8.GetLength());
-
-			ret = true;
-		}
-		else if(unicode && w != _T(""))
-		{
-			std::byte header[2];
-			header[0] = (std::byte)0xFF;
-			header[1] = (std::byte)0xFE;
-			f.Write(&header, 2);
-			f.Write(w.GetBuffer(), w.GetLength() * sizeof(wchar_t));
-
-			ret = true;
-		}
-		else if(asci && a != _T(""))
-		{
-			f.Write(a.GetBuffer(), a.GetLength());
-
-			ret = true;
-		}
-
-		f.Close();
-	}
-
-	return ret;
-}
-
-BOOL CClip::WriteTextToHtmlFile(CString path)
-{
-	BOOL ret = false;
-
-	CFile f;
-	if (f.Open(path, CFile::modeWrite | CFile::modeCreate))
-	{
-		IClipFormat *pFormat = this->Clips()->FindFormatEx(theApp.m_HTML_Format);
-		if (pFormat != NULL)
-		{
-			CStringA html = pFormat->GetAsCStringA();
-
-			int pos = html.Find("<html");
-			if (pos >= 0)
-			{
-				html = html.Mid(pos);
-			}
-			else
-			{
-				html = html;
-			}
-
-			f.Write(html.GetBuffer(), html.GetLength());			
-		}
-
-		f.Close();
-	}
-
-	return ret;
-}
-
-BOOL CClip::WriteImageToFile(CString path)
-{
-	BOOL ret = false;
-
-	CClipFormat *bitmap = this->m_Formats.FindFormat(CF_DIB);
-	if (bitmap)
-	{
-		LPVOID pvData = GlobalLock(bitmap->m_hgData);
-		ULONG size = (ULONG)GlobalSize(bitmap->m_hgData);
-
-		BITMAPINFO *lpBI = (BITMAPINFO *)pvData;
-
-		int nPaletteEntries = 1 << lpBI->bmiHeader.biBitCount;
-		if (lpBI->bmiHeader.biBitCount > 8)
-			nPaletteEntries = 0;
-		else if (lpBI->bmiHeader.biClrUsed != 0)
-			nPaletteEntries = lpBI->bmiHeader.biClrUsed;
-
-		BITMAPFILEHEADER BFH;
-		memset(&BFH, 0, sizeof(BITMAPFILEHEADER));
-		BFH.bfType = 'MB';
-		BFH.bfSize = sizeof(BITMAPFILEHEADER) + size;
-		BFH.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + nPaletteEntries * sizeof(RGBQUAD);
-
-		// Create stream with 0 size
-		IStream* pIStream = NULL;
-		if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream) == S_OK)
-		{
-
-			//write the file to the stream object
-			pIStream->Write(&BFH, sizeof(BITMAPFILEHEADER), NULL);
-			pIStream->Write(pvData, size, NULL);
-
-			CImage i;
-			i.Load(pIStream);
-
-			if (i.Save(path) == S_OK)
-			{
-				ret = true;
-			}
-
-			pIStream->Release();
-		}
-	}
-	else
-	{
-		CClipFormat *png = this->m_Formats.FindFormat(theApp.m_PNG_Format);
-		if (png)
-		{
-			IStream* pIStream = NULL;
-			if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream) == S_OK)
-			{
-				LPVOID pvData = GlobalLock(png->m_hgData);
-				ULONG size = (ULONG)GlobalSize(png->m_hgData);
-
-				pIStream->Write(pvData, size, NULL);
-
-				GlobalUnlock(png->m_hgData);
-
-				CImage i;
-				i.Load(pIStream);
-
-				if (i.Save(path) == S_OK)
-				{
-					ret = true;
-				}
-
-				pIStream->Release();
-			}
-		}
-	}
-	return ret;
-}
-
-bool CClip::AddFileDataToData(CString &errorMessage)
-{
-	INT_PTR size = m_Formats.GetSize();
-	if (size <= 0)
-	{
-		errorMessage = _T("No CF_HDROP formats to convert");
-		return false;
-	}
-
-	bool addedFileData = false;
-
-	int nCF_HDROPIndex = -1;
-	int dittoDataIndex = -1;
-	for (int i = 0; i < size; i++)
-	{
-		if (m_Formats[i].m_cfType == CF_HDROP)
-		{
-			nCF_HDROPIndex = i;
-		}
-		else if(m_Formats[i].m_cfType == theApp.m_DittoFileData)
-		{
-			dittoDataIndex = i;
-		}
-	}	
-
-	if (nCF_HDROPIndex < 0)
-	{
-		errorMessage = _T("No CF_HDROP formats to convert");
-		return false;
-	}
-	else if (dittoDataIndex >= 0)
-	{
-		return false;
-	}
-	else
-	{
-		using namespace nsPath;
-
-		HDROP drop = (HDROP)GlobalLock(m_Formats[nCF_HDROPIndex].m_hgData);
-		int nNumFiles = DragQueryFile(drop, -1, NULL, 0);
-		
-		TCHAR filePath[MAX_PATH];
-
-		CString newDesc = _T("File Contents - ");
-				
-		for (int nFile = 0; nFile < nNumFiles; nFile++)
-		{
-			if (DragQueryFile(drop, nFile, filePath, sizeof(filePath)) > 0)
-			{
-				CFile file;
-				CFileException ex;
-				if (file.Open(filePath, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone, &ex))
-				{
-					int fileSize = (int)file.GetLength();
-					int maxSize = CGetSetOptions::GetMaxFileContentsSize();
-					if (fileSize < maxSize)
-					{
-						CString src(filePath);
-						CStringA csFilePath = CTextConvert::UnicodeToUTF8(src);
-						
-						int bufferSize = (int)fileSize + csFilePath.GetLength() + 1 + md5StringLength + 1;;
-						char *pBuffer = new char[bufferSize];
-						if (pBuffer != NULL)
-						{
-							//data contents
-							//original file<null terminator>md5<null terminator>file data
-
-							memset(pBuffer, 0, bufferSize);
-							strncpy(pBuffer, csFilePath, csFilePath.GetLength());
-						
-							//move the buffer start past the file path and md5 string
-							char *bufferStart = pBuffer + csFilePath.GetLength() + 1 + md5StringLength + 1;
-
-							int readBytes = (int)file.Read(bufferStart, fileSize);
-
-							CMd5 md5;
-							CStringA md5String = md5.CalcMD5FromString(bufferStart, fileSize);
-
-							char *bufferMd5 = pBuffer + csFilePath.GetLength() + 1;
-							strncpy(bufferMd5, md5String, md5StringLength);
-
-							AddFormat(theApp.m_DittoFileData, pBuffer, bufferSize);
-
-							addedFileData = true;
-
-							newDesc += filePath;
-							newDesc += _T("\n");
-
-							Log(StrF(_T("Saving file contents to Ditto Database, file: %s, size: %d, md5: %s"), filePath, fileSize, md5String));
-						}
-					}
-					else
-					{
-						const int MAX_FILE_SIZE_BUFFER = 255;
-						TCHAR szFileSize[MAX_FILE_SIZE_BUFFER];
-						TCHAR szMaxFileSize[MAX_FILE_SIZE_BUFFER];
-						StrFormatByteSize(fileSize, szFileSize, MAX_FILE_SIZE_BUFFER);
-						StrFormatByteSize(maxSize, szMaxFileSize, MAX_FILE_SIZE_BUFFER);
-
-						errorMessage += StrF(_T("File is to large: %s, Size: %s, Max Size: %s\r\n"), filePath, szFileSize, szMaxFileSize);
-					}
-				}
-				else
-				{
-					TCHAR szError[200];
-					ex.GetErrorMessage(szError, 200);
-					errorMessage += StrF(_T("Error opening file: %s, Error: %s\r\n"), filePath, szError);
-				}
-			}
-		}
-
-		GlobalUnlock(m_Formats[nCF_HDROPIndex].m_hgData);
-
-		if (addedFileData)
-		{
-			for (int i = 0; i < size; i++)
-			{
-				this->m_Formats.RemoveAt(i, 1);
-			}
-
-			this->m_Desc = newDesc;
-
-			if (this->ModifyDescription())
-			{
-				if (this->AddToDataTable() == FALSE)
-				{
-					errorMessage += _T("Error saving data to database.");
-				}
-			}
-			else
-			{
-				errorMessage += _T("Error saving main table to database.");
-			}
-		}
-	}
-
-	return addedFileData;
-}
-
-Gdiplus::Bitmap *CClip::CreateGdiplusBitmap()
-{
-	Gdiplus::Bitmap *gdipBitmap = NULL;
-
-	CClipFormat *png = this->m_Formats.FindFormat(GetFormatID(_T("PNG")));
-	if (png != NULL)
-	{
-		gdipBitmap = png->CreateGdiplusBitmap();
-	}
-	else
-	{
-		CClipFormat *dib = this->m_Formats.FindFormat(CF_DIB);
-		if (dib != NULL)
-		{
-			gdipBitmap = dib->CreateGdiplusBitmap();
-		}
-	}
-
-	return gdipBitmap;
-}
-
-/*----------------------------------------------------------------------------*\
-CClipList
-\*----------------------------------------------------------------------------*/
-
-CClipList::~CClipList()
-{
-	CClip* pClip;
-	while(GetCount())
-	{
-		pClip = RemoveHead();
-		delete pClip;
-	}
-}
-
-// returns the number of clips actually saved
-// while this does empty the Format Data, it does not delete the Clips.
-int CClipList::AddToDB(bool bLatestOrder)
-{
-	Log(_T("AddToDB - Start"));
-
-	int savedCount = 0;
-	CClip* pClip;
-	POSITION pos;
-	bool bResult;
-	
-	INT_PTR remaining = GetCount();
-	pos = GetHeadPosition();
-	while(pos)
-	{
-		Log(StrF(_T("AddToDB - while(pos), Start Remaining %d"), remaining));
-		remaining--;
-		
-		pClip = GetNext(pos);
-		ASSERT(pClip);
-		
-		if(bLatestOrder)
-		{
-			pClip->MakeLatestOrder();
-			pClip->MakeLatestGroupOrder();
-		}
-
-		bResult = pClip->AddToDB();
-		if(bResult)
-		{
-			savedCount++;
-		}
-
-		Log(StrF(_T("AddToDB - while(pos), End Remaining %d, save count: %d"), remaining, savedCount));
-	}
-
-	Log(StrF(_T("AddToDB - Start, count: %d"), savedCount));
-	
-	return savedCount;
-}
-
-const CClipList& CClipList::operator=(const CClipList &cliplist)
-{
-	POSITION pos;
-	CClip* pClip;
-	
-	pos = cliplist.GetHeadPosition();
-	while(pos)
-	{
-		pClip = cliplist.GetNext(pos);
-		ASSERT(pClip);
-
-		CClip *pNewClip = new CClip;
-		if(pNewClip)
-		{
-			*pNewClip = *pClip;
-			
-			AddTail(pNewClip);
-		}
-	}
-	
-	return *this;
-}
+// Clip.cpp: implementations of the Clip interfaces
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "CP_Main.h"
+#include "Clip.h"
+#include "DatabaseUtilities.h"
+#include "Crc32Dynamic.h"
+#include "sqlite\CppSQLite3.h"
+#include "shared/TextConvert.h"
+#include "zlib/zlib.h"
+#include "Misc.h"
+#include "Md5.h"
+#include "ChaiScriptOnCopy.h"
+#include "DittoChaiScript.h"
+#include "ImageHelper.h"
+
+#include <Mmsystem.h>
+#include <memory>
+
+#include "Path.h"
+#include <set>
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+
+/*----------------------------------------------------------------------------*\
+COleDataObjectEx
+\*----------------------------------------------------------------------------*/
+
+HGLOBAL COleDataObjectEx::GetGlobalData(CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
+{
+    HGLOBAL hGlobal = COleDataObject::GetGlobalData(cfFormat, lpFormatEtc);
+	if(hGlobal)
+	{
+		if(!::IsValid(hGlobal))
+		{
+			Log( StrF(
+				_T("COleDataObjectEx::GetGlobalData(\"%s\"): ERROR: Invalid (NULL) data returned."),
+				GetFormatName(cfFormat) ) );
+			::GlobalFree( hGlobal );
+			hGlobal = NULL;
+		}
+		return hGlobal;
+	}
+	
+	// The data isn't in global memory, so try getting an IStream interface to it.
+	STGMEDIUM stg;
+	
+	if(!GetData(cfFormat, &stg))
+	{
+		return 0;
+	}
+	
+	switch(stg.tymed)
+	{
+	case TYMED_HGLOBAL:
+		hGlobal = stg.hGlobal;
+		break;
+		
+	case TYMED_ISTREAM:
+		{
+			UINT            uDataSize;
+			LARGE_INTEGER	li;
+			ULARGE_INTEGER	uli;
+			
+			li.HighPart = li.LowPart = 0;
+			
+			if ( SUCCEEDED( stg.pstm->Seek ( li, STREAM_SEEK_END, &uli )))
+			{
+				hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, uli.LowPart );
+				
+				void* pv = GlobalLock(hGlobal);
+				stg.pstm->Seek(li, STREAM_SEEK_SET, NULL);
+				HRESULT result = stg.pstm->Read(pv, uli.LowPart, (PULONG)&uDataSize);
+				GlobalUnlock(hGlobal);
+				
+				if( FAILED(result) )
+					hGlobal = GlobalFree(hGlobal);
+			}
+			break;  // case TYMED_ISTREAM
+		}
+	} // end switch
+	
+	ReleaseStgMedium(&stg);
+	
+	if(hGlobal && !::IsValid(hGlobal))
+	{
+		Log( StrF(
+			_T("COleDataObjectEx::GetGlobalData(\"%s\"): ERROR: Invalid (NULL) data returned."),
+			GetFormatName(cfFormat)));
+		::GlobalFree(hGlobal);
+		hGlobal = NULL;
+	}
+	
+	return hGlobal;
+}
+
+std::shared_ptr<CClipTypes> COleDataObjectEx::GetAvailableTypes()
+{
+	std::shared_ptr<CClipTypes> types = std::make_shared<CClipTypes>();
+
+	// GetNextFormat API has a bug that cannot find avaliable formats correctly. (ex. CF_DIB)
+	// So, Use EnumClipboardFormats API.
+	if (!OpenClipboard(theApp.m_MainhWnd))
+		return types;
+
+	int format = 0;
+	do
+	{
+		format = EnumClipboardFormats(format);
+		// Currently CF_MAX is not valid format
+		// See https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
+		if (format == 0 || format == CF_MAX)
+			continue;
+		types->Add(format);
+	} while (format != 0);
+
+	CloseClipboard();
+	return types;
+}
+
+/*----------------------------------------------------------------------------*\
+CClipFormat - holds the data of one clip format.
+\*----------------------------------------------------------------------------*/
+CClipFormat::CClipFormat(CLIPFORMAT cfType, HGLOBAL hgData, int parentId)
+{
+	m_cfType = cfType;
+	m_hgData = hgData;
+	m_autoDeleteData = true;
+	m_parentId = parentId;
+}
+
+CClipFormat::~CClipFormat() 
+{ 
+	Free(); 
+}
+
+void CClipFormat::Clear()
+{
+	m_cfType = 0;
+	m_hgData = 0;
+	m_dataId = -1;
+	m_parentId = -1;
+}
+
+void CClipFormat::Free()
+{
+	if(m_autoDeleteData && m_hgData)
+	{
+		m_hgData = ::GlobalFree( m_hgData );
+		m_hgData = NULL;
+	}
+}
+
+Gdiplus::Bitmap *CClipFormat::CreateGdiplusBitmap()
+{
+	if (this->m_cfType != CF_DIB && this->m_cfType == theApp.m_PNG_Format)
+		return NULL;
+
+	Gdiplus::Bitmap *gdipBitmap;
+	if (this->m_cfType == theApp.m_PNG_Format)
+		gdipBitmap = PNGImageHelper::GdipImageFromHGLOBAL(this->m_hgData);
+	else
+		gdipBitmap = DIBImageHelper::GdipImageFromHGLOBAL(this->m_hgData);
+
+	return gdipBitmap;
+}
+
+/*----------------------------------------------------------------------------*\
+CClipFormats - holds an array of CClipFormat
+\*----------------------------------------------------------------------------*/
+// returns a pointer to the CClipFormat in this array which matches the given type
+//  or NULL if that type doesn't exist in this array.
+CClipFormat* CClipFormats::FindFormat(UINT cfType)
+{
+	CClipFormat* pCF;
+	INT_PTR count = GetSize();
+
+	for(int i=0; i < count; i++)
+	{
+		pCF = &ElementAt(i);
+		if(pCF->m_cfType == cfType)
+			return pCF;
+	}
+	return NULL;
+}
+
+bool CClipFormats::RemoveFormat(CLIPFORMAT cfType)
+{
+	bool removed = false;
+	CClipFormat* pCF;
+	INT_PTR count = GetSize();
+
+	for (int i = 0; i < count; i++)
+	{
+		pCF = &ElementAt(i);
+		if (pCF->m_cfType == cfType)
+		{
+			this->RemoveAt(i);
+			removed = true;
+			break;
+		}
+	}
+	return removed;
+}
+
+
+
+
+/*----------------------------------------------------------------------------*\
+CClip - holds multiple CClipFormats and CopyClipboard() statistics
+\*----------------------------------------------------------------------------*/
+
+DWORD CClip::m_LastAddedCRC = 0;
+int CClip::m_lastAddedID = -1;
+
+CClip::CClip() : 
+	m_id(0), 
+	m_CRC(0),
+	m_parentId(-1),
+	m_dontAutoDelete(FALSE),
+	m_shortCut(0),
+	m_bIsGroup(FALSE),
+	m_param1(0),
+	m_clipOrder(0),
+	m_stickyClipOrder(INVALID_STICKY),
+	m_stickyClipGroupOrder(INVALID_STICKY),
+	m_clipGroupOrder(0),
+	m_globalShortCut(FALSE),
+	m_moveToGroupShortCut(0),
+	m_globalMoveToGroupShortCut(FALSE)
+{
+	m_copyReason = CopyReasonEnum::COPY_TO_UNKOWN;
+	m_addToDbStickyEnum = AddToDbStickyEnum::INVALID;
+}
+
+CClip::~CClip()
+{
+	EmptyFormats();
+}
+
+void CClip::Clear()
+{
+	m_id = -1;
+	m_Time = 0;
+	m_Desc = "";
+	m_CRC = 0;
+	m_parentId = -1;
+	m_dontAutoDelete = FALSE;
+	m_shortCut = 0;
+	m_bIsGroup = FALSE;
+	m_csQuickPaste = "";
+	m_param1 = 0;
+	m_globalShortCut = FALSE;
+	m_moveToGroupShortCut = 0;
+	m_globalMoveToGroupShortCut = 0;
+	
+	EmptyFormats();
+}
+
+const CClip& CClip::operator=(const CClip &clip)
+{
+	const CClipFormat* pCF;
+
+	m_id = clip.m_id;
+	m_Time = clip.m_Time;
+	m_lastPasteDate = clip.m_lastPasteDate;
+	m_CRC = clip.m_CRC;
+	m_parentId = clip.m_parentId;
+	m_dontAutoDelete = clip.m_dontAutoDelete;
+	m_shortCut = clip.m_shortCut;
+	m_bIsGroup = clip.m_bIsGroup;
+	m_csQuickPaste = clip.m_csQuickPaste;
+	m_moveToGroupShortCut = clip.m_moveToGroupShortCut;
+	m_globalMoveToGroupShortCut = clip.m_globalMoveToGroupShortCut;
+
+	INT_PTR nCount = clip.m_Formats.GetSize();
+	
+	for(int i = 0; i < nCount; i++)
+	{
+		pCF = &clip.m_Formats.GetData()[i];
+
+		LPVOID pvData = GlobalLock(pCF->m_hgData);
+		if(pvData)
+		{
+			AddFormat(pCF->m_cfType, pvData, (UINT)GlobalSize(pCF->m_hgData));
+		}
+		GlobalUnlock(pCF->m_hgData);
+	}
+
+	//Set this after since in could get the wrong description in AddFormat
+	m_Desc = clip.m_Desc;
+
+	return *this;
+}
+
+void CClip::EmptyFormats()
+{
+	// free global memory in m_Formats
+	for(INT_PTR i = m_Formats.GetSize()-1; i >= 0; i--)
+	{
+		m_Formats[i].Free();
+		m_Formats.RemoveAt(i);
+	}
+}
+
+// Adds a new Format to this Clip by copying the given data.
+bool CClip::AddFormat(CLIPFORMAT cfType, void* pData, UINT nLen, bool setDesc)
+{
+	ASSERT(pData && nLen);
+	HGLOBAL hGlobal = ::NewGlobalP(pData, nLen);
+	ASSERT(hGlobal);
+
+	// update the Clip statistics
+	m_Time = m_Time.GetCurrentTime();
+
+	if (setDesc)
+	{
+		if (cfType != CF_UNICODETEXT || !SetDescFromText(hGlobal, true))
+			SetDescFromType();
+	}
+	
+	CClipFormat format(cfType,hGlobal);
+	CClipFormat *pFormat;
+	
+	pFormat = m_Formats.FindFormat(cfType);
+	// if the format type already exists as part of this clip, replace the data
+	if(pFormat)
+	{
+		pFormat->Free();
+		pFormat->m_hgData = format.m_hgData;
+	}
+	else
+	{
+		m_Formats.Add(format);
+	}
+	
+	format.m_hgData = 0; // now owned by m_Formats
+	return true;
+}
+
+// Fills this CClip with the contents of the clipboard.
+int CClip::LoadFromClipboard(CClipTypes* pClipTypes, bool checkClipboardIgnore, CString activeApp, CString activeAppTitle)
+{
+	if(pClipTypes == NULL || pClipTypes->GetSize() == 0)
+	{
+		ASSERT(0); // this feature is not currently used... it is an error if it is.
+		Log(_T("no types were given to accept, skipping this clipboard change"));
+		return FALSE;
+	}
+
+	COleDataObjectEx oleData;
+	CClipTypes* pTypes = pClipTypes;
+
+	// m_Formats should be empty when this is called.
+	ASSERT(m_Formats.GetSize() == 0);
+	
+	// If the data is supposed to be private, then return
+	if(::IsClipboardFormatAvailable(theApp.m_cfIgnoreClipboard))
+	{
+		Log(_T("Clipboard ignore type is on the clipboard, skipping this clipboard change"));
+		return FALSE;
+	}
+
+	//If we are saving a multi paste then delay us connecting to the clipboard
+	//to allow the ctrl-v to do a paste
+	if(::IsClipboardFormatAvailable(theApp.m_cfDelaySavingData))
+	{
+		Log(_T("Delay clipboard type is on the clipboard, delaying 1500 ms to allow ctrl-v to work"));
+		Sleep(1500);
+	}
+		
+	//Attach to the clipboard
+	if(!oleData.AttachClipboard())
+	{
+		Log(_T("failed to attache to clipboard, skipping this clipboard change"));
+		ASSERT(0); // does this ever happen?
+		return FALSE;
+	}
+	
+	oleData.EnsureClipboardObject();
+	
+	
+	m_Desc = "[Ditto Error] BAD DESCRIPTION";
+	
+	// Get Description String
+	// NOTE: We make sure that the description always corresponds to the
+	//  data saved by using the exact same globalmem instance as the source
+	//  for both... i.e. we only fetch the description format type once.
+	CClipFormat cfDesc;
+	bool bIsDescSet = false;
+
+	cfDesc.m_cfType = CF_UNICODETEXT;	
+	if(oleData.IsDataAvailable(cfDesc.m_cfType))
+	{
+		for (int i = 0; i < 10; i++)
+		{
+			cfDesc.m_hgData = oleData.GetGlobalData(cfDesc.m_cfType);
+			if (cfDesc.m_hgData == NULL)
+			{
+				Log(StrF(_T("Tried to set description from cf_unicode, data is NULL, try: %d"), i+1));
+			}
+			else
+			{
+				break;
+			}
+			Sleep(10);
+		}
+		bIsDescSet = SetDescFromText(cfDesc.m_hgData, true);
+
+		Log(StrF(_T("Tried to set description from cf_unicode text, Set: %d, Desc: [%s]"), bIsDescSet, m_Desc.Left(30)));
+	}
+
+	if(bIsDescSet == false)
+	{
+		cfDesc.m_cfType = CF_TEXT;	
+		if(oleData.IsDataAvailable(cfDesc.m_cfType))
+		{
+			for (int i = 0; i < 10; i++)
+			{
+				cfDesc.m_hgData = oleData.GetGlobalData(cfDesc.m_cfType);
+				if (cfDesc.m_hgData == NULL)
+				{
+					Log(StrF(_T("Tried to set description from cf_text, data is NULL, try: %d"), i + 1));
+				}
+				else
+				{
+					break;
+				}
+				Sleep(10);
+			}
+
+			bIsDescSet = SetDescFromText(cfDesc.m_hgData, false);
+
+			Log(StrF(_T("Tried to set description from cf_text text, Set: %d, Desc: [%s]"), bIsDescSet, m_Desc.Left(30)));
+		}
+	}
+
+	INT_PTR nSize;
+	CClipFormat cf;
+	INT_PTR numTypes = pTypes->GetSize();
+
+	Log(StrF(_T("Begin enumerating over supported types, Count: %d"), numTypes));
+
+	for(int i = 0; i < numTypes; i++)
+	{
+		cf.m_cfType = pTypes->ElementAt(i);
+
+		if (cf.m_cfType == CF_DIB &&
+			g_Opt.m_excludeCF_DIBInExcel &&
+			activeApp.MakeLower() == _T("excel.exe"))
+		{
+			continue;
+		}
+
+		BOOL bSuccess = false;
+		Log(StrF(_T("Begin try and load type %s"), GetFormatName(cf.m_cfType)));
+		
+		// is this the description we already fetched?
+		if(cf.m_cfType == cfDesc.m_cfType)
+		{
+			cf = cfDesc;
+			cfDesc.m_hgData = 0; // cf owns it now (to go into m_Formats)
+		}
+		else if(!oleData.IsDataAvailable(cf.m_cfType))
+		{
+			Log(StrF(_T("End of load - Data is not available for type %s"), GetFormatName(cf.m_cfType)));
+			continue;
+		}
+		else
+		{
+			for (int tries = 0; tries < 2; tries++)
+			{
+				cf.m_hgData = oleData.GetGlobalData(cf.m_cfType);
+				if (cf.m_hgData != NULL)
+					break;
+
+				Log(StrF(_T("Tried to get data for type: %s, data is NULL, try: %d"), GetFormatName(cf.m_cfType), tries + 1));
+				Sleep(5);
+			}
+		}
+		
+		if(cf.m_hgData)
+		{
+			nSize = GlobalSize(cf.m_hgData);
+			if(nSize > 0)
+			{
+				if(g_Opt.m_lMaxClipSizeInBytes > 0 && (int)nSize > g_Opt.m_lMaxClipSizeInBytes)
+				{
+					CString cs;
+					cs.Format(_T("Maximum clip size reached max size = %d, clip size = %d"), g_Opt.m_lMaxClipSizeInBytes, nSize);
+					Log(cs);
+
+					oleData.Release();
+					return -1;
+				}
+
+				ASSERT(::IsValid(cf.m_hgData));
+				
+				m_Formats.Add(cf);
+				bSuccess = true;
+			}
+			else
+			{
+				ASSERT(FALSE); // a valid GlobalMem with 0 size is strange
+				cf.Free();
+				Log(StrF(_T("Data length is 0 for type %s"), GetFormatName(cf.m_cfType)));
+			}
+			cf.m_hgData = 0; // m_Formats owns it now
+		}
+
+		Log(StrF(_T("End of load - type %s, Success: %d"), GetFormatName(cf.m_cfType), bSuccess));
+	}
+
+	Log(StrF(_T("End enumerating over supported types, Count: %d"), numTypes));
+	
+	m_Time = CTime::GetCurrentTime();
+	
+	if(!bIsDescSet)
+	{
+		SetDescFromType();
+
+		Log(StrF(_T("Setting description from type, Desc: [%s]"), m_Desc.Left(30)));
+	}
+	
+	// if the description was in a type that is not supported,
+	//we have to free it since it wasn't added to m_Formats
+	if(cfDesc.m_hgData)
+	{
+		cfDesc.Free();
+	}
+	
+	oleData.Release();
+	
+	if(m_Formats.GetSize() == 0)
+	{
+		Log(_T("No clip types were in supported types array"));
+		return FALSE;
+	}
+
+	bool calledOnCopyScript = false;
+	try
+	{
+		for (auto & listItem : g_Opt.m_copyScripts.m_list)
+		{
+			if (listItem.m_active)
+			{
+				Log(StrF(_T("Start of process copy name: %s, script: %s"), listItem.m_name, listItem.m_script));
+
+				ChaiScriptOnCopy onCopy;
+				CDittoChaiScript clipData(this, (LPCSTR)CTextConvert::UnicodeToAnsi(activeApp), (LPCSTR)CTextConvert::UnicodeToAnsi(activeAppTitle));
+				if (onCopy.ProcessScript(clipData, (LPCSTR)CTextConvert::UnicodeToAnsi(listItem.m_script)) == false)
+				{
+					Log(StrF(_T("End of process copy name: %s, returned false, not saving this copy to Ditto, last Error: %s"), listItem.m_name, onCopy.m_lastError));
+
+					return -1;
+				}
+
+				calledOnCopyScript = true;
+
+				Log(StrF(_T("End of process copy name: %s, returned true, last Error: %s"), listItem.m_name, onCopy.m_lastError));
+			}
+			else
+			{
+				Log(StrF(_T("Script is not active, not processing name: %s, script: %s"), listItem.m_name, listItem.m_script));
+			}
+		}
+	}
+	catch (CException *ex)
+	{
+		TCHAR szCause[255];
+		ex->GetErrorMessage(szCause, 255);
+		CString cs;
+		cs.Format(_T("save copy exception: %s"), szCause);
+		Log(cs);
+	}
+	catch (...)
+	{
+		Log(_T("save copy exception 2"));	
+	}
+
+	//copy script could have changed the data, make sure the description matches
+	if (calledOnCopyScript)
+	{
+		auto uString = this->GetUnicodeTextFormat();
+		if (uString != _T(""))
+		{
+			if (uString.GetLength() > g_Opt.m_bDescTextSize)
+			{
+				m_Desc = uString.Left(g_Opt.m_bDescTextSize);
+			}
+			else
+			{
+				m_Desc = uString;
+			}
+		}
+		else
+		{
+			auto aString = this->GetCFTextTextFormat();
+			if (aString != "")
+			{
+				if (aString.GetLength() > g_Opt.m_bDescTextSize)
+				{
+					m_Desc = aString.Left(g_Opt.m_bDescTextSize);
+				}
+				else
+				{
+					m_Desc = aString;
+				}
+			}
+			else
+			{
+				SetDescFromType();
+			}
+		}
+
+		Log(StrF(_T("Called on copy script, this could change the description, regenerated desc: %s"), m_Desc));
+	}
+
+	if (this->m_Desc != _T(""))
+	{
+		std::wstring stringData(this->m_Desc);
+		if (g_Opt.m_regexHelper.TextMatchFilters(activeApp, stringData))
+		{
+			return -1;
+		}
+	}
+
+	return TRUE;
+}
+
+bool CClip::SetDescFromText(HGLOBAL hgData, bool unicode)
+{
+	if(hgData == 0)
+		return false;
+	
+	bool bRet = false;
+	INT_PTR bufLen = 0;
+
+	if(unicode)
+	{
+		TCHAR* text = (TCHAR *) GlobalLock(hgData);
+		bufLen = GlobalSize(hgData);
+
+		m_Desc = CString(text, bufLen/(sizeof(wchar_t)));
+		bRet = true;
+	}
+	else
+	{
+		char* text = (char *) GlobalLock(hgData);
+		bufLen = GlobalSize(hgData);
+	
+		m_Desc = CString(text, bufLen);
+		bRet = true;
+	}
+		
+	if(bufLen > g_Opt.m_bDescTextSize)
+	{
+		m_Desc = m_Desc.Left(g_Opt.m_bDescTextSize);
+	}
+	
+	//Unlock the data
+	GlobalUnlock(hgData);
+	
+	return bRet;
+}
+
+bool CClip::SetDescFromType()
+{
+	INT_PTR size = m_Formats.GetSize();
+	if(size <= 0)
+	{
+		return false;
+	}
+
+	int nCF_HDROPIndex = -1;
+	for(int i = 0; i < size; i++)
+	{
+		if(m_Formats[i].m_cfType == CF_HDROP)
+		{
+			nCF_HDROPIndex = i;
+		}
+	}
+
+	if(nCF_HDROPIndex >= 0)
+	{
+		using namespace nsPath;
+
+		HDROP drop = (HDROP)GlobalLock(m_Formats[nCF_HDROPIndex].m_hgData);
+		int nNumFiles = min(5, DragQueryFile(drop, -1, NULL, 0));
+
+		if(nNumFiles > 1)
+			m_Desc = "Copied Files - ";
+		else
+			m_Desc = "Copied File - ";
+
+		TCHAR file[MAX_PATH];
+		
+		for(int nFile = 0; nFile < nNumFiles; nFile++)
+		{
+			if(DragQueryFile(drop, nFile, file, sizeof(file)) > 0)
+			{
+				CPath path(file);
+				m_Desc += path.GetName();
+				m_Desc += " - ";
+				m_Desc += file;
+				m_Desc += "\n";
+			}
+		}
+		
+		GlobalUnlock(m_Formats[nCF_HDROPIndex].m_hgData);
+	}
+	else
+	{
+		m_Desc = GetFormatName(m_Formats[0].m_cfType);
+	}
+
+	return m_Desc.GetLength() > 0;
+}
+
+bool CClip::AddToDB(bool bCheckForDuplicates)
+{
+	bool bResult;
+	try
+	{
+		m_Time = CTime::GetCurrentTime().GetTime();
+
+		m_CRC = GenerateCRC();
+
+		if(bCheckForDuplicates &&
+			m_parentId < 0)
+		{	
+			int nID = FindDuplicate();
+			if(nID >= 0)
+			{
+				MakeLatestOrder();
+				MakeLatestGroupOrder();
+
+				CString sql;
+				
+				sql.Format(_T("UPDATE Main SET clipOrder = %f where lID = %d;"), 
+								m_clipOrder, nID);
+
+				int ret = theApp.m_db.execDML(sql);
+
+				int groupRet = -1;
+
+				if(m_parentId > -1)
+				{
+					sql.Format(_T("UPDATE Main SET clipGroupOrder = %f where lID = %d;"), 
+						m_clipGroupOrder, nID);
+
+					groupRet = theApp.m_db.execDML(sql);
+				}
+
+
+				m_id = nID;
+
+				Log(StrF(_T("Found duplicate clip in db, Id: %d, ParentId: %d crc: %d, NewOrder: %f, GroupOrder %f, Ret: %d, GroupRet: %d, SQL: %s"), 
+										nID, m_parentId, m_CRC, m_clipOrder, m_clipGroupOrder, ret, groupRet, sql));
+
+				return true;
+			}
+		}
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
+
+	int removeStickySettingClipId = -1;
+
+	if (m_addToDbStickyEnum == AddToDbStickyEnum::MAKE_TOP_STICKY)
+	{
+		m_stickyClipOrder = this->GetNewTopSticky(m_parentId, -1);
+	}
+	else if (m_addToDbStickyEnum == AddToDbStickyEnum::MAKE_LAST_STICKY)
+	{
+		m_stickyClipOrder = this->GetNewLastSticky(m_parentId, -1);
+	}
+	else if (m_addToDbStickyEnum == AddToDbStickyEnum::REPLACE_TOP_STICKY)
+	{
+		m_stickyClipOrder = this->GetNewTopSticky(m_parentId, -1);
+		removeStickySettingClipId = GetExistingTopStickyClipId(m_parentId);
+	}
+	
+	bResult = false;
+	if(AddToMainTable())
+	{		
+		bResult = AddToDataTable();
+	}
+
+	if(bResult)
+	{
+		if(g_Opt.m_csPlaySoundOnCopy.IsEmpty() == FALSE)
+			PlaySound(g_Opt.m_csPlaySoundOnCopy, NULL, SND_FILENAME|SND_ASYNC);
+
+		if (removeStickySettingClipId > 0)
+		{
+			RemoveStickySetting(removeStickySettingClipId, m_parentId);
+		}
+	}
+	
+	// should be emptied by AddToDataTable
+	//ASSERT(m_Formats.GetSize() == 0);
+	
+	return bResult;
+}
+
+// if a duplicate exists, set recset to the duplicate and return true
+int CClip::FindDuplicate()
+{
+	try
+	{
+		//If they are allowing duplicates still check 
+		//the last copied item
+		if(g_Opt.m_bAllowDuplicates)
+		{
+			if (g_Opt.m_allowBackToBackDuplicates == FALSE)
+			{
+				if (m_CRC == m_LastAddedCRC)
+					return m_lastAddedID;
+			}
+		}
+		else
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Main WHERE CRC = %d"), m_CRC);
+				
+			if(q.eof() == false)
+			{
+				return q.getIntField(_T("lID"));
+			}
+		}
+	}
+	CATCH_SQLITE_EXCEPTION
+		
+	return -1;
+}
+
+
+
+DWORD CClip::GenerateCRC()
+{
+	CClipFormat* pCF;
+	DWORD dwCRC = 0xFFFFFFFF;
+
+	CCrc32Dynamic *pCrc32 = new CCrc32Dynamic;
+	if(pCrc32)
+	{
+		//Generate a CRC value for all copied data
+
+		INT_PTR size = m_Formats.GetSize();
+		for(int i = 0; i < size ; i++)
+		{
+			pCF = & m_Formats.ElementAt(i);
+			
+			const unsigned char *Data = (const unsigned char *)GlobalLock(pCF->m_hgData);
+			if(Data)
+			{
+				if (CGetSetOptions::GetAdjustClipsForCRC())
+				{
+					//Try and remove known things that change in rtf (word and outlook)
+					if (pCF->m_cfType == theApp.m_RTFFormat)
+					{
+						CStringA CStringData((char*)Data);
+
+						//In word and outlook I was finding that data in the \\datastore section was always changing, remove this for the crc check
+						RemoveRTFSection(CStringData, "{\\*\\datastore");
+
+						//In word and outlook rsid values are always changing, remove these for the crc check
+						DeleteParamFromRTF(CStringData, "\\rsid", true);
+						DeleteParamFromRTF(CStringData, "\\insrsid", true);
+						DeleteParamFromRTF(CStringData, "\\mdispDef1", false);
+
+						pCrc32->GenerateCrc32((const LPBYTE)CStringData.GetBuffer(), (DWORD)CStringData.GetLength(), dwCRC);
+					}
+					else
+					{
+						//i've seen examble where the text size was 10 but the data size was 20, leading to random crc values
+						//try and only check the crc for the actual text
+						int dataLength = (int)GlobalSize(pCF->m_hgData);
+						if (pCF->m_cfType == CF_TEXT)
+						{
+							dataLength = min(dataLength, ((int)strlen((char*)Data) + 1));
+						}
+						else if (pCF->m_cfType == CF_UNICODETEXT)
+						{
+							dataLength = min(dataLength, (((int)wcslen((wchar_t*)Data) + 1) * 2));
+						}
+						pCrc32->GenerateCrc32((const LPBYTE)Data, (DWORD)dataLength, dwCRC);
+					}
+				}
+				else
+				{
+					pCrc32->GenerateCrc32((const LPBYTE)Data, (DWORD)GlobalSize(pCF->m_hgData), dwCRC);
+				}
+			}
+			GlobalUnlock(pCF->m_hgData);
+		}
+
+		dwCRC = ~dwCRC;
+
+		delete pCrc32;
+	}
+
+	return dwCRC;
+}
+
+// assigns m_ID
+bool CClip::AddToMainTable()
+{
+	try
+	{
+		m_Desc.Replace(_T("'"), _T("''"));
+		m_csQuickPaste.Replace(_T("'"), _T("''"));
+
+		CString cs;
+		cs.Format(_T("INSERT into Main (lDate, mText, lShortCut, lDontAutoDelete, CRC, bIsGroup, lParentID, QuickPasteText, clipOrder, clipGroupOrder, globalShortCut, lastPasteDate, stickyClipOrder, stickyClipGroupOrder, MoveToGroupShortCut, GlobalMoveToGroupShortCut) ")
+						_T("values(%d, '%s', %d, %d, %d, %d, %d, '%s', %f, %f, %d, %d, %f, %f, %d, %d);"),
+							(int)m_Time.GetTime(),
+							m_Desc,
+							m_shortCut,
+							m_dontAutoDelete,
+							m_CRC,
+							m_bIsGroup,
+							m_parentId,
+							m_csQuickPaste,
+							m_clipOrder,
+							m_clipGroupOrder,
+							m_globalShortCut,
+							(int)CTime::GetCurrentTime().GetTime(),
+							m_stickyClipOrder,
+							m_stickyClipGroupOrder,
+							m_moveToGroupShortCut,
+							m_globalMoveToGroupShortCut);
+
+		theApp.m_db.execDML(cs);
+
+		m_id = (long)theApp.m_db.lastRowId();
+
+		Log(StrF(_T("Added clip to main table, Id: %d, ParentId: %d Desc: %s, Order: %f, GroupOrder: %f"), m_id, m_parentId, m_Desc, m_clipOrder, m_clipGroupOrder));
+
+		m_LastAddedCRC = m_CRC;
+		m_lastAddedID = m_id;
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
+	
+	return true;
+}
+
+bool CClip::ModifyMainTable()
+{
+	bool bRet = false;
+	try
+	{
+		m_Desc.Replace(_T("'"), _T("''"));
+		m_csQuickPaste.Replace(_T("'"), _T("''"));
+
+		theApp.m_db.execDMLEx(_T("UPDATE Main SET lShortCut = %d, ")
+			_T("mText = '%s', ")
+			_T("lParentID = %d, ")
+			_T("lDontAutoDelete = %d, ")
+			_T("QuickPasteText = '%s', ")
+			_T("clipOrder = %f, ")
+			_T("clipGroupOrder = %f, ")
+			_T("globalShortCut = %d, ")
+			_T("stickyClipOrder = %f, ")
+			_T("stickyClipGroupOrder = %f, ")
+			_T("MoveToGroupShortCut = %d, ")
+			_T("GlobalMoveToGroupShortCut = %d ")
+			_T("WHERE lID = %d;"), 
+			m_shortCut, 
+			m_Desc, 
+			m_parentId, 
+			m_dontAutoDelete, 
+			m_csQuickPaste,
+			m_clipOrder,
+			m_clipGroupOrder,
+			m_globalShortCut,
+			m_stickyClipOrder,
+			m_stickyClipGroupOrder,
+			m_moveToGroupShortCut,
+			m_globalMoveToGroupShortCut,
+			m_id);
+
+		bRet = true;
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
+
+	return bRet;
+}
+
+bool CClip::ModifyDescription()
+{
+	bool bRet = false;
+	try
+	{
+		m_Desc.Replace(_T("'"), _T("''"));
+
+		theApp.m_db.execDMLEx(_T("UPDATE Main SET mText = '%s' ")
+			_T("WHERE lID = %d;"),
+			m_Desc,
+			m_id);
+
+		bRet = true;
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
+
+		return bRet;
+}
+
+// Empties m_Formats as it saves them to the Data Table.
+bool CClip::AddToDataTable()
+{
+	CClipFormat* pCF;
+
+	try
+	{
+		CppSQLite3Statement stmt = theApp.m_db.compileStatement(_T("insert into Data values (NULL, ?, ?, ?);"));
+		
+		for(INT_PTR i = m_Formats.GetSize()-1; i >= 0 ; i--)
+		{
+			pCF = &m_Formats.ElementAt(i);
+
+			CString formatName = GetFormatName(pCF->m_cfType);
+			int clipSize = 0;
+			
+			stmt.bind(1, m_id);
+			stmt.bind(2, formatName);
+
+			const unsigned char *Data = (const unsigned char *)GlobalLock(pCF->m_hgData);
+			if(Data)
+			{
+				clipSize = (int)GlobalSize(pCF->m_hgData);
+				stmt.bind(3, Data, clipSize);
+			}
+			GlobalUnlock(pCF->m_hgData);
+			
+			stmt.execDML();
+			stmt.reset();
+
+			pCF->m_dataId = (long)theApp.m_db.lastRowId();
+
+			Log(StrF(_T("Added ClipData to DB, Id: %d, ParentId: %d Type: %s, size: %d"), pCF->m_dataId, m_id, formatName, clipSize));
+		}
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
+		
+	return true;
+}
+
+void CClip::MoveUp(int parentId)
+{
+	try
+	{
+		//In a group, not a sticky
+		if(parentId > -1 && m_stickyClipGroupOrder == INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder == -(2147483647) AND clipGroupOrder > %f ORDER BY clipGroupOrder ASC LIMIT 1"), parentId, m_clipGroupOrder);
+			if (q.eof() == false)
+			{
+				int idAbove = q.getIntField(_T("lID"));
+				double orderAbove = q.getFloatField(_T("clipGroupOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND clipGroupOrder > %f AND stickyClipGroupOrder == -(2147483647) ORDER BY clipGroupOrder ASC LIMIT 1"), parentId, orderAbove);
+				if (q2.eof() == false)
+				{ 
+					int idTwoAbove = q2.getIntField(_T("lID"));
+					double orderTwoAbove = q2.getFloatField(_T("clipGroupOrder"));
+
+					m_clipGroupOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
+				}
+				else
+				{
+					m_clipGroupOrder = orderAbove + 1;
+				}
+			}
+		}
+		// main group, not a sticky
+		else if(parentId <= -1 && m_stickyClipOrder == INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder > %f AND stickyClipOrder == -(2147483647) ORDER BY clipOrder ASC LIMIT 1"), m_clipOrder);
+			if (q.eof() == false)
+			{
+				int idAbove = q.getIntField(_T("lID"));
+				double orderAbove = q.getFloatField(_T("clipOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder > %f AND stickyClipOrder == -(2147483647) ORDER BY clipOrder ASC LIMIT 1"), orderAbove);
+				if (q2.eof() == false)
+				{ 
+					int idTwoAbove = q2.getIntField(_T("lID"));
+					double orderTwoAbove = q2.getFloatField(_T("clipOrder"));
+
+					m_clipOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
+				}
+				else
+				{
+					m_clipOrder = orderAbove + 1;
+				}
+			}
+		}
+		//In a group, a sticky clip
+		else if(parentId > -1 && m_stickyClipGroupOrder != INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder > %f ORDER BY stickyClipGroupOrder ASC LIMIT 1"), parentId, m_stickyClipGroupOrder);
+			if (q.eof() == false)
+			{
+				int idAbove = q.getIntField(_T("lID"));
+				double orderAbove = q.getFloatField(_T("stickyClipGroupOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder > %f ORDER BY stickyClipGroupOrder ASC LIMIT 1"), parentId, orderAbove);
+				if (q2.eof() == false)
+				{
+					int idTwoAbove = q2.getIntField(_T("lID"));
+					double orderTwoAbove = q2.getFloatField(_T("stickyClipGroupOrder"));
+
+					m_stickyClipGroupOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
+				}
+				else
+				{
+					m_stickyClipGroupOrder = orderAbove + 1;
+				}
+			}
+		}
+		//not in a group, a sticky clip
+		else if(parentId <= -1 && m_stickyClipOrder != INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder > %f ORDER BY stickyClipOrder ASC LIMIT 1"), m_stickyClipOrder);
+			if (q.eof() == false)
+			{
+				int idAbove = q.getIntField(_T("lID"));
+				double orderAbove = q.getFloatField(_T("stickyClipOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder > %f ORDER BY stickyClipOrder ASC LIMIT 1"), orderAbove);
+				if (q2.eof() == false)
+				{
+					int idTwoAbove = q2.getIntField(_T("lID"));
+					double orderTwoAbove = q2.getFloatField(_T("stickyClipOrder"));
+
+					m_stickyClipOrder = orderAbove + (orderTwoAbove - orderAbove) / 2.0;
+				}
+				else
+				{
+					m_stickyClipOrder = orderAbove + 1;
+				}
+			}
+		}
+	}
+	CATCH_SQLITE_EXCEPTION
+}
+
+void CClip::MoveDown(int parentId)
+{
+	try
+	{
+		//In a group, not a sticky
+		if(parentId > -1 && m_stickyClipGroupOrder == INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND clipGroupOrder < %f AND stickyClipGroupOrder = -(2147483647) ORDER BY clipGroupOrder DESC LIMIT 1"), parentId, m_clipGroupOrder);
+			if (q.eof() == false)
+			{
+				int idBelow = q.getIntField(_T("lID"));
+				double orderBelow = q.getFloatField(_T("clipGroupOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipGroupOrder FROM Main Where lParentID = %d AND clipGroupOrder < %f AND stickyClipGroupOrder = -(2147483647) ORDER BY clipGroupOrder DESC LIMIT 1"), parentId, orderBelow);
+				if (q2.eof() == false)
+				{ 
+					int idTwoBelow = q2.getIntField(_T("lID"));
+					double orderTwoBelow = q2.getFloatField(_T("clipGroupOrder"));
+
+					m_clipGroupOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
+				}
+				else
+				{
+					m_clipGroupOrder = orderBelow - 1;
+				}
+			}
+		}
+		// main group, not a sticky
+		else if(parentId <= -1 && m_stickyClipOrder == INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder < %f AND stickyClipOrder = -(2147483647) ORDER BY clipOrder DESC LIMIT 1"), m_clipOrder);
+			if (q.eof() == false)
+			{
+				int idBelow = q.getIntField(_T("lID"));
+				double orderBelow = q.getFloatField(_T("clipOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, clipOrder FROM Main Where clipOrder < %f AND stickyClipOrder = -(2147483647) ORDER BY clipOrder DESC LIMIT 1"), orderBelow);
+				if (q2.eof() == false)
+				{ 
+					int idTwoBelow = q2.getIntField(_T("lID"));
+					double orderTwoBelow = q2.getFloatField(_T("clipOrder"));
+
+					m_clipOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
+				}
+				else
+				{
+					m_clipOrder = orderBelow - 1;
+				}
+			}
+		}
+		//In a group, a sticky clip
+		else if(parentId > -1 && m_stickyClipGroupOrder != INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder < %f ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId, m_stickyClipGroupOrder);
+			if (q.eof() == false)
+			{
+				int idBelow = q.getIntField(_T("lID"));
+				double orderBelow = q.getFloatField(_T("stickyClipGroupOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder FROM Main Where lParentID = %d AND stickyClipGroupOrder <> -(2147483647) AND stickyClipGroupOrder < %f ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId, orderBelow);
+				if (q2.eof() == false)
+				{
+					int idTwoBelow = q2.getIntField(_T("lID"));
+					double orderTwoBelow = q2.getFloatField(_T("stickyClipGroupOrder"));
+
+					m_stickyClipGroupOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
+				}
+				else
+				{
+					m_stickyClipGroupOrder = orderBelow - 1;
+				}
+			}
+		}
+		//not in a group, a sticky clip
+		else if(parentId <= -1 && m_stickyClipOrder != INVALID_STICKY)
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder < %f ORDER BY stickyClipOrder DESC LIMIT 1"), m_stickyClipOrder);
+			if (q.eof() == false)
+			{
+				int idBelow = q.getIntField(_T("lID"));
+				double orderBelow = q.getFloatField(_T("stickyClipOrder"));
+
+				CppSQLite3Query q2 = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipOrder FROM Main Where stickyClipOrder <> -(2147483647) AND stickyClipOrder < %f ORDER BY stickyClipOrder DESC LIMIT 1"), orderBelow);
+				if (q2.eof() == false)
+				{
+					int idTwoBelow = q2.getIntField(_T("lID"));
+					double orderTwoBelow = q2.getFloatField(_T("stickyClipOrder"));
+
+					m_stickyClipOrder = orderBelow + (orderTwoBelow - orderBelow) / 2.0;
+				}
+				else
+				{
+					m_stickyClipOrder = orderBelow - 1;
+				}
+			}
+		}
+	}
+	CATCH_SQLITE_EXCEPTION
+}
+
+void CClip::MakeStickyTop(int parentId)
+{
+	if (parentId < 0)
+	{
+		m_stickyClipOrder = GetNewTopSticky(parentId, m_id);
+	}
+	else
+	{
+		m_stickyClipGroupOrder = GetNewTopSticky(parentId, m_id);
+	}
+}
+
+void CClip::MakeStickyLast(int parentId)
+{
+	if (parentId < 0)
+	{
+		m_stickyClipOrder = GetNewLastSticky(parentId, m_id);
+	}
+	else
+	{
+		m_stickyClipGroupOrder = GetNewLastSticky(parentId, m_id);
+	}
+}
+
+bool CClip::RemoveStickySetting(int parentId)
+{
+	bool reset = false;
+	if (parentId < 0)
+	{
+		if (m_stickyClipOrder != INVALID_STICKY)
+		{
+			m_stickyClipOrder = INVALID_STICKY;
+			reset = true;
+		}
+	}
+	else
+	{
+		if (m_stickyClipGroupOrder != INVALID_STICKY)
+		{
+			m_stickyClipGroupOrder = INVALID_STICKY;
+			reset = true;
+		}
+	}
+
+	return reset;
+}
+
+bool CClip::RemoveStickySetting(int clipId, int parentId)
+{
+	bool reset = false;
+	if (parentId < 0)
+	{
+		int c = theApp.m_db.execDMLEx(_T("UPDATE Main SET stickyClipOrder = %f WHERE lID = %d"), (double)INVALID_STICKY, clipId);
+		int y = 0;
+	}
+	else
+	{
+		int c = theApp.m_db.execDMLEx(_T("UPDATE Main SET stickyClipGroupOrder = %f WHERE lID = %d"), (double)INVALID_STICKY, clipId);
+		int y = 0;
+	}
+
+	return reset;
+}
+
+double CClip::GetExistingTopStickyClipId(int parentId)
+{
+	int existingTopClipId = -1;
+
+	try
+	{
+		if (parentId < 0)
+		{
+			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT lID FROM Main WHERE stickyClipOrder <> -(2147483647) ORDER BY stickyClipOrder DESC LIMIT 1"));
+			if (q.eof() == false)
+			{
+				existingTopClipId = q.getIntField(_T("lID"));
+			}
+		}
+		else
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Main WHERE lParentID = %d AND stickyClipGroupOrder <> -(2147483647) ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId);
+			if (q.eof() == false)
+			{
+				existingTopClipId = q.getIntField(_T("lID"));
+			}
+		}
+
+	}
+	CATCH_SQLITE_EXCEPTION
+
+	return existingTopClipId;
+}
+
+double CClip::GetNewTopSticky(int parentId, int clipId)
+{
+	double newOrder = 1;
+	double existingMaxOrder = 0;
+	CString existingDesc = _T("");
+
+	try
+	{
+		if (parentId < 0)
+		{
+			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT lID, stickyClipOrder, mText FROM Main WHERE stickyClipOrder <> -(2147483647) ORDER BY stickyClipOrder DESC LIMIT 1"));
+			if (q.eof() == false)
+			{
+				existingMaxOrder = q.getFloatField(_T("stickyClipOrder"));
+				existingDesc = q.getStringField(_T("mText"));
+				newOrder = existingMaxOrder + 1;
+			}
+		}
+		else
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID, stickyClipGroupOrder, mText FROM Main WHERE lParentID = %d AND stickyClipGroupOrder <> -(2147483647) ORDER BY stickyClipGroupOrder DESC LIMIT 1"), parentId);
+			if (q.eof() == false)
+			{
+				existingMaxOrder = q.getFloatField(_T("stickyClipGroupOrder"));
+				newOrder = existingMaxOrder + 1;
+			}
+		}
+
+		if (newOrder == 0.0)
+			newOrder += 1;
+
+		Log(StrF(_T("GetNewTopSticky, Id: %d, parentId: %d, CurrentMax: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMaxOrder, existingDesc, newOrder));
+	}
+	CATCH_SQLITE_EXCEPTION
+
+	return newOrder;
+}
+
+double CClip::GetNewLastSticky(int parentId, int clipId)
+{
+	double newOrder = 1;
+	double existingMaxOrder = 0;
+	CString existingDesc = _T("");
+
+	try
+	{
+		if (parentId < 0)
+		{
+			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT stickyClipOrder, mText FROM Main WHERE stickyClipOrder <> -(2147483647) ORDER BY stickyClipOrder LIMIT 1"));
+			if (q.eof() == false)
+			{
+				existingMaxOrder = q.getFloatField(_T("stickyClipOrder"));
+				existingDesc = q.getStringField(_T("mText"));
+				newOrder = existingMaxOrder - 1;
+			}
+		}
+		else
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT stickyClipGroupOrder, mText FROM Main WHERE lParentID = %d AND stickyClipGroupOrder <> -(2147483647) ORDER BY stickyClipGroupOrder LIMIT 1"), parentId);
+			if (q.eof() == false)
+			{
+				existingMaxOrder = q.getFloatField(_T("stickyClipGroupOrder"));
+				newOrder = existingMaxOrder - 1;
+			}
+		}
+
+		if (newOrder == 0.0)
+			newOrder -= 1;
+
+		Log(StrF(_T("GetNewLastSticky, Id: %d, parentId: %d, CurrentMax: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMaxOrder, existingDesc, newOrder));
+	}
+	CATCH_SQLITE_EXCEPTION
+
+		return newOrder;
+}
+
+void CClip::MakeLatestOrder()
+{
+	m_clipOrder = GetNewOrder(-1, m_id);
+}
+
+void CClip::MakeLatestGroupOrder()
+{
+	if(m_parentId > -1)
+	{
+		m_clipGroupOrder = GetNewOrder(m_parentId, m_id);
+	}
+}
+
+void CClip::MakeLastOrder()
+{
+	m_clipOrder = GetNewLastOrder(-1, m_id);
+}
+
+void CClip::MakeLastGroupOrder()
+{
+	if (m_parentId > -1)
+	{
+		m_clipGroupOrder = GetNewLastOrder(m_parentId, m_id);
+	}
+}
+
+double CClip::GetNewOrder(int parentId, int clipId)
+{
+	double newOrder = 0;
+	double existingMaxOrder = 0;
+	CString existingDesc = _T("");
+
+	try
+	{
+		if(parentId < 0)
+		{
+			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT clipOrder, mText FROM Main ORDER BY clipOrder DESC LIMIT 1"));			
+			if(q.eof() == false)
+			{
+				existingMaxOrder = q.getFloatField(_T("clipOrder"));
+				existingDesc = q.getStringField(_T("mText"));
+				newOrder = existingMaxOrder + 1;
+			}
+		}
+		else
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT clipGroupOrder, mText FROM Main WHERE lParentID = %d ORDER BY clipGroupOrder DESC LIMIT 1"), parentId);			
+			if(q.eof() == false)
+			{
+				existingMaxOrder = q.getFloatField(_T("clipGroupOrder"));
+				newOrder = existingMaxOrder + 1;
+			}
+		}
+
+		Log(StrF(_T("GetNewOrder, Id: %d, parentId: %d, CurrentMax: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMaxOrder, existingDesc, newOrder));
+	}
+	CATCH_SQLITE_EXCEPTION
+
+	return newOrder;
+}
+
+double CClip::GetNewLastOrder(int parentId, int clipId)
+{
+	double newOrder = 0;
+	double existingMinOrder = 0;
+	CString existingDesc = _T("");
+
+	try
+	{
+		if (parentId < 0)
+		{
+			CppSQLite3Query q = theApp.m_db.execQuery(_T("SELECT clipOrder, mText FROM Main where clipOrder notnull ORDER BY clipOrder ASC LIMIT 1"));
+			if (q.eof() == false)
+			{
+				existingMinOrder = q.getFloatField(_T("clipOrder"));
+				existingDesc = q.getStringField(_T("mText"));
+				newOrder = existingMinOrder - 1;
+			}
+		}
+		else
+		{
+			CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT clipGroupOrder, mText FROM Main WHERE lParentID = %d AND clipGroupOrder notnull ORDER BY clipGroupOrder ASC LIMIT 1"), parentId);
+			if (q.eof() == false)
+			{
+				existingMinOrder = q.getFloatField(_T("clipGroupOrder"));
+				newOrder = existingMinOrder - 1;
+			}
+		}
+
+		Log(StrF(_T("GetLastOrder, Id: %d, parentId: %d, CurrentMin: %f, CurrentDesc: %s, NewMax: %f"), clipId, parentId, existingMinOrder, existingDesc, newOrder));
+	}
+	CATCH_SQLITE_EXCEPTION
+
+		return newOrder;
+}
+
+BOOL CClip::LoadMainTable(int id)
+{
+	bool bRet = false;
+	try
+	{
+		CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT * FROM Main WHERE lID = %d"), id);
+
+		if(q.eof() == false)
+		{
+			m_Time = q.getIntField(_T("lDate"));
+			m_Desc = q.getStringField(_T("mText"));
+			m_CRC = q.getIntField(_T("CRC"));
+			m_parentId = q.getIntField(_T("lParentID"));
+			m_dontAutoDelete = q.getIntField(_T("lDontAutoDelete"));
+			m_shortCut = q.getIntField(_T("lShortCut"));
+			m_bIsGroup = q.getIntField(_T("bIsGroup"));
+			m_csQuickPaste = q.getStringField(_T("QuickPasteText"));
+			m_clipOrder = q.getFloatField(_T("clipOrder"));
+			m_clipGroupOrder = q.getFloatField(_T("clipGroupOrder"));
+			m_globalShortCut = q.getIntField(_T("globalShortCut"));
+			m_lastPasteDate = q.getIntField(_T("lastPasteDate"));
+			m_stickyClipOrder = q.getFloatField(_T("stickyClipOrder"));
+			m_stickyClipGroupOrder = q.getFloatField(_T("stickyClipGroupOrder"));
+			m_moveToGroupShortCut = q.getIntField(_T("MoveToGroupShortCut"));
+			m_globalMoveToGroupShortCut = q.getIntField(_T("GlobalMoveToGroupShortCut"));
+
+			m_id = id;
+
+			bRet = true;
+		}
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(FALSE)
+
+	return bRet;
+}
+
+// STATICS
+
+// Allocates a Global containing the requested Clip Format Data
+HGLOBAL CClip::LoadFormat(int id, UINT cfType)
+{
+	HGLOBAL hGlobal = 0;
+	try
+	{
+		CString csSQL;
+		
+		csSQL.Format(
+			_T("SELECT Data.ooData FROM Data ")
+			_T("INNER JOIN Main ON Main.lID = Data.lParentID ")
+			_T("WHERE Main.lID = %d ")
+			_T("AND Data.strClipBoardFormat = \'%s\'"),
+			id,
+			GetFormatName(cfType));
+
+		CppSQLite3Query q = theApp.m_db.execQuery(csSQL);
+
+		if(q.eof() == false)
+		{
+			int nDataLen = 0;
+			const unsigned char *cData = q.getBlobField(0, nDataLen);
+			if(cData == NULL)
+			{
+				return false;
+			}
+
+			hGlobal = NewGlobalP((LPVOID)cData, nDataLen);
+		}
+	}
+	CATCH_SQLITE_EXCEPTION
+		
+	return hGlobal;
+}
+
+bool CClip::LoadFormats(int id, bool bOnlyLoad_CF_TEXT, bool includeRichTextForTextOnly)
+{
+	DWORD startTick = GetTickCount();
+	CClipFormat cf;
+	HGLOBAL hGlobal = 0;
+	m_Formats.RemoveAll();
+
+	try
+	{	
+		//Open the data table for all that have the parent id
+
+		//Order by Data.lID so that when generating CRC it's always in the same order as the first time
+		//we generated it
+		CString csSQL;
+
+		CString textFilter = _T("");
+		if(bOnlyLoad_CF_TEXT)
+		{
+			textFilter = _T("(strClipBoardFormat = 'CF_TEXT' OR strClipBoardFormat = 'CF_UNICODETEXT' OR strClipBoardFormat = 'CF_HDROP'");
+
+			if(includeRichTextForTextOnly)
+			{
+				textFilter = textFilter + _T(" OR strClipBoardFormat = 'Rich Text Format') AND ");
+			}
+			else
+			{
+				textFilter = textFilter + _T(") AND ");
+			}
+		}
+
+		csSQL.Format(
+			_T("SELECT lID, lParentID, strClipBoardFormat, ooData FROM Data ")
+			_T("WHERE %s lParentID = %d ORDER BY Data.lID desc"), textFilter, id);
+
+		CppSQLite3Query q = theApp.m_db.execQuery(csSQL);
+
+		while(q.eof() == false)
+		{
+			cf.m_dataId = q.getIntField(_T("lID"));
+			cf.m_parentId = q.getIntField(_T("lParentID"));
+			cf.m_cfType = GetFormatID(q.getStringField(_T("strClipBoardFormat")));
+			
+			if(bOnlyLoad_CF_TEXT)
+			{
+				if(cf.m_cfType != CF_TEXT && 
+					cf.m_cfType != CF_UNICODETEXT &&
+					cf.m_cfType != CF_HDROP &&
+					(cf.m_cfType != theApp.m_RTFFormat && !includeRichTextForTextOnly))
+				{
+					q.nextRow();
+					continue;
+				}
+			}
+
+			int nDataLen = 0;
+			const unsigned char *cData = q.getBlobField(_T("ooData"), nDataLen);
+			if(cData != NULL)
+			{
+				hGlobal = NewGlobalP((LPVOID)cData, nDataLen);
+			}
+			
+			cf.m_hgData = hGlobal;
+			m_Formats.Add(cf);
+
+			q.nextRow();
+		}
+
+		// formats owns all the data
+		cf.m_hgData = 0;
+	}
+	CATCH_SQLITE_EXCEPTION_AND_RETURN(false)
+		
+	DWORD endTick = GetTickCount();
+	if((endTick-startTick) > 150)
+		Log(StrF(_T("Paste Timing LoadFormats: %d, ClipId: %d"), endTick-startTick, id));
+
+	return m_Formats.GetSize() > 0;
+}
+
+void CClip::LoadTypes(int id, CClipTypes& types)
+{
+	types.RemoveAll();
+	try
+	{
+		CString csSQL;
+		// get formats for Clip "lID" (Main.lID) using the corresponding Main.lDataID
+		
+		//Order by Data.lID so that when generating CRC it's always in the same order as the first time
+		//we generated it
+		csSQL.Format(
+			_T("SELECT strClipBoardFormat FROM Data ")
+			_T("INNER JOIN Main ON Main.lID = Data.lParentID ")
+			_T("WHERE Main.lID = %d ORDER BY Data.lID desc"), id);
+
+		CppSQLite3Query q = theApp.m_db.execQuery(csSQL);			
+
+		while(q.eof() == false)
+		{		
+			types.Add(GetFormatID(q.getStringField(0)));
+			q.nextRow();
+		}
+	}
+	CATCH_SQLITE_EXCEPTION
+}
+
+bool CClip::SaveFromEditWnd(BOOL bUpdateDesc)
+{
+	bool bRet = false;
+
+	try
+	{
+		theApp.m_db.execDMLEx(_T("DELETE FROM Data WHERE lParentID = %d;"), m_id);
+
+		DWORD CRC = GenerateCRC();
+
+		AddToDataTable();
+
+		theApp.m_db.execDMLEx(_T("UPDATE Main SET CRC = %d WHERE lID = %d"), CRC, m_id);
+		
+		if(bUpdateDesc)
+		{
+			m_Desc.Replace(_T("'"), _T("''"));
+			theApp.m_db.execDMLEx(_T("UPDATE Main SET mText = '%s' WHERE lID = %d"), m_Desc, m_id);
+		}
+
+		bRet = true;
+	}
+	CATCH_SQLITE_EXCEPTION
+
+	return bRet;
+}
+
+CStringW CClip::GetUnicodeTextFormat()
+{
+	IClipFormat *pFormat = this->Clips()->FindFormatEx(CF_UNICODETEXT);
+	if(pFormat != NULL)
+	{
+		return pFormat->GetAsCString();
+	}
+
+	return _T("");
+}
+
+CStringA CClip::GetCFTextTextFormat()
+{
+	IClipFormat *pFormat = this->Clips()->FindFormatEx(CF_TEXT);
+	if(pFormat != NULL)
+	{
+		return pFormat->GetAsCStringA();
+	}
+
+	return _T("");
+}
+
+BOOL CClip::WriteTextToFile(CString path, BOOL unicode, BOOL asci, BOOL utf8)
+{
+	BOOL ret = false;
+
+	CFile f;
+	if(f.Open(path, CFile::modeWrite|CFile::modeCreate))
+	{
+		CStringW w = GetUnicodeTextFormat();
+		CStringA a = GetCFTextTextFormat();
+		
+		if(utf8 && w != _T(""))
+		{
+			CStringA convToUtf8 = CTextConvert::UnicodeToUTF8(w);
+			std::byte header[2];
+			header[0] = (std::byte)0xEF;
+			header[1] = (std::byte)0xBB;
+			f.Write(&header, 2);
+			f.Write(convToUtf8.GetBuffer(), convToUtf8.GetLength());
+
+			ret = true;
+		}
+		else if(unicode && w != _T(""))
+		{
+			std::byte header[2];
+			header[0] = (std::byte)0xFF;
+			header[1] = (std::byte)0xFE;
+			f.Write(&header, 2);
+			f.Write(w.GetBuffer(), w.GetLength() * sizeof(wchar_t));
+
+			ret = true;
+		}
+		else if(asci && a != _T(""))
+		{
+			f.Write(a.GetBuffer(), a.GetLength());
+
+			ret = true;
+		}
+
+		f.Close();
+	}
+
+	return ret;
+}
+
+BOOL CClip::WriteTextToHtmlFile(CString path)
+{
+	BOOL ret = false;
+
+	CFile f;
+	if (f.Open(path, CFile::modeWrite | CFile::modeCreate))
+	{
+		IClipFormat *pFormat = this->Clips()->FindFormatEx(theApp.m_HTML_Format);
+		if (pFormat != NULL)
+		{
+			CStringA html = pFormat->GetAsCStringA();
+
+			int pos = html.Find("<html");
+			if (pos >= 0)
+			{
+				html = html.Mid(pos);
+			}
+			else
+			{
+				html = html;
+			}
+
+			f.Write(html.GetBuffer(), html.GetLength());			
+		}
+
+		f.Close();
+	}
+
+	return ret;
+}
+
+BOOL CClip::WriteImageToFile(CString path)
+{
+	CClipFormat *bitmap = this->m_Formats.FindFormat(CF_DIB);
+	CClipFormat *png = this->m_Formats.FindFormat(theApp.m_PNG_Format);
+	if (!bitmap && !png) return false;
+	
+	std::shared_ptr<CImage> i;
+	// png is more closer to original
+	if (png)
+		i = PNGImageHelper::CImageFromHGLOBAL(png->m_hgData);
+	else
+		i = DIBImageHelper::CImageFromHGLOBAL(bitmap->m_hgData);
+
+	return i->Save(path) == S_OK;
+}
+
+bool CClip::AddFileDataToData(CString &errorMessage)
+{
+	INT_PTR size = m_Formats.GetSize();
+	if (size <= 0)
+	{
+		errorMessage = _T("No CF_HDROP formats to convert");
+		return false;
+	}
+
+	bool addedFileData = false;
+
+	int nCF_HDROPIndex = -1;
+	int dittoDataIndex = -1;
+	for (int i = 0; i < size; i++)
+	{
+		if (m_Formats[i].m_cfType == CF_HDROP)
+		{
+			nCF_HDROPIndex = i;
+		}
+		else if(m_Formats[i].m_cfType == theApp.m_DittoFileData)
+		{
+			dittoDataIndex = i;
+		}
+	}	
+
+	if (nCF_HDROPIndex < 0)
+	{
+		errorMessage = _T("No CF_HDROP formats to convert");
+		return false;
+	}
+	else if (dittoDataIndex >= 0)
+	{
+		return false;
+	}
+
+	using namespace nsPath;
+
+	HDROP drop = (HDROP)GlobalLock(m_Formats[nCF_HDROPIndex].m_hgData);
+	int nNumFiles = DragQueryFile(drop, -1, NULL, 0);
+
+	TCHAR filePath[MAX_PATH];
+
+	CString newDesc = _T("File Contents - ");
+	int maxSize = CGetSetOptions::GetMaxFileContentsSize();
+	for (int nFile = 0; nFile < nNumFiles; nFile++)
+	{
+		if (DragQueryFile(drop, nFile, filePath, sizeof(filePath)) == 0)
+			continue;
+
+		CFile file;
+		CFileException ex;
+		if (!file.Open(filePath, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone, &ex))
+		{
+			TCHAR szError[200];
+			ex.GetErrorMessage(szError, 200);
+			errorMessage += StrF(_T("Error opening file: %s, Error: %s\r\n"), filePath, szError);
+			continue;
+		}
+
+		int fileSize = (int)file.GetLength();
+		if (fileSize >= maxSize)
+		{
+			const int MAX_FILE_SIZE_BUFFER = 255;
+			TCHAR szFileSize[MAX_FILE_SIZE_BUFFER];
+			TCHAR szMaxFileSize[MAX_FILE_SIZE_BUFFER];
+			StrFormatByteSize(fileSize, szFileSize, MAX_FILE_SIZE_BUFFER);
+			StrFormatByteSize(maxSize, szMaxFileSize, MAX_FILE_SIZE_BUFFER);
+
+			errorMessage += StrF(_T("File is to large: %s, Size: %s, Max Size: %s\r\n"), filePath, szFileSize, szMaxFileSize);
+			continue;
+		}
+
+		CString src(filePath);
+		CStringA csFilePath = CTextConvert::UnicodeToUTF8(src);
+
+		//data contents
+		//original file<null terminator>md5<null terminator>file data
+		int bufferSize = (int)fileSize + csFilePath.GetLength() + 1 + md5StringLength + 1;;
+		char* pBuffer = new char[bufferSize]();
+		strncpy(pBuffer, csFilePath, csFilePath.GetLength());
+
+		//move the buffer start past the file path and md5 string
+		char* bufferStart = pBuffer + csFilePath.GetLength() + 1 + md5StringLength + 1;
+
+		int readBytes = (int)file.Read(bufferStart, fileSize);
+
+		CMd5 md5;
+		CStringA md5String = md5.CalcMD5FromString(bufferStart, fileSize);
+
+		char* bufferMd5 = pBuffer + csFilePath.GetLength() + 1;
+		strncpy(bufferMd5, md5String, md5StringLength);
+
+		AddFormat(theApp.m_DittoFileData, pBuffer, bufferSize);
+
+		addedFileData = true;
+
+		newDesc += filePath;
+		newDesc += _T("\n");
+
+		Log(StrF(_T("Saving file contents to Ditto Database, file: %s, size: %d, md5: %s"), filePath, fileSize, md5String));
+	}
+
+	GlobalUnlock(m_Formats[nCF_HDROPIndex].m_hgData);
+
+	if (!addedFileData)
+		return false;
+
+	for (int i = 0; i < size; i++)
+	{
+		this->m_Formats.RemoveAt(i, 1);
+	}
+
+	this->m_Desc = newDesc;
+
+	if (this->ModifyDescription())
+	{
+		if (this->AddToDataTable() == FALSE)
+		{
+			errorMessage += _T("Error saving data to database.");
+		}
+	}
+	else
+	{
+		errorMessage += _T("Error saving main table to database.");
+	}
+
+	return addedFileData;
+}
+
+Gdiplus::Bitmap *CClip::CreateGdiplusBitmap()
+{
+	CClipFormat *png = this->m_Formats.FindFormat(GetFormatID(_T("PNG")));
+	if (png != NULL)
+		return png->CreateGdiplusBitmap();
+
+	CClipFormat *dib = this->m_Formats.FindFormat(CF_DIB);
+	if (dib != NULL)
+		return dib->CreateGdiplusBitmap();
+}
+
+/*----------------------------------------------------------------------------*\
+CClipList
+\*----------------------------------------------------------------------------*/
+
+CClipList::~CClipList()
+{
+	CClip* pClip;
+	while(GetCount())
+	{
+		pClip = RemoveHead();
+		delete pClip;
+	}
+}
+
+// returns the number of clips actually saved
+// while this does empty the Format Data, it does not delete the Clips.
+int CClipList::AddToDB(bool bLatestOrder)
+{
+	Log(_T("AddToDB - Start"));
+
+	int savedCount = 0;
+	CClip* pClip;
+	POSITION pos;
+	bool bResult;
+	
+	INT_PTR remaining = GetCount();
+	pos = GetHeadPosition();
+	while(pos)
+	{
+		Log(StrF(_T("AddToDB - while(pos), Start Remaining %d"), remaining));
+		remaining--;
+		
+		pClip = GetNext(pos);
+		ASSERT(pClip);
+		
+		if(bLatestOrder)
+		{
+			pClip->MakeLatestOrder();
+			pClip->MakeLatestGroupOrder();
+		}
+
+		bResult = pClip->AddToDB();
+		if(bResult)
+		{
+			savedCount++;
+		}
+
+		Log(StrF(_T("AddToDB - while(pos), End Remaining %d, save count: %d"), remaining, savedCount));
+	}
+
+	Log(StrF(_T("AddToDB - Start, count: %d"), savedCount));
+	
+	return savedCount;
+}
+
+const CClipList& CClipList::operator=(const CClipList &cliplist)
+{
+	POSITION pos;
+	CClip* pClip;
+	
+	pos = cliplist.GetHeadPosition();
+	while(pos)
+	{
+		pClip = cliplist.GetNext(pos);
+		ASSERT(pClip);
+
+		CClip *pNewClip = new CClip;
+		if(pNewClip)
+		{
+			*pNewClip = *pClip;
+			
+			AddTail(pNewClip);
+		}
+	}
+	
+	return *this;
+}

+ 3 - 1
Clip.h

@@ -1,4 +1,4 @@
-// ProcessCopy.h: classes for saving the clipboard to db
+// Clip.h: classes for manage clips
 //
 //////////////////////////////////////////////////////////////////////
 
@@ -10,6 +10,7 @@
 #endif // _MSC_VER > 1000
 #include <afxole.h>
 #include <afxtempl.h>
+#include <memory>
 #include "tinyxml\tinyxml.h"
 #include "Shared\IClip.h"
 #include "Misc.h"
@@ -27,6 +28,7 @@ class COleDataObjectEx : public COleDataObject
 public:
 	// creates global from IStream if necessary
 	HGLOBAL GetGlobalData(CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc = NULL);
+	std::shared_ptr<CClipTypes> GetAvailableTypes();
 };
 
 /*----------------------------------------------------------------------------*\

+ 7 - 31
CopyThread.cpp

@@ -70,41 +70,21 @@ void CCopyThread::OnClipboardChange(CString activeWindow, CString activeWindowTi
 	CClip* pClip = new CClip;
 	pClip->m_copyReason = theApp.GetCopyReason();
 
+	COleDataObjectEx oleData;
+	std::shared_ptr<CClipTypes> availableTypes = oleData.GetAvailableTypes();
 	CClipTypes* pSupportedTypes = m_LocalConfig.m_pSupportedTypes;
-	bool bDeleteMemory = false;
 
 	//If we are copying from a Ditto Buffer then save all to the database, so when we paste this it will paste 
 	//just like you were using Ctrl-V
 	if(theApp.m_CopyBuffer.Active())
 	{
 		Log(_T("LoadFromClipboard - Copy buffer Active Start"));
-
-		pSupportedTypes = new CClipTypes;
-		if(pSupportedTypes)
-		{
-			bDeleteMemory = true;
-			COleDataObject oleData;
-
-			if(oleData.AttachClipboard())
-			{
-				oleData.BeginEnumFormats();
-
-				FORMATETC format;
-				while(oleData.GetNextFormat(&format))
-				{
-					pSupportedTypes->Add(format.cfFormat);
-				}
-
-				oleData.Release();
-			}
-		}
-		else
-		{
-			pSupportedTypes = m_LocalConfig.m_pSupportedTypes;
-		}
-
+		pSupportedTypes = availableTypes.get();
 		Log(_T("LoadFromClipboard - Copy buffer Active End"));
 	}
+	else if (CGetSetOptions::GetSupportAllTypes()) {
+		pSupportedTypes = availableTypes.get();
+	}
 
 	Log(_T("LoadFromClipboard - Before"));
 	int bResult = pClip->LoadFromClipboard(pSupportedTypes, true, activeWindow, activeWindowTitle);
@@ -128,11 +108,7 @@ void CCopyThread::OnClipboardChange(CString activeWindow, CString activeWindowTi
 		}
 	}
 
-	if(bDeleteMemory)
-	{
-		delete pSupportedTypes;
-		pSupportedTypes = NULL;
-	}
+	pSupportedTypes = NULL;
 	
 	if(bResult != TRUE)
 	{

+ 13 - 14
ExternalWindowTracker.cpp

@@ -227,7 +227,19 @@ void ExternalWindowTracker::SendPaste(bool activateTarget)
 	CSendKeys send;
 	send.AllKeysUp();
 
-	if(activateTarget == false)
+	if(activateTarget)
+	{
+		DWORD startTick = GetTickCount();
+
+		ActivateTarget();
+		theApp.PumpMessageEx();
+		WaitForActiveWnd(activeWnd, max(25, g_Opt.WaitForActiveWndTimeout()));
+	
+		DWORD endTick = GetTickCount();
+		if((endTick-startTick) > 150)
+			Log(StrF(_T("Paste Timing Send Paste around activate Target: %d"), endTick-startTick));
+	}
+	else
 	{
 		activeWnd = ::GetForegroundWindow();
 	}
@@ -237,19 +249,6 @@ void ExternalWindowTracker::SendPaste(bool activateTarget)
 	DWORD delay = g_Opt.SendKeysDelay();
 	DWORD sendKeysDelay = g_Opt.RealSendKeysDelay();
 
-	DWORD startTick = GetTickCount();
-
-	if(activateTarget)
-	{
-		ActivateTarget();
-		theApp.PumpMessageEx();
-		WaitForActiveWnd(activeWnd, max(25, g_Opt.WaitForActiveWndTimeout()));
-	}
-
-	DWORD endTick = GetTickCount();
-	if((endTick-startTick) > 150)
-		Log(StrF(_T("Paste Timing Send Paste around activate Target: %d"), endTick-startTick));
-
 	m_dittoHasFocus = false;
 	Log(StrF(_T("Sending paste to app %s key stroke: %s, SeDelay: %d"), csPasteToApp, csPasteString, delay));
 

+ 20 - 0
ImageHelper.cpp

@@ -0,0 +1,20 @@
+#include "stdafx.h"
+#include "ImageHelper.h"
+
+void DIBImageHelper::prependStream(IStream* pIStream, LPVOID pvData, ULONG size)
+{
+	BITMAPINFO* lpBI = (BITMAPINFO*)pvData;
+
+	int nPaletteEntries = 1 << lpBI->bmiHeader.biBitCount;
+	if (lpBI->bmiHeader.biBitCount > 8)
+		nPaletteEntries = 0;
+	else if (lpBI->bmiHeader.biClrUsed != 0)
+		nPaletteEntries = lpBI->bmiHeader.biClrUsed;
+
+	BITMAPFILEHEADER BFH;
+	memset(&BFH, 0, sizeof(BITMAPFILEHEADER));
+	BFH.bfType = 'MB';
+	BFH.bfSize = sizeof(BITMAPFILEHEADER) + size;
+	BFH.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + nPaletteEntries * sizeof(RGBQUAD);
+	pIStream->Write(&BFH, sizeof(BITMAPFILEHEADER), NULL);
+}

+ 63 - 0
ImageHelper.h

@@ -0,0 +1,63 @@
+#include "stdafx.h"
+#include "Misc.h"
+#include <memory>
+
+template <class Concrete, class GdipImage>
+class ImageHelper abstract
+{
+public:
+	static GdipImage* GdipImageFromHGLOBAL(HGLOBAL hGlobal) {
+		IStream* pIStream = StreamFromHGLOBAL(hGlobal);
+		if (!pIStream)
+			return NULL;
+
+		GdipImage* gdipImage = GdipImage::FromStream(pIStream);
+		pIStream->Release();
+		return gdipImage;
+	};
+	static std::shared_ptr<CImage> CImageFromHGLOBAL(HGLOBAL hGlobal) {
+		IStream* pIStream = StreamFromHGLOBAL(hGlobal);
+		if (!pIStream)
+			return NULL;
+
+		std::shared_ptr<CImage> cImage = std::make_shared<CImage>();
+		cImage->Load(pIStream);
+		pIStream->Release();
+
+		return cImage;
+	};
+	static IStream* StreamFromHGLOBAL(HGLOBAL hGlobal) {
+		if (!hGlobal)
+			return NULL;
+
+		IStream* pIStream = NULL;
+		if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream) != S_OK)
+			return NULL;
+
+		LPVOID pvData = GlobalLock(hGlobal);
+		ULONG size = (ULONG)GlobalSize(hGlobal);
+
+		Concrete::prependStream(pIStream, pvData, size);
+
+		pIStream->Write(pvData, size, NULL);
+		GlobalUnlock(hGlobal);
+
+		return pIStream;
+	};
+};
+
+class BitmapImageHelper : public ImageHelper<BitmapImageHelper, Gdiplus::Bitmap>
+{
+public:
+	static void prependStream(IStream* pIStream, LPVOID pvData, ULONG size) {};
+};
+
+class PNGImageHelper : public BitmapImageHelper
+{
+};
+
+class DIBImageHelper : public ImageHelper<DIBImageHelper, Gdiplus::Bitmap>
+{
+public:
+	static void prependStream(IStream* pIStream, LPVOID pvData, ULONG size);
+};

+ 30 - 0
Misc.cpp

@@ -10,6 +10,7 @@
 #include <sys/stat.h> 
 #include "Path.h"
 #include <regex>
+#include <vector>
 
 CString GetIPAddress()
 {
@@ -383,6 +384,35 @@ int CompareGlobalHH( HGLOBAL hLeft, HGLOBAL hRight, SIZE_T ulBufLen)
 	return result;
 }
 
+// https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
+std::vector<CLIPFORMAT> GetSystemClipFormats()
+{
+	std::vector<CLIPFORMAT> v = {
+		CF_TEXT,
+		CF_BITMAP,
+		CF_METAFILEPICT,
+		CF_SYLK,
+		CF_DIF,
+		CF_TIFF,
+		CF_OEMTEXT,
+		CF_DIB,
+		CF_PALETTE,
+		CF_PENDATA,
+		CF_RIFF,
+		CF_WAVE,
+		CF_UNICODETEXT,
+		CF_ENHMETAFILE,
+		CF_HDROP,
+		CF_LOCALE,
+		CF_OWNERDISPLAY,
+		CF_DSPTEXT,
+		CF_DSPBITMAP,
+		CF_DSPMETAFILEPICT,
+		CF_DSPENHMETAFILE
+	};
+
+	return v;
+}
 
 //Do not change these these are stored in the database
 CLIPFORMAT GetFormatID(LPCTSTR cbName)

+ 2 - 0
Misc.h

@@ -7,6 +7,7 @@
 #endif // _MSC_VER > 1000
 
 #include "Shared/ArrayEx.h"
+#include <vector>
 
 #define VK_MOUSE_CLICK 0x01
 #define VK_MOUSE_DOUBLE_CLICK 0x02
@@ -112,6 +113,7 @@ BOOL DecryptString(UCHAR *pData, int nLenIn, UCHAR*& pOutput, int &nLenOutput);
 int GetScreenWidth();
 int GetScreenHeight();
 
+std::vector<CLIPFORMAT> GetSystemClipFormats();
 CLIPFORMAT GetFormatID(LPCTSTR cbName);
 CString GetFormatName(CLIPFORMAT cbType);
 BOOL PreTranslateGuiDll(MSG *pMsg);

+ 74 - 81
OleClipSource.cpp

@@ -913,58 +913,58 @@ void COleClipSource::SaveDittoFileDataToFile(CClip &clip)
 		if (pCF->m_cfType == theApp.m_DittoFileData)
 		{
 			IClipFormat *dittoFileData = &clip.m_Formats.ElementAt(i);
-			if (dittoFileData != NULL)
-			{
-				HGLOBAL data = dittoFileData->Data();
-				char * stringData = (char *)GlobalLock(data);
+			if (dittoFileData == NULL) 
+				continue;
 
-				//original source is store in the first string ending in the null terminator
-				CStringA src(stringData);
-				stringData += src.GetLength() + 1;
+			HGLOBAL data = dittoFileData->Data();
+			char * stringData = (char *)GlobalLock(data);
 
-				CStringA originalMd5(stringData);
-				stringData += originalMd5.GetLength() + 1;
+			//original source is store in the first string ending in the null terminator
+			CStringA src(stringData);
+			stringData += src.GetLength() + 1;
 
-				int dataSize = (int)GlobalSize(data) - (src.GetLength() + 1) - (originalMd5.GetLength() + 1);
+			CStringA originalMd5(stringData);
+			stringData += originalMd5.GetLength() + 1;
 
-				CMd5 calcMd5;
-				CStringA md5String = calcMd5.CalcMD5FromString(stringData, dataSize);
+			int dataSize = (int)GlobalSize(data) - (src.GetLength() + 1) - (originalMd5.GetLength() + 1);
 
-				CString unicodeFilePath = CTextConvert::Utf8ToUnicode(src);
+			CMd5 calcMd5;
+			CStringA md5String = calcMd5.CalcMD5FromString(stringData, dataSize);
 
-				CString unicodeMd5 = CTextConvert::Utf8ToUnicode(md5String);
+			CString unicodeFilePath = CTextConvert::Utf8ToUnicode(src);
 
-				Log(StrF(_T("Saving file contents from Ditto, original file: %s, size: %d, md5: %s"), unicodeFilePath, dataSize, unicodeMd5));
+			CString unicodeMd5 = CTextConvert::Utf8ToUnicode(md5String);
 
-				if (md5String == originalMd5)
-				{
-					using namespace nsPath;
-					CPath path(unicodeFilePath);
-					CString fileName = path.GetName();
+			Log(StrF(_T("Saving file contents from Ditto, original file: %s, size: %d, md5: %s"), unicodeFilePath, dataSize, unicodeMd5));
 
-					CString newFilePath = CGetSetOptions::GetPath(PATH_DRAG_FILES);
-					newFilePath += fileName;
+			if (md5String != originalMd5)
+			{
+				Log(StrF(_T("MD5 ERROR, file: %s, original md5: %s, calc md5: %s"), unicodeFilePath, originalMd5, md5String));
+				continue;
+			}
 
-					CFile f;
-					if (f.Open(newFilePath, CFile::modeWrite | CFile::modeCreate))
-					{
-						f.Write(stringData, dataSize);
+			using namespace nsPath;
+			CPath path(unicodeFilePath);
+			CString fileName = path.GetName();
 
-						f.Close();
+			CString newFilePath = CGetSetOptions::GetPath(PATH_DRAG_FILES);
+			newFilePath += fileName;
 
-						savedFile = true;
-						hDrpData.AddFile(newFilePath);
-					}
-					else
-					{
-						Log(StrF(_T("Error saving file: %s"), unicodeFilePath));
-					}
-				}
-				else
-				{
-					Log(StrF(_T("MD5 ERROR, file: %s, original md5: %s, calc md5: %s"), unicodeFilePath, originalMd5, md5String));
-				}
+			CFile f;
+			if (f.Open(newFilePath, CFile::modeWrite | CFile::modeCreate))
+			{
+				f.Write(stringData, dataSize);
+
+				f.Close();
+
+				savedFile = true;
+				hDrpData.AddFile(newFilePath);
 			}
+			else
+			{
+				Log(StrF(_T("Error saving file: %s"), unicodeFilePath));
+			}
+
 		}
 		else if (pCF->m_cfType == CF_HDROP)
 		{
@@ -1283,56 +1283,49 @@ HGLOBAL COleClipSource::ConvertToFileDrop()
 
 			fileClip.WriteTextToFile(file, TRUE, FALSE, FALSE);
 			fileList.AddFile(file);
+			continue;
 		}
-		else
+
+		CClipFormat *asciiText = fileClip.m_Formats.FindFormat(CF_TEXT);
+		if (asciiText)
 		{
-			CClipFormat *asciiText = fileClip.m_Formats.FindFormat(CF_TEXT);
-			if (asciiText)
+			CString name = _T("text");
+			CString file;
+			if (customDragName != _T(""))
 			{
-				CString name = _T("text");
-				CString file;
-				if (customDragName != _T(""))
-				{
-					name = customDragName;
-					file.Format(_T("%s%s.txt"), path, name);
-				}
-				else
-				{
-					file.Format(_T("%s%s_%d.txt"), path, name, dragId++);
-				}
-
-				fileClip.WriteTextToFile(file, FALSE, TRUE, FALSE);
-				fileList.AddFile(file);
+				name = customDragName;
+				file.Format(_T("%s%s.txt"), path, name);
 			}
 			else
 			{
-				CClipFormat *png = NULL;
-				CClipFormat *bitmap = fileClip.m_Formats.FindFormat(CF_DIB);
-				if (bitmap == NULL)
-				{
-					png = fileClip.m_Formats.FindFormat(theApp.m_PNG_Format);
-				}
+				file.Format(_T("%s%s_%d.txt"), path, name, dragId++);
+			}
 
-				if (bitmap != NULL ||
-					png != NULL)
-				{
-					CString name = _T("image");
-					CString file;
-					if (customDragName != _T(""))
-					{
-						name = customDragName;
-						file.Format(_T("%s%s.png"), path, name);
-					}
-					else
-					{
-						file.Format(_T("%s%s_%d.png"), path, name, dragId++);
-					}
+			fileClip.WriteTextToFile(file, FALSE, TRUE, FALSE);
+			fileList.AddFile(file);
+			continue;
+		}
 
-					if (fileClip.WriteImageToFile(file))
-					{
-						fileList.AddFile(file);
-					}
-				}
+		CClipFormat *png = fileClip.m_Formats.FindFormat(theApp.m_PNG_Format);
+		CClipFormat *bitmap = fileClip.m_Formats.FindFormat(CF_DIB);
+		if (bitmap != NULL ||
+			png != NULL)
+		{
+			CString name = _T("image");
+			CString file;
+			if (customDragName != _T(""))
+			{
+				name = customDragName;
+				file.Format(_T("%s%s.png"), path, name);
+			}
+			else
+			{
+				file.Format(_T("%s%s_%d.png"), path, name, dragId++);
+			}
+
+			if (fileClip.WriteImageToFile(file))
+			{
+				fileList.AddFile(file);
 			}
 		}
 	}

+ 11 - 5
Options.cpp

@@ -83,6 +83,7 @@ BOOL CGetSetOptions::m_maintainSearchView = FALSE;
 CString CGetSetOptions::m_tempDragFileName = "";
 CTime CGetSetOptions::m_tempDragFileNameSetTime;
 BOOL CGetSetOptions::m_refreshViewAfterPasting = TRUE;
+BOOL CGetSetOptions::m_supportAllTypes = FALSE;
 
 
 CGetSetOptions::CGetSetOptions()
@@ -1915,11 +1916,6 @@ CString CGetSetOptions::GetPasteString(CString csAppName)
 	CString csString = GetProfileString(csAppName, _T(""), _T("PasteStrings"));
 	if (csString.IsEmpty())
 	{
-		//edge is really slow to set focus so add a delay before sending the paste
-		if (csAppName == L"MicrosoftEdge.exe")
-		{
-			return _T("{DELAY 500}^{VKEY86}");
-		}
 		return GetDefaultPasteString();
 	}
 
@@ -2987,3 +2983,13 @@ void CGetSetOptions::SetSlugifySeparator(CString val)
 	SetProfileString("SlugifySeparator", val);
 }
 
+BOOL CGetSetOptions::GetSupportAllTypes()
+{
+	return GetProfileLong("SupportAllTypes", FALSE);
+}
+
+void CGetSetOptions::SetSupportAllTypes(BOOL val)
+{
+	m_refreshViewAfterPasting = val;
+	SetProfileLong("SupportAllTypes", val);
+}

+ 4 - 0
Options.h

@@ -676,6 +676,10 @@ public:
 
 	static CString GetSlugifySeparator();
 	static void SetSlugifySeparator(CString val);
+
+	static BOOL m_supportAllTypes;
+	static BOOL GetSupportAllTypes();
+	static void SetSupportAllTypes(BOOL val);
 };
 
 // global for easy access and for initialization of fast access variables

+ 1 - 0
OptionsTypes.cpp

@@ -95,6 +95,7 @@ BOOL COptionsTypes::OnInitDialog()
 			m_List.AddString(_T("CF_HDROP"));
 			m_List.AddString(_T("CF_DIB"));
 			m_List.AddString(GetFormatName(GetFormatID(_T("HTML Format"))));
+			m_List.AddString(GetFormatName(GetFormatID(_T("PNG"))));
 		}
 
 		while(q.eof() == false)

+ 34 - 37
ProcessPaste.cpp

@@ -29,48 +29,45 @@ BOOL CProcessPaste::DoPaste()
 	try
 	{
 		m_pOle->m_pasteOptions = m_pasteOptions;
-
-		if (m_pOle->DoImmediateRender())
+		if (!m_pOle->DoImmediateRender())
 		{
-			// MarkAsPasted() must be done first since it makes use of
-			//  m_pOle->m_ClipIDs and m_pOle is inaccessible after
-			//  SetClipboard is called.
-			MarkAsPasted(m_pasteOptions.m_updateClipOrder);
-
-			// Ignore the clipboard change that we will cause IF:
-			// 1) we are pasting a single element, since the element is already
-			//    in the db and its lDate was updated by MarkAsPas???ted().
-			// OR
-			// 2) we are pasting multiple, but g_Opt.m_bSaveMultiPaste is false
-			if (GetClipIDs().GetSize() == 1 || !g_Opt.m_bSaveMultiPaste)
-			{
-				m_pOle->CacheGlobalData(theApp.m_cfIgnoreClipboard, NewGlobalP("Ignore", sizeof("Ignore")));
-			}
-			else
-			{
-				m_pOle->CacheGlobalData(theApp.m_cfDelaySavingData, NewGlobalP("Delay", sizeof("Delay")));
-			}
-
-			m_pOle->SetClipboard(); // m_pOle is now managed by the OLE clipboard
+			return ret;
+		}
 
-			// The Clipboard now owns the allocated memory
-			// and will delete this data object
-			// when new data is put on the Clipboard
-			m_pOle = NULL; // m_pOle should not be accessed past this point
+		// MarkAsPasted() must be done first since it makes use of
+		//  m_pOle->m_ClipIDs and m_pOle is inaccessible after
+		//  SetClipboard is called.
+		MarkAsPasted(m_pasteOptions.m_updateClipOrder);
+
+		// Ignore the clipboard change that we will cause IF:
+		// 1) we are pasting a single element, since the element is already
+		//    in the db and its lDate was updated by MarkAsPasted().
+		// OR
+		// 2) we are pasting multiple, but g_Opt.m_bSaveMultiPaste is false
+		if (GetClipIDs().GetSize() == 1 || !g_Opt.m_bSaveMultiPaste)
+		{
+			m_pOle->CacheGlobalData(theApp.m_cfIgnoreClipboard, NewGlobalP("Ignore", sizeof("Ignore")));
+		}
+		else
+		{
+			m_pOle->CacheGlobalData(theApp.m_cfDelaySavingData, NewGlobalP("Delay", sizeof("Delay")));
+		}
 
-			if (m_bSendPaste)
-			{
-				Log(_T("Sending Paste to active window"));
-				theApp.m_activeWnd.SendPaste(m_bActivateTarget);
-			}
-			else if (m_bActivateTarget)
-			{
-				Log(_T("Activating active window"));
-				theApp.m_activeWnd.ActivateTarget();
-			}
+		m_pOle->SetClipboard(); // m_pOle is now managed by the OLE clipboard
 
-			ret = TRUE;
+		if (m_bSendPaste)
+		{
+			Log(_T("Sending Paste to active window"));
+			theApp.m_activeWnd.SendPaste(m_bActivateTarget);
+		}
+		else if (m_bActivateTarget)
+		{
+			Log(_T("Activating active window"));
+			theApp.m_activeWnd.ActivateTarget();
 		}
+
+		ret = TRUE;
+	
 	}
 	catch (CException *ex)
 	{

+ 24 - 25
QPasteWnd.cpp

@@ -5184,38 +5184,37 @@ bool CQPasteWnd::DoExportToBitMapFile()
 				png = toSave.m_Formats.FindFormat(theApp.m_PNG_Format);
 			}
 
-			if (bitmap != NULL ||
-				png != NULL)
+			if (bitmap == NULL && png == NULL)
+				continue;
+
+			CString savePath = startingFilePath;
+			if (IDs.GetCount() > 1 ||
+				FileExists(startingFilePath))
 			{
-				CString savePath = startingFilePath;
-				if (IDs.GetCount() > 1 ||
-					FileExists(startingFilePath))
-				{
-					savePath = _T("");
+				savePath = _T("");
 
-					for (int y = lastFileCheckId; y < 1000000; y++)
+				for (int y = lastFileCheckId; y < 1000000; y++)
+				{
+					CString testFilePath;
+					testFilePath.Format(_T("%s%s_%d.%s"), csPath, csFileName, y, csExt);
+					if (FileExists(testFilePath) == FALSE)
 					{
-						CString testFilePath;
-						testFilePath.Format(_T("%s%s_%d.%s"), csPath, csFileName, y, csExt);
-						if (FileExists(testFilePath) == FALSE)
-						{
-							savePath = testFilePath;
-							lastFileCheckId = y+1;
-							break;
-						}
+						savePath = testFilePath;
+						lastFileCheckId = y+1;
+						break;
 					}
 				}
+			}
 
-				if (savePath != _T(""))
-				{
-					toSave.WriteImageToFile(savePath);
+			if (savePath != _T(""))
+			{
+				toSave.WriteImageToFile(savePath);
 
-					ret = true;
-				}
-				else
-				{
-					Log(StrF(_T("Failed to find a valid file name for starting path: %s"), startingFilePath));
-				}
+				ret = true;
+			}
+			else
+			{
+				Log(StrF(_T("Failed to find a valid file name for starting path: %s"), startingFilePath));
 			}
 		}
 	}

+ 24 - 26
SendKeys.cpp

@@ -394,9 +394,10 @@ WORD CSendKeys::StringToVKey(LPCTSTR KeyString, int &idx)
 // If we are in a modifier group this function does nothing
 void CSendKeys::PopUpShiftKeys()
 {
-  if (!m_bUsingParens)
-  {
-    if (m_bShiftDown)
+  if (m_bUsingParens)
+		return;
+
+  if (m_bShiftDown)
 	{
       SendKeyUp(VK_SHIFT);
 	}
@@ -429,8 +430,7 @@ void CSendKeys::PopUpShiftKeys()
       SendKeyUp(VK_LWIN);
 	}
 
-    m_bWinDown = m_bShiftDown = m_bLShiftDown = m_bRShiftDown = m_bControlDown = m_bLControlDown = m_bRControlDown = m_bAltDown = false;
-  }
+	m_bWinDown = m_bShiftDown = m_bLShiftDown = m_bRShiftDown = m_bControlDown = m_bLControlDown = m_bRControlDown = m_bAltDown = false;
 }
 
 // Sends a key string
@@ -582,27 +582,25 @@ bool CSendKeys::SendKeys(LPCTSTR KeysString, bool Wait)
         }
 
         // A valid key to send?
-		if (MKey != INVALIDKEY)
-		{
-			if (MKey == VK_LCONTROL ||
-				MKey == VK_RCONTROL)
-			{
-				m_bLControlDown = (MKey == VK_LCONTROL);
-				m_bRControlDown = (MKey == VK_RCONTROL);
-				SendKeyDown(MKey, 1, false);
-			}
-			else if (MKey == VK_LSHIFT ||
-					 MKey == VK_RSHIFT)
-			{
-				m_bLShiftDown = (MKey == VK_LSHIFT);
-				m_bRShiftDown = (MKey == VK_RSHIFT);
-				SendKeyDown(MKey, 1, false);
-			}
-			else
-			{		
-				SendKey(MKey, NumTimes, true);	
-				PopUpShiftKeys();
-			}
+        if (MKey != INVALIDKEY)
+        {
+          if (MKey == VK_LCONTROL || MKey == VK_RCONTROL)
+          {
+            m_bLControlDown = (MKey == VK_LCONTROL);
+            m_bRControlDown = (MKey == VK_RCONTROL);
+            SendKeyDown(MKey, 1, false);
+          }
+          else if (MKey == VK_LSHIFT || MKey == VK_RSHIFT)
+          {
+            m_bLShiftDown = (MKey == VK_LSHIFT);
+            m_bRShiftDown = (MKey == VK_RSHIFT);
+            SendKeyDown(MKey, 1, false);
+          }
+          else
+          {
+            SendKey(MKey, NumTimes, true);	
+            PopUpShiftKeys();
+          }
         }
       }
       break;