Преглед изворни кода

- open external app to compare apps
- save clips to file

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@726 595ec19a-5cb4-439b-94a8-42fb3063c22c

sabrogden пре 11 година
родитељ
комит
bb795db9f2
16 измењених фајлова са 607 додато и 28 уклоњено
  1. 3 0
      ActionEnums.h
  2. 31 14
      CP_Main.rc
  3. 2 0
      CP_Main.vcxproj
  4. 18 4
      CP_Main.vcxproj.filters
  5. 93 4
      Clip.cpp
  6. 5 0
      Clip.h
  7. 158 0
      ClipCompare.cpp
  8. 16 0
      ClipCompare.h
  9. 1 0
      DittoSetup/BuildDitto.bld
  10. 10 0
      Options.cpp
  11. 3 0
      Options.h
  12. 37 0
      OptionsQuickPaste.cpp
  13. 2 0
      OptionsQuickPaste.h
  14. 205 3
      QPasteWnd.cpp
  15. 9 1
      QPasteWnd.h
  16. 14 2
      Resource.h

+ 3 - 0
ActionEnums.h

@@ -36,6 +36,9 @@ public:
 		MOVE_CLIP_TO_GROUP,
 		ELEVATE_PRIVlEGES,
 		SHOW_IN_TASKBAR,
+		COMPARE_SELECTED_CLIPS,
+		SELECT_LEFT_SIDE_COMPARE,
+		SELECT_RIGHT_SITE_AND_DO_COMPARE,
 	};
 };
 

+ 31 - 14
CP_Main.rc

@@ -281,6 +281,16 @@ BEGIN
         MENUITEM "View Full Description",       32793,MFT_STRING,MFS_ENABLED
         MENUITEM "View Groups",                 32819,MFT_STRING,MFS_ENABLED
         MENUITEM "Paste Plain Text Only",       32841,MFT_STRING,MFS_ENABLED
+        POPUP "Paste Special",                  65535,MFT_STRING,MFS_ENABLED
+        BEGIN
+            MENUITEM "Text as File Drop",           ID_PASTESPECIAL_TEXTASFILEDROP,MFT_STRING,MFS_ENABLED
+        END
+        POPUP "Compare",                        65535,MFT_STRING,MFS_ENABLED
+        BEGIN
+            MENUITEM "Select Left Text",            ID_COMPARE_SELECTLEFTCOMPARE,MFT_STRING,MFS_ENABLED
+            MENUITEM "Select Right Text And Compare Against Left", ID_COMPARE_COMPAREAGAINST,MFT_STRING,MFS_ENABLED
+            MENUITEM "Compare",                     ID_COMPARE_COMPARE,MFT_STRING,MFS_ENABLED
+        END
         MENUITEM MFT_SEPARATOR
         MENUITEM "Delete Entry",                32801,MFT_STRING,MFS_ENABLED
         MENUITEM MFT_SEPARATOR
@@ -302,9 +312,13 @@ BEGIN
             MENUITEM "Remove Sticky Setting",       ID_STICKYCLIPS_REMOVESTICKYSETTING,MFT_STRING,MFS_ENABLED
         END
         MENUITEM MFT_SEPARATOR
-        MENUITEM "Import Clip(s)",              32855,MFT_STRING,MFS_ENABLED
-        MENUITEM "Export Clip(s)",              32853,MFT_STRING,MFS_ENABLED
-        MENUITEM "View as QRCode",              ID_MENU_VIEWASQRCODE,MFT_STRING,MFS_ENABLED
+        POPUP "Import/Export",                  ID_MENU_EXPORT32888,MFT_STRING,MFS_ENABLED
+        BEGIN
+            MENUITEM "Import Clip(s)",              32855,MFT_STRING,MFS_ENABLED
+            MENUITEM "Export Clip(s)",              32853,MFT_STRING,MFS_ENABLED
+            MENUITEM "View as QRCode",              ID_MENU_VIEWASQRCODE,MFT_STRING,MFS_ENABLED
+            MENUITEM "Export Clip(s) to Text File", ID_EXPORT_EXPORTTOTEXTFILE,MFT_STRING,MFS_ENABLED
+        END
         MENUITEM MFT_SEPARATOR
         MENUITEM "Options...",                  32805,MFT_STRING,MFS_ENABLED
         MENUITEM MFT_SEPARATOR
@@ -400,7 +414,7 @@ BEGIN
     PUSHBUTTON      "&Delete",IDC_DELETE,147,30,50,14
 END
 
-IDD_OPTIONS_QUICK_PASTE DIALOGEX 0, 0, 367, 258
+IDD_OPTIONS_QUICK_PASTE DIALOGEX 0, 0, 367, 276
 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Quick Paste"
 FONT 8, "MS Shell Dlg", 0, 0, 0x1
@@ -427,21 +441,24 @@ BEGIN
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,108,335,10
     CONTROL         "Prompt when deleting clips",IDC_CHECK_PROMPT_DELETE_CLIP,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,118,335,10
-    COMBOBOX        IDC_COMBO_THEME,59,161,130,95,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
-    PUSHBUTTON      "About Theme",IDC_BUTTON_THEME,198,161,106,12
-    PUSHBUTTON      "Font",IDC_BUTTON_FONT,18,179,143,17
-    PUSHBUTTON      "Default Font",IDC_BUTTON_DEFAULT_FAULT,164,182,70,12
-    CONTROL         "At Caret",IDC_AT_CARET,"Button",BS_AUTORADIOBUTTON,22,214,93,10
-    CONTROL         "At Cursor",IDC_AT_CURSOR,"Button",BS_AUTORADIOBUTTON,22,224,97,10
-    CONTROL         "At Previous Position",IDC_AT_PREVIOUS,"Button",BS_AUTORADIOBUTTON,22,234,117,10
+    COMBOBOX        IDC_COMBO_THEME,59,180,130,95,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+    PUSHBUTTON      "About Theme",IDC_BUTTON_THEME,198,180,106,12
+    PUSHBUTTON      "Font",IDC_BUTTON_FONT,18,198,143,17
+    PUSHBUTTON      "Default Font",IDC_BUTTON_DEFAULT_FAULT,164,201,70,12
+    CONTROL         "At Caret",IDC_AT_CARET,"Button",BS_AUTORADIOBUTTON,22,233,93,10
+    CONTROL         "At Cursor",IDC_AT_CURSOR,"Button",BS_AUTORADIOBUTTON,22,243,97,10
+    CONTROL         "At Previous Position",IDC_AT_PREVIOUS,"Button",BS_AUTORADIOBUTTON,22,253,117,10
     LTEXT           "%",IDC_STATIC,199,55,8,12,SS_CENTERIMAGE
-    GROUPBOX        "Popup Positioning",IDC_STATIC_POPUP,10,205,138,41
-    LTEXT           "Theme",IDC_STATIC_THEME,18,161,36,12,SS_CENTERIMAGE
+    GROUPBOX        "Popup Positioning",IDC_STATIC_POPUP,10,224,138,41
+    LTEXT           "Theme",IDC_STATIC_THEME,18,180,36,12,SS_CENTERIMAGE
     CONTROL         "Always Show Scroll Bar",IDC_CHECK_SHOW_SCROLL_BAR,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,128,335,10
     CONTROL         "Elevated privileges to paste into elevated apps",IDC_CHECK_ELEVATE_PRIVILEGES,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,139,335,10
     CONTROL         "Show In Taskbar",IDC_CHECK_SHOW_IN_TASKBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,150,335,10
+    EDITTEXT        IDC_EDIT_DIFF_PATH,109,162,218,14,ES_AUTOHSCROLL
+    LTEXT           "Diff Application Path",IDC_STATIC_DIFF,25,164,80,8
+    PUSHBUTTON      "...",IDC_BUTTON_DIFF_BROWSE,332,162,17,14
 END
 
 IDD_OPTIONS_KEYSTROKES DIALOGEX 0, 0, 350, 206
@@ -877,7 +894,7 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 360
         TOPMARGIN, 7
-        BOTTOMMARGIN, 250
+        BOTTOMMARGIN, 268
     END
 
     IDD_OPTIONS_KEYSTROKES, DIALOG

+ 2 - 0
CP_Main.vcxproj

@@ -443,6 +443,7 @@
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
+    <ClCompile Include="ClipCompare.cpp" />
     <ClCompile Include="ClipFormatQListCtrl.cpp" />
     <ClCompile Include="Clip_ImportExport.cpp" />
     <ClCompile Include="ClipboardSaveRestore.cpp" />
@@ -1821,6 +1822,7 @@
     <ClInclude Include="ArrayEx.h" />
     <ClInclude Include="AutoSendToClientThread.h" />
     <ClInclude Include="CGdiPlusBitmap.h" />
+    <ClInclude Include="ClipCompare.h" />
     <ClInclude Include="ClipFormatQListCtrl.h" />
     <ClInclude Include="CreateQRCodeImage.h" />
     <ClInclude Include="DeleteClipData.h" />

+ 18 - 4
CP_Main.vcxproj.filters

@@ -373,11 +373,18 @@
     <ClCompile Include="DeleteClipData.cpp">
       <Filter>source</Filter>
     </ClCompile>
-    <ClCompile Include="QRCodeViewer.cpp" />
-    <ClCompile Include="CreateQRCodeImage.cpp" />
     <ClCompile Include="QRCode\rsecc.c">
       <Filter>QRCode</Filter>
     </ClCompile>
+    <ClCompile Include="ClipCompare.cpp">
+      <Filter>source</Filter>
+    </ClCompile>
+    <ClCompile Include="CreateQRCodeImage.cpp">
+      <Filter>source</Filter>
+    </ClCompile>
+    <ClCompile Include="QRCodeViewer.cpp">
+      <Filter>source</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="sqlite\CppSQLite3.h">
@@ -803,11 +810,18 @@
     <ClInclude Include="DeleteClipData.h">
       <Filter>header</Filter>
     </ClInclude>
-    <ClInclude Include="QRCodeViewer.h" />
-    <ClInclude Include="CreateQRCodeImage.h" />
     <ClInclude Include="QRCode\rsecc.h">
       <Filter>QRCode</Filter>
     </ClInclude>
+    <ClInclude Include="ClipCompare.h">
+      <Filter>header</Filter>
+    </ClInclude>
+    <ClInclude Include="CreateQRCodeImage.h">
+      <Filter>header</Filter>
+    </ClInclude>
+    <ClInclude Include="QRCodeViewer.h">
+      <Filter>header</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="CP_Main.rc">

+ 93 - 4
Clip.cpp

@@ -1041,16 +1041,22 @@ bool CClip::LoadFormats(int id, bool bOnlyLoad_CF_TEXT)
 		//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') AND ");
+		}
+
 		csSQL.Format(
-			_T("SELECT Data.lID, strClipBoardFormat, ooData FROM Data ")
-			_T("INNER JOIN Main ON Main.lID = Data.lParentID ")
-			_T("WHERE Main.lID = %d ORDER BY Data.lID desc"), id);
+			_T("SELECT 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_dbId = q.getIntField(_T("lID"));
+			cf.m_dbId = q.getIntField(_T("lParentID"));
 			cf.m_cfType = GetFormatID(q.getStringField(_T("strClipBoardFormat")));
 			
 			if(bOnlyLoad_CF_TEXT)
@@ -1140,6 +1146,89 @@ bool CClip::SaveFromEditWnd(BOOL bUpdateDesc)
 	return bRet;
 }
 
+CStringW CClip::GetUnicodeTextFormat()
+{
+	IClipFormat *pFormat = this->Clips()->FindFormatEx(CF_UNICODETEXT);
+	if(pFormat != NULL)
+	{
+		wchar_t *stringData = (wchar_t *)GlobalLock(pFormat->Data());
+		if(stringData != NULL)
+		{
+			CStringW string(stringData);
+
+			GlobalUnlock(pFormat->Data());
+
+			return string;
+		}
+	}
+
+	return _T("");
+}
+
+CStringA CClip::GetCFTextTextFormat()
+{
+	IClipFormat *pFormat = this->Clips()->FindFormatEx(CF_TEXT);
+	if(pFormat != NULL)
+	{
+		char *stringData = (char *)GlobalLock(pFormat->Data());
+		if(stringData != NULL)
+		{
+			CStringA string(stringData);
+			
+			GlobalUnlock(pFormat->Data());
+
+			return string;
+		}
+	}
+
+	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::ConvertToUTF8(w, convToUtf8);
+			byte header[2];
+			header[0] = 0xEF;
+			header[1] = 0xBB;
+			f.Write(&header, 2);
+			f.Write(convToUtf8.GetBuffer(), convToUtf8.GetLength());
+
+			ret = true;
+		}
+		else if(unicode && w != _T(""))
+		{
+			byte header[2];
+			header[0] = 0xFF;
+			header[1] = 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;
+}
+
 /*----------------------------------------------------------------------------*\
 CClipList
 \*----------------------------------------------------------------------------*/

+ 5 - 0
Clip.h

@@ -135,6 +135,11 @@ public:
 	DWORD GenerateCRC();
 	void MoveUp();
 
+	CStringW GetUnicodeTextFormat();
+	CStringA GetCFTextTextFormat();
+
+	BOOL WriteTextToFile(CString path, BOOL unicode, BOOL asci, BOOL utf8);
+
 	// Allocates a Global containing the requested Clip's Format Data
 	static HGLOBAL LoadFormat(int id, UINT cfType);
 	// Fills "formats" with the Data of all Formats in the db for the given Clip ID

+ 158 - 0
ClipCompare.cpp

@@ -0,0 +1,158 @@
+#include "stdafx.h"
+#include "ClipCompare.h"
+#include "Misc.h"
+#include "Options.h"
+
+CClipCompare::CClipCompare(void)
+{
+}
+
+
+CClipCompare::~CClipCompare(void)
+{
+}
+
+
+void CClipCompare::Compare(int leftId, int rightId)
+{
+	CClip leftClip;
+	if(leftClip.LoadFormats(leftId, true))
+	{
+		CClip rightClip;
+		if(rightClip.LoadFormats(rightId, true))
+		{
+			bool saveW = true;
+			bool saveA = true;
+			
+			if(leftClip.GetUnicodeTextFormat() == _T("") || rightClip.GetUnicodeTextFormat() == _T(""))
+			{
+				saveW = false;
+			}
+
+			if(leftClip.GetCFTextTextFormat() == "" || rightClip.GetCFTextTextFormat() == "")
+			{
+				saveA = false;
+			}
+
+			if(saveW || saveA)
+			{
+				CString leftFile = SaveToFile(leftId, &leftClip, saveW, saveA);
+				CString rightFile = SaveToFile(rightId, &rightClip, saveW, saveA);
+
+				CString path = GetComparePath();
+
+				if(path != _T(""))
+				{
+					SHELLEXECUTEINFO sei = { sizeof(sei) };
+					sei.lpFile = path;
+					CString csParam;
+					csParam.Format(_T("%s %s"), leftFile, rightFile);
+					sei.lpParameters = csParam;
+					sei.nShow = SW_NORMAL;
+
+					Log(StrF(_T("Comparing two clips, left Id %d, right Id %d, Path: %s %s"), leftId, rightId, path, csParam));
+
+					if (!ShellExecuteEx(&sei))
+					{
+					}
+				}
+				else
+				{
+					Log(StrF(_T("CClipCompare::Compare, No Valid compare apps, not doing compare")));
+
+					ShellExecute(NULL, _T("open"), _T("http://winmerge.org/"), NULL,NULL, SW_SHOW);
+				}
+			}
+			else	
+			{
+				Log(StrF(_T("CClipCompare::Compare, did not find valid text for both passed in clips")));
+			}
+		}
+		else
+		{
+			Log(StrF(_T("CClipCompare::Compare, Failed to load RIGHT clip formats Id: %d"), rightId));
+		}
+	}
+	else
+	{
+		Log(StrF(_T("CClipCompare::Compare, Failed to load LEFT clip formats Id: %d"), leftId));
+	}
+}
+
+CString CClipCompare::GetComparePath()
+{
+	CString path = CGetSetOptions::GetDiffApp();
+
+	if(path != _T(""))
+	{
+		return path;
+	}
+
+	path = _T("C:\\Program Files (x86)\\Beyond Compare 3\\BCompare.exe");
+	if(FileExists(path))
+	{
+		return path;
+	}
+
+	path = _T("C:\\Program Files\\Beyond Compare 3\\BCompare.exe");
+	if(FileExists(path))
+	{
+		return path;
+	}
+
+	path = _T("C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe");
+	if(FileExists(path))
+	{
+		return path;
+	}
+
+	path = _T("C:\\Program Files (x86)\\Araxis\\Araxis Merge\\compare.exe");
+	if(FileExists(path))
+	{
+		return path;
+	}
+
+	path = _T("C:\\Program Files\\Araxis\\Araxis Merge\\compare.exe");
+	if(FileExists(path))
+	{
+		return path;
+	}
+
+	return _T("");
+}
+
+CString CClipCompare::SaveToFile(int id, CClip *pClip, bool saveW, bool SaveA)
+{
+	CString path;
+	wchar_t wchPath[MAX_PATH];
+	if (GetTempPathW(MAX_PATH, wchPath))
+	{
+		CString cs;
+		cs.Format(_T("%sditto_compare_%d.txt"), wchPath, id);
+
+		if(FileExists(cs))
+		{
+			for(int i = 0; i < 1000; i++)
+			{			
+				cs.Format(_T("%sditto_compare_%d.txt"), wchPath, id);
+				if(FileExists(cs))
+				{
+					path = cs;
+					break;
+				}
+			}
+		}
+		else
+		{
+			path = cs;
+		}
+
+		if(path != _T("") && 
+			pClip != NULL)
+		{
+			pClip->WriteTextToFile(path, saveW, SaveA, false);
+		}
+	}
+
+	return path;
+}

+ 16 - 0
ClipCompare.h

@@ -0,0 +1,16 @@
+#pragma once
+#include "Clip.h"
+
+class CClipCompare
+{
+public:
+	CClipCompare(void);
+	~CClipCompare(void);
+
+	void Compare(int leftId, int rightId);
+
+protected:
+	CString SaveToFile(int id, CClip *clip, bool saveW, bool SaveA);
+	CString GetComparePath();
+};
+

+ 1 - 0
DittoSetup/BuildDitto.bld

@@ -4,6 +4,7 @@
 		<step action='Set Macro'>
 			<MacroName>LOGFILE</MacroName>
 			<MacroValue>%workDir%\%branch%\Log.txt</MacroValue>
+			<checked type='11'>0</checked>
 			<name>Default Log File</name>
 		</step>
 		<step action='Read INI'>

+ 10 - 0
Options.cpp

@@ -2142,4 +2142,14 @@ void CGetSetOptions::SetShowInTaskBar(BOOL val)
 BOOL CGetSetOptions::GetShowInTaskBar()
 {
 	return GetProfileLong(_T("ShowInTaskBar"), FALSE);
+}
+
+void CGetSetOptions::SetDiffApp(CString val)
+{
+	SetProfileString(_T("DiffApp"), val);
+}
+
+CString	CGetSetOptions::GetDiffApp()
+{
+	return GetProfileString(_T("DiffApp"), _T(""));
 }

+ 3 - 0
Options.h

@@ -457,6 +457,9 @@ public:
 
 	static void		SetShowInTaskBar(BOOL val);
 	static BOOL		GetShowInTaskBar();
+
+	static void		SetDiffApp(CString val);
+	static CString	GetDiffApp();
 };
 
 // global for easy access and for initialization of fast access variables

+ 37 - 0
OptionsQuickPaste.cpp

@@ -60,6 +60,7 @@ void COptionsQuickPaste::DoDataExchange(CDataExchange* pDX)
 	DDX_Control(pDX, IDC_CHECK_SHOW_SCROLL_BAR, m_alwaysShowScrollBar);
 	DDX_Control(pDX, IDC_CHECK_ELEVATE_PRIVILEGES, m_elevatedPrivileges);
 	DDX_Control(pDX, IDC_CHECK_SHOW_IN_TASKBAR, m_showInTaskBar);
+	DDX_Control(pDX, IDC_EDIT_DIFF_PATH, m_diffPathEditBox);
 }
 
 
@@ -69,6 +70,7 @@ BEGIN_MESSAGE_MAP(COptionsQuickPaste, CPropertyPage)
 	ON_BN_CLICKED(IDC_BUTTON_DEFAULT_FAULT, OnButtonDefaultFault)
 	//}}AFX_MSG_MAP
 	ON_BN_CLICKED(IDC_BUTTON_THEME, OnBnClickedButtonTheme)
+	ON_BN_CLICKED(IDC_BUTTON_DIFF_BROWSE, &COptionsQuickPaste::OnBnClickedButtonDiffBrowse)
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
@@ -117,10 +119,14 @@ BOOL COptionsQuickPaste::OnInitDialog()
 		ft->GetLogFont(&m_LogFont);
 	}
 
+	m_diffPathEditBox.SetWindowText(CGetSetOptions::GetDiffApp());
+
 	CString cs;
 	cs.Format(_T("Font - %s"), m_LogFont.lfFaceName);
 	m_btFont.SetWindowText(cs);
 
+	
+
 	FillThemes();
 
 	theApp.m_Language.UpdateOptionQuickPaste(this);
@@ -178,6 +184,10 @@ BOOL COptionsQuickPaste::OnApply()
 	{
 		g_Opt.SetTheme("");
 	}
+
+	CString diffPath;
+	m_diffPathEditBox.GetWindowText(diffPath);
+	g_Opt.SetDiffApp(diffPath);
 	
 	return CPropertyPage::OnApply();
 }
@@ -285,3 +295,30 @@ void COptionsQuickPaste::OnBnClickedButtonTheme()
 		MessageBox(csError, _T("Ditto"), MB_OK);
 	}
 }
+
+
+void COptionsQuickPaste::OnBnClickedButtonDiffBrowse()
+{
+	OPENFILENAME	FileName;
+	TCHAR			szFileName[400];
+	TCHAR			szDir[400];
+
+	memset(&FileName, 0, sizeof(FileName));
+	memset(szFileName, 0, sizeof(szFileName));
+	memset(&szDir, 0, sizeof(szDir));
+	FileName.lStructSize = sizeof(FileName);
+	FileName.lpstrTitle = _T("Diff Application");
+	FileName.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST;
+	FileName.nMaxFile = 400;
+	FileName.lpstrFile = szFileName;
+	FileName.lpstrInitialDir = szDir;
+	FileName.lpstrFilter = _T("*.exe");
+	FileName.lpstrDefExt = _T("");
+
+	if(GetOpenFileName(&FileName) == 0)
+		return;
+
+	CString csPath(FileName.lpstrFile);
+
+	m_diffPathEditBox.SetWindowText(csPath);
+}

+ 2 - 0
OptionsQuickPaste.h

@@ -76,6 +76,8 @@ public:
 	CComboBox m_cbTheme;
 	afx_msg void OnBnClickedButtonTheme();
 	CButton m_alwaysShowScrollBar;
+	CEdit m_diffPathEditBox;
+	afx_msg void OnBnClickedButtonDiffBrowse();
 };
 
 //{{AFX_INSERT_LOCATION}}

+ 205 - 3
QPasteWnd.cpp

@@ -17,6 +17,7 @@
 #include <algorithm>
 #include "QRCodeViewer.h"
 #include "CreateQRCodeImage.h"
+#include "ClipCompare.h"
 //#include "MyDropTarget.h"
 
 #ifdef _DEBUG
@@ -63,6 +64,7 @@ CQPasteWnd::CQPasteWnd()
     m_bHandleSearchTextChange = true;
     m_bModifersMoveActive = false;
 	m_showScrollBars = false;
+	m_leftSelectedCompareId = 0;
 }
 
 CQPasteWnd::~CQPasteWnd()
@@ -210,6 +212,10 @@ ON_COMMAND(ID_MENU_CONTAINSTEXTSEARCHONLY, OnMenuSimpleTextSearch)
 //ON_WM_PAINT()
 ON_COMMAND(ID_QUICKOPTIONS_SHOWINTASKBAR, &CQPasteWnd::OnQuickoptionsShowintaskbar)
 ON_COMMAND(ID_MENU_VIEWASQRCODE, &CQPasteWnd::OnMenuViewasqrcode)
+ON_COMMAND(ID_EXPORT_EXPORTTOTEXTFILE, &CQPasteWnd::OnExportExporttotextfile)
+ON_COMMAND(ID_COMPARE_COMPARE, &CQPasteWnd::OnCompareCompare)
+ON_COMMAND(ID_COMPARE_SELECTLEFTCOMPARE, &CQPasteWnd::OnCompareSelectleftcompare)
+ON_COMMAND(ID_COMPARE_COMPAREAGAINST, &CQPasteWnd::OnCompareCompareagainst)
 END_MESSAGE_MAP()
 
 
@@ -2693,6 +2699,15 @@ bool CQPasteWnd::DoAction(DWORD actionId)
 	case ActionEnums::SHOW_IN_TASKBAR:
 		ret = DoShowInTaskBar();
 		break;
+	case ActionEnums::COMPARE_SELECTED_CLIPS:
+		ret = DoClipCompare();
+		break;
+	case ActionEnums::SELECT_LEFT_SIDE_COMPARE:
+		ret = DoSelectLeftSideCompare();
+		break;
+	case ActionEnums::SELECT_RIGHT_SITE_AND_DO_COMPARE:
+		ret = DoSelectRightSideAndDoCompare();
+		break;
 	}
 
 	return ret;
@@ -3200,6 +3215,83 @@ bool CQPasteWnd::DoShowInTaskBar()
 	return true;
 }
 
+bool CQPasteWnd::DoClipCompare()
+{
+	if(::GetFocus() == m_lstHeader.GetSafeHwnd())
+	{
+		ARRAY IDs;
+		m_lstHeader.GetSelectionItemData(IDs);
+
+		if(IDs.GetCount() > 1)
+		{
+			CClipCompare compare;
+			compare.Compare(IDs[0], IDs[1]);
+
+			return true;
+		}
+		else
+		{
+			Log(StrF(_T("DoClipCompare, at least 2 clips need to be selected, count: %d"), IDs.GetCount()));
+		}
+	}
+
+	return false;
+}
+
+bool CQPasteWnd::DoSelectLeftSideCompare()
+{
+	if(::GetFocus() == m_lstHeader.GetSafeHwnd())
+	{
+		ARRAY IDs;
+		m_lstHeader.GetSelectionItemData(IDs);
+
+		if(IDs.GetCount() > 0)
+		{
+			m_leftSelectedCompareId = IDs[0];
+
+			return true;
+		}
+		else
+		{
+			Log(StrF(_T("DoSelectLeftSideCompare, no selected clip, not assigning left side")));
+		}
+	}
+
+	return false;
+}
+
+bool CQPasteWnd::DoSelectRightSideAndDoCompare()
+{
+	if(::GetFocus() == m_lstHeader.GetSafeHwnd())
+	{
+		if(m_leftSelectedCompareId > 0)
+		{
+			ARRAY IDs;
+			m_lstHeader.GetSelectionItemData(IDs);
+
+			if(IDs.GetCount() > 0)
+			{
+				int rightId = IDs[0];
+
+				CClipCompare compare;
+				compare.Compare(m_leftSelectedCompareId, rightId);
+
+				return true;
+			}
+			else
+			{
+				Log(StrF(_T("DoSelectRightSideAndDoCompare, no selected clips")));
+			}
+		}
+		else
+		{
+			Log(StrF(_T("DoSelectRightSideAndDoCompare, no left side selected, select left side first")));
+		}
+	}
+
+	return false;
+}
+
 LRESULT CQPasteWnd::OnCancelFilter(WPARAM wParam, LPARAM lParam)
 {
 	this->DoAction(ActionEnums::CANCELFILTER);
@@ -4304,14 +4396,14 @@ void CQPasteWnd::OnMenuViewasqrcode()
 					{
 						CStringW string(stringData);
 
+						GlobalUnlock(pFormat->Data());
+
 						CCreateQRCodeImage p;
 						int imageSize = 0;
 						unsigned char* bitmapData = p.CreateImage(string, imageSize);
 
 						if(bitmapData != NULL)
-						{						
-							GlobalUnlock(pFormat->Data());
-
+						{
 							QRCodeViewer *viewer = new QRCodeViewer();
 
 							LOGFONT lf;
@@ -4326,3 +4418,113 @@ void CQPasteWnd::OnMenuViewasqrcode()
 		}
 	}	
 }
+
+
+void CQPasteWnd::OnExportExporttotextfile()
+{
+	CClipIDs IDs;
+	INT_PTR lCount = m_lstHeader.GetSelectedCount();
+	if(lCount <= 0)
+	{
+		return ;
+	}
+
+	m_lstHeader.GetSelectionItemData(IDs);
+	lCount = IDs.GetSize();
+	if(lCount <= 0)
+	{
+		return ;
+	}
+
+	OPENFILENAME ofn;
+	TCHAR szFile[400];
+	TCHAR szDir[400];
+
+	memset(&szFile, 0, sizeof(szFile));
+	memset(szDir, 0, sizeof(szDir));
+	memset(&ofn, 0, sizeof(ofn));
+
+	CString csInitialDir = CGetSetOptions::GetLastImportDir();
+	STRCPY(szDir, csInitialDir);
+
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = m_hWnd;
+	ofn.lpstrFile = szFile;
+	ofn.nMaxFile = sizeof(szFile);
+	ofn.lpstrFilter = _T("Exported Ditto Clips (.txt)\0*.txt\0\0");
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFileTitle = NULL;
+	ofn.nMaxFileTitle = 0;
+	ofn.lpstrInitialDir = szDir;
+	ofn.lpstrDefExt = _T("txt");
+	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT;
+
+	m_bHideWnd = false;
+
+	if(GetSaveFileName(&ofn))
+	{
+		using namespace nsPath;
+		CString startingFilePath = ofn.lpstrFile;
+		CPath path(ofn.lpstrFile);
+		CString csPath = path.GetPath();
+		CString csExt = path.GetExtension();
+		path.RemoveExtension();
+		CString csFileName = path.GetName();
+		
+		CGetSetOptions::SetLastExportDir(csPath);
+		
+		for(int i = 0; i < IDs.GetCount(); i++)
+		{
+			int id = IDs[i];
+
+			CClip clip;
+			if(clip.LoadFormats(id, true))
+			{	
+				CString savePath = startingFilePath;
+				if(IDs.GetCount() > 1 ||
+					FileExists(startingFilePath))
+				{
+					savePath = _T("");
+
+					for(int y = 1; y < 1001; y++)
+					{			
+						CString testFilePath;
+						testFilePath.Format(_T("%s%s_%d.%s"), csPath, csFileName, y, csExt);
+						if(FileExists(testFilePath) == FALSE)
+						{				
+							savePath = testFilePath;
+							break;
+						}
+					}
+				}
+				
+				if(savePath != _T(""))
+				{
+					clip.WriteTextToFile(savePath, true, true, false);
+				}
+				else
+				{
+					Log(StrF(_T("Failed to find a valid file name for starting path: %s"), startingFilePath));
+				}
+			}
+		}		
+	}
+
+	m_bHideWnd = true;
+}
+
+
+void CQPasteWnd::OnCompareCompare()
+{
+	DoAction(ActionEnums::COMPARE_SELECTED_CLIPS);
+}
+
+void CQPasteWnd::OnCompareSelectleftcompare()
+{
+	DoAction(ActionEnums::SELECT_LEFT_SIDE_COMPARE);
+}
+
+void CQPasteWnd::OnCompareCompareagainst()
+{
+	DoAction(ActionEnums::SELECT_RIGHT_SITE_AND_DO_COMPARE);
+}

+ 9 - 1
QPasteWnd.h

@@ -158,6 +158,7 @@ public:
     CCriticalSection m_CritSection;
     CAccels m_actions;
 	bool m_showScrollBars;
+	int m_leftSelectedCompareId;
 
     void RefreshNc();
     void UpdateStatus(bool bRepaintImmediately = false); // regenerates the status (caption) text
@@ -235,7 +236,10 @@ public:
 	bool DoActionPasteSelectedPlainText();
 	bool DoActionMoveClipToGroup();
 	bool DoActionElevatePrivleges();
-	bool DoShowInTaskBar();
+	bool DoShowInTaskBar();	
+	bool DoClipCompare();
+	bool DoSelectLeftSideCompare();
+	bool DoSelectRightSideAndDoCompare();
 
     // Generated message map functions
 protected:
@@ -378,4 +382,8 @@ protected:
 public:
 	afx_msg void OnQuickoptionsShowintaskbar();
 	afx_msg void OnMenuViewasqrcode();
+	afx_msg void OnExportExporttotextfile();
+	afx_msg void OnCompareCompare();
+	afx_msg void OnCompareSelectleftcompare();
+	afx_msg void OnCompareCompareagainst();
 };

+ 14 - 2
Resource.h

@@ -365,6 +365,10 @@
 #define IDC_BUTTON_APPLY                2118
 #define IDC_STATIC_GROUP_SEARCH         2120
 #define IDC_MFCPROPERTYGRID1            2121
+#define IDC_STATIC_DIFF                 2122
+#define IDC_EDIT_DIFF_PATH              2123
+#define IDC_BUTTON2                     2124
+#define IDC_BUTTON_DIFF_BROWSE          2124
 #define ID_FIRST_OPTION                 32771
 #define ID_FIRST_EXIT                   32772
 #define ID_FIRST_SHOWQUICKPASTE         32773
@@ -473,6 +477,14 @@
 #define ID_QUICKOPTIONS_SHOWINTASKBAR   32885
 #define ID_FIRST_DELETECLIPDATA         32886
 #define ID_MENU_VIEWASQRCODE            32887
+#define ID_MENU_EXPORT32888             32888
+#define ID_EXPORT_EXPORTTOTEXTFILE      32889
+#define ID_MENU_PASTESPECIAL            32890
+#define ID_PASTESPECIAL_TEXTASFILEDROP  32891
+#define ID_MENU_COMPARE                 32892
+#define ID_COMPARE_SELECTLEFTCOMPARE    32893
+#define ID_COMPARE_COMPAREAGAINST       32894
+#define ID_COMPARE_COMPARE              32895
 
 // Next default values for new objects
 // 
@@ -480,8 +492,8 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_3D_CONTROLS                     1
 #define _APS_NEXT_RESOURCE_VALUE        239
-#define _APS_NEXT_COMMAND_VALUE         32888
-#define _APS_NEXT_CONTROL_VALUE         2122
+#define _APS_NEXT_COMMAND_VALUE         32896
+#define _APS_NEXT_CONTROL_VALUE         2125
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif