فهرست منبع

Bring back the internal editor for clips

Scott Brogden 1 ماه پیش
والد
کامیت
839b41f3e3
41فایلهای تغییر یافته به همراه8789 افزوده شده و 3 حذف شده
  1. 6 0
      .gitignore
  2. 41 0
      CP_Main.vcxproj
  3. 99 0
      CP_Main.vcxproj.filters
  4. 32 0
      res/CP_Main.rc2
  5. 11 3
      src/CP_Main.cpp
  6. 27 0
      src/Clip.cpp
  7. 1 0
      src/Clip.h
  8. 304 0
      src/DittoRulerRichEditCtrl.cpp
  9. 29 0
      src/DittoRulerRichEditCtrl.h
  10. 290 0
      src/EditFrameWnd.cpp
  11. 57 0
      src/EditFrameWnd.h
  12. 422 0
      src/EditWnd.cpp
  13. 54 0
      src/MainFrm.cpp
  14. 3 0
      src/MainFrm.h
  15. 39 0
      src/Options.cpp
  16. 6 0
      src/Options.h
  17. 319 0
      src/RulerRichEditCtrl/External/ColourPicker.cpp
  18. 114 0
      src/RulerRichEditCtrl/External/ColourPicker.h
  19. 913 0
      src/RulerRichEditCtrl/External/ColourPopup.cpp
  20. 128 0
      src/RulerRichEditCtrl/External/ColourPopup.h
  21. 436 0
      src/RulerRichEditCtrl/External/StdioFileEx.cpp
  22. 103 0
      src/RulerRichEditCtrl/External/StdioFileEx.h
  23. 214 0
      src/RulerRichEditCtrl/FontComboBox.cpp
  24. 57 0
      src/RulerRichEditCtrl/FontComboBox.h
  25. 451 0
      src/RulerRichEditCtrl/RRECRuler.cpp
  26. 39 0
      src/RulerRichEditCtrl/RRECRuler.h
  27. 393 0
      src/RulerRichEditCtrl/RRECToolbar.cpp
  28. 79 0
      src/RulerRichEditCtrl/RRECToolbar.h
  29. 202 0
      src/RulerRichEditCtrl/RulerRichEdit.cpp
  30. 36 0
      src/RulerRichEditCtrl/RulerRichEdit.h
  31. 2190 0
      src/RulerRichEditCtrl/RulerRichEditCtrl.cpp
  32. 174 0
      src/RulerRichEditCtrl/RulerRichEditCtrl.h
  33. 40 0
      src/RulerRichEditCtrl/RulerRichEditCtrl.rc
  34. 137 0
      src/RulerRichEditCtrl/SizeComboBox.cpp
  35. 50 0
      src/RulerRichEditCtrl/SizeComboBox.h
  36. 406 0
      src/RulerRichEditCtrl/StdGrfx.cpp
  37. 41 0
      src/RulerRichEditCtrl/StdGrfx.h
  38. 737 0
      src/RulerRichEditCtrl/TextFile/TextFile.cpp
  39. 50 0
      src/RulerRichEditCtrl/TextFile/TextFile.h
  40. 59 0
      src/RulerRichEditCtrl/ids.h
  41. BIN
      src/RulerRichEditCtrl/toolbar.bmp

+ 6 - 0
.gitignore

@@ -60,3 +60,9 @@ Debug/portable
 *.exp
 *.map
 *.tlh
+/Addins/DittoUtil/Win32/Debug
+/Debug/EditClips
+/EncryptDecrypt/Win32/Debug
+/focusdll/Win32/Debug
+/ICU_Loader/Debug
+/Win32/Debug

+ 41 - 0
CP_Main.vcxproj

@@ -565,6 +565,7 @@
     <ClCompile Include="src\DittoCopyBuffer.cpp" />
     <ClCompile Include="src\DittoDropTarget.cpp" />
     <ClCompile Include="src\DittoPopupWindow.cpp" />
+    <ClCompile Include="src\DittoRulerRichEditCtrl.cpp" />
     <ClCompile Include="src\DittoWindow.cpp" />
     <ClCompile Include="src\DPI.cpp" />
     <ClCompile Include="src\DrawHTML.C">
@@ -573,7 +574,9 @@
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
     </ClCompile>
+    <ClCompile Include="src\EditFrameWnd.cpp" />
     <ClCompile Include="src\EditWithButton.cpp" />
+    <ClCompile Include="src\EditWnd.cpp" />
     <ClCompile Include="src\EventThread.cpp" />
     <ClCompile Include="src\ExternalWindowTracker.cpp" />
     <ClCompile Include="src\FileRecieve.cpp" />
@@ -706,6 +709,17 @@
     <ClCompile Include="src\RegExFilterHelper.cpp" />
     <ClCompile Include="src\RichEditCtrlEx.cpp" />
     <ClCompile Include="src\RichTextAggregator.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\External\ColourPicker.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\External\ColourPopup.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\External\StdioFileEx.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\FontComboBox.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\RRECRuler.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\RRECToolbar.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\RulerRichEdit.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\RulerRichEditCtrl.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\SizeComboBox.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\StdGrfx.cpp" />
+    <ClCompile Include="src\RulerRichEditCtrl\TextFile\TextFile.cpp" />
     <ClCompile Include="src\SaveAnimation.cpp" />
     <ClCompile Include="src\ScriptEditor.cpp" />
     <ClCompile Include="src\ScrollHelper.cpp" />
@@ -776,6 +790,18 @@
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;_AFXDLL</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NDEBUG;_AFXDLL</PreprocessorDefinitions>
     </ResourceCompile>
+    <ResourceCompile Include="src\RulerRichEditCtrl\RulerRichEditCtrl.rc" >
+	  <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
+	</ResourceCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="resource.h" />
@@ -821,9 +847,11 @@
     <ClInclude Include="src\DittoCopyBuffer.h" />
     <ClInclude Include="src\DittoDropTarget.h" />
     <ClInclude Include="src\DittoPopupWindow.h" />
+    <ClInclude Include="src\DittoRulerRichEditCtrl.h" />
     <ClInclude Include="src\DittoWindow.h" />
     <ClInclude Include="src\DPI.h" />
     <ClInclude Include="src\DrawHTML.h" />
+    <ClInclude Include="src\EditFrameWnd.h" />
     <ClInclude Include="src\EditWithButton.h" />
     <ClInclude Include="src\EditWnd.h" />
     <ClInclude Include="src\EventThread.h" />
@@ -906,6 +934,18 @@
     <ClInclude Include="src\RegExFilterHelper.h" />
     <ClInclude Include="src\RichEditCtrlEx.h" />
     <ClInclude Include="src\RichTextAggregator.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\External\ColourPicker.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\External\ColourPopup.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\External\StdioFileEx.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\FontComboBox.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\ids.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\RRECRuler.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\RRECToolbar.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\RulerRichEdit.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\RulerRichEditCtrl.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\SizeComboBox.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\StdGrfx.h" />
+    <ClInclude Include="src\RulerRichEditCtrl\TextFile\TextFile.h" />
     <ClInclude Include="src\SaveAnimation.h" />
     <ClInclude Include="src\ScriptEditor.h" />
     <ClInclude Include="src\ScrollHelper.h" />
@@ -1205,6 +1245,7 @@
     <Image Include="res\system_menu_72.png" />
     <Image Include="res\system_menu_78.png" />
     <Image Include="res\system_menu_84.png" />
+    <Image Include="src\RulerRichEditCtrl\toolbar.bmp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 99 - 0
CP_Main.vcxproj.filters

@@ -451,6 +451,48 @@
     <ClCompile Include="src\QRCode\split.c">
       <Filter>src\QRCode</Filter>
     </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\FontComboBox.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\RRECRuler.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\RRECToolbar.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\RulerRichEdit.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\RulerRichEditCtrl.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\SizeComboBox.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\StdGrfx.cpp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\TextFile\TextFile.cpp">
+      <Filter>src\RulerRichEditCtrl\TextFile</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\External\ColourPicker.cpp">
+      <Filter>src\RulerRichEditCtrl\External</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\External\ColourPopup.cpp">
+      <Filter>src\RulerRichEditCtrl\External</Filter>
+    </ClCompile>
+    <ClCompile Include="src\RulerRichEditCtrl\External\StdioFileEx.cpp">
+      <Filter>src\RulerRichEditCtrl\External</Filter>
+    </ClCompile>
+    <ClCompile Include="src\DittoRulerRichEditCtrl.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\EditFrameWnd.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\EditWnd.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\About.h">
@@ -928,11 +970,56 @@
     <ClInclude Include="src\QRCode\split.h">
       <Filter>src\QRCode</Filter>
     </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\FontComboBox.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\ids.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\RRECRuler.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\RRECToolbar.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\RulerRichEdit.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\RulerRichEditCtrl.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\SizeComboBox.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\StdGrfx.h">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\TextFile\TextFile.h">
+      <Filter>src\RulerRichEditCtrl\TextFile</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\External\ColourPicker.h">
+      <Filter>src\RulerRichEditCtrl\External</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\External\ColourPopup.h">
+      <Filter>src\RulerRichEditCtrl\External</Filter>
+    </ClInclude>
+    <ClInclude Include="src\RulerRichEditCtrl\External\StdioFileEx.h">
+      <Filter>src\RulerRichEditCtrl\External</Filter>
+    </ClInclude>
+    <ClInclude Include="src\DittoRulerRichEditCtrl.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\EditFrameWnd.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="CP_Main.rc">
       <Filter>res</Filter>
     </ResourceCompile>
+    <ResourceCompile Include="src\RulerRichEditCtrl\RulerRichEditCtrl.rc">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </ResourceCompile>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="RulerRichEditCtrl\toolbar.bmp">
@@ -1415,6 +1502,15 @@
     <Filter Include="src\QRCode">
       <UniqueIdentifier>{4651147a-f9c4-41fe-a076-10be8ad8cfbb}</UniqueIdentifier>
     </Filter>
+    <Filter Include="src\RulerRichEditCtrl">
+      <UniqueIdentifier>{f864d5e8-8cca-488e-b506-d8177e85262d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\RulerRichEditCtrl\TextFile">
+      <UniqueIdentifier>{78b7e533-7922-4cc1-82a6-0d14c3b82eba}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\RulerRichEditCtrl\External">
+      <UniqueIdentifier>{f6e3888b-0fa5-4ef0-88a0-b280da7bc15a}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <Midl Include="CP_Main.idl" />
@@ -1582,5 +1678,8 @@
     <Image Include="res\Ditto2_NoDb.ico">
       <Filter>res</Filter>
     </Image>
+    <Image Include="src\RulerRichEditCtrl\toolbar.bmp">
+      <Filter>src\RulerRichEditCtrl</Filter>
+    </Image>
   </ItemGroup>
 </Project>

+ 32 - 0
res/CP_Main.rc2

@@ -11,3 +11,35 @@
 // Add manually edited resources here...
 
 /////////////////////////////////////////////////////////////////////////////
+
+#include "..\src\RulerRichEditCtrl/RulerRichEditCtrl.rc"
+
+/*
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar resources
+//
+
+#include "RulerRichEditCtrl/ids.h"
+
+TOOLBAR_CONTROL         BITMAP  DISCARDABLE     "RulerRichEditCtrl/toolbar.bmp"
+
+TOOLBAR_CONTROL TOOLBAR DISCARDABLE  16, 15
+BEGIN
+    BUTTON      BUTTON_1
+    BUTTON      BUTTON_2
+    SEPARATOR
+    BUTTON      BUTTON_3
+    BUTTON      BUTTON_4
+    BUTTON      BUTTON_5
+    SEPARATOR
+    BUTTON      BUTTON_6
+    BUTTON      BUTTON_7
+    BUTTON      BUTTON_8
+    SEPARATOR
+    BUTTON      BUTTON_9
+    BUTTON      BUTTON_10
+    BUTTON      BUTTON_11
+END
+
+*/

+ 11 - 3
src/CP_Main.cpp

@@ -1092,8 +1092,6 @@ BOOL CCP_MainApp::GetClipData(long parentId, CClipFormat &Clip)
 	return bRet;
 }
 
-
-
 bool CCP_MainApp::EditItems(CClipIDs &Ids, bool bShowError, bool forceTextEdit)
 {
 	bool ret = false;	
@@ -1158,6 +1156,16 @@ bool CCP_MainApp::EditItems(CClipIDs &Ids, bool bShowError, bool forceTextEdit)
 			continue;
 		}
 
+		if((unicodeFile || asciFile || rtfFile) && exePath == _T(""))
+		{
+			Log(StrF(_T("Clip id %d is a text or rtf file without a specific editor set, using internal editor"), Ids[i]));
+
+			CClipIDs id;
+			id.Add(Ids[i]);
+			m_pMainFrame->ShowEditWnd(id);
+			continue;
+		}
+
 		CString startingFilePath = StrF(_T("%sEditClip_%d.%s"), CGetSetOptions::GetPath(PATH_EDIT_CLIPS), id, extension);
 
 		if (id == -1)
@@ -1237,7 +1245,7 @@ bool CCP_MainApp::EditItems(CClipIDs &Ids, bool bShowError, bool forceTextEdit)
 		ret = true;
 	}
 
-	return true;
+	return ret;
 }
 
 void CCP_MainApp::PumpMessageEx(HWND hWnd)

+ 27 - 0
src/Clip.cpp

@@ -2051,6 +2051,33 @@ Gdiplus::Bitmap *CClip::CreateGdiplusBitmap()
 	return nullptr;
 }
 
+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;
+}
+
 /*----------------------------------------------------------------------------*\
 CClipList
 \*----------------------------------------------------------------------------*/

+ 1 - 0
src/Clip.h

@@ -186,6 +186,7 @@ public:
 	DWORD GenerateCRC();
 	void MoveUp(int parentId);
 	void MoveDown(int parentId);
+	bool SaveFromEditWnd(BOOL bUpdateDesc);
 
 	CStringW GetUnicodeTextFormat();
 	CStringA GetCFTextTextFormat();

+ 304 - 0
src/DittoRulerRichEditCtrl.cpp

@@ -0,0 +1,304 @@
+#include "stdafx.h"
+#include "clip.h"
+#include "CP_Main.h"
+#include "..\shared\TextConvert.h"
+#include ".\dittorulerricheditctrl.h"
+#include "CopyProperties.h"
+
+CDittoRulerRichEditCtrl::CDittoRulerRichEditCtrl(void)
+{	
+	m_lID = -1;
+}
+
+CDittoRulerRichEditCtrl::~CDittoRulerRichEditCtrl(void)
+{
+}
+
+bool CDittoRulerRichEditCtrl::LoadItem(long lID, CString csDesc)
+{	
+	bool bSetText = false;
+	CClipFormat Clip;
+	m_lID = lID;
+	m_csDescription = csDesc;
+	
+	//If creating a new clip
+	if(m_lID < 0)
+	{
+		m_rtf.SetModify(FALSE);
+		return false;
+	}
+
+	Clip.m_cfType = RegisterClipboardFormat(CF_RTF);
+	if(theApp.GetClipData(lID, Clip) && Clip.m_hgData)
+	{
+		CString cs(Clip.GetAsCStringA());
+		SetRTF(cs);
+		bSetText = true;		
+
+		Clip.Free();
+		Clip.Clear();
+	}
+
+	if(bSetText == false)
+	{
+		Clip.m_cfType = CF_UNICODETEXT;
+		if(theApp.GetClipData(lID, Clip) && Clip.m_hgData)
+		{
+			SetText(Clip.GetAsCString());
+			bSetText = true;		
+
+			Clip.Free();
+			Clip.Clear();
+		}
+	}
+
+	if(bSetText == false)
+	{
+		Clip.m_cfType = CF_TEXT;
+		if(theApp.GetClipData(lID, Clip) && Clip.m_hgData)
+		{
+			CString csText(Clip.GetAsCStringA());
+			SetText(csText);
+
+			bSetText = true;
+		
+			Clip.Free();
+			Clip.Clear();
+		}
+	}
+
+	m_rtf.SetModify(FALSE);
+
+	return bSetText;
+}
+
+long CDittoRulerRichEditCtrl::GetTypeFlags(long lID)
+{
+	long lRet = stNONE;
+
+	try
+	{
+		CLIPFORMAT cfType = CF_TEXT;
+		CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Data WHERE lParentID = %d AND strClipboardFormat = '%s'"), lID, GetFormatName(cfType));
+		if(q.eof() == false)
+		{
+			lRet |= stCF_TEXT;
+		}
+
+		cfType = CF_UNICODETEXT;
+		q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Data WHERE lParentID = %d AND strClipboardFormat = '%s'"), lID, GetFormatName(cfType));
+		if(q.eof() == false)
+		{
+			lRet |= stCF_UNICODETEXT;
+		}
+
+		cfType = RegisterClipboardFormat(_T("Rich Text Format"));
+		q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Data WHERE lParentID = %d AND strClipboardFormat = '%s'"), lID, GetFormatName(cfType));
+		if(q.eof() == false)
+		{
+			lRet |= stRTF;
+		}
+	}
+	CATCH_SQLITE_EXCEPTION
+
+	return lRet;
+}
+
+void CDittoRulerRichEditCtrl::d()
+{
+	CString cs = m_rtf.GetText();
+	CString s;
+	s.Format(_T("error = %d, %s"), GetLastError(), cs);
+	MessageBox(s);
+}
+
+int CDittoRulerRichEditCtrl::SaveToDB(BOOL bUpdateDesc)
+{
+	int nRet = FALSE;
+
+	if(m_rtf.GetModify() == FALSE)
+	{
+		Log(_T("Clip has not been modified"));
+		return DIDNT_NEED_TO_SAVE;
+	}
+
+	bool bSetModifyToFalse = true;
+	try
+	{
+		//only save the types if they have them set as save types, mainly rtf type
+		int saveTypes = 0;
+		CClipTypes* pTypes = theApp.LoadTypesFromDB();
+
+		INT_PTR numTypes = pTypes->GetSize();
+		for (int i = 0; i < numTypes; i++)
+		{
+			if (pTypes->ElementAt(i) == theApp.m_RTFFormat)
+			{
+				saveTypes |= stRTF;
+			}
+			else if (pTypes->ElementAt(i) == CF_TEXT ||
+				pTypes->ElementAt(i) == CF_UNICODETEXT)
+			{
+				saveTypes |= stCF_TEXT;
+				saveTypes |= stCF_UNICODETEXT;
+			}
+		}
+
+		CClip Clip;
+		Clip.m_id = m_lID;
+		if(saveTypes & stRTF)
+		{
+			LoadRTFData(Clip);
+		}
+
+		if(saveTypes & stCF_TEXT || saveTypes & stCF_UNICODETEXT)
+		{
+			LoadTextData(Clip);
+		}
+
+		if(Clip.m_Formats.GetSize() <= 0)
+		{
+			return FALSE;
+		}
+
+		theApp.m_db.execDML(_T("begin transaction;"));
+
+		if(m_lID >= 0)
+		{
+			Clip.SaveFromEditWnd(bUpdateDesc);
+		}
+		else
+		{
+			bSetModifyToFalse = false;
+			Clip.MakeLatestOrder();
+			CCopyProperties Prop(-1, this, &Clip);
+			Prop.SetHandleKillFocus(true);
+			Prop.SetToTopMost(false);
+			if(Prop.DoModal() == IDOK)
+			{
+				Clip.AddToDB();
+				m_csDescription = Clip.m_Desc;
+				m_lID = Clip.m_id;
+				bUpdateDesc = TRUE;
+				bSetModifyToFalse = true;
+			}
+		}
+
+		nRet = SAVED_CLIP_TO_DB;
+
+		theApp.m_db.execDML(_T("commit transaction;"));
+
+		if(bUpdateDesc)
+			theApp.RefreshView();
+	}
+	CATCH_SQLITE_EXCEPTION
+
+	if(bSetModifyToFalse)
+		m_rtf.SetModify(FALSE);
+
+	return nRet;
+}
+
+bool CDittoRulerRichEditCtrl::LoadRTFData(CClip &Clip)
+{
+	CString csRTFOriginal = GetRTF();
+	if(csRTFOriginal.IsEmpty())
+	{
+		Log(_T("Rtf is empty, returning"));
+		return false;
+	}
+
+	//remove the line feed at the end, not sure why the righ text always adds this
+	CString right = csRTFOriginal.Right(9);
+	if (right == _T("\\par\r\n}\r\n"))
+	{
+		CString r = csRTFOriginal.Left(csRTFOriginal.GetLength() - 9);
+		r += _T("}");
+		csRTFOriginal = r;
+	}
+
+	CStringA csRTF = CTextConvert::UnicodeToAnsi(csRTFOriginal);
+	CClipFormat format;
+	format.m_cfType = RegisterClipboardFormat(_T("Rich Text Format"));
+	int nLength = csRTF.GetLength() + 1;
+	format.m_hgData = NewGlobalP(csRTF.GetBuffer(nLength), nLength);
+	Clip.m_Formats.Add(format);
+	format.m_hgData = NULL; //Clip.m_formats owns data now
+
+	return true;
+}
+
+bool CDittoRulerRichEditCtrl::LoadTextData(CClip &Clip)
+{
+	CString csText = GetText();
+	if(csText.IsEmpty())
+	{
+		for(int i = 0; i < 20; i++)
+		{
+			Sleep(100);
+			csText = GetText();
+			if(csText.IsEmpty() == FALSE)
+				break;
+
+			Log(StrF(_T("Get Text still empty pass = %d"), i));
+		}
+		if(csText.IsEmpty())
+		{
+			Log(_T("Get Text still empty pass returning"));
+			return false;
+		}
+	}
+
+	CClipFormat format;
+
+#ifdef _UNICODE
+	format.m_cfType = CF_UNICODETEXT;
+#else
+	format.m_cfType = CF_TEXT;
+#endif
+
+	int nLength = csText.GetLength() * sizeof(TCHAR) + sizeof(TCHAR);
+	format.m_hgData = NewGlobalP(csText.GetBuffer(nLength), nLength);
+
+	Clip.SetDescFromText(format.m_hgData, true);
+	m_csDescription = Clip.m_Desc;
+	m_csDescription = m_csDescription.Left(15);
+
+	Clip.m_Formats.Add(format);
+	format.m_hgData = NULL; //Clip.m_formats owns data now
+
+	return true;
+}
+
+bool CDittoRulerRichEditCtrl::CloseEdit(bool bPrompt, BOOL bUpdateDesc)
+{
+	if(m_rtf.GetModify())
+	{		
+		int nRet = IDYES;
+		
+		if(bPrompt)
+		{
+			CString cs;
+			cs.Format(_T("%s '%s'"), theApp.m_Language.GetString("SaveChanges", "Do you want to save changes to"), m_csDescription);
+
+			::SetForegroundWindow(m_hWnd);
+			nRet = MessageBox(cs, _T("Ditto"), MB_YESNOCANCEL);
+		}
+
+		if(nRet == IDYES)
+		{
+			if(SaveToDB(bUpdateDesc) == false)
+			{
+				CString cs;
+				cs.Format(_T("%s '%s'"), theApp.m_Language.GetString("ErrorSaving", "Error saving clip"), m_csDescription);
+				MessageBox(cs, _T("Ditto"), MB_OK);
+			}
+		}
+		else if(nRet == IDCANCEL)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}

+ 29 - 0
src/DittoRulerRichEditCtrl.h

@@ -0,0 +1,29 @@
+#pragma once
+#include "rulerricheditctrl\rulerricheditctrl.h"
+
+#define SAVED_CLIP_TO_DB	1
+#define DIDNT_NEED_TO_SAVE	2
+class CDittoRulerRichEditCtrl :	public CRulerRichEditCtrl
+{
+public:
+	CDittoRulerRichEditCtrl(void);
+	~CDittoRulerRichEditCtrl(void);
+
+	enum eSaveTypes{stNONE = 0x1, stCF_TEXT = 0x2, stCF_UNICODETEXT = 0x4, stRTF = 0x8};
+
+	bool LoadItem(long lID, CString csDesc);
+	int SaveToDB(BOOL bUpdateDesc);
+	long GetTypeFlags(long lID);
+	bool CloseEdit(bool bPrompt, BOOL bUpdateDesc);
+	long GetDBID()		{ return m_lID; }
+	CString GetDesc()	{ return m_csDescription; }
+
+	void d();
+	    
+protected:
+	long m_lID;
+	CString m_csDescription;
+
+	bool LoadRTFData(CClip &Clip);
+	bool LoadTextData(CClip &Clip);
+};

+ 290 - 0
src/EditFrameWnd.cpp

@@ -0,0 +1,290 @@
+// EditFrameWnd.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "CP_Main.h"
+#include "EditFrameWnd.h"
+#include ".\editframewnd.h"
+
+#define TIMER_BUTTON_UP 1
+
+// CEditFrameWnd
+
+IMPLEMENT_DYNCREATE(CEditFrameWnd, CFrameWnd)
+
+CEditFrameWnd::CEditFrameWnd()
+{
+	m_bAutoMenuEnable = FALSE;
+}
+
+CEditFrameWnd::~CEditFrameWnd()
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CEditFrameWnd, CFrameWnd)
+	ON_WM_CREATE()
+	ON_WM_DESTROY()
+	ON_WM_SIZE()
+	ON_COMMAND(ID_BUTTON_SAVE, OnDummy)
+	ON_COMMAND(ID_BUTTON_SAVE_ALL, OnDummy)
+	ON_COMMAND(ID_BUTTON_CLOSE, OnDummy)
+	ON_COMMAND(ID_BUTTON_NEW, OnDummy)
+	ON_COMMAND(ID_BUTTON_SAVE_CLOSE_CLIPBOARD, OnDummy)
+	ON_WM_SETFOCUS()
+	ON_WM_CLOSE()
+	ON_WM_NCCALCSIZE()
+	ON_WM_NCPAINT()
+	ON_WM_NCHITTEST()
+	ON_WM_NCLBUTTONDOWN()
+	ON_WM_NCLBUTTONUP()
+	ON_WM_NCMOUSEMOVE()
+	ON_WM_NCLBUTTONDBLCLK()
+	ON_WM_WINDOWPOSCHANGING()
+	ON_WM_TIMER()
+	ON_MESSAGE(WM_DPICHANGED, OnDpiChanged)
+	ON_WM_MOVING()
+	ON_WM_ENTERSIZEMOVE()
+END_MESSAGE_MAP()
+
+
+// CEditFrameWnd message handlers
+
+int CEditFrameWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
+		return -1;
+
+	ModifyStyle(WS_CAPTION|WS_BORDER|WS_OVERLAPPED|0x0000C000|WS_THICKFRAME|WS_DLGFRAME|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX, 0, SWP_DRAWFRAME); 
+	ModifyStyleEx(WS_EX_CLIENTEDGE, 0, 0);
+		
+	CString csTitle = theApp.m_Language.GetString("Ditto_Edit", "Ditto Edit");
+	m_EditWnd.Create(NULL, csTitle, WS_CHILD, CRect(0, 0, 0, 0), this, 100, NULL);
+	m_EditWnd.ShowWindow(SW_SHOW);
+
+	MoveControls();   
+
+	m_DittoWindow.DoCreate(this);
+	m_DittoWindow.m_bDrawChevron = false;
+	m_DittoWindow.SetCaptionColors(g_Opt.m_Theme.CaptionLeft(), g_Opt.m_Theme.CaptionRight(), g_Opt.m_Theme.Border());
+	m_DittoWindow.SetCaptionOn(this, CAPTION_TOP, true, g_Opt.m_Theme.GetCaptionSize(), g_Opt.m_Theme.GetCaptionFontSize());
+
+	m_crIcon.SetRect(-2, -15, 15, 0);
+
+	SetWindowText(_T("Ditto Editor"));
+
+	return 0;
+}
+
+void CEditFrameWnd::OnDestroy()
+{
+	CFrameWnd::OnDestroy();
+
+	CRect rect;
+	GetWindowRect(&rect);
+	CGetSetOptions::SetEditWndSize(rect.Size());
+	CGetSetOptions::SetEditWndPoint(rect.TopLeft());
+
+	::SendMessage(m_hNotifyWnd, WM_EDIT_WND_CLOSING, 0, 0);
+}
+
+void CEditFrameWnd::OnSize(UINT nType, int cx, int cy)
+{
+	CFrameWnd::OnSize(nType, cx, cy);
+	MoveControls();
+}
+
+void CEditFrameWnd::MoveControls()
+{
+	if(::IsWindow(m_EditWnd.GetSafeHwnd()))
+	{
+		CRect cr;
+		GetClientRect(cr);
+		m_EditWnd.MoveWindow(cr);
+	}
+}
+
+bool CEditFrameWnd::EditIds(CClipIDs &Ids)
+{
+	return m_EditWnd.EditIds(Ids);
+}
+
+BOOL CEditFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
+{
+	if(cs.hMenu!=NULL)  
+	{
+		::DestroyMenu(cs.hMenu);      // delete menu if loaded
+		cs.hMenu = NULL;              // no menu for this window
+	}
+
+	WNDCLASS wc;	
+	wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
+	wc.lpfnWndProc = AfxWndProc;
+	wc.cbClsExtra = 0;
+	wc.cbWndExtra = 0;
+	wc.hInstance = AfxGetInstanceHandle();
+	wc.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
+	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+	wc.lpszMenuName =  NULL;
+	wc.lpszClassName = _T("Ditto Edit Wnd");
+
+	// Create the QPaste window class
+	if (!AfxRegisterClass(&wc))
+		return FALSE;
+
+	cs.lpszClass = wc.lpszClassName;
+
+	return CFrameWnd::PreCreateWindow(cs);
+}
+
+void CEditFrameWnd::OnDummy()
+{
+
+}
+
+
+void CEditFrameWnd::OnSetFocus(CWnd* pOldWnd)
+{
+	CFrameWnd::OnSetFocus(pOldWnd);
+
+	m_EditWnd.SetFocus();
+}
+
+void CEditFrameWnd::OnClose()
+{
+	if(m_EditWnd.CloseEdits(true) == false)
+		return;
+
+	CFrameWnd::OnClose();
+}
+
+void CEditFrameWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) 
+{
+	CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
+
+	m_DittoWindow.DoNcCalcSize(bCalcValidRects, lpncsp);
+}
+
+void CEditFrameWnd::OnNcPaint()
+{
+	m_DittoWindow.DoNcPaint(this);	
+}
+
+HITTEST_RET CEditFrameWnd::OnNcHitTest(CPoint point) 
+{
+	UINT Ret = m_DittoWindow.DoNcHitTest(this, point);
+	if(Ret == -1)
+		return CWnd::OnNcHitTest(point);
+
+	return Ret;
+}
+void CEditFrameWnd::OnNcLButtonDown(UINT nHitTest, CPoint point)
+{
+	int buttonPressed = m_DittoWindow.DoNcLButtonDown(this, nHitTest, point);
+
+	if (buttonPressed != 0)
+	{
+		SetTimer(TIMER_BUTTON_UP, 100, NULL);
+	}
+
+	CFrameWnd::OnNcLButtonDown(nHitTest, point);
+}
+
+void CEditFrameWnd::OnNcLButtonUp(UINT nHitTest, CPoint point)
+{
+	if(m_DittoWindow.DoNcLButtonUp(this, nHitTest, point) > 0)
+		return;
+
+	KillTimer(TIMER_BUTTON_UP);
+
+	CFrameWnd::OnNcLButtonUp(nHitTest, point);
+}
+
+void CEditFrameWnd::OnNcMouseMove(UINT nHitTest, CPoint point)
+{
+	m_DittoWindow.DoNcMouseMove(this, nHitTest, point);
+
+	CFrameWnd::OnNcMouseMove(nHitTest, point);
+}
+
+BOOL CEditFrameWnd::PreTranslateMessage(MSG* pMsg)
+{
+	m_DittoWindow.DoPreTranslateMessage(pMsg);
+
+	return CFrameWnd::PreTranslateMessage(pMsg);
+}
+
+bool CEditFrameWnd::CloseAll()
+{
+	return m_EditWnd.CloseEdits(true);
+}
+void CEditFrameWnd::OnNcLButtonDblClk(UINT nHitTest, CPoint point)
+{
+	CPoint pt(point);
+	ScreenToClient(&pt);
+	if(m_crIcon.PtInRect(pt))
+	{
+		CloseAll();
+		OnClose();
+		return;
+	}
+
+	CFrameWnd::OnNcLButtonDblClk(nHitTest, point);
+}
+
+void CEditFrameWnd::OnWindowPosChanging(WINDOWPOS* lpwndpos)
+{
+	CFrameWnd::OnWindowPosChanging(lpwndpos);
+}
+
+void CEditFrameWnd::OnTimer(UINT_PTR nIDEvent)
+{
+	switch (nIDEvent)
+	{
+		case TIMER_BUTTON_UP:
+		{
+			if ((GetKeyState(VK_LBUTTON) & 0x100) == 0)
+			{
+				m_DittoWindow.DoNcLButtonUp(this, 0, CPoint(0, 0));
+				KillTimer(TIMER_BUTTON_UP);
+			}
+			break;
+		}
+	}
+
+	CWnd::OnTimer(nIDEvent);
+}
+
+LRESULT CEditFrameWnd::OnDpiChanged(WPARAM wParam, LPARAM lParam)
+{
+	int dpi = HIWORD(wParam);
+	m_DittoWindow.OnDpiChanged(this, dpi);
+
+	RECT* const prcNewWindow = (RECT*)lParam;
+	SetWindowPos(NULL,
+		prcNewWindow->left,
+		prcNewWindow->top,
+		prcNewWindow->right - prcNewWindow->left,
+		prcNewWindow->bottom - prcNewWindow->top,
+		SWP_NOZORDER | SWP_NOACTIVATE);
+
+	log(StrF(_T("CEditFrameWnd::OnDpiChanged dpi: %d width: %d, height: %d"), dpi, (prcNewWindow->right - prcNewWindow->left), (prcNewWindow->bottom - prcNewWindow->top)));
+
+	this->Invalidate();
+	this->UpdateWindow();
+
+	return TRUE;
+}
+
+void CEditFrameWnd::OnMoving(UINT fwSide, LPRECT pRect)
+{
+	CWnd::OnMoving(fwSide, pRect);
+	m_snap.OnSnapMoving(m_hWnd, pRect);
+}
+
+void CEditFrameWnd::OnEnterSizeMove()
+{
+	m_snap.OnSnapEnterSizeMove(m_hWnd);
+	CWnd::OnEnterSizeMove();
+}

+ 57 - 0
src/EditFrameWnd.h

@@ -0,0 +1,57 @@
+#pragma once
+
+#include "EditWnd.h"
+#include "ClipIds.h"
+#include "DittoWindow.h"
+#include "SnapWindow.h"
+
+class CEditFrameWnd : public CFrameWnd
+{
+	DECLARE_DYNCREATE(CEditFrameWnd)
+	CEditFrameWnd();
+
+	bool EditIds(CClipIDs &Ids);
+	void SetNotifyWnd(HWND hWnd)	{ m_hNotifyWnd = hWnd; }
+	bool CloseAll();
+
+protected:
+	virtual ~CEditFrameWnd();
+
+	CEditWnd m_EditWnd;
+	HWND m_hNotifyWnd;
+	CDittoWindow m_DittoWindow;
+	CRect m_crIcon;
+	SnapWindow m_snap;
+
+protected:
+	DECLARE_MESSAGE_MAP()
+
+	void MoveControls();
+
+public:
+	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+	afx_msg void OnDestroy();
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnDummy();
+	afx_msg void OnNcPaint();
+	afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp);
+	afx_msg HITTEST_RET OnNcHitTest(CPoint point);
+protected:
+	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
+public:
+	afx_msg void OnSetFocus(CWnd* pOldWnd);
+	afx_msg void OnClose();
+	afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point);
+	afx_msg void OnNcLButtonUp(UINT nHitTest, CPoint point);
+	afx_msg void OnNcMouseMove(UINT nHitTest, CPoint point);
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+	afx_msg void OnNcLButtonDblClk(UINT nHitTest, CPoint point);
+	afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos);
+	afx_msg void OnTimer(UINT_PTR nIDEvent);
+	afx_msg LRESULT OnDpiChanged(WPARAM wParam, LPARAM lParam);
+	afx_msg void OnMoving(UINT fwSide, LPRECT pRect);
+	afx_msg void OnEnterSizeMove();
+};
+
+
+

+ 422 - 0
src/EditWnd.cpp

@@ -0,0 +1,422 @@
+// EditWnd.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "CP_Main.h"
+#include "EditWnd.h"
+#include ".\editwnd.h"
+#include "SaveAnimation.h"
+#include "ProcessPaste.h"
+
+IMPLEMENT_DYNAMIC(CEditWnd, CWnd)
+CEditWnd::CEditWnd()
+{
+	m_lLastSaveID = -1;
+}
+
+CEditWnd::~CEditWnd()
+{
+}
+
+BEGIN_MESSAGE_MAP(CEditWnd, CWnd)
+	ON_WM_CREATE()
+	ON_WM_SIZE()
+	ON_WM_SETFOCUS()
+	ON_COMMAND(ID_BUTTON_SAVE, OnSave)
+	ON_COMMAND(ID_BUTTON_SAVE_ALL, OnSaveAll)
+	ON_COMMAND(ID_BUTTON_CLOSE, OnClose)
+	ON_COMMAND(ID_BUTTON_NEW, OnNew)
+	ON_COMMAND(ID_BUTTON_SAVE_CLOSE_CLIPBOARD, OnSaveCloseClipboard)
+	ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+int CEditWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+	if (CWnd::OnCreate(lpCreateStruct) == -1)
+		return -1;
+
+	m_ToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_TOOLTIPS);
+	m_ToolBar.LoadToolBar(IDR_EDIT_WND);
+	m_ToolBar.EnableWindow();
+	
+	m_Tabs.Create(WS_CHILD|WS_VISIBLE|WS_TABSTOP|SCS_TOP, CRect(0, 0, 0, 0), this, 101);
+	
+	m_Font.CreatePointFont(90, _T("Arial Unicode MS"));
+	m_cbUpdateDescription.Create(theApp.m_Language.GetString("Update_Desc", "Update clip description on save?"), WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, CRect(0,0,0,0), this, 101);
+	m_cbUpdateDescription.SetFont(&m_Font);
+
+	if(CGetSetOptions::GetUpdateDescWhenSavingClip())
+	{
+		m_cbUpdateDescription.SetCheck(BST_CHECKED);
+	}
+
+	MoveControls();
+
+	m_ToolTip.Create(this);
+	CRect cr;
+	CString csText;
+
+	m_ToolBar.GetItemRect(m_ToolBar.CommandToIndex(ID_BUTTON_NEW), cr);
+	csText.Format(_T("%s     Ctrl - N"), theApp.m_Language.GetString("New_Clip", "New Clip"));
+	m_ToolTip.AddTool(&m_ToolBar, csText, cr, 1);
+
+	m_ToolBar.GetItemRect(m_ToolBar.CommandToIndex(ID_BUTTON_SAVE), cr);
+	csText.Format(_T("%s    Ctrl - S"), theApp.m_Language.GetString("Save", "Save"));
+	m_ToolTip.AddTool(&m_ToolBar, csText, cr, 2);
+
+	m_ToolBar.GetItemRect(m_ToolBar.CommandToIndex(ID_BUTTON_SAVE_ALL), cr);
+	csText.Format(_T("%s    Ctrl - Shift - S"), theApp.m_Language.GetString("Save_All", "Save All"));
+	m_ToolTip.AddTool(&m_ToolBar, csText, cr, 3);
+
+	m_ToolBar.GetItemRect(m_ToolBar.CommandToIndex(ID_BUTTON_CLOSE), cr);
+	csText.Format(_T("%s    Escape"), theApp.m_Language.GetString("Close", "Close Current Tab"));
+	m_ToolTip.AddTool(&m_ToolBar, csText, cr, 4);
+
+	m_ToolBar.GetItemRect(m_ToolBar.CommandToIndex(ID_BUTTON_SAVE_CLOSE_CLIPBOARD), cr);
+	csText.Format(_T("%s    Shift - Escape"), theApp.m_Language.GetString("Save_Close", "Save, Close and place on clipboard"));
+	m_ToolTip.AddTool(&m_ToolBar, csText, cr, 4);
+
+	return 0;
+}
+
+void CEditWnd::OnSize(UINT nType, int cx, int cy)
+{
+	CWnd::OnSize(nType, cx, cy);
+
+	if(::IsWindow(m_ToolBar.GetSafeHwnd()))
+	{
+		MoveControls();
+	}
+}
+
+void CEditWnd::MoveControls()
+{
+	static int nToolbarHeight = 23;
+	CRect rc;
+	GetClientRect(rc);
+	m_ToolBar.MoveWindow(0, 0, rc.Width(), nToolbarHeight);
+
+	m_Tabs.MoveWindow(0, nToolbarHeight, rc.Width(), rc.Height()-nToolbarHeight - 20);
+
+	m_cbUpdateDescription.MoveWindow(2, rc.Height()-20, rc.Width()-2, 20);
+}
+
+void CEditWnd::OnSaveAll() 
+{
+	BOOL bUpdateDesc = m_cbUpdateDescription.GetCheck();
+	INT_PTR size = m_Edits.size();
+	for(int tab = 0; tab < size; tab++)
+	{
+		DoSaveItem(tab);
+	}
+}
+
+void CEditWnd::OnSave() 
+{
+	DoSave();
+}
+
+bool CEditWnd::DoSave()
+{
+	bool bRet = false;
+	int nTab = m_Tabs.GetActiveTab();
+	if(nTab >= 0 && nTab < (int)m_Edits.size())
+	{
+		bRet = DoSaveItem(nTab);
+	}
+
+	return bRet;
+}
+
+bool CEditWnd::DoSaveItem(int index)
+{
+	bool bRet = false;
+	BOOL bUpdateDesc = m_cbUpdateDescription.GetCheck();
+
+	CDittoRulerRichEditCtrl *pEdit = m_Edits[index];
+	if(pEdit)
+	{
+		int nRet = pEdit->SaveToDB(bUpdateDesc);
+		if(nRet > 0)
+		{
+			if(bUpdateDesc)
+			{
+				m_Tabs.SetTabTitle(index, pEdit->GetDesc());
+			}
+
+			if(nRet == SAVED_CLIP_TO_DB)
+			{
+				CSaveAnimation Ani;
+				CRect cr;
+				pEdit->GetWindowRect(cr);
+				CRect crSave;
+				m_ToolBar.GetItemRect(2, crSave);
+				Ani.DoAnimation(cr, crSave, this);
+			}
+
+			m_lLastSaveID = pEdit->GetDBID();
+			bRet = true;
+		}
+		else
+		{
+			CString cs;
+			cs.Format(_T("%s '%s'"), theApp.m_Language.GetString("ErrorSaving", "Error saving clip"), m_Tabs.GetTabTitle(index));
+			MessageBox(cs, _T("Ditto"), MB_OK);
+		}
+	}
+
+	return bRet;
+}
+
+bool CEditWnd::EditIds(CClipIDs &Ids)
+{
+	INT_PTR count = min(Ids.GetSize(), 10);
+	for(int i = 0; i < count; i++)
+	{
+		if(IsIDAlreadyInEdit(Ids[i], true) < 0)
+		{
+			AddItem(Ids[i]);
+		}
+	}
+
+	MoveControls();
+
+	return true;
+}
+
+bool CEditWnd::AddItem(int id)
+{
+	bool bRet = false;
+	CDittoRulerRichEditCtrl *pEdit = new CDittoRulerRichEditCtrl;
+	if(pEdit)
+	{
+		CString csTitle;
+
+		if(id >= 0)
+		{
+			try
+			{				
+				CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT mText FROM Main where lID = %d"), id);
+				if(q.eof() == false)
+				{
+					csTitle = q.getStringField(_T("mText"));
+					csTitle = csTitle.Left(15);
+				}
+			}
+			CATCH_SQLITE_EXCEPTION
+		}
+		else
+		{
+			csTitle = theApp.m_Language.GetString("New", "New");
+		}
+
+		pEdit->Create(WS_TABSTOP|WS_CHILD|WS_VISIBLE, CRect(100, 100, 105, 105), this, 100, TRUE);
+		pEdit->ShowRuler();
+		pEdit->ShowToolbar();
+		pEdit->LoadItem(id, csTitle);		
+
+		m_Tabs.AddItem(csTitle, pEdit);
+		int nTab = m_Tabs.GetTabCount();
+		m_Tabs.SetActiveTab(nTab-1);
+
+		m_Edits.push_back(pEdit);
+		bRet = true;
+	}
+
+	return bRet;
+
+}
+
+int CEditWnd::IsIDAlreadyInEdit(int id, bool bSetFocus)
+{
+	INT_PTR size = m_Edits.size();
+	for(int i = 0; i < size; i++)
+	{
+		CDittoRulerRichEditCtrl *pEdit = m_Edits[i];
+		if(pEdit)
+		{
+			if(pEdit->GetDBID() == id)
+			{
+				if(bSetFocus)
+				{
+					m_Tabs.SetActiveTab(i);
+				}
+				return (int)i;
+			}
+		}
+	}
+	return -1;
+}
+
+void CEditWnd::OnDestroy()
+{
+	CWnd::OnDestroy();
+
+	INT_PTR size = m_Edits.size();
+	for(int i = 0; i < size; i++)
+	{
+		CDittoRulerRichEditCtrl *pEdit = m_Edits[i];
+		if(pEdit)
+		{
+			pEdit->DestroyWindow();
+
+			delete pEdit;
+			pEdit = NULL;
+		}
+	}
+
+	m_Edits.erase(m_Edits.begin(), m_Edits.end());
+
+	CGetSetOptions::SetUpdateDescWhenSavingClip(m_cbUpdateDescription.GetCheck());
+}
+
+void CEditWnd::OnSetFocus(CWnd* pOldWnd)
+{
+	CWnd::OnSetFocus(pOldWnd);
+
+	int nTab = m_Tabs.GetActiveTab();
+	if(nTab >= 0 && nTab < (int)m_Edits.size())
+	{
+		CDittoRulerRichEditCtrl *pEdit = m_Edits[nTab];
+		if(pEdit)
+		{
+			pEdit->SetFocus();
+		}
+	}
+}
+
+bool CEditWnd::CloseEdits(bool bPrompt)
+{
+	BOOL bUpdateDesc = m_cbUpdateDescription.GetCheck();
+
+	int nTab = 0;
+	for(std::vector<CDittoRulerRichEditCtrl*>::iterator it = m_Edits.begin(); it != m_Edits.end();)
+	{
+		CDittoRulerRichEditCtrl *pEdit = *it;
+		if(pEdit)
+		{
+			if(pEdit->CloseEdit(bPrompt, bUpdateDesc) == false)
+				return false;
+
+			m_Tabs.DeleteItem(nTab);
+			pEdit->DestroyWindow();
+			delete pEdit;
+			pEdit = NULL;
+
+			it = m_Edits.erase(it);
+		}
+		else
+		{
+			it++;
+		}
+		nTab++;
+	}
+
+	return true;
+}
+
+void CEditWnd::OnClose()
+{	
+	BOOL bUpdateDesc = m_cbUpdateDescription.GetCheck();
+	int nTab = m_Tabs.GetActiveTab();
+	if(nTab >= 0 && nTab < (int)m_Edits.size())
+	{
+		CDittoRulerRichEditCtrl *pEdit = m_Edits[nTab];
+		if(pEdit)
+		{
+			if(pEdit->CloseEdit(true, bUpdateDesc))
+			{
+				m_Tabs.DeleteItem(nTab);
+				pEdit->DestroyWindow();
+				delete pEdit;
+				pEdit = NULL;
+				m_Edits.erase(m_Edits.begin()+nTab);
+
+				if(m_Edits.size() <= 0)
+				{
+					CWnd *pParent = GetParent();
+					if(pParent)
+					{
+						pParent->SendMessage(WM_CLOSE, 0, 0);
+					}
+				}
+			}
+		}
+	}
+}
+
+void CEditWnd::OnNew()
+{
+	AddItem(-1);
+}
+
+void CEditWnd::OnSaveCloseClipboard()
+{
+	if(DoSave())
+	{
+		if(m_lLastSaveID >= 0)
+		{
+			CProcessPaste Paste;
+			Paste.GetClipIDs().Add(m_lLastSaveID);
+			Paste.m_bSendPaste = false;
+			Paste.DoPaste();
+		}
+
+		OnClose();
+	}
+}
+
+BOOL CEditWnd::PreTranslateMessage(MSG* pMsg)
+{
+	m_ToolTip.RelayEvent(pMsg);
+
+	if(pMsg->message == WM_KEYDOWN)
+	{
+		if(pMsg->wParam == VK_ESCAPE)
+		{
+			if(GetKeyState(VK_SHIFT) & 0x8000)
+			{
+				OnSaveCloseClipboard();
+			}
+			else
+			{
+				OnClose();
+			}
+			return TRUE;
+		}
+		else if(pMsg->wParam == 'S')
+		{
+			if(CONTROL_PRESSED)
+			{
+				if(GetKeyState(VK_SHIFT) & 0x8000)
+				{
+					OnSaveAll();
+					return TRUE;
+				}
+				else
+				{
+					OnSave();
+					return TRUE;
+				}
+			}
+		}
+		else if(pMsg->wParam == 'N')
+		{
+			if(CONTROL_PRESSED)
+			{
+				OnNew();
+			}
+		}
+	}
+	else if(pMsg->message == WM_SYSKEYDOWN) // ALT key is held down
+	{
+		switch( pMsg->wParam )
+		{
+			case VK_F4: 
+				OnClose();
+				return TRUE;
+		}
+	}
+
+	return CWnd::PreTranslateMessage(pMsg);
+}

+ 54 - 0
src/MainFrm.cpp

@@ -49,6 +49,7 @@ BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
 	ON_MESSAGE(WM_SEND_RECIEVE_ERROR, OnErrorOnSendRecieve)
 	ON_MESSAGE(WM_SHOW_ERROR_MSG, OnErrorMsg)
 	ON_COMMAND(ID_FIRST_IMPORT, OnFirstImport)
+	ON_MESSAGE(WM_EDIT_WND_CLOSING, OnEditWndClose)
 	ON_WM_DESTROY()
 	ON_COMMAND(ID_FIRST_NEWCLIP, OnFirstNewclip)
 	ON_MESSAGE(WM_SET_CONNECTED, OnSetConnected)
@@ -95,6 +96,7 @@ END_MESSAGE_MAP()
 
 CMainFrame::CMainFrame()
 {
+	m_pEditFrameWnd = NULL;
     m_keyStateModifiers = 0;
     m_startKeyStateTime = 0;
     m_bMovedSelectionMoveKeyState = false;
@@ -955,6 +957,14 @@ BOOL CMainFrame::PreTranslateMessage(MSG *pMsg)
 
 void CMainFrame::OnClose()
 {
+	if (m_pEditFrameWnd)
+	{
+		if (m_pEditFrameWnd->CloseAll() == false)
+		{
+			return;
+		}
+	}
+
     CloseAllOpenDialogs();
 
     Log(_T("OnClose - before stop MainFrm thread"));
@@ -1140,6 +1150,45 @@ void CMainFrame::OnFirstHelp()
     CHyperLink::GotoURL(_T("https://github.com/sabrogden/Ditto/wiki"), SW_SHOW);
 }
 
+void CMainFrame::ShowEditWnd(CClipIDs& Ids)
+{
+	CWaitCursor wait;
+
+	bool bCreatedWindow = false;
+	if (m_pEditFrameWnd == NULL)
+	{
+		m_pEditFrameWnd = new CEditFrameWnd;
+		m_pEditFrameWnd->LoadFrame(IDR_MAINFRAME);
+		bCreatedWindow = true;
+	}
+	if (m_pEditFrameWnd)
+	{
+		m_pEditFrameWnd->EditIds(Ids);
+		m_pEditFrameWnd->SetNotifyWnd(m_hWnd);
+
+		if (bCreatedWindow)
+		{
+			CSize sz;
+			CPoint pt;
+			CGetSetOptions::GetEditWndSize(sz);
+			CGetSetOptions::GetEditWndPoint(pt);
+			CRect cr(pt, sz);
+			EnsureWindowVisible(&cr);
+			m_pEditFrameWnd->MoveWindow(cr);
+		}
+
+		m_pEditFrameWnd->ShowWindow(SW_SHOW);
+		m_pEditFrameWnd->SetForegroundWindow();
+		m_pEditFrameWnd->SetFocus();
+	}
+}
+
+LRESULT CMainFrame::OnEditWndClose(WPARAM wParam, LPARAM lParam)
+{
+	m_pEditFrameWnd = NULL;
+	return TRUE;
+}
+
 void CMainFrame::ShowErrorMessage(CString csTitle, CString csMessage)
 {
     Log(StrF(_T("ShowErrorMessage %s - %s"), csTitle, csMessage));
@@ -1182,6 +1231,11 @@ LRESULT CMainFrame::OnOpenCloseWindow(WPARAM wParam, LPARAM lParam)
 
 void CMainFrame::OnDestroy()
 {
+	if (m_pEditFrameWnd)
+	{
+		m_pEditFrameWnd->DestroyWindow();
+	}
+
     CFrameWnd::OnDestroy();
 }
 

+ 3 - 0
src/MainFrm.h

@@ -8,6 +8,7 @@
 #include "PowerManager.h"
 #include "DittoPopupWindow.h"
 #include "NTray.h"
+#include "EditFrameWnd.h"
 
 #define CLOSE_WINDOW_TIMER				1	
 #define HIDE_ICON_TIMER					2
@@ -92,6 +93,7 @@ public:
 	void RefreshShowInTaskBar();
 
     void ShowEditWnd(CClipIDs &Ids);
+    CEditFrameWnd* m_pEditFrameWnd;
 
 
     // Generated message map functions
@@ -115,6 +117,7 @@ protected:
     afx_msg LRESULT OnAddToDatabaseFromSocket(WPARAM wParam, LPARAM lParam);
     afx_msg LRESULT OnErrorOnSendRecieve(WPARAM wParam, LPARAM lParam);
 	afx_msg LRESULT OnErrorMsg(WPARAM wParam, LPARAM lParam);
+    afx_msg LRESULT OnEditWndClose(WPARAM wParam, LPARAM lParam);
     afx_msg LRESULT OnSetConnected(WPARAM wParam, LPARAM lParam);
 	afx_msg LRESULT OnOpenCloseWindow(WPARAM wParam, LPARAM lParam);
 	afx_msg LRESULT OnLoadClipOnClipboard(WPARAM wParam, LPARAM lParam);

+ 39 - 0
src/Options.cpp

@@ -3167,4 +3167,43 @@ void CGetSetOptions::SetClipEditSaveDelayAfterSaveSeconds(int val)
 BOOL CGetSetOptions::GetClipEditSaveDelayAfterSaveSeconds()
 {
 	return GetProfileLong("ClipEditSaveDelayAfterSaveSeconds", 3);
+}
+
+BOOL CGetSetOptions::SetEditWndSize(CSize size)
+{
+	BOOL bRet = SetResolutionProfileLong("EditWndCX", size.cx);
+	bRet = SetResolutionProfileLong("EditWndCY", size.cy);
+
+	return bRet;
+}
+
+void CGetSetOptions::GetEditWndSize(CSize& size)
+{
+	size.cx = GetResolutionProfileLong("EditWndCX", 600);
+	size.cy = GetResolutionProfileLong("EditWndCY", 600);
+	if (size.cx <= 0 && size.cy <= 0)
+	{
+		size.cx = 600;
+		size.cy = 600;
+	}
+}
+
+BOOL CGetSetOptions::SetEditWndPoint(CPoint point)
+{
+	BOOL bRet = SetResolutionProfileLong("EditWndX", point.x);
+	bRet = SetResolutionProfileLong("EditWndY", point.y);
+
+	return bRet;
+}
+
+void CGetSetOptions::GetEditWndPoint(CPoint& point)
+{
+	point.x = GetResolutionProfileLong("EditWndX", 100);
+	point.y = GetResolutionProfileLong("EditWndY", 100);
+
+	if (point.x <= 0 && point.y <= 0)
+	{
+		point.x = 100;
+		point.y = 100;
+	}
 }

+ 6 - 0
src/Options.h

@@ -715,6 +715,12 @@ public:
 	static int	m_clipEditSaveDelayAfterSaveSeconds;
 	static void SetClipEditSaveDelayAfterSaveSeconds(int val);
 	static BOOL GetClipEditSaveDelayAfterSaveSeconds();
+
+	static BOOL SetEditWndSize(CSize size);
+	static void GetEditWndSize(CSize& size);
+
+	static BOOL SetEditWndPoint(CPoint point);
+	static void GetEditWndPoint(CPoint& point);
 };
 
 // global for easy access and for initialization of fast access variables

+ 319 - 0
src/RulerRichEditCtrl/External/ColourPicker.cpp

@@ -0,0 +1,319 @@
+// ColourPicker.cpp : implementation file
+//
+// ColourPicker is a drop-in colour picker control. Check out the 
+// header file or the accompanying HTML doc file for details.
+//
+// Written by Chris Maunder ([email protected])
+// Extended by Alexander Bischofberger ([email protected])
+// Copyright (c) 1998.
+//
+// This code may be used in compiled form in any way you desire. This
+// file may be redistributed unmodified by any means PROVIDING it is 
+// not sold for profit without the authors written consent, and 
+// providing that this notice and the authors name is included. If 
+// the source code in  this file is used in any commercial application 
+// then a simple email would be nice.
+//
+// This file is provided "as is" with no expressed or implied warranty.
+// The author accepts no liability if it causes any damage to your
+// computer, causes your pet cat to fall ill, increases baldness or
+// makes you car start emitting strange noises when you start it up.
+//
+// Expect bugs.
+// 
+// Please use and enjoy. Please let me know of any bugs/mods/improvements 
+// that you have found/implemented and I will fix/incorporate them into this
+// file. 
+//
+// Updated 16 May 1998
+//         31 May 1998 - added Default text (CJM)
+//          9 Jan 1999 - minor vis update
+
+#include "stdafx.h"
+#include "ColourPopup.h"
+#include "ColourPicker.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+void AFXAPI DDX_ColourPicker(CDataExchange *pDX, int nIDC, COLORREF& crColour)
+{
+    HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
+    ASSERT (hWndCtrl != NULL);                
+    
+    CColourPicker* pColourPicker = (CColourPicker*) CWnd::FromHandle(hWndCtrl);
+    if (pDX->m_bSaveAndValidate)
+    {
+        crColour = pColourPicker->GetColour();
+    }
+    else // initializing
+    {
+        pColourPicker->SetColour(crColour);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker
+
+CColourPicker::CColourPicker()
+{
+    SetBkColour(GetSysColor(COLOR_3DFACE));
+    SetTextColour(GetSysColor(COLOR_BTNTEXT));
+
+    m_bTrackSelection = FALSE;
+    m_nSelectionMode = CP_MODE_BK;
+    m_bActive = FALSE;
+
+    m_strDefaultText = _T("Automatic");
+    m_strCustomText  = _T("More Colours...");
+}
+
+CColourPicker::~CColourPicker()
+{
+}
+
+IMPLEMENT_DYNCREATE(CColourPicker, CButton)
+
+BEGIN_MESSAGE_MAP(CColourPicker, CButton)
+    //{{AFX_MSG_MAP(CColourPicker)
+    ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
+    ON_WM_CREATE()
+	ON_MESSAGE(CPN_SELENDOK, OnColorButton)
+    //}}AFX_MSG_MAP
+    //ON_MESSAGE(CPN_SELENDOK,     OnSelEndOK)
+    //ON_MESSAGE(CPN_SELENDCANCEL, OnSelEndCancel)
+    //ON_MESSAGE(CPN_SELCHANGE,    OnSelChange)
+END_MESSAGE_MAP()
+
+LRESULT CColourPicker::OnColorButton(WPARAM w, LPARAM l)
+{
+	GetParent()->SendMessage(CPN_SELENDOK, w, (LPARAM)l);
+
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker message handlers
+
+//LRESULT CColourPicker::OnSelEndOK(LPARAM lParam, WPARAM wParam)
+//{
+//    COLORREF crNewColour = (COLORREF) lParam;
+//    m_bActive = FALSE;
+//    SetColour(crNewColour);
+//
+//    CWnd *pParent = GetParent();
+//    if (pParent) {
+//        pParent->SendMessage(CPN_CLOSEUP, lParam, (WPARAM) GetDlgCtrlID());
+//        pParent->SendMessage(CPN_SELENDOK, lParam, (WPARAM) GetDlgCtrlID());
+//    }
+//
+//    if (crNewColour != GetColour())
+//        if (pParent) pParent->SendMessage(CPN_SELCHANGE, lParam, (WPARAM) GetDlgCtrlID());
+//
+//    return TRUE;
+//}
+//
+//LRESULT CColourPicker::OnSelEndCancel(LPARAM lParam, WPARAM wParam)
+//{
+//    m_bActive = FALSE;
+//    SetColour((COLORREF) lParam);
+//
+//    CWnd *pParent = GetParent();
+//    if (pParent) {
+//        pParent->SendMessage(CPN_CLOSEUP, lParam, (WPARAM) GetDlgCtrlID());
+//        pParent->SendMessage(CPN_SELENDCANCEL, lParam, (WPARAM) GetDlgCtrlID());
+//    }
+//
+//    return TRUE;
+//}
+//
+//LRESULT CColourPicker::OnSelChange(LPARAM lParam, WPARAM wParam)
+//{
+//    if (m_bTrackSelection) SetColour((COLORREF) lParam);
+//
+//    CWnd *pParent = GetParent();
+//    if (pParent) pParent->SendMessage(CPN_SELCHANGE, lParam, (WPARAM) GetDlgCtrlID());
+//
+//    return TRUE;
+//}
+
+int CColourPicker::OnCreate(LPCREATESTRUCT lpCreateStruct) 
+{
+    if (CButton::OnCreate(lpCreateStruct) == -1)
+        return -1;
+    
+    SetWindowSize();    // resize appropriately
+    return 0;
+}
+
+// On mouse click, create and show a CColourPopup window for colour selection
+BOOL CColourPicker::OnClicked()
+{
+    m_bActive = TRUE;
+    CRect rect;
+    GetWindowRect(rect);
+    new CColourPopup(CPoint(rect.left, rect.bottom),    // Point to display popup
+                     GetColour(),                       // Selected colour
+                     this,                              // parent
+                     m_strDefaultText,                  // "Default" text area
+                     m_strCustomText);                  // Custom Text
+
+    CWnd *pParent = GetParent();
+    if (pParent)
+        pParent->SendMessage(CPN_DROPDOWN, (LPARAM)GetColour(), (WPARAM) GetDlgCtrlID());
+
+    // Docs say I should return FALSE to stop the parent also getting the message.
+    // HA! What a joke.
+
+    return TRUE;
+}
+
+void CColourPicker::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
+{
+    ASSERT(lpDrawItemStruct);
+    
+    CDC*    pDC     = CDC::FromHandle(lpDrawItemStruct->hDC);
+    CRect   rect    = lpDrawItemStruct->rcItem;
+    UINT    state   = lpDrawItemStruct->itemState;
+    CString m_strText;
+
+    CSize Margins(::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE));
+
+    // Draw arrow
+    if (m_bActive) state |= ODS_SELECTED;
+    pDC->DrawFrameControl(&m_ArrowRect, DFC_SCROLL, DFCS_SCROLLDOWN  | 
+                          ((state & ODS_SELECTED) ? DFCS_PUSHED : 0) |
+                          ((state & ODS_DISABLED) ? DFCS_INACTIVE : 0));
+
+    pDC->DrawEdge(rect, EDGE_SUNKEN, BF_RECT);
+
+    // Must reduce the size of the "client" area of the button due to edge thickness.
+    rect.DeflateRect(Margins.cx, Margins.cy);
+
+    // Fill remaining area with colour
+    rect.right -= m_ArrowRect.Width();
+
+    CBrush brush( ((state & ODS_DISABLED) || m_crColourBk == CLR_DEFAULT)? 
+                  ::GetSysColor(COLOR_3DFACE) : m_crColourBk);
+    CBrush* pOldBrush = (CBrush*) pDC->SelectObject(&brush);
+	pDC->SelectStockObject(NULL_PEN);
+    pDC->Rectangle(rect);
+    pDC->SelectObject(pOldBrush);
+
+    // Draw the window text (if any)
+    GetWindowText(m_strText);
+    if (m_strText.GetLength())
+    {
+        pDC->SetBkMode(TRANSPARENT);
+        if (state & ODS_DISABLED)
+        {
+            rect.OffsetRect(1,1);
+            pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
+            pDC->DrawText(m_strText, rect, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
+            rect.OffsetRect(-1,-1);
+            pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
+            pDC->DrawText(m_strText, rect, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
+        }
+        else
+        {
+            pDC->SetTextColor((m_crColourText == CLR_DEFAULT)? 0 : m_crColourText);
+            pDC->DrawText(m_strText, rect, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
+        }
+    }
+
+    // Draw focus rect
+    if (state & ODS_FOCUS) 
+    {
+        rect.DeflateRect(1,1);
+        pDC->DrawFocusRect(rect);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker overrides
+
+void CColourPicker::PreSubclassWindow() 
+{
+    ModifyStyle(0, BS_OWNERDRAW);        // Make it owner drawn
+    CButton::PreSubclassWindow();
+    SetWindowSize();                     // resize appropriately
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker attributes
+
+COLORREF CColourPicker::GetColour()
+{ 
+    return (m_nSelectionMode == CP_MODE_TEXT)? 
+        GetTextColour(): GetBkColour(); 
+}
+
+void CColourPicker::SetColour(COLORREF crColour)
+{ 
+    (m_nSelectionMode == CP_MODE_TEXT)? 
+        SetTextColour(crColour): SetBkColour(crColour); 
+}
+
+void CColourPicker::SetBkColour(COLORREF crColourBk)
+{
+    m_crColourBk = crColourBk;
+    if (IsWindow(m_hWnd))
+        RedrawWindow();
+}
+
+void CColourPicker::SetTextColour(COLORREF crColourText)
+{
+    m_crColourText = crColourText;
+    if (IsWindow(m_hWnd)) 
+        RedrawWindow();
+}
+
+void CColourPicker::SetDefaultText(LPCTSTR szDefaultText)
+{
+    m_strDefaultText = (szDefaultText)? szDefaultText : _T("");
+}
+
+void CColourPicker::SetCustomText(LPCTSTR szCustomText)
+{
+    m_strCustomText = (szCustomText)? szCustomText : _T("");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker implementation
+
+void CColourPicker::SetWindowSize()
+{
+    // Get size dimensions of edges
+    CSize MarginSize(::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE));
+
+    // Get size of dropdown arrow
+    int nArrowWidth = max(::GetSystemMetrics(SM_CXHTHUMB), 5*MarginSize.cx);
+    int nArrowHeight = max(::GetSystemMetrics(SM_CYVTHUMB), 5*MarginSize.cy);
+    CSize ArrowSize(max(nArrowWidth, nArrowHeight), max(nArrowWidth, nArrowHeight));
+
+    // Get window size
+    CRect rect;
+    GetWindowRect(rect);
+
+    CWnd* pParent = GetParent();
+    if (pParent)
+        pParent->ScreenToClient(rect);
+
+    // Set window size at least as wide as 2 arrows, and as high as arrow + margins
+    int nWidth = max(rect.Width(), 2*ArrowSize.cx + 2*MarginSize.cx);
+	int nHeight = max( rect.Height(), ArrowSize.cy+2*MarginSize.cy);
+
+    MoveWindow(rect.left, rect.top, nWidth, nHeight, TRUE);
+
+    // Get the new coords of this window
+    GetWindowRect(rect);
+    ScreenToClient(rect);
+
+    // Get the rect where the arrow goes, and convert to client coords.
+    m_ArrowRect.SetRect(rect.right - ArrowSize.cx - MarginSize.cx, 
+                        rect.top + MarginSize.cy, rect.right - MarginSize.cx,
+                        rect.bottom - MarginSize.cy);
+}

+ 114 - 0
src/RulerRichEditCtrl/External/ColourPicker.h

@@ -0,0 +1,114 @@
+#if !defined(AFX_COLOURPICKER_H__D0B75901_9830_11D1_9C0F_00A0243D1382__INCLUDED_)
+#define AFX_COLOURPICKER_H__D0B75901_9830_11D1_9C0F_00A0243D1382__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+// ColourPicker.h : header file
+//
+// Written by Chris Maunder ([email protected])
+// Extended by Alexander Bischofberger ([email protected])
+// Copyright (c) 1998.
+//
+// This code may be used in compiled form in any way you desire. This
+// file may be redistributed unmodified by any means PROVIDING it is 
+// not sold for profit without the authors written consent, and 
+// providing that this notice and the authors name is included. If 
+// the source code in  this file is used in any commercial application 
+// then a simple email would be nice.
+//
+// This file is provided "as is" with no expressed or implied warranty.
+// The author accepts no liability if it causes any damage whatsoever.
+// It's free - so you get what you pay for.
+
+#include "ColourPopup.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker window
+
+void AFXAPI DDX_ColourPicker(CDataExchange *pDX, int nIDC, COLORREF& crColour);
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPicker window
+
+#define CP_MODE_TEXT 1  // edit text colour
+#define CP_MODE_BK   2  // edit background colour (default)
+
+class CColourPicker : public CButton
+{
+// Construction
+public:
+    CColourPicker();
+    DECLARE_DYNCREATE(CColourPicker);
+
+// Attributes
+public:
+    COLORREF GetColour();
+    void     SetColour(COLORREF crColour); 
+
+    void     SetDefaultText(LPCTSTR szDefaultText);
+    void     SetCustomText(LPCTSTR szCustomText);
+
+    void     SetTrackSelection(BOOL bTracking = TRUE)  { m_bTrackSelection = bTracking; }
+    BOOL     GetTrackSelection()                       { return m_bTrackSelection; }
+
+    void     SetSelectionMode(UINT nMode)              { m_nSelectionMode = nMode; }
+    UINT     GetSelectionMode()                        { return m_nSelectionMode; };
+
+    void     SetBkColour(COLORREF crColourBk);
+    COLORREF GetBkColour()                             { return m_crColourBk; }
+    
+    void     SetTextColour(COLORREF crColourText);
+    COLORREF GetTextColour()                           { return m_crColourText;}
+
+// Operations
+public:
+
+// Overrides
+    // ClassWizard generated virtual function overrides
+    //{{AFX_VIRTUAL(CColourPicker)
+    public:
+    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+    protected:
+    virtual void PreSubclassWindow();
+    //}}AFX_VIRTUAL
+
+// Implementation
+public:
+    virtual ~CColourPicker();
+
+protected:
+    void SetWindowSize();
+
+// protected attributes
+protected:
+    BOOL     m_bActive,                // Is the dropdown active?
+             m_bTrackSelection;        // track colour changes?
+    COLORREF m_crColourBk;
+    COLORREF m_crColourText;
+    UINT     m_nSelectionMode;
+    CRect    m_ArrowRect;
+    CString  m_strDefaultText;
+    CString  m_strCustomText;
+
+    // Generated message map functions
+protected:
+    //{{AFX_MSG(CColourPicker)
+    afx_msg BOOL OnClicked();
+    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+	LRESULT OnColorButton(WPARAM w, LPARAM l);
+    //}}AFX_MSG
+    /*afx_msg LRESULT OnSelEndOK(LPARAM lParam, WPARAM wParam);
+    afx_msg LRESULT OnSelEndCancel(LPARAM lParam, WPARAM wParam);
+    afx_msg LRESULT OnSelChange(LPARAM lParam, WPARAM wParam);*/
+
+    DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_COLOURPICKER_H__D0B75901_9830_11D1_9C0F_00A0243D1382__INCLUDED_)

+ 913 - 0
src/RulerRichEditCtrl/External/ColourPopup.cpp

@@ -0,0 +1,913 @@
+// ColourPopup.cpp : implementation file
+//
+// Written by Chris Maunder ([email protected])
+// Extended by Alexander Bischofberger ([email protected])
+// Copyright (c) 1998.
+//
+// Updated 30 May 1998 to allow any number of colours, and to
+//                     make the appearance closer to Office 97. 
+//                     Also added "Default" text area.         (CJM)
+//
+//         13 June 1998 Fixed change of focus bug (CJM)
+//         30 June 1998 Fixed bug caused by focus bug fix (D'oh!!)
+//                      Solution suggested by Paul Wilkerson.
+//
+// ColourPopup is a helper class for the colour picker control
+// CColourPicker. Check out the header file or the accompanying 
+// HTML doc file for details.
+//
+// This code may be used in compiled form in any way you desire. This
+// file may be redistributed unmodified by any means PROVIDING it is 
+// not sold for profit without the authors written consent, and 
+// providing that this notice and the authors name is included. 
+//
+// This file is provided "as is" with no expressed or implied warranty.
+// The author accepts no liability if it causes any damage to you or your
+// computer whatsoever. It's free, so don't hassle me about it.
+//
+// Expect bugs.
+// 
+// Please use and enjoy. Please let me know of any bugs/mods/improvements 
+// that you have found/implemented and I will fix/incorporate them into this
+// file. 
+
+#include "stdafx.h"
+#include <math.h>
+#include "ColourPicker.h"
+#include "ColourPopup.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define DEFAULT_BOX_VALUE -3
+#define CUSTOM_BOX_VALUE  -2
+#define INVALID_COLOUR    -1
+
+#define MAX_COLOURS      100
+
+
+ColourTableEntry CColourPopup::m_crColours[] = 
+{
+    { RGB(0x00, 0x00, 0x00),    _T("Black")             },
+    { RGB(0xA5, 0x2A, 0x00),    _T("Brown")             },
+    { RGB(0x00, 0x40, 0x40),    _T("Dark Olive Green")  },
+    { RGB(0x00, 0x55, 0x00),    _T("Dark Green")        },
+    { RGB(0x00, 0x00, 0x5E),    _T("Dark Teal")         },
+    { RGB(0x00, 0x00, 0x8B),    _T("Dark blue")         },
+    { RGB(0x4B, 0x00, 0x82),    _T("Indigo")            },
+    { RGB(0x28, 0x28, 0x28),    _T("Dark grey")         },
+
+    { RGB(0x8B, 0x00, 0x00),    _T("Dark red")          },
+    { RGB(0xFF, 0x68, 0x20),    _T("Orange")            },
+    { RGB(0x8B, 0x8B, 0x00),    _T("Dark yellow")       },
+    { RGB(0x00, 0x93, 0x00),    _T("Green")             },
+    { RGB(0x38, 0x8E, 0x8E),    _T("Teal")              },
+    { RGB(0x00, 0x00, 0xFF),    _T("Blue")              },
+    { RGB(0x7B, 0x7B, 0xC0),    _T("Blue-grey")         },
+    { RGB(0x66, 0x66, 0x66),    _T("Grey - 40")         },
+
+    { RGB(0xFF, 0x00, 0x00),    _T("Red")               },
+    { RGB(0xFF, 0xAD, 0x5B),    _T("Light orange")      },
+    { RGB(0x32, 0xCD, 0x32),    _T("Lime")              }, 
+    { RGB(0x3C, 0xB3, 0x71),    _T("Sea green")         },
+    { RGB(0x7F, 0xFF, 0xD4),    _T("Aqua")              },
+    { RGB(0x7D, 0x9E, 0xC0),    _T("Light blue")        },
+    { RGB(0x80, 0x00, 0x80),    _T("Violet")            },
+    { RGB(0x7F, 0x7F, 0x7F),    _T("Grey - 50")         },
+
+    { RGB(0xFF, 0xC0, 0xCB),    _T("Pink")              },
+    { RGB(0xFF, 0xD7, 0x00),    _T("Gold")              },
+    { RGB(0xFF, 0xFF, 0x00),    _T("Yellow")            },    
+    { RGB(0x00, 0xFF, 0x00),    _T("Bright green")      },
+    { RGB(0x40, 0xE0, 0xD0),    _T("Turquoise")         },
+    { RGB(0xC0, 0xFF, 0xFF),    _T("Skyblue")           },
+    { RGB(0x48, 0x00, 0x48),    _T("Plum")              },
+    { RGB(0xC0, 0xC0, 0xC0),    _T("Light grey")        },
+
+    { RGB(0xFF, 0xE4, 0xE1),    _T("Rose")              },
+    { RGB(0xD2, 0xB4, 0x8C),    _T("Tan")               },
+    { RGB(0xFF, 0xFF, 0xE0),    _T("Light yellow")      },
+    { RGB(0x98, 0xFB, 0x98),    _T("Pale green ")       },
+    { RGB(0xAF, 0xEE, 0xEE),    _T("Pale turquoise")    },
+    { RGB(0x68, 0x83, 0x8B),    _T("Pale blue")         },
+    { RGB(0xE6, 0xE6, 0xFA),    _T("Lavender")          },
+    { RGB(0xFF, 0xFF, 0xFF),    _T("White")             }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup
+
+CColourPopup::CColourPopup()
+{
+    Initialise();
+}
+
+CColourPopup::CColourPopup(CPoint p, COLORREF crColour, CWnd* pParentWnd,
+                           LPCTSTR szDefaultText /* = NULL */,
+                           LPCTSTR szCustomText  /* = NULL */)
+{
+    Initialise();
+
+    m_crColour       = m_crInitialColour = crColour;
+    m_pParent        = pParentWnd;
+    m_strDefaultText = (szDefaultText)? szDefaultText : _T("");
+    m_strCustomText  = (szCustomText)?  szCustomText  : _T("");
+
+    CColourPopup::Create(p, crColour, pParentWnd, szDefaultText, szCustomText);
+}
+
+void CColourPopup::Initialise()
+{
+    m_nNumColours       = sizeof(m_crColours)/sizeof(ColourTableEntry);
+    ASSERT(m_nNumColours <= MAX_COLOURS);
+    if (m_nNumColours > MAX_COLOURS)
+        m_nNumColours = MAX_COLOURS;
+
+    m_nNumColumns       = 0;
+    m_nNumRows          = 0;
+    m_nBoxSize          = 18;
+    m_nMargin           = ::GetSystemMetrics(SM_CXEDGE);
+    m_nCurrentSel       = INVALID_COLOUR;
+    m_nChosenColourSel  = INVALID_COLOUR;
+    m_pParent           = NULL;
+    m_crColour          = m_crInitialColour = RGB(0,0,0);
+
+    m_bChildWindowVisible = FALSE;
+
+    // Idiot check: Make sure the colour square is at least 5 x 5;
+    if (m_nBoxSize - 2*m_nMargin - 2 < 5) m_nBoxSize = 5 + 2*m_nMargin + 2;
+
+    // Create the font
+    NONCLIENTMETRICS ncm;
+    ncm.cbSize = sizeof(NONCLIENTMETRICS);
+    VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
+    m_Font.CreateFontIndirect(&(ncm.lfMessageFont));
+
+    // Create the palette
+    struct {
+        LOGPALETTE    LogPalette;
+        PALETTEENTRY  PalEntry[MAX_COLOURS];
+    } pal;
+
+    LOGPALETTE* pLogPalette = (LOGPALETTE*) &pal;
+    pLogPalette->palVersion    = 0x300;
+    pLogPalette->palNumEntries = (WORD) m_nNumColours; 
+
+    for (int i = 0; i < m_nNumColours; i++)
+    {
+        pLogPalette->palPalEntry[i].peRed   = GetRValue(m_crColours[i].crColour);
+        pLogPalette->palPalEntry[i].peGreen = GetGValue(m_crColours[i].crColour);
+        pLogPalette->palPalEntry[i].peBlue  = GetBValue(m_crColours[i].crColour);
+        pLogPalette->palPalEntry[i].peFlags = 0;
+    }
+
+    m_Palette.CreatePalette(pLogPalette);
+}
+
+CColourPopup::~CColourPopup()
+{
+    m_Font.DeleteObject();
+    m_Palette.DeleteObject();
+}
+
+BOOL CColourPopup::Create(CPoint p, COLORREF crColour, CWnd* pParentWnd,
+                          LPCTSTR szDefaultText /* = NULL */,
+                          LPCTSTR szCustomText  /* = NULL */)
+{
+    ASSERT(pParentWnd && ::IsWindow(pParentWnd->GetSafeHwnd()));
+    ASSERT(pParentWnd->IsKindOf(RUNTIME_CLASS(CColourPicker)));
+
+    m_pParent  = pParentWnd;
+    m_crColour = m_crInitialColour = crColour;
+
+    // Get the class name and create the window
+    CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS|CS_HREDRAW|CS_VREDRAW,
+                                              0,
+                                              (HBRUSH) (COLOR_BTNFACE+1), 
+                                              0);
+
+    if (!CWnd::CreateEx(0, szClassName, _T(""), WS_VISIBLE|WS_POPUP, 
+                        p.x, p.y, 100, 100, // size updated soon
+                        pParentWnd->GetSafeHwnd(), 0, NULL))
+        return FALSE;
+
+    // Store the Custom text
+    if (szCustomText != NULL) 
+        m_strCustomText = szCustomText;
+
+    // Store the Default Area text
+    if (szDefaultText != NULL) 
+        m_strDefaultText = szDefaultText;
+        
+    // Set the window size
+    SetWindowSize();
+
+    // Create the tooltips
+    CreateToolTips();
+
+    // Find which cell (if any) corresponds to the initial colour
+    FindCellFromColour(crColour);
+
+    // Capture all mouse events for the life of this window
+    SetCapture();
+
+    return TRUE;
+}
+
+BEGIN_MESSAGE_MAP(CColourPopup, CWnd)
+    //{{AFX_MSG_MAP(CColourPopup)
+    ON_WM_NCDESTROY()
+    ON_WM_LBUTTONUP()
+    ON_WM_PAINT()
+    ON_WM_MOUSEMOVE()
+    ON_WM_KEYDOWN()
+    ON_WM_QUERYNEWPALETTE()
+    ON_WM_PALETTECHANGED()
+	ON_WM_KILLFOCUS()
+	ON_WM_ACTIVATEAPP()
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup message handlers
+
+// For tooltips
+BOOL CColourPopup::PreTranslateMessage(MSG* pMsg) 
+{
+    m_ToolTip.RelayEvent(pMsg);
+
+    // Fix (Adrian Roman): Sometimes if the picker loses focus it is never destroyed
+    if (GetCapture()->GetSafeHwnd() != m_hWnd)
+        SetCapture(); 
+
+    return CWnd::PreTranslateMessage(pMsg);
+}
+
+// If an arrow key is pressed, then move the selection
+void CColourPopup::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
+{
+    int row = GetRow(m_nCurrentSel),
+        col = GetColumn(m_nCurrentSel);
+
+    if (nChar == VK_DOWN) 
+    {
+        if (row == DEFAULT_BOX_VALUE) 
+            row = col = 0; 
+        else if (row == CUSTOM_BOX_VALUE)
+        {
+            if (m_strDefaultText.GetLength())
+                row = col = DEFAULT_BOX_VALUE;
+            else
+                row = col = 0;
+        }
+        else
+        {
+            row++;
+            if (GetIndex(row,col) < 0)
+            {
+                if (m_strCustomText.GetLength())
+                    row = col = CUSTOM_BOX_VALUE;
+                else if (m_strDefaultText.GetLength())
+                    row = col = DEFAULT_BOX_VALUE;
+                else
+                    row = col = 0;
+            }
+        }
+        ChangeSelection(GetIndex(row, col));
+    }
+
+    if (nChar == VK_UP) 
+    {
+        if (row == DEFAULT_BOX_VALUE)
+        {
+            if (m_strCustomText.GetLength())
+                row = col = CUSTOM_BOX_VALUE;
+            else
+           { 
+                row = GetRow(m_nNumColours-1); 
+                col = GetColumn(m_nNumColours-1); 
+            }
+        }
+        else if (row == CUSTOM_BOX_VALUE)
+        { 
+            row = GetRow(m_nNumColours-1); 
+            col = GetColumn(m_nNumColours-1); 
+        }
+        else if (row > 0) row--;
+        else /* row == 0 */
+        {
+            if (m_strDefaultText.GetLength())
+                row = col = DEFAULT_BOX_VALUE;
+            else if (m_strCustomText.GetLength())
+                row = col = CUSTOM_BOX_VALUE;
+            else
+            { 
+                row = GetRow(m_nNumColours-1); 
+                col = GetColumn(m_nNumColours-1); 
+            }
+        }
+        ChangeSelection(GetIndex(row, col));
+    }
+
+    if (nChar == VK_RIGHT) 
+    {
+        if (row == DEFAULT_BOX_VALUE) 
+            row = col = 0; 
+        else if (row == CUSTOM_BOX_VALUE)
+        {
+            if (m_strDefaultText.GetLength())
+                row = col = DEFAULT_BOX_VALUE;
+            else
+                row = col = 0;
+        }
+        else if (col < m_nNumColumns-1) 
+            col++;
+        else 
+        { 
+            col = 0; row++;
+        }
+
+        if (GetIndex(row,col) == INVALID_COLOUR)
+        {
+            if (m_strCustomText.GetLength())
+                row = col = CUSTOM_BOX_VALUE;
+            else if (m_strDefaultText.GetLength())
+                row = col = DEFAULT_BOX_VALUE;
+            else
+                row = col = 0;
+        }
+
+        ChangeSelection(GetIndex(row, col));
+    }
+
+    if (nChar == VK_LEFT) 
+    {
+        if (row == DEFAULT_BOX_VALUE)
+        {
+            if (m_strCustomText.GetLength())
+                row = col = CUSTOM_BOX_VALUE;
+            else
+           { 
+                row = GetRow(m_nNumColours-1); 
+                col = GetColumn(m_nNumColours-1); 
+            }
+        }
+        else if (row == CUSTOM_BOX_VALUE)
+        { 
+            row = GetRow(m_nNumColours-1); 
+            col = GetColumn(m_nNumColours-1); 
+        }
+        else if (col > 0) col--;
+        else /* col == 0 */
+        {
+            if (row > 0) { row--; col = m_nNumColumns-1; }
+            else 
+            {
+                if (m_strDefaultText.GetLength())
+                    row = col = DEFAULT_BOX_VALUE;
+                else if (m_strCustomText.GetLength())
+                    row = col = CUSTOM_BOX_VALUE;
+                else
+                { 
+                    row = GetRow(m_nNumColours-1); 
+                    col = GetColumn(m_nNumColours-1); 
+                }
+            }
+        }
+        ChangeSelection(GetIndex(row, col));
+    }
+
+    if (nChar == VK_ESCAPE) 
+    {
+        m_crColour = m_crInitialColour;
+        EndSelection(CPN_SELENDCANCEL);
+        return;
+    }
+
+    if (nChar == VK_RETURN || nChar == VK_SPACE)
+    {
+        EndSelection(CPN_SELENDOK);
+        return;
+    }
+
+    CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
+}
+
+// auto-deletion
+void CColourPopup::OnNcDestroy() 
+{
+    CWnd::OnNcDestroy();
+    delete this;
+}
+
+void CColourPopup::OnPaint() 
+{
+    CPaintDC dc(this); // device context for painting
+
+    // Draw the Default Area text
+    if (m_strDefaultText.GetLength())
+        DrawCell(&dc, DEFAULT_BOX_VALUE);
+ 
+    // Draw colour cells
+    for (int i = 0; i < m_nNumColours; i++)
+        DrawCell(&dc, i);
+    
+    // Draw custom text
+    if (m_strCustomText.GetLength())
+        DrawCell(&dc, CUSTOM_BOX_VALUE);
+
+    // Draw raised window edge (ex-window style WS_EX_WINDOWEDGE is sposed to do this,
+    // but for some reason isn't
+    CRect rect;
+    GetClientRect(rect);
+    dc.DrawEdge(rect, EDGE_RAISED, BF_RECT);
+}
+
+void CColourPopup::OnMouseMove(UINT nFlags, CPoint point) 
+{
+    int nNewSelection = INVALID_COLOUR;
+
+    // Translate points to be relative raised window edge
+    point.x -= m_nMargin;
+    point.y -= m_nMargin;
+
+    // First check we aren't in text box
+    if (m_strCustomText.GetLength() && m_CustomTextRect.PtInRect(point))
+        nNewSelection = CUSTOM_BOX_VALUE;
+    else if (m_strDefaultText.GetLength() && m_DefaultTextRect.PtInRect(point))
+        nNewSelection = DEFAULT_BOX_VALUE;
+    else
+    {
+        // Take into account text box
+        if (m_strDefaultText.GetLength()) 
+            point.y -= m_DefaultTextRect.Height();  
+
+        // Get the row and column
+        nNewSelection = GetIndex(point.y / m_nBoxSize, point.x / m_nBoxSize);
+
+        // In range? If not, default and exit
+        if (nNewSelection < 0 || nNewSelection >= m_nNumColours)
+        {
+            CWnd::OnMouseMove(nFlags, point);
+            return;
+        }
+    }
+
+    // OK - we have the row and column of the current selection (may be CUSTOM_BOX_VALUE)
+    // Has the row/col selection changed? If yes, then redraw old and new cells.
+    if (nNewSelection != m_nCurrentSel)
+        ChangeSelection(nNewSelection);
+
+    CWnd::OnMouseMove(nFlags, point);
+}
+
+// End selection on LButtonUp
+void CColourPopup::OnLButtonUp(UINT nFlags, CPoint point) 
+{    
+    CWnd::OnLButtonUp(nFlags, point);
+
+    DWORD pos = GetMessagePos();
+    point = CPoint(LOWORD(pos), HIWORD(pos));
+
+    if (m_WindowRect.PtInRect(point))
+        EndSelection(CPN_SELENDOK);
+    else
+        EndSelection(CPN_SELENDCANCEL);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup implementation
+
+int CColourPopup::GetIndex(int row, int col) const
+{ 
+    if ((row == CUSTOM_BOX_VALUE || col == CUSTOM_BOX_VALUE) && m_strCustomText.GetLength())
+        return CUSTOM_BOX_VALUE;
+    else if ((row == DEFAULT_BOX_VALUE || col == DEFAULT_BOX_VALUE) && m_strDefaultText.GetLength())
+        return DEFAULT_BOX_VALUE;
+    else if (row < 0 || col < 0 || row >= m_nNumRows || col >= m_nNumColumns)
+        return INVALID_COLOUR;
+    else
+    {
+        if (row*m_nNumColumns + col >= m_nNumColours)
+            return INVALID_COLOUR;
+        else
+            return row*m_nNumColumns + col;
+    }
+}
+
+int CColourPopup::GetRow(int nIndex) const               
+{ 
+    if (nIndex == CUSTOM_BOX_VALUE && m_strCustomText.GetLength())
+        return CUSTOM_BOX_VALUE;
+    else if (nIndex == DEFAULT_BOX_VALUE && m_strDefaultText.GetLength())
+        return DEFAULT_BOX_VALUE;
+    else if (nIndex < 0 || nIndex >= m_nNumColours)
+        return INVALID_COLOUR;
+    else
+        return nIndex / m_nNumColumns; 
+}
+
+int CColourPopup::GetColumn(int nIndex) const            
+{ 
+    if (nIndex == CUSTOM_BOX_VALUE && m_strCustomText.GetLength())
+        return CUSTOM_BOX_VALUE;
+    else if (nIndex == DEFAULT_BOX_VALUE && m_strDefaultText.GetLength())
+        return DEFAULT_BOX_VALUE;
+    else if (nIndex < 0 || nIndex >= m_nNumColours)
+        return INVALID_COLOUR;
+    else
+        return nIndex % m_nNumColumns; 
+}
+
+void CColourPopup::FindCellFromColour(COLORREF crColour)
+{
+    if (crColour == CLR_DEFAULT && m_strDefaultText.GetLength())
+    {
+        m_nChosenColourSel = DEFAULT_BOX_VALUE;
+        return;
+    }
+
+    for (int i = 0; i < m_nNumColours; i++)
+    {
+        if (GetColour(i) == crColour)
+        {
+            m_nChosenColourSel = i;
+            return;
+        }
+    }
+
+    if (m_strCustomText.GetLength())
+        m_nChosenColourSel = CUSTOM_BOX_VALUE;
+    else
+        m_nChosenColourSel = INVALID_COLOUR;
+}
+
+// Gets the dimensions of the colour cell given by (row,col)
+BOOL CColourPopup::GetCellRect(int nIndex, const LPRECT& rect)
+{
+    if (nIndex == CUSTOM_BOX_VALUE)
+    {
+        ::SetRect(rect, 
+                  m_CustomTextRect.left,  m_CustomTextRect.top,
+                  m_CustomTextRect.right, m_CustomTextRect.bottom);
+        return TRUE;
+    }
+    else if (nIndex == DEFAULT_BOX_VALUE)
+    {
+        ::SetRect(rect, 
+                  m_DefaultTextRect.left,  m_DefaultTextRect.top,
+                  m_DefaultTextRect.right, m_DefaultTextRect.bottom);
+        return TRUE;
+    }
+
+    if (nIndex < 0 || nIndex >= m_nNumColours)
+        return FALSE;
+
+    rect->left = GetColumn(nIndex) * m_nBoxSize + m_nMargin;
+    rect->top  = GetRow(nIndex) * m_nBoxSize + m_nMargin;
+
+    // Move everything down if we are displaying a default text area
+    if (m_strDefaultText.GetLength()) 
+        rect->top += (m_nMargin + m_DefaultTextRect.Height());
+
+    rect->right = rect->left + m_nBoxSize;
+    rect->bottom = rect->top + m_nBoxSize;
+
+    return TRUE;
+}
+
+// Works out an appropriate size and position of this window
+void CColourPopup::SetWindowSize()
+{
+    CSize TextSize;
+
+    // If we are showing a custom or default text area, get the font and text size.
+    if (m_strCustomText.GetLength() || m_strDefaultText.GetLength())
+    {
+        CClientDC dc(this);
+        CFont* pOldFont = (CFont*) dc.SelectObject(&m_Font);
+
+        // Get the size of the custom text (if there IS custom text)
+        TextSize = CSize(0,0);
+        if (m_strCustomText.GetLength())
+            TextSize = dc.GetTextExtent(m_strCustomText);
+
+        // Get the size of the default text (if there IS default text)
+        if (m_strDefaultText.GetLength())
+        {
+            CSize DefaultSize = dc.GetTextExtent(m_strDefaultText);
+            if (DefaultSize.cx > TextSize.cx) TextSize.cx = DefaultSize.cx;
+            if (DefaultSize.cy > TextSize.cy) TextSize.cy = DefaultSize.cy;
+        }
+
+        dc.SelectObject(pOldFont);
+        TextSize += CSize(2*m_nMargin,2*m_nMargin);
+
+        // Add even more space to draw the horizontal line
+        TextSize.cy += 2*m_nMargin + 2;
+    }
+
+    // Get the number of columns and rows
+    //m_nNumColumns = (int) sqrt((double)m_nNumColours);    // for a square window (yuk)
+    m_nNumColumns = 8;
+    m_nNumRows = m_nNumColours / m_nNumColumns;
+    if (m_nNumColours % m_nNumColumns) m_nNumRows++;
+
+    // Get the current window position, and set the new size
+    CRect rect;
+    GetWindowRect(rect);
+
+    m_WindowRect.SetRect(rect.left, rect.top, 
+                         rect.left + m_nNumColumns*m_nBoxSize + 2*m_nMargin,
+                         rect.top  + m_nNumRows*m_nBoxSize + 2*m_nMargin);
+
+    // if custom text, then expand window if necessary, and set text width as
+    // window width
+    if (m_strDefaultText.GetLength()) 
+    {
+        if (TextSize.cx > m_WindowRect.Width())
+            m_WindowRect.right = m_WindowRect.left + TextSize.cx;
+        TextSize.cx = m_WindowRect.Width()-2*m_nMargin;
+
+        // Work out the text area
+        m_DefaultTextRect.SetRect(m_nMargin, m_nMargin, 
+                                  m_nMargin+TextSize.cx, 2*m_nMargin+TextSize.cy);
+        m_WindowRect.bottom += m_DefaultTextRect.Height() + 2*m_nMargin;
+    }
+
+    // if custom text, then expand window if necessary, and set text width as
+    // window width
+    if (m_strCustomText.GetLength()) 
+    {
+        if (TextSize.cx > m_WindowRect.Width())
+            m_WindowRect.right = m_WindowRect.left + TextSize.cx;
+        TextSize.cx = m_WindowRect.Width()-2*m_nMargin;
+
+        // Work out the text area
+        m_CustomTextRect.SetRect(m_nMargin, m_WindowRect.Height(), 
+                                 m_nMargin+TextSize.cx, 
+                                 m_WindowRect.Height()+m_nMargin+TextSize.cy);
+        m_WindowRect.bottom += m_CustomTextRect.Height() + 2*m_nMargin;
+   }
+
+    // Need to check it'll fit on screen: Too far right?
+    CSize ScreenSize(::GetSystemMetrics(SM_CXSCREEN), ::GetSystemMetrics(SM_CYSCREEN));
+    if (m_WindowRect.right > ScreenSize.cx)
+        m_WindowRect.OffsetRect(-(m_WindowRect.right - ScreenSize.cx), 0);
+
+    // Too far left?
+    if (m_WindowRect.left < 0)
+        m_WindowRect.OffsetRect( -m_WindowRect.left, 0);
+
+    // Bottom falling out of screen?
+    if (m_WindowRect.bottom > ScreenSize.cy)
+    {
+        CRect ParentRect;
+        m_pParent->GetWindowRect(ParentRect);
+        m_WindowRect.OffsetRect(0, -(ParentRect.Height() + m_WindowRect.Height()));
+    }
+
+    // Set the window size and position
+    MoveWindow(m_WindowRect, TRUE);
+}
+
+void CColourPopup::CreateToolTips()
+{
+    // Create the tool tip
+    if (!m_ToolTip.Create(this)) return;
+
+    // Add a tool for each cell
+    for (int i = 0; i < m_nNumColours; i++)
+    {
+        CRect rect;
+        if (!GetCellRect(i, rect)) continue;
+            m_ToolTip.AddTool(this, GetColourName(i), rect, 1);
+    }
+}
+
+void CColourPopup::ChangeSelection(int nIndex)
+{
+    CClientDC dc(this);        // device context for drawing
+
+    if (nIndex > m_nNumColours)
+        nIndex = CUSTOM_BOX_VALUE; 
+
+    if ((m_nCurrentSel >= 0 && m_nCurrentSel < m_nNumColours) ||
+        m_nCurrentSel == CUSTOM_BOX_VALUE || m_nCurrentSel == DEFAULT_BOX_VALUE)
+    {
+        // Set Current selection as invalid and redraw old selection (this way
+        // the old selection will be drawn unselected)
+        int OldSel = m_nCurrentSel;
+        m_nCurrentSel = INVALID_COLOUR;
+        DrawCell(&dc, OldSel);
+    }
+
+    // Set the current selection as row/col and draw (it will be drawn selected)
+    m_nCurrentSel = nIndex;
+    DrawCell(&dc, m_nCurrentSel);
+
+    // Store the current colour
+    if (m_nCurrentSel == CUSTOM_BOX_VALUE)
+        m_pParent->SendMessage(CPN_SELCHANGE, (WPARAM) m_crInitialColour, 0);
+    else if (m_nCurrentSel == DEFAULT_BOX_VALUE)
+    {
+        m_crColour = CLR_DEFAULT;
+        m_pParent->SendMessage(CPN_SELCHANGE, (WPARAM) CLR_DEFAULT, 0);
+    }
+    else
+    {
+        m_crColour = GetColour(m_nCurrentSel);
+        m_pParent->SendMessage(CPN_SELCHANGE, (WPARAM) m_crColour, 0);
+    }
+}
+
+void CColourPopup::EndSelection(int nMessage)
+{
+    ReleaseCapture();
+
+    // If custom text selected, perform a custom colour selection
+    if (nMessage != CPN_SELENDCANCEL && m_nCurrentSel == CUSTOM_BOX_VALUE)
+    {
+        m_bChildWindowVisible = TRUE;
+
+        CColorDialog dlg(m_crInitialColour, CC_FULLOPEN | CC_ANYCOLOR, this);
+
+        if (dlg.DoModal() == IDOK)
+            m_crColour = dlg.GetColor();
+        else
+            nMessage = CPN_SELENDCANCEL;
+
+        m_bChildWindowVisible = FALSE;
+    } 
+
+    if (nMessage == CPN_SELENDCANCEL)
+        m_crColour = m_crInitialColour;
+
+    m_pParent->SendMessage(nMessage, (WPARAM) m_crColour, 0);
+    
+    // Kill focus bug fixed by Martin Wawrusch
+    if (!m_bChildWindowVisible)
+        DestroyWindow();
+}
+
+void CColourPopup::DrawCell(CDC* pDC, int nIndex)
+{
+    // For the Custom Text area
+    if (m_strCustomText.GetLength() && nIndex == CUSTOM_BOX_VALUE)
+    {
+        // The extent of the actual text button
+        CRect TextButtonRect = m_CustomTextRect;
+        TextButtonRect.top += 2*m_nMargin;
+
+        // Fill background
+        pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));
+
+        // Draw horizontal line
+        pDC->FillSolidRect(m_CustomTextRect.left+2*m_nMargin, m_CustomTextRect.top,
+                           m_CustomTextRect.Width()-4*m_nMargin, 1, ::GetSysColor(COLOR_3DSHADOW));
+        pDC->FillSolidRect(m_CustomTextRect.left+2*m_nMargin, m_CustomTextRect.top+1,
+                           m_CustomTextRect.Width()-4*m_nMargin, 1, ::GetSysColor(COLOR_3DHILIGHT));
+
+        TextButtonRect.DeflateRect(1,1);
+
+        // fill background
+        if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
+            pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DLIGHT));
+        else
+            pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));
+
+        // Draw button
+        if (m_nCurrentSel == nIndex) 
+            pDC->DrawEdge(TextButtonRect, BDR_RAISEDINNER, BF_RECT);
+        else if (m_nChosenColourSel == nIndex)
+            pDC->DrawEdge(TextButtonRect, BDR_SUNKENOUTER, BF_RECT);
+
+        // Draw custom text
+        CFont *pOldFont = (CFont*) pDC->SelectObject(&m_Font);
+        pDC->SetBkMode(TRANSPARENT);
+        pDC->DrawText(m_strCustomText, TextButtonRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+        pDC->SelectObject(pOldFont);
+
+        return;
+    }        
+
+    // For the Default Text area
+    if (m_strDefaultText.GetLength() && nIndex == DEFAULT_BOX_VALUE)
+    {
+        // Fill background
+        pDC->FillSolidRect(m_DefaultTextRect, ::GetSysColor(COLOR_3DFACE));
+
+        // The extent of the actual text button
+        CRect TextButtonRect = m_DefaultTextRect;
+        TextButtonRect.DeflateRect(1,1);
+
+        // fill background
+        if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
+            pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DLIGHT));
+        else
+            pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));
+
+        // Draw thin line around text
+        CRect LineRect = TextButtonRect;
+        LineRect.DeflateRect(2*m_nMargin,2*m_nMargin);
+        CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
+        CPen* pOldPen = pDC->SelectObject(&pen);
+        pDC->SelectStockObject(NULL_BRUSH);
+        pDC->Rectangle(LineRect);
+        pDC->SelectObject(pOldPen);
+
+        // Draw button
+        if (m_nCurrentSel == nIndex) 
+            pDC->DrawEdge(TextButtonRect, BDR_RAISEDINNER, BF_RECT);
+        else if (m_nChosenColourSel == nIndex)
+            pDC->DrawEdge(TextButtonRect, BDR_SUNKENOUTER, BF_RECT);
+
+        // Draw custom text
+        CFont *pOldFont = (CFont*) pDC->SelectObject(&m_Font);
+        pDC->SetBkMode(TRANSPARENT);
+        pDC->DrawText(m_strDefaultText, TextButtonRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+        pDC->SelectObject(pOldFont);
+
+        return;
+    }        
+
+    CRect rect;
+    if (!GetCellRect(nIndex, rect)) return;
+
+    // Select and realize the palette
+    CPalette* pOldPalette = NULL;
+    if (pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
+    {
+        pOldPalette = pDC->SelectPalette(&m_Palette, FALSE);
+        pDC->RealizePalette();
+    }
+
+    // fill background
+    if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
+        pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DHILIGHT));
+    else
+        pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
+
+    // Draw button
+    if (m_nCurrentSel == nIndex) 
+        pDC->DrawEdge(rect, BDR_RAISEDINNER, BF_RECT);
+    else if (m_nChosenColourSel == nIndex)
+        pDC->DrawEdge(rect, BDR_SUNKENOUTER, BF_RECT);
+
+    CBrush brush(PALETTERGB(GetRValue(GetColour(nIndex)), 
+                            GetGValue(GetColour(nIndex)), 
+                            GetBValue(GetColour(nIndex)) ));
+    CPen   pen;
+    pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
+
+    CBrush* pOldBrush = (CBrush*) pDC->SelectObject(&brush);
+    CPen*   pOldPen   = (CPen*)   pDC->SelectObject(&pen);
+
+    // Draw the cell colour
+    rect.DeflateRect(m_nMargin+1, m_nMargin+1);
+    pDC->Rectangle(rect);
+
+    // restore DC and cleanup
+    pDC->SelectObject(pOldBrush);
+    pDC->SelectObject(pOldPen);
+    brush.DeleteObject();
+    pen.DeleteObject();
+
+    if (pOldPalette && pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
+        pDC->SelectPalette(pOldPalette, FALSE);
+}
+
+BOOL CColourPopup::OnQueryNewPalette() 
+{
+    Invalidate();    
+    return CWnd::OnQueryNewPalette();
+}
+
+void CColourPopup::OnPaletteChanged(CWnd* pFocusWnd) 
+{
+    CWnd::OnPaletteChanged(pFocusWnd);
+
+    if (pFocusWnd->GetSafeHwnd() != GetSafeHwnd())
+        Invalidate();
+}
+
+void CColourPopup::OnKillFocus(CWnd* pNewWnd) 
+{
+	CWnd::OnKillFocus(pNewWnd);
+
+    ReleaseCapture();
+    //DestroyWindow(); - causes crash when Custom colour dialog appears.
+}
+
+// KillFocus problem fix suggested by Paul Wilkerson.
+void CColourPopup::OnActivateApp(BOOL bActive, DWORD hTask) 
+{
+	CWnd::OnActivateApp(bActive, hTask);
+
+	// If Deactivating App, cancel this selection
+	if (!bActive)
+		 EndSelection(CPN_SELENDCANCEL);
+}

+ 128 - 0
src/RulerRichEditCtrl/External/ColourPopup.h

@@ -0,0 +1,128 @@
+#if !defined(AFX_COLOURPOPUP_H__D0B75902_9830_11D1_9C0F_00A0243D1382__INCLUDED_)
+#define AFX_COLOURPOPUP_H__D0B75902_9830_11D1_9C0F_00A0243D1382__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+// ColourPopup.h : header file
+//
+// Written by Chris Maunder ([email protected])
+// Extended by Alexander Bischofberger ([email protected])
+// Copyright (c) 1998.
+//
+// This code may be used in compiled form in any way you desire. This
+// file may be redistributed unmodified by any means PROVIDING it is 
+// not sold for profit without the authors written consent, and 
+// providing that this notice and the authors name is included. If 
+// the source code in  this file is used in any commercial application 
+// then a simple email would be nice.
+//
+// This file is provided "as is" with no expressed or implied warranty.
+// The author accepts no liability if it causes any damage whatsoever.
+// It's free - so you get what you pay for.
+
+
+// CColourPopup messages
+#define CPN_SELCHANGE        WM_USER + 1001        // Colour Picker Selection change
+#define CPN_DROPDOWN         WM_USER + 1002        // Colour Picker drop down
+#define CPN_CLOSEUP          WM_USER + 1003        // Colour Picker close up
+#define CPN_SELENDOK         WM_USER + 1004        // Colour Picker end OK
+#define CPN_SELENDCANCEL     WM_USER + 1005        // Colour Picker end (cancelled)
+
+// forward declaration
+class CColourPicker;
+
+// To hold the colours and their names
+typedef struct {
+    COLORREF crColour;
+    TCHAR    *szName;
+} ColourTableEntry;
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup window
+
+class CColourPopup : public CWnd
+{
+// Construction
+public:
+    CColourPopup();
+    CColourPopup(CPoint p, COLORREF crColour, CWnd* pParentWnd,
+                 LPCTSTR szDefaultText = NULL, LPCTSTR szCustomText = NULL);
+    void Initialise();
+
+// Attributes
+public:
+
+// Operations
+public:
+    BOOL Create(CPoint p, COLORREF crColour, CWnd* pParentWnd, 
+                LPCTSTR szDefaultText = NULL, LPCTSTR szCustomText = NULL);
+
+// Overrides
+    // ClassWizard generated virtual function overrides
+    //{{AFX_VIRTUAL(CColourPopup)
+    public:
+    virtual BOOL PreTranslateMessage(MSG* pMsg);
+    //}}AFX_VIRTUAL
+
+// Implementation
+public:
+    virtual ~CColourPopup();
+
+protected:
+    BOOL GetCellRect(int nIndex, const LPRECT& rect);
+    void FindCellFromColour(COLORREF crColour);
+    void SetWindowSize();
+    void CreateToolTips();
+    void ChangeSelection(int nIndex);
+    void EndSelection(int nMessage);
+    void DrawCell(CDC* pDC, int nIndex);
+
+    COLORREF GetColour(int nIndex)              { return m_crColours[nIndex].crColour; }
+    LPCTSTR GetColourName(int nIndex)           { return m_crColours[nIndex].szName; }
+    int  GetIndex(int row, int col) const;
+    int  GetRow(int nIndex) const;
+    int  GetColumn(int nIndex) const;
+
+// protected attributes
+protected:
+    static ColourTableEntry m_crColours[];
+    int            m_nNumColours;
+    int            m_nNumColumns, m_nNumRows;
+    int            m_nBoxSize, m_nMargin;
+    int            m_nCurrentSel;
+    int            m_nChosenColourSel;
+    CString        m_strDefaultText;
+    CString        m_strCustomText;
+    CRect          m_CustomTextRect, m_DefaultTextRect, m_WindowRect;
+    CFont          m_Font;
+    CPalette       m_Palette;
+    COLORREF       m_crInitialColour, m_crColour;
+    CToolTipCtrl   m_ToolTip;
+    CWnd*          m_pParent;
+
+    BOOL           m_bChildWindowVisible;
+
+    // Generated message map functions
+protected:
+    //{{AFX_MSG(CColourPopup)
+    afx_msg void OnNcDestroy();
+    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+    afx_msg void OnPaint();
+    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
+    afx_msg BOOL OnQueryNewPalette();
+    afx_msg void OnPaletteChanged(CWnd* pFocusWnd);
+	afx_msg void OnKillFocus(CWnd* pNewWnd);
+	afx_msg void OnActivateApp(BOOL bActive, DWORD hTask);
+	//}}AFX_MSG
+    DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_COLOURPOPUP_H__D0B75902_9830_11D1_9C0F_00A0243D1382__INCLUDED_)

+ 436 - 0
src/RulerRichEditCtrl/External/StdioFileEx.cpp

@@ -0,0 +1,436 @@
+// StdioFileEx.cpp: implementation of the CStdioFileEx class.
+//
+// Version 1.1 23 August 2003. Incorporated fixes from Dennis Jeryd.
+// Version 1.3 19 February 2005. Incorporated fixes Howard J Oh and some of my own.
+//
+// Copyright David Pritchard 2003-2005. [email protected]
+//
+// You can use this class freely, but please keep my ego happy 
+// by leaving this comment in place.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "StdioFileEx.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+/*static*/ const UINT CStdioFileEx::modeWriteUnicode = 0x20000; // Add this flag to write in Unicode
+
+CStdioFileEx::CStdioFileEx(): CStdioFile()
+{
+	m_bIsUnicodeText = false;
+}
+
+CStdioFileEx::CStdioFileEx(LPCTSTR lpszFileName,UINT nOpenFlags)
+	:CStdioFile(lpszFileName, ProcessFlags(lpszFileName, nOpenFlags))
+{
+}
+
+BOOL CStdioFileEx::Open(LPCTSTR lpszFileName,UINT nOpenFlags,CFileException* pError /*=NULL*/)
+{
+	// Process any Unicode stuff
+	ProcessFlags(lpszFileName, nOpenFlags);
+
+	return CStdioFile::Open(lpszFileName, nOpenFlags, pError);
+}
+
+BOOL CStdioFileEx::ReadString(CString& rString)
+{
+	BOOL			bReadData;
+	LPTSTR		lpsz;
+	int			nLen = 0; //, nMultiByteBufferLength = 0, nChars = 0;
+	CString		sTemp;
+
+	// If at position 0, discard byte-order mark before reading
+	if (!m_pStream || (GetPosition() == 0 && m_bIsUnicodeText))
+	{
+		wchar_t	cDummy;
+		Read(&cDummy, sizeof(wchar_t));
+	}
+
+// If compiled for Unicode
+#ifdef _UNICODE
+	// Do standard stuff -- both ANSI and Unicode cases seem to work OK
+	bReadData = CStdioFile::ReadString(rString);
+#else
+
+	if (!m_bIsUnicodeText)
+	{
+		// Do standard stuff -- read ANSI in ANSI
+		bReadData = CStdioFile::ReadString(rString);
+	}
+	else
+	{
+		const int nMAX_LINE_CHARS = 4096;
+		wchar_t* pszUnicodeString = new wchar_t[nMAX_LINE_CHARS]; 
+		char* pszMultiByteString= new char[nMAX_LINE_CHARS];  
+
+		// Read as Unicode, convert to ANSI; fixed by Dennis Jeryd 6/8/03
+		bReadData = (NULL != fgetws(pszUnicodeString, nMAX_LINE_CHARS, m_pStream));
+		if (GetMultiByteStringFromUnicodeString(pszUnicodeString, pszMultiByteString, nMAX_LINE_CHARS))
+		{
+			rString = (CString)pszMultiByteString;
+		}
+
+		if (pszUnicodeString)
+		{
+			delete pszUnicodeString;
+		}
+
+		if (pszMultiByteString)
+		{
+			delete pszMultiByteString;
+		}
+	}
+#endif
+
+	// Then remove end-of-line character if in Unicode text mode
+	if (bReadData)
+	{
+		// Copied from FileTxt.cpp but adapted to Unicode and then adapted for end-of-line being just '\r'. 
+		nLen = rString.GetLength();
+		if (nLen > 1 && rString.Mid(nLen-2) == sNEWLINE)
+		{
+			rString.GetBufferSetLength(nLen-2);
+		}
+		else
+		{
+			lpsz = rString.GetBuffer(0);
+			if (nLen != 0 && (lpsz[nLen-1] == _T('\r') || lpsz[nLen-1] == _T('\n')))
+			{
+				rString.GetBufferSetLength(nLen-1);
+			}
+		}
+	}
+
+	return bReadData;
+}
+
+// --------------------------------------------------------------------------------------------
+//
+//	CStdioFileEx::WriteString()
+//
+// --------------------------------------------------------------------------------------------
+// Returns:    void
+// Parameters: LPCTSTR lpsz
+//
+// Purpose:		Writes string to file either in Unicode or multibyte, depending on whether the caller specified the
+//					CStdioFileEx::modeWriteUnicode flag. Override of base class function.
+// Notes:		If writing in Unicode we need to:
+//						a) Write the Byte-order-mark at the beginning of the file
+//						b) Write all strings in byte-mode
+//					-	If we were compiled in Unicode, we need to convert Unicode to multibyte if 
+//						we want to write in multibyte
+//					-	If we were compiled in multi-byte, we need to convert multibyte to Unicode if 
+//						we want to write in Unicode.
+// Exceptions:	None.
+//
+void CStdioFileEx::WriteString(LPCTSTR lpsz)
+{
+	// If writing Unicode and at the start of the file, need to write byte mark
+	if (m_nFlags & CStdioFileEx::modeWriteUnicode)
+	{
+		// If at position 0, write byte-order mark before writing anything else
+		if (!m_pStream || GetPosition() == 0)
+		{
+			wchar_t cBOM = (wchar_t)nUNICODE_BOM;
+			CFile::Write(&cBOM, sizeof(wchar_t));
+		}
+	}
+
+// If compiled in Unicode...
+#ifdef _UNICODE
+
+	// If writing Unicode, no conversion needed
+	if (m_nFlags & CStdioFileEx::modeWriteUnicode)
+	{
+		// Write in byte mode
+		CFile::Write(lpsz, lstrlen(lpsz) * sizeof(wchar_t));
+	}
+	// Else if we don't want to write Unicode, need to convert
+	else
+	{
+		int		nChars = lstrlen(lpsz) + 1;				// Why plus 1? Because yes
+		int		nBufferSize = nChars * sizeof(char);
+		wchar_t*	pszUnicodeString	= new wchar_t[nChars]; 
+		char	*	pszMultiByteString= new char[nChars];  
+		int		nCharsWritten = 0;
+
+		// Copy string to Unicode buffer
+		lstrcpy(pszUnicodeString, lpsz);
+
+		// Get multibyte string
+		nCharsWritten = 
+			GetMultiByteStringFromUnicodeString(pszUnicodeString, pszMultiByteString, ( short ) nBufferSize, GetACP());
+		
+		if (nCharsWritten > 0)
+		{
+			//   CFile::Write((const void*)pszMultiByteString, lstrlen(lpsz));
+
+			// Do byte-mode write using actual chars written (fix by Howard J Oh)
+			CFile::Write((const void*)pszMultiByteString,
+				nCharsWritten*sizeof(char));
+		}
+
+		if (pszUnicodeString && pszMultiByteString)
+		{
+			delete [] pszUnicodeString;
+			delete [] pszMultiByteString;
+		}
+	}
+// Else if *not* compiled in Unicode
+#else
+	// If writing Unicode, need to convert
+	if (m_nFlags & CStdioFileEx::modeWriteUnicode)
+	{
+		int		nChars = lstrlen(lpsz) + 1;	 // Why plus 1? Because yes
+		wchar_t*	pszUnicodeString	= new wchar_t[nChars];
+		char	*	pszMultiByteString= new char[nChars]; 
+		int		nCharsWritten = 0;
+		
+		// Copy string to multibyte buffer
+		lstrcpy(pszMultiByteString, lpsz);
+
+		nCharsWritten =
+			GetUnicodeStringFromMultiByteString(pszMultiByteString,
+			pszUnicodeString, nChars, GetACP());
+		
+		if (nCharsWritten > 0)
+		{
+			//   CFile::Write(pszUnicodeString, lstrlen(lpsz) * sizeof(wchar_t));
+
+			// Write in byte mode. Write actual number of chars written * bytes (fix by Howard J Oh)
+			CFile::Write(pszUnicodeString, nCharsWritten*sizeof(wchar_t));
+		}
+		else
+		{
+			ASSERT(false);
+		}
+
+		if (pszUnicodeString && pszMultiByteString)
+		{
+			delete [] pszUnicodeString;
+			delete [] pszMultiByteString;
+		}
+	}
+	// Else if we don't want to write Unicode, no conversion needed
+	else
+	{
+		// Do standard stuff
+		//CStdioFile::WriteString(lpsz);
+		
+		// Do byte-mode write. This avoids annoying "interpretation" of \n's as	\r\n
+		CFile::Write((const void*)lpsz, lstrlen(lpsz)*sizeof(char));
+	}
+
+#endif
+}
+
+UINT CStdioFileEx::ProcessFlags(const CString& sFilePath, UINT& nOpenFlags)
+{
+	m_bIsUnicodeText = false;
+
+	// If we have writeUnicode we must have write or writeRead as well
+#ifdef _DEBUG
+	if (nOpenFlags & CStdioFileEx::modeWriteUnicode)
+	{
+		ASSERT(nOpenFlags & CFile::modeWrite || nOpenFlags & CFile::modeReadWrite);
+	}
+#endif
+
+	// If reading in text mode and not creating... ; fixed by Dennis Jeryd 6/8/03
+	if (nOpenFlags & CFile::typeText && !(nOpenFlags & CFile::modeCreate) && !(nOpenFlags & CFile::modeWrite ))
+	{
+		m_bIsUnicodeText = IsFileUnicode(sFilePath);
+
+		// If it's Unicode, switch to binary mode
+		if (m_bIsUnicodeText)
+		{
+			nOpenFlags ^= CFile::typeText;
+			nOpenFlags |= CFile::typeBinary;
+		}
+	}
+
+	m_nFlags = nOpenFlags;
+
+	return nOpenFlags;
+}
+
+// --------------------------------------------------------------------------------------------
+//
+//	CStdioFileEx::IsFileUnicode()
+//
+// --------------------------------------------------------------------------------------------
+// Returns:    bool
+// Parameters: const CString& sFilePath
+//
+// Purpose:		Determines whether a file is Unicode by reading the first character and detecting
+//					whether it's the Unicode byte marker.
+// Notes:		None.
+// Exceptions:	None.
+//
+/*static*/ bool CStdioFileEx::IsFileUnicode(const CString& sFilePath)
+{
+	CFile				file;
+	bool				bIsUnicode = false;
+	wchar_t			cFirstChar;
+	CFileException	exFile;
+
+	// Open file in binary mode and read first character
+	if (file.Open(sFilePath, CFile::typeBinary | CFile::modeRead, &exFile))
+	{
+		// If byte is Unicode byte-order marker, let's say it's Unicode
+		if (file.Read(&cFirstChar, sizeof(wchar_t)) > 0 && cFirstChar == (wchar_t)nUNICODE_BOM)
+		{
+			bIsUnicode = true;
+		}
+
+		file.Close();
+	}
+	else
+	{
+		// Handle error here if you like
+	}
+
+	return bIsUnicode;
+}
+
+unsigned long CStdioFileEx::GetCharCount()
+{
+	int				nCharSize;
+	unsigned long	nByteCount, nCharCount = 0;
+
+	if (m_pStream)
+	{
+		// Get size of chars in file
+		nCharSize = m_bIsUnicodeText ? sizeof(wchar_t): sizeof(char);
+
+		// If Unicode, remove byte order mark from count
+		nByteCount = (long)GetLength();
+		
+		if (m_bIsUnicodeText)
+		{
+			nByteCount = nByteCount - sizeof(wchar_t);
+		}
+
+		// Calc chars
+		nCharCount = (nByteCount / nCharSize);
+	}
+
+	return nCharCount;
+}
+
+// --------------------------------------------------------------------------------------------
+//
+//	CStdioFileEx::GetUnicodeStringFromMultiByteString()
+//
+// --------------------------------------------------------------------------------------------
+// Returns:    int - num. of chars written (0 means error)
+// Parameters: char *		szMultiByteString		(IN)		Multi-byte input string
+//					wchar_t*		szUnicodeString		(OUT)		Unicode outputstring
+//					int&			nUnicodeBufferSize	(IN/OUT)	Size of Unicode output buffer(IN)
+//																			Actual bytes written to buffer (OUT)
+//					UINT			nCodePage				(IN)		Code page used to perform conversion
+//																			Default = -1 (Get local code page).
+//
+// Purpose:		Gets a Unicode string from a MultiByte string.
+// Notes:		None.
+// Exceptions:	None.
+//
+int CStdioFileEx::GetUnicodeStringFromMultiByteString(IN char * szMultiByteString, OUT wchar_t* szUnicodeString, IN OUT int& nUnicodeBufferSize, IN UINT nCodePage)
+{
+	int		nCharsWritten = 0;
+		
+	if (szUnicodeString && szMultiByteString)
+	{
+		// If no code page specified, take default for system
+		if (nCodePage == -1)
+		{
+			nCodePage = GetACP();
+		}
+
+		try 
+		{
+			// Zero out buffer first. NB: nUnicodeBufferSize is NUMBER OF CHARS, NOT BYTES!
+			memset((void*)szUnicodeString, '\0', sizeof(wchar_t) *
+				nUnicodeBufferSize);
+
+			nCharsWritten = MultiByteToWideChar(nCodePage,MB_PRECOMPOSED,szMultiByteString,-1,szUnicodeString,nUnicodeBufferSize);
+		}
+		catch(...)
+		{
+			TRACE(_T("Controlled exception in MultiByteToWideChar!\n"));
+		}
+	}
+
+	// Now fix nCharsWritten
+	if (nCharsWritten > 0)
+	{
+		nCharsWritten--;
+	}
+	
+	ASSERT(nCharsWritten > 0);
+	return nCharsWritten;
+}
+
+// --------------------------------------------------------------------------------------------
+//
+//	CStdioFileEx::GetMultiByteStringFromUnicodeString()
+//
+// --------------------------------------------------------------------------------------------
+// Returns:    int - number of characters written. 0 means error
+// Parameters: wchar_t *	szUnicodeString			(IN)	Unicode input string
+//					char*			szMultiByteString			(OUT)	Multibyte output string
+//					short			nMultiByteBufferSize		(IN)	Multibyte buffer size
+//					UINT			nCodePage					(IN)	Code page used to perform conversion
+//																			Default = -1 (Get local code page).
+//
+// Purpose:		Gets a MultiByte string from a Unicode string
+// Notes:		None.
+// Exceptions:	None.
+//
+int CStdioFileEx::GetMultiByteStringFromUnicodeString(wchar_t * szUnicodeString, char* szMultiByteString, 
+																			short nMultiByteBufferSize, UINT nCodePage)
+{
+	BOOL		bUsedDefChar	= FALSE;
+	int		nCharsWritten = 0;
+
+	if (szUnicodeString && szMultiByteString) 
+	{
+		// Zero out buffer first
+		memset((void*)szMultiByteString, '\0', nMultiByteBufferSize);
+		
+		// If no code page specified, take default for system
+		if (nCodePage == -1)
+		{
+			nCodePage = GetACP();
+		}
+
+		try 
+		{
+			nCharsWritten = WideCharToMultiByte(nCodePage, WC_COMPOSITECHECK | WC_SEPCHARS,
+							szUnicodeString,-1, szMultiByteString, nMultiByteBufferSize, sDEFAULT_UNICODE_FILLER_CHAR, &bUsedDefChar);
+		}
+		catch(...) 
+		{
+			TRACE(_T("Controlled exception in WideCharToMultiByte!\n"));
+		}
+	} 
+
+	// Now fix nCharsWritten 
+	if (nCharsWritten > 0)
+	{
+		nCharsWritten--;
+	}
+	
+	return nCharsWritten;
+}

+ 103 - 0
src/RulerRichEditCtrl/External/StdioFileEx.h

@@ -0,0 +1,103 @@
+// StdioFileEx.h: interface for the CStdioFileEx class.
+//
+// Version 1.1 23 August 2003. Incorporated fixes from Dennis Jeryd.
+// Version 1.3 19 February 2005. Incorporated fixes Howard J Oh and some of my own.
+//
+// Copyright David Pritchard 2003-2004. [email protected]
+//
+// You can use this class freely, but please keep my ego happy 
+// by leaving this comment in place.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_STDIOFILEEX_H__41AFE3CA_25E0_482F_8B00_C40775BCDB81__INCLUDED_)
+#define AFX_STDIOFILEEX_H__41AFE3CA_25E0_482F_8B00_C40775BCDB81__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define	nUNICODE_BOM						0xFEFF		// Unicode "byte order mark" which goes at start of file
+#define	sNEWLINE								_T("\r\n")	// New line characters
+#define	sDEFAULT_UNICODE_FILLER_CHAR	"#"			// Filler char used when no conversion from Unicode to local code page is possible
+
+class CStdioFileEx: public CStdioFile
+{
+public:
+	CStdioFileEx();
+	CStdioFileEx( LPCTSTR lpszFileName, UINT nOpenFlags );
+
+	virtual BOOL	Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL );
+	virtual BOOL	ReadString(CString& rString);
+	virtual void	WriteString( LPCTSTR lpsz );
+	bool				IsFileUnicodeText()	{ return m_bIsUnicodeText; }	
+	unsigned long	GetCharCount();
+
+	// Additional flag to allow Unicode text writing
+	static const UINT modeWriteUnicode;
+
+	// static utility functions
+
+	// --------------------------------------------------------------------------------------------
+	//
+	//	CStdioFileEx::GetUnicodeStringFromMultiByteString()
+	//
+	// --------------------------------------------------------------------------------------------
+	// Returns:    int - num. of chars written (0 means error)
+	// Parameters: char *		szMultiByteString		(IN)		Multi-byte input string
+	//					wchar_t*		szUnicodeString		(OUT)		Unicode outputstring
+	//					int&			nUnicodeBufferSize	(IN/OUT)	Size of Unicode output buffer(IN)
+	//																			Actual bytes written to buffer (OUT)
+	//					UINT			nCodePage				(IN)		Code page used to perform conversion
+	//																			Default = -1 (Get local code page).
+	//
+	// Purpose:		Gets a Unicode string from a MultiByte string.
+	// Notes:		None.
+	// Exceptions:	None.
+	//
+	static int		GetUnicodeStringFromMultiByteString(IN char * szMultiByteString, OUT wchar_t* szUnicodeString, IN OUT int& nUnicodeBufferSize, IN UINT nCodePage=-1);
+
+	// --------------------------------------------------------------------------------------------
+	//
+	//	CStdioFileEx::GetMultiByteStringFromUnicodeString()
+	//
+	// --------------------------------------------------------------------------------------------
+	// Returns:    int - number of characters written. 0 means error
+	// Parameters: wchar_t *	szUnicodeString			(IN)	Unicode input string
+	//					char*			szMultiByteString			(OUT)	Multibyte output string
+	//					short			nMultiByteBufferSize		(IN)	Multibyte buffer size
+	//					UINT			nCodePage					(IN)	Code page used to perform conversion
+	//																			Default = -1 (Get local code page).
+	//
+	// Purpose:		Gets a MultiByte string from a Unicode string.
+	// Notes:		.
+	// Exceptions:	None.
+	//
+	static int			GetMultiByteStringFromUnicodeString(wchar_t * szUnicodeString,char* szMultiByteString,
+																			short nMultiByteBufferSize,UINT nCodePage=-1);
+
+
+	// --------------------------------------------------------------------------------------------
+	//
+	//	CStdioFileEx::IsFileUnicode()
+	//
+	// --------------------------------------------------------------------------------------------
+	// Returns:    bool
+	// Parameters: const CString& sFilePath
+	//
+	// Purpose:		Determines whether a file is Unicode by reading the first character and detecting
+	//					whether it's the Unicode byte marker.
+	// Notes:		None.
+	// Exceptions:	None.
+	//
+	static bool IsFileUnicode(const CString& sFilePath);
+
+
+protected:
+	UINT ProcessFlags(const CString& sFilePath, UINT& nOpenFlags);
+
+	bool		m_bIsUnicodeText;
+	UINT		m_nFlags;
+};
+
+#endif // !defined(AFX_STDIOFILEEX_H__41AFE3CA_25E0_482F_8B00_C40775BCDB81__INCLUDED_)

+ 214 - 0
src/RulerRichEditCtrl/FontComboBox.cpp

@@ -0,0 +1,214 @@
+/* ==========================================================================
+	File :			FontComboBox.cpp
+
+	Class :			CFontComboBox
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+					Iain Clarke
+
+	Date :			2005-05-06
+
+	Purpose :		CFontComboBox is derived from "CComboBox" and is an 
+					autosizing no-frills combobox for display of the fonts 
+					installed in the system.. 
+
+	Description :	Simpel derived class with members to fill the box, to 
+					autosize the dropdown and select an entry by name.
+
+	Usage :			Create as any combobox, and call "FillCombo" to fill 
+					the control with the names of the fonts installed in 
+					the system. Call "SelectFontName" to select a font by 
+					name.
+
+   ========================================================================*/
+
+#include "stdafx.h"
+#include "FontComboBox.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// Enumeration callback function for 
+// installed fonts
+BOOL CALLBACK EnumFontProc( LPLOGFONT lplf, LPTEXTMETRIC /*lptm*/, DWORD /*dwType*/, LPARAM lpData )	
+{	
+	CFontComboBox *caller = reinterpret_cast< CFontComboBox* > ( lpData );		
+	caller->AddString( lplf->lfFaceName );
+
+	CClientDC dc( caller );
+	dc.SelectStockObject( ANSI_VAR_FONT );
+
+	// Add a "0" for the margin.
+	CSize sz = dc.GetTextExtent( CString( lplf->lfFaceName ) + CString( "0" ) );
+
+	caller->SetMaxWidth( max( caller->GetMaxWidth(), sz.cx ) );
+
+	return TRUE;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CFontComboBox
+
+CFontComboBox::CFontComboBox()
+/* ============================================================
+	Function :		CFontComboBox::CFontComboBox
+	Description :	ctor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+	m_maxWidth = 0;
+}
+
+CFontComboBox::~CFontComboBox()
+/* ============================================================
+	Function :		CFontComboBox::~CFontComboBox
+	Description :	dtor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+BEGIN_MESSAGE_MAP(CFontComboBox, CComboBox)
+	//{{AFX_MSG_MAP(CFontComboBox)
+	ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropdown)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CFontComboBox message handlers
+
+void CFontComboBox::OnDropdown() 
+/* ============================================================
+	Function :		CFontComboBox::OnDropdown
+	Description :	Sets the dropped down width of the combo 
+					control to the max width of the string in 
+					the list
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+   int scroll = ::GetSystemMetrics(SM_CXVSCROLL);
+   SetDroppedWidth( m_maxWidth + scroll );
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CFontComboBox implementation
+
+void CFontComboBox::FillCombo()
+/* ============================================================
+	Function :		CFontComboBox::FillCombo
+	Description :	Fills the combo with the font names from 
+					the system
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to fill the combo with the installed 
+					fonts.
+
+   ============================================================*/
+{
+
+	CClientDC dc( this );		
+	::EnumFonts( dc, 0, ( FONTENUMPROC ) EnumFontProc, ( LPARAM ) this );
+
+}
+
+void CFontComboBox::SetMaxWidth( int maxWidth )
+/* ============================================================
+	Function :		CFontComboBox::SetMaxWidth
+	Description :	Sets the "m_maxWidth" member.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	int maxWidth	-	New width of the drop
+										down list
+
+	Usage :			Call to set a max width for the strings in 
+					the list, used to set the width of the drop 
+					down list. Called from the font enum 
+					callback.
+
+   ============================================================*/
+{
+
+	m_maxWidth = maxWidth;
+
+}
+
+int CFontComboBox::GetMaxWidth() const
+/* ============================================================
+	Function :		CFontComboBox::GetMaxWidth
+	Description :	Returns the "m_maxWidth" member of the 
+					control.
+	Access :		Public
+					
+	Return :		int		-	Width of the longest string, in 
+								pixels.
+	Parameters :	none
+
+	Usage :			Call to get the width necessary to display 
+					the longest string in the combo drop down.
+
+   ============================================================*/
+{
+
+	return m_maxWidth;
+
+}
+
+void CFontComboBox::SelectFontName( const CString& font )
+/* ============================================================
+	Function :		CFontComboBox::SelectFontName
+	Description :	Selects "font" in the list, if it 
+					exists.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	const CString& font	-	Name of the font to 
+											select.
+
+	Usage :			Call to set/change the selection of the 
+					combo.
+
+   ============================================================*/
+{
+
+	int max = GetCount();
+	for( int t = 0 ; t < max ; t++ )
+	{
+		CString data;
+		GetLBText( t, data );
+		if( data == font )
+		{
+			SetCurSel( t );
+			return;
+		}
+	}
+
+}
+

+ 57 - 0
src/RulerRichEditCtrl/FontComboBox.h

@@ -0,0 +1,57 @@
+#if !defined(AFX_FONTCOMBOBOX_H__B88A8EAC_D643_444C_B9B0_87CE8DC81E89__INCLUDED_)
+#define AFX_FONTCOMBOBOX_H__B88A8EAC_D643_444C_B9B0_87CE8DC81E89__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// FontComboBox.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CFontComboBox window
+
+class CFontComboBox : public CComboBox
+{
+// Construction
+public:
+	CFontComboBox();
+
+// Attributes
+public:
+
+// Operations
+public:
+	void FillCombo();
+	void SetMaxWidth( int maxWidth );
+	int GetMaxWidth() const;
+	void SelectFontName( const CString& font );
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CFontComboBox)
+	public:
+	//}}AFX_VIRTUAL
+
+// Implementation
+public:
+	virtual ~CFontComboBox();
+
+	// Generated message map functions
+protected:
+	//{{AFX_MSG(CFontComboBox)
+	afx_msg void OnDropdown();
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+
+private:
+	int m_maxWidth;
+
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_FONTCOMBOBOX_H__B88A8EAC_D643_444C_B9B0_87CE8DC81E89__INCLUDED_)

+ 451 - 0
src/RulerRichEditCtrl/RRECRuler.cpp

@@ -0,0 +1,451 @@
+/* ==========================================================================
+	File :			RRECRuler.cpp
+
+	Class :			CRRECRuler
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+					Iain Clarke
+
+	Date :			2004-04-19
+
+	Purpose :		This class encapsulates a ruler that can be used with 
+					"CRulerRichEditCtrl". The class is derived from "CWnd", and 
+					draws a ruler. 
+
+	Description :	A plain "CWnd"-derived class. The mouse messages 
+					"WM_LBUTTONDOWN", "WM_MOUSEMOVE" and "WM_LBUTTONUP" are handled, 
+					and a registered message is sent to the control parent.	
+
+	Usage :			Only tested with "CRulerRichEditCtrl". Add a member to the 
+					parent class, and create it with "Create". Handle the 
+					registered message "urm_RULERACTION" in the parent class. 
+					The parent class is also expected to handle 
+					"urm_GETSCROLLPOS", which should return the current 
+					horisontal scrollbar position.
+
+   ========================================================================*/
+
+#include "stdafx.h"
+#include "StdGrfx.h"
+#include "RRECRuler.h"
+#include "ids.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+CRRECRuler::CRRECRuler()
+/* ============================================================
+	Function :		CRRECRuler::CRRECRuler
+	Description :	constructor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+
+	m_physicalInch = 0;
+	m_mode = MODE_METRIC;
+	m_margin = 0;
+
+}
+
+CRRECRuler::~CRRECRuler()
+/* ============================================================
+	Function :		CRRECRuler::~CRRECRuler
+	Description :	destructor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+BEGIN_MESSAGE_MAP(CRRECRuler, CWnd)
+	ON_WM_PAINT()
+	ON_WM_ERASEBKGND()
+	ON_WM_LBUTTONDOWN()
+	ON_WM_LBUTTONUP()
+	ON_WM_MOUSEMOVE()
+END_MESSAGE_MAP()
+
+
+BOOL CRRECRuler::Create( const CRect& rect, CWnd* parent, UINT id )
+/* ============================================================
+	Function :		CRRECRuler::Create
+	Description :	Creates the ruler control
+	Access :		Public
+					
+	Return :		BOOL				-	Result, "TRUE" if ok.
+	Parameters :	const CRect& rect	-	Coordinate rect
+					CWnd* parent		-	Parent
+					UINT id				-	Child window id.
+					
+	Usage :			Call to create the ruler control
+
+   ============================================================*/
+{
+	BOOL result = CWnd::Create( NULL, NULL, WS_CHILD | WS_VISIBLE, rect, parent, id );
+	if( result )
+	{
+		CClientDC dc( this );
+		m_physicalInch = dc.GetDeviceCaps( LOGPIXELSX );
+	}
+
+	return result;
+
+}
+
+void CRRECRuler::OnPaint()
+/* ============================================================
+	Function :		CRRECRuler::OnPaint
+	Description :	Handles the "WM_PAINT" message from Windows.
+					Draws the ruler to a memory "CDC" that is 
+					blitted to the screen (double buffering)
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+	CPaintDC mainDC(this);
+
+	// Set up data
+	int pos = (int)GetParent()->SendMessage( urm_GETSCROLLPOS );
+
+	CRect rect;
+	GetClientRect( rect );
+
+	// Create off-screen canvas
+	CDC dc;
+	CBitmap bmp;
+	bmp.CreateCompatibleBitmap( &mainDC, rect.Width(), rect.Height() );
+	dc.CreateCompatibleDC( &mainDC );
+	CBitmap* oldbmp = dc.SelectObject( &bmp );
+
+	// Set up canvas
+	dc.FillSolidRect( rect, RGB( 255, 255, 255 ) );
+	dc.SelectStockObject( BLACK_PEN );
+	dc.SelectStockObject( DEFAULT_GUI_FONT );
+	dc.SetBkColor( RGB( 255, 255, 255 ) );
+
+	// Set up data for the inner ruler window
+	CRect winRect( rect );
+	winRect.top += 3;
+	winRect.bottom -= 5;
+	winRect.right -= 3;
+	winRect.left += m_margin - 2;
+
+	int midpoint = winRect.top + ( winRect.Height() / 2 );
+	int leftmarg = winRect.left + 2 - pos;
+	int width = winRect.Height();
+	int t;
+
+	// Print the values in the ruler scale
+	if( m_mode == MODE_INCH )
+	{
+		int inch4 = ( int ) ( ( double ) m_physicalInch / 4.0 +.5 );
+		int inch8 = ( int ) ( ( double ) m_physicalInch / 8.0 +.5 );
+
+		// Drawing scale markers
+		for( t = ( leftmarg + ( int ) ( inch8 +.5 ) ) ; t < rect.right - m_margin ; t += ( int ) ( inch8 + .5 ) )
+		{
+			dc.MoveTo( t, midpoint - 1 );
+			dc.LineTo( t, midpoint + 1 );
+		}
+
+		for( t = leftmarg + inch4 ; t < rect.right - m_margin ; t += inch4 )
+		{
+			dc.MoveTo( t, midpoint - 3 );
+			dc.LineTo( t, midpoint + 3 );
+		}
+
+		CRect rectInch;
+		CString counter;
+		int count = 1;
+
+		// Drawing numbers
+		for( t = leftmarg + m_physicalInch ; t < rect.right - m_margin ; t += m_physicalInch )
+		{
+
+			rectInch.SetRect( t - width / 2, winRect.top + 2, t + width / 2, winRect.bottom - 2 );
+			counter.Format( _T( "%i" ), count );
+			dc.DrawText( counter, rectInch, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
+			count++;
+
+		}
+	}
+	else
+	{
+		int cm = ( int ) ( ( double ) m_physicalInch / 2.54 + .5 );
+		int cm2 = ( int ) ( ( double ) cm / 2.0 );
+
+		// Drawing scale markers
+		for( t = leftmarg + cm2 ; t < rect.right - m_margin ; t += cm2 )
+		{
+			dc.MoveTo( t, midpoint - 1 );
+			dc.LineTo( t, midpoint + 2 );
+		}
+
+		CRect rectNum;
+		CString counter;
+		int count = 1;
+
+		// Drawing numbers
+		for( t = leftmarg + cm ; t < rect.right - m_margin ; t += cm )
+		{
+			rectNum.SetRect( t - width / 2, winRect.top + 2, t + width / 2, winRect.bottom - 2 );
+			counter.Format( _T( "%i" ), count );
+			dc.DrawText( counter, rectNum, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
+			count++;
+
+		}
+	}
+
+	// Mask the surroundings
+	CRect left( rect );
+	left.right = winRect.left;
+	CRect right( rect );
+	right.left = rect.right - 3;
+	CRect top( rect );
+	top.bottom = winRect.top;
+	CRect bottom( rect );
+	bottom.top = winRect.bottom;
+
+	dc.FillRect( left, CStdGrfx::dialogBrush() );
+	dc.FillRect( right, CStdGrfx::dialogBrush() );
+	dc.FillRect( top, CStdGrfx::dialogBrush() );
+	dc.FillRect( bottom, CStdGrfx::dialogBrush() );
+
+	// Frame the inside
+	CStdGrfx::drawdoublesunken3dFrame( &dc, winRect );
+
+	// Draw tab markers
+	int max = (int)m_tabs.GetSize();
+	for( t = 0 ; t < max ; t++ )
+	{
+
+		int x = ( leftmarg + m_tabs[ t ] - 2 );
+		if( x > winRect.left && x + 3 < winRect.right )
+		{
+			dc.MoveTo( x, midpoint + 5 );
+			dc.LineTo( x + 6, midpoint + 5 );
+			dc.MoveTo( x, midpoint + 6 );
+			dc.LineTo( x + 6, midpoint + 6 );
+
+			dc.MoveTo( x + 2, midpoint + 7 );
+			dc.LineTo( x + 2, midpoint + 10 );
+			dc.MoveTo( x + 3, midpoint + 7 );
+			dc.LineTo( x + 3, midpoint + 10 );
+		}
+	}
+
+	// Frame the outside
+	dc.SelectObject( CStdGrfx::highlightPen() );
+	dc.MoveTo( rect.left + 1, rect.top );
+	dc.LineTo( rect.right - 1, rect.top );
+
+	// Restore...
+	dc.SelectStockObject( BLACK_PEN );
+
+	//... and out to screen
+	mainDC.BitBlt( 0, 0, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY );
+	dc.SelectObject( oldbmp );
+}
+
+BOOL CRRECRuler::OnEraseBkgnd( CDC* /*pDC*/ ) 
+/* ============================================================
+	Function :		CRRECRuler::OnEraseBkgnd
+	Description :	Returns "TRUE" to avoid flicker.
+	Access :		Protected
+					
+	Return :		BOOL		-	Always "TRUE"
+	Parameters :	CDC* pDC	-	Not used
+					
+	Usage :			Called from MFC. 
+
+   ============================================================*/
+{
+	
+	return TRUE;
+
+}
+
+void CRRECRuler::SetMode( int mode )
+/* ============================================================
+	Function :		CRRECRuler::SetMode
+	Description :	Sets the internal mode, that is, if the 
+					ruler should display inches or centimeters.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	int mode	-	Mode to use, "MODE_INCH" or 
+									"MODE_METRIC" (default)
+					
+	Usage :			Call to change the mode.
+
+   ============================================================*/
+{
+
+	m_mode = mode;
+
+	if( m_hWnd )
+	{
+
+		RedrawWindow();
+
+	}
+
+}
+
+void CRRECRuler::SetMargin( int margin )
+/* ============================================================
+	Function :		CRRECRuler::SetMargin
+	Description :	Sets the margin, in pixels, of the ruler.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	int margin	-	The margin to set
+					
+	Usage :			Call to set the margin of the ruler scale.
+
+   ============================================================*/
+{
+
+	m_margin = margin;
+
+	if( m_hWnd )
+	{
+
+		RedrawWindow();
+
+	}
+
+}
+
+int CRRECRuler::GetMode() const
+/* ============================================================
+	Function :		CRRECRuler::GetMode
+	Description :	Gets the mode, that is, either "MODE_INCH" or 
+					"MODE_METRIC", that is used to draw the ruler.
+	Access :		Public
+					
+	Return :		int		-	The mode, either "MODE_INCH" or 
+								"MODE_METRIC"
+	Parameters :	none
+
+	Usage :			Call to get the current mode.
+
+   ============================================================*/
+{
+
+	return m_mode;
+
+}
+
+void CRRECRuler::SetTabStops( const CDWordArray& arr )
+/* ============================================================
+	Function :		CRRECRuler::SetTabStops
+	Description :	Sets the tab stops in the internal tab stop 
+					array, in device pixels
+	Access :		Public
+					
+	Return :		void
+	Parameters :	const CDWordArray& arr	-	The array to 
+												copy from
+					
+	Usage :			Call to set the tab stops of the ruler.
+
+   ============================================================*/
+{
+
+	m_tabs.RemoveAll();
+	int max = (int)arr.GetSize();
+	for (int t = 0 ; t < max ; t++)
+	{
+		m_tabs.Add(arr[ t ]);
+	}
+
+}
+
+void CRRECRuler::OnLButtonDown(UINT nFlags, CPoint point)
+/* ============================================================
+	Function :		CRRECRuler::OnLButtonDown
+	Description :	Handles the "WM_LBUTTONDOWN" message. We send 
+					a message with the current cursor position 
+					to the parent control.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	UINT nFlags		-	Not used
+					CPoint point	-	Cursor position
+					
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	SetCapture();
+	GetParent()->SendMessage( urm_RULERACTION, DOWN, ( LPARAM ) &point );
+	CWnd::OnLButtonDown( nFlags,  point );
+
+}
+
+void CRRECRuler::OnLButtonUp(UINT nFlags, CPoint point)
+/* ============================================================
+	Function :		CRRECRuler::OnLButtonUp
+	Description :	Handles the "WM_LBUTTONUP" message. We send 
+					a message with the current cursor position 
+					to the parent control.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	UINT nFlags		-	Not used
+					CPoint point	-	Cursor position
+					
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	ReleaseCapture();
+	GetParent()->SendMessage( urm_RULERACTION, UP, ( LPARAM ) &point );
+	CWnd::OnLButtonUp( nFlags,  point );
+
+}
+
+void CRRECRuler::OnMouseMove(UINT nFlags, CPoint point)
+/* ============================================================
+	Function :		CRRECRuler::OnMouseMove
+	Description :	Handles the "WM_MOUSEMOVE" message. We send 
+					a message with the current cursor position 
+					to the parent control.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	UINT nFlags		-	Not used
+					CPoint point	-	Cursor position
+					
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	GetParent()->SendMessage( urm_RULERACTION, MOVE, ( LPARAM ) &point );
+	CWnd::OnMouseMove( nFlags,  point );
+
+}
+

+ 39 - 0
src/RulerRichEditCtrl/RRECRuler.h

@@ -0,0 +1,39 @@
+#ifndef _RRECRULER_H_
+#define _RRECRULER_H_
+
+class CRRECRuler : public CWnd 
+{
+
+public:
+// Construction/creation/destruction
+	CRRECRuler();
+	virtual ~CRRECRuler();
+	virtual BOOL Create( const CRect& rect, CWnd* parent, UINT id );
+
+// Implementation
+	void	SetMode( int mode );
+	int		GetMode() const;
+	void	SetMargin( int margin );
+	void	SetTabStops( const CDWordArray& arr );
+
+protected:
+// Message handlers
+	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+	afx_msg void OnPaint();
+	DECLARE_MESSAGE_MAP()
+
+private:
+// Internal data
+	int m_margin;		// Left margin of ruler in pixels
+	int m_mode;			// MODE_INCH/MODE_METRIC, what units 
+						// to use for the ruler measure.
+	int m_physicalInch;	// The number of device pixels for a 
+						// physical inch of the window CDC
+
+	CDWordArray	m_tabs;	// Tabulator settings in device pixels
+};
+
+#endif // _RRECRULER_H_

+ 393 - 0
src/RulerRichEditCtrl/RRECToolbar.cpp

@@ -0,0 +1,393 @@
+/* ==========================================================================
+	File :			RRECToolbar.cpp
+
+	Class :			CRRECToolbar
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+					Iain Clarke
+
+	Date :			2004-05-07
+
+	Purpose :		This class encapsulates a toolbar that can be used with 
+					"CRulerRichEditCtrl". The class is derived from "CToolBarCtrl", 
+					and manages a formatting toolbar
+
+	Description :	A "CToolBarCtrl"-derived class. Reads a toolbar resource 
+					with the ID "TOOLBAR_CONTROL" and adds combo controls for 
+					font name and -size, as well as a color picker at the 
+					positions "FONT_NAME_POS", "FONT_SIZE_POS" and 
+					"FONT_COLOR_POS" respectively.
+
+	Usage :			Created by the rich edit mini-editor.
+
+   ========================================================================*/
+
+#include "stdafx.h"
+#include "RRECToolbar.h"
+#include "ids.h"
+
+#include <tchar.h>
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+extern UINT urm_SETCURRENTFONTNAME;
+extern UINT urm_SETCURRENTFONTSIZE;
+extern UINT urm_SETCURRENTFONTCOLOR;
+
+/////////////////////////////////////////////////////////////////////////////
+// CRRECToolbar
+
+CRRECToolbar::CRRECToolbar()
+/* ============================================================
+	Function :		CRRECToolbar::CRRECToolbar
+	Description :	ctor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+CRRECToolbar::~CRRECToolbar()
+/* ============================================================
+	Function :		CRRECToolbar::~CRRECToolbar
+	Description :	dtor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+BOOL CRRECToolbar::Create( CWnd* parent, CRect& rc )
+/* ============================================================
+	Function :		CRRECToolbar::Create
+	Description :	Creates the toolbar control
+	Access :		Public
+					
+	Return :		BOOL			-	"TRUE" if success
+	Parameters :	CWnd* parent	-	Parent editor
+					CRect& rc		-	Rectangle to place 
+										toolbar in.
+
+	Usage :			Called from the parent editor
+
+   ============================================================*/
+{
+
+	BOOL result = FALSE;
+
+	HINSTANCE hInstance = AfxFindResourceHandle( MAKEINTRESOURCE( TOOLBAR_CONTROL ), RT_TOOLBAR );
+	if(!hInstance)
+		return FALSE;
+
+	HRSRC hRsrc = ::FindResource( hInstance, MAKEINTRESOURCE( TOOLBAR_CONTROL ), RT_TOOLBAR );
+	if( !hRsrc )
+		return FALSE;
+
+	HGLOBAL hGlobal = LoadResource( hInstance, hRsrc );
+	if (hGlobal == NULL)
+		return FALSE;
+
+	CToolBarData* pData = ( CToolBarData* ) LockResource( hGlobal );
+	if (pData == NULL)
+		return FALSE;
+
+	ASSERT( pData->wVersion == 1 );
+
+	TBBUTTON tb, tbSep;
+	memset ( &tb, 0, sizeof( tb ) );
+	memset ( &tbSep, 0, sizeof( tbSep ) );
+
+	result = CToolBarCtrl::Create(WS_VISIBLE|WS_CHILD, rc, parent, TOOLBAR_CONTROL);
+
+	if( result )
+	{
+		SetButtonStructSize( sizeof ( tb ) );
+
+		CSize sz ( pData->wWidth, pData->wHeight );
+		SetBitmapSize( sz );
+		sz.cx += 4;
+		sz.cy += 4;
+		SetButtonSize( sz );
+
+		// Loop through adding buttons.
+		tb.fsState = TBSTATE_ENABLED;
+		tb.fsStyle = TBSTYLE_BUTTON;
+		tb.iString = -1;
+		tb.iBitmap = 0;
+
+		tbSep.iString = -1;
+		tbSep.fsStyle = TBSTYLE_SEP;
+
+		for( WORD w = 0; w < pData->wItemCount; w++ )
+		{
+			if ( pData->items()[ w ] == 0 )
+				AddButtons( 1, &tbSep );
+			else
+			{
+				tb.idCommand = pData->items()[ w ];
+				AddButtons( 1, &tb );
+				tb.iBitmap++;
+			}
+		}
+
+		HBITMAP	hBitmap = (HBITMAP) ::LoadImage( hInstance, MAKEINTRESOURCE( TOOLBAR_CONTROL ), IMAGE_BITMAP, 0,0, LR_LOADMAP3DCOLORS );
+		if( !hBitmap )
+			return FALSE;
+
+		BITMAP bm;
+		memset( &bm, 0, sizeof ( bm ) );
+		::GetObject( hBitmap, sizeof ( bm ), &bm );
+		AddBitmap( bm.bmWidth / pData->wWidth, CBitmap::FromHandle ( hBitmap ) );
+
+		UnlockResource( hGlobal );
+		FreeResource( hGlobal );
+
+		/////////////////////////////////////
+		// Map in combo boxes
+		//
+
+		CRect rect;
+
+		TBBUTTONINFO tbi;
+		tbi.cbSize = sizeof( TBBUTTONINFO );
+		tbi.cx = FONT_COMBO_WIDTH;
+		tbi.dwMask = TBIF_SIZE | 0x80000000;  // By index
+
+		SetButtonInfo( FONT_NAME_POS, &tbi );
+		GetItemRect( FONT_NAME_POS, &rect );
+		rect.bottom += COMBO_HEIGHT;
+
+		// The font name combo
+		if( m_font.Create( WS_CHILD | 
+							WS_VSCROLL |
+							WS_VISIBLE |
+							CBS_AUTOHSCROLL | 
+							CBS_DROPDOWN | 
+							CBS_SORT | 
+							CBS_HASSTRINGS, 
+							rect, this, DROPDOWN_FONT ) )
+		{
+
+			m_font.SetFont( CFont::FromHandle( ( HFONT ) ::GetStockObject( ANSI_VAR_FONT ) ) );
+			m_font.FillCombo();
+
+			tbi.cx = COMBO_WIDTH;
+			SetButtonInfo( FONT_SIZE_POS, &tbi );
+			GetItemRect( FONT_SIZE_POS, &rect );
+			rect.bottom += COMBO_HEIGHT;
+
+			// The font size combo
+			if( m_size.Create( WS_CHILD | 
+								WS_VISIBLE | 
+								CBS_AUTOHSCROLL | 
+								CBS_DROPDOWNLIST | 
+								CBS_HASSTRINGS, 
+								rect, this, DROPDOWN_SIZE ) )
+			{
+
+				m_size.SetFont( CFont::FromHandle( ( HFONT ) ::GetStockObject( ANSI_VAR_FONT ) ) );
+				m_size.FillCombo();
+				CString color;
+				CString defaultText;
+				CString customText;
+				color.LoadString( STRING_COLOR );
+				defaultText.LoadString( STRING_DEFAULT );
+				customText.LoadString( STRING_CUSTOM );
+
+				tbi.cx = COLOR_WIDTH;
+				SetButtonInfo( FONT_COLOR_POS, &tbi );
+				GetItemRect( FONT_COLOR_POS, &rect );
+
+				// The color picker
+				if( m_color.Create( color,
+									WS_VISIBLE|
+									WS_CHILD,
+									rect, this, BUTTON_COLOR ) )
+				{
+
+					m_color.SetDefaultText( defaultText );
+					m_color.SetCustomText( customText );
+					m_color.SetSelectionMode( CP_MODE_TEXT );
+					m_color.SetBkColour( RGB( 255, 255, 255 ) );
+
+					m_color.SetFont( CFont::FromHandle( ( HFONT ) ::GetStockObject( ANSI_VAR_FONT ) ) );
+					result = TRUE;
+
+				}
+
+			}
+
+		}
+
+	}
+
+	return result;
+
+}
+
+BEGIN_MESSAGE_MAP(CRRECToolbar, CToolBarCtrl)
+	//{{AFX_MSG_MAP(CRRECToolbar)
+		// NOTE - the ClassWizard will add and remove mapping macros here.
+	ON_CBN_SELCHANGE(DROPDOWN_FONT, OnSelchangeFont)
+	ON_CBN_SELCHANGE(DROPDOWN_SIZE, OnSelchangeSize)
+	ON_MESSAGE(CPN_SELENDOK, OnColorButton)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CRRECToolbar message handlers
+
+void CRRECToolbar::OnSelchangeFont() 
+/* ============================================================
+	Function :		CRRECToolbar::OnSelchangeFont
+	Description :	Changes the font of the selected text in 
+					the editor.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC when the selection changes 
+					in the font name combo.
+
+   ============================================================*/
+{
+
+	CString font;
+	int index = m_font.GetCurSel();
+	if( index != CB_ERR )
+	{
+		m_font.GetLBText( index, font );
+		GetParent()->SendMessage( urm_SETCURRENTFONTNAME, ( WPARAM ) ( LPCTSTR ) font, 0 );
+
+	}	
+}
+
+void CRRECToolbar::OnSelchangeSize() 
+/* ============================================================
+	Function :		CRRECToolbar::OnSelchangeSize
+	Description :	Changes the size of the selected text in 
+					the editor.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC when the selection changes 
+					in the font size combo.
+
+   ============================================================*/
+{
+	int size = 0;
+	int index = m_size.GetCurSel();
+	if( index != CB_ERR )
+	{
+
+		CString sz;
+		m_size.GetLBText( index, sz );
+		size = _ttoi( ( LPCTSTR ) sz );
+
+		GetParent()->SendMessage( urm_SETCURRENTFONTSIZE, 0, ( LPARAM ) size );
+
+	}
+	
+}
+
+LRESULT CRRECToolbar::OnColorButton( WPARAM w, LPARAM l) 
+/* ============================================================
+	Function :		CRRECToolbar::OnColorButton
+	Description :	Mapped to the color picker defined 
+					"CPN_SELENDOK" message, sent when the color 
+					is changed in the picker.
+	Access :		Protected
+					
+	Return :		LRESULT	-	Not used
+	Parameters :	WPARAM	-	Not used
+					LPARAM	-	Not used
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+	GetParent()->SendMessage( urm_SETCURRENTFONTCOLOR, 0, ( LPARAM ) w );
+	
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRRECToolbar UI updaters
+
+void CRRECToolbar::SetFontName( const CString& font )
+/* ============================================================
+	Function :		CRRECToolbar::SetFontName
+	Description :	Selects the font name "font" in the font 
+					name combo on the toolbar.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to set the selected font name
+
+   ============================================================*/
+{
+
+	if( m_font.m_hWnd )
+		m_font.SelectFontName( font );
+
+}
+
+void CRRECToolbar::SetFontSize( int size )
+/* ============================================================
+	Function :		CRRECToolbar::SetFontSize
+	Description :	Selects the font size "size" in the font 
+					size combo on the toolbar.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to set the selected font size
+
+   ============================================================*/
+{
+
+	if( m_size.m_hWnd )
+		m_size.SelectSize( size );
+
+}
+
+void CRRECToolbar::SetFontColor( COLORREF color )
+/* ============================================================
+	Function :		CRRECToolbar::SetFontColor
+	Description :	Selects the font color "color" in the font 
+					color picker on the toolbar.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to set the color picker color
+
+   ============================================================*/
+{
+
+	if( m_color.m_hWnd )
+		m_color.SetColour( color );
+
+}
+

+ 79 - 0
src/RulerRichEditCtrl/RRECToolbar.h

@@ -0,0 +1,79 @@
+#if !defined(AFX_RRECTOOLBAR_H__E0596D7D_418C_49B9_AC57_2F0BF93053C9__INCLUDED_)
+#define AFX_RRECTOOLBAR_H__E0596D7D_418C_49B9_AC57_2F0BF93053C9__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// RRECToolbar.h : header file
+//
+
+#include "FontComboBox.h"
+#include "SizeComboBox.h"
+#include "External/ColourPicker.h"
+
+struct CToolBarData
+{
+	WORD wVersion;
+	WORD wWidth;
+	WORD wHeight;
+	WORD wItemCount;
+
+	WORD* items()
+		{ return ( WORD* )( this + 1 ); }
+
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CRRECToolbar window
+
+class CRRECToolbar : public CToolBarCtrl
+{
+// Construction
+public:
+	CRRECToolbar();
+	BOOL Create( CWnd* parent, CRect& rect );
+
+// Attributes
+public:
+
+// Operations
+public:
+
+	void SetFontName( const CString& font );
+	void SetFontSize( int size );
+	void SetFontColor( COLORREF color );
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CRRECToolbar)
+	//}}AFX_VIRTUAL
+
+// Implementation
+public:
+	virtual ~CRRECToolbar();
+
+	// Generated message map functions
+protected:
+	//{{AFX_MSG(CRRECToolbar)
+		// NOTE - the ClassWizard will add and remove member functions here.
+	afx_msg void OnSelchangeFont();
+	afx_msg void OnSelchangeSize();
+	afx_msg LRESULT OnColorButton( WPARAM w, LPARAM l);
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+
+private:
+
+	CFontComboBox	m_font;
+	CSizeComboBox	m_size;
+	CColourPicker	m_color;
+
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_RRECTOOLBAR_H__E0596D7D_418C_49B9_AC57_2F0BF93053C9__INCLUDED_)

+ 202 - 0
src/RulerRichEditCtrl/RulerRichEdit.cpp

@@ -0,0 +1,202 @@
+/* ==========================================================================
+	File :			RuleRichEdit.cpp
+
+	Class :			CRulerRichEdit
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+					Iain Clarke
+
+	Date :			2004-04-17
+
+	Purpose :		"CRulerRichEdit" is derived from "CWnd". 
+
+	Description :	The class, in addition to the normal "CWnd", 
+					handles horizontal scrollbar messages - forcing an 
+					update of the parent (to synchronize the ruler). The 
+					change notification is called for the same reason. 
+					"WM_GETDLGCODE" is handled, we want all keys in a 
+					dialog box instantiation.
+
+	Usage :			This class is only useful as a child of the 
+					"CRulerRichEditCtrl".
+
+   ========================================================================*/
+
+#include "stdafx.h"
+#include "ids.h"
+#include "RulerRichEdit.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#ifdef _UNICODE
+	#define RTF_CLASS RICHEDIT_CLASSW
+#else
+	#define RTF_CLASS RICHEDIT_CLASSA
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEdit
+
+CRulerRichEdit::CRulerRichEdit()
+/* ============================================================
+	Function :		CRulerRichEdit::CRulerRichEdit
+	Description :	constructor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+CRulerRichEdit::~CRulerRichEdit()
+/* ============================================================
+	Function :		CRulerRichEdit::~CRulerRichEdit
+	Description :	destructor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+BEGIN_MESSAGE_MAP(CRulerRichEdit, CRichEditCtrlEx)
+	//{{AFX_MSG_MAP(CRulerRichEdit)
+	ON_WM_HSCROLL()
+	ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
+	ON_WM_GETDLGCODE()
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+BOOL CRulerRichEdit::Create( DWORD style, CRect rect, CWnd* parent )
+/* ============================================================
+	Function :		CRulerRichEdit::~CRulerRichEdit
+	Description :	Creates the control
+	Access :		Public
+					
+	Return :		BOOL			-	"TRUE" if created ok
+	Parameters :	DWORD			-	Style for the control
+					CRect rect		-	Position of the control
+					CWnd* parent	-	Parent of the control
+
+	Usage :			Call to create the control
+
+   ============================================================*/
+{
+
+	return CWnd::Create (L"RICHEDIT50W", NULL, style, rect, parent, RTF_CONTROL );
+
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEdit message handlers
+
+void CRulerRichEdit::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
+/* ============================================================
+	Function :		CRulerRichEdit::OnHScroll
+	Description :	Handles the "WM_HSCROLL" message.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	UINT nSBCode			-	Type of operation
+					UINT nPos				-	New position
+					CScrollBar* pScrollBar	-	Pointer to scrollbar
+					
+	Usage :			Called from MFC. Updates the ruler.
+
+   ============================================================*/
+{
+
+	CWnd::OnHScroll( nSBCode, nPos, pScrollBar );
+
+	SCROLLINFO	si;
+	ZeroMemory( &si, sizeof( SCROLLINFO ) );
+	si.cbSize = sizeof( SCROLLINFO );
+	GetScrollInfo( SB_HORZ, &si );
+
+	if ( nSBCode == SB_THUMBTRACK )
+	{
+
+		si.nPos = nPos;
+		SetScrollInfo( SB_HORZ, &si );
+
+	}
+
+	UpdateRuler();
+
+}
+
+void CRulerRichEdit::OnChange() 
+/* ============================================================
+	Function :		CRulerRichEdit::OnChange
+	Description :	Handles change-notifications.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC. Updates the ruler if text 
+					is deleted, for example.
+
+   ============================================================*/
+{
+
+	UpdateRuler();
+
+}
+
+UINT CRulerRichEdit::OnGetDlgCode() 
+/* ============================================================
+	Function :		CRulerRichEdit::OnGetDlgCode
+	Description :	Handles the "WM_GETDLGCODE" message.
+	Access :		Protected
+					
+	Return :		UINT	-	The keys we are interested in.
+	Parameters :	none
+
+	Usage :			Called from MFC. Handled to make sure 
+					keypresses stays with us.
+
+   ============================================================*/
+{
+
+	return DLGC_WANTALLKEYS;
+	
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEdit private helpers
+
+void CRulerRichEdit::UpdateRuler()
+/* ============================================================
+	Function :		CRulerRichEdit::UpdateRuler
+	Description :	Updates the ruler.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to update the parent ruler field.
+
+   ============================================================*/
+{
+
+	CRect rect;
+	GetClientRect( rect );
+	rect.top = TOOLBAR_HEIGHT;
+	rect.bottom = rect.top + TOP_HEIGHT;
+	GetParent()->RedrawWindow( rect );
+
+}
+

+ 36 - 0
src/RulerRichEditCtrl/RulerRichEdit.h

@@ -0,0 +1,36 @@
+#if !defined(AFX_RULERRICHEDIT_H__E10A8ED3_2E1D_402E_A599_003214085F1A__INCLUDED_)
+#define AFX_RULERRICHEDIT_H__E10A8ED3_2E1D_402E_A599_003214085F1A__INCLUDED_
+
+// RulerRichEdit.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEdit window
+#include "../RichEditCtrlEx.h"
+
+class CRulerRichEdit : public CRichEditCtrlEx
+{
+public:
+// Construction/creation/destruction
+	CRulerRichEdit();
+	virtual ~CRulerRichEdit();
+
+	BOOL Create( DWORD style, CRect rect, CWnd* parent );
+
+protected:
+// Message handlers
+	//{{AFX_MSG(CRulerRichEdit)
+	afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
+	afx_msg void OnChange();
+	afx_msg UINT OnGetDlgCode();
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+
+private:
+// Private helpers
+	void UpdateRuler();
+
+};
+
+#endif // !defined(AFX_RULERRICHEDIT_H__E10A8ED3_2E1D_402E_A599_003214085F1A__INCLUDED_)

+ 2190 - 0
src/RulerRichEditCtrl/RulerRichEditCtrl.cpp

@@ -0,0 +1,2190 @@
+/* ==========================================================================
+	File :			RuleRichEditCtrl.cpp
+
+	Class :			CRulerRichEditCtrl
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+					Iain Clarke
+
+	Date :			2004-04-17
+
+	Purpose :		"CRulerRichEditCtrl" is a "CWnd" derived class containing an 
+					embedded RTF-control, a ruler-control with dragable tab-
+					positions and a formatting toolbar. The class can be used 
+					to - for example - add a complete mini-editor to a modal 
+					or modeless dialog box. 
+
+	Description :	The class mainly handles mouse messages. The mouse
+					messages are sent from the ruler control, and are 
+					button down, where the a check is made for the cursor 
+					located on one of the tab-markers, mouse move, where an 
+					XORed line is drawn across the RTF-control and button up, 
+					where a new tab position is set. The class also handles 
+					the toolbar buttons, setting styles as 
+					appropriate for the selected text.
+
+	Usage :			Add a "CRulerRichEditCtrl"-member to the parent class. 
+					Call Create to create the control. "GetRichEditCtrl" can 
+					be used to access the embedded RTF-control. Remember to 
+					call "AfxInitRichEdit(2)"!
+
+					The contents can be saved to disk by calling "Save", and 
+					loaded from disk by calling "Load". The two functions 
+					will automatically display a file dialog if the file 
+					name parameter of the calls are left empty.
+
+					"GetRTF" and "SetRTF" can be used to get and set the 
+					contents of the embedded RTF-control as RTF 
+					respectively.
+
+					The ruler measures can be displayed as inches or 
+					centimeters, by calling "SetMode". "GetMode" will get the 
+					current mode.
+
+   ========================================================================*/
+
+#include "stdafx.h"
+#include "StdGrfx.h"
+#include "RulerRichEditCtrl.h"
+#include "TextFile/TextFile.h"
+#include "..\Options.h"
+#include "..\Misc.h"
+#include "..\HyperLink.h"
+
+#include "ids.h"
+#include ".\rulerricheditctrl.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Registered messages for ruler/CRulerRichEditCtrl communication
+
+UINT urm_RULERACTION = ::RegisterWindowMessage( _T( "_RULERRICHEDITCTRL_RULER_TRACK_" ) );
+UINT urm_GETSCROLLPOS = ::RegisterWindowMessage( _T( "_RULERRICHEDITCTRL_GET_SCROLL_POS_" ) );
+UINT urm_SETCURRENTFONTNAME = ::RegisterWindowMessage( _T( "_RULERRICHEDITCTRL_SET_CURRENT_FONT_NAME" ) );
+UINT urm_SETCURRENTFONTSIZE = ::RegisterWindowMessage( _T( "_RULERRICHEDITCTRL_SET_CURRENT_FONT_SIZE" ) );
+UINT urm_SETCURRENTFONTCOLOR = ::RegisterWindowMessage( _T( "_RULERRICHEDITCTRL_SET_CURRENT_FONT_COLOR" ) );
+
+/////////////////////////////////////////////////////////////////////////////
+// Stream callback functions
+// Callbacks to the Save and Load functions.
+
+static DWORD CALLBACK StreamOut( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
+{
+
+	// Setting up temp buffer
+	char*	buff;
+	buff = new char[cb + 1];
+	buff[cb] = ( char ) 0;
+	strncpy(buff, (LPCSTR)pbBuff, cb);
+	int max = (int)strlen(buff);
+
+	CString* str = (CString*) dwCookie;
+
+#ifdef _UNICODE
+
+	// We want to convert the buff to wide chars
+	int length = ::MultiByteToWideChar(CP_UTF8, 0, buff, max, NULL, 0);
+	if(length)
+	{
+		TCHAR* wBuff = new TCHAR[length+1];
+		::MultiByteToWideChar(CP_UTF8, 0, buff, max, wBuff, length);
+		wBuff[length] = 0;
+		*str += wBuff;
+		delete[] wBuff;
+	}
+
+#else
+
+	*str += buff;
+
+#endif
+
+	delete[] buff;
+	*pcb = max;
+	
+	return 0;
+
+}
+
+static DWORD CALLBACK StreamIn( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
+{
+
+	CString* str = ( ( CString* ) dwCookie );
+
+#ifdef _UNICODE
+
+	// Unicode is only supported for SF_TEXT, so we need
+	// to convert
+	LPCTSTR ptr = str->GetBuffer( (*str).GetLength() );
+	int length = ::WideCharToMultiByte( CP_UTF8, 0, ptr, -1, NULL, 0, NULL, NULL );
+	int max = min( cb, length );
+	if( length )
+	{
+		char* buff = new char[ length ];
+		::WideCharToMultiByte( CP_UTF8, 0, ptr, -1, buff, length + 1, NULL, NULL );
+		strncpy( (LPSTR) pbBuff, buff, max );
+		delete[] buff;
+	}
+	str->ReleaseBuffer();
+
+#else
+
+	int max = min( cb, (*str).GetLength() );
+	strncpy( ( LPSTR ) pbBuff, (*str) , max  );
+
+#endif
+
+	(*str) = (*str).Right( (*str).GetLength() - max );
+
+	*pcb = max;
+
+	return 0;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEditCtrl
+
+CRulerRichEditCtrl::CRulerRichEditCtrl() : m_pen( PS_DOT, 0, RGB( 0, 0, 0 ) )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::CRulerRichEditCtrl
+	Description :	constructor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+	m_rulerPosition = 0;
+	m_margin = 0;
+	m_movingtab = -1;
+	m_offset = 0;
+	m_readOnly = FALSE;
+	m_bInWrapMode = g_Opt.GetEditWordWrap();
+	ShowToolbar();
+	ShowRuler();
+}
+
+CRulerRichEditCtrl::~CRulerRichEditCtrl()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::~CRulerRichEditCtrl
+	Description :	destructor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+	m_pen.DeleteObject();
+}
+
+
+BOOL CRulerRichEditCtrl::Create( DWORD dwStyle, const RECT &rect, CWnd* pParentWnd, UINT nID, BOOL autohscroll )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::Create
+	Description :	Creates the control and sub controls.
+	Access :		Public
+					
+	Return :		BOOL				-	"TRUE" if created OK.
+	Parameters :	DWORD dwStyle		-	Style of the control, 
+											normally "WS_CHILD" 
+											and "WS_VISIBLE".
+					const RECT &rect	-	Placement rectangle.
+					CWnd* pParentWnd	-	Parent window.
+					UINT nID			-	Control ID
+					BOOL autohscroll	-	"TRUE" if the RTF-control
+											should have the 
+											"ES_AUTOHSCROLL" style
+											set.
+
+	Usage :			Call to create the control.
+
+   ============================================================*/
+{	
+	BOOL result = CWnd::Create(NULL, _T( "" ), dwStyle, rect, pParentWnd, nID);
+	if ( result )
+	{
+
+		result = FALSE;
+		// Save screen resolution for
+		// later on.
+		CClientDC dc( this );
+		m_physicalInch = dc.GetDeviceCaps( LOGPIXELSX );
+
+		// Create sub-controls
+		if( CreateRTFControl( autohscroll ) )
+		{
+			CreateMargins();
+			if( CreateToolbar() )
+			{
+				if( CreateRuler() )
+				{
+					UpdateToolbarButtons();
+					result = TRUE;
+				}
+			}
+
+			//Do wrap will reverse the saved option so initially set it as opposite of what it's saved as
+			m_bInWrapMode = !m_bInWrapMode;
+			DoWrap();
+		}		
+	}
+
+	return result;
+}
+
+BOOL CRulerRichEditCtrl::CreateToolbar()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::CreateToolbar
+	Description :	Creates the toolbar control
+	Access :		Private
+
+	Return :		BOOL	-	"TRUE" if the toolbar was created ok.
+	Parameters :	none
+
+	Usage :			Called during control creation
+
+   ============================================================*/
+{
+
+	CRect rect;
+	GetClientRect( rect );
+
+	CRect toolbarRect( 0, 0, rect.right, TOOLBAR_HEIGHT );
+	return m_toolbar.Create( this, toolbarRect );
+}
+
+BOOL CRulerRichEditCtrl::CreateRuler()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::CreateRuler
+	Description :	Creates the ruler control
+	Access :		Private
+
+	Return :		BOOL	-	"TRUE" if created ok.
+	Parameters :	none
+
+	Usage :			Called during control creation
+
+   ============================================================*/
+{
+	CRect rect;
+	GetClientRect( rect );
+
+	CRect rulerRect( 0, TOOLBAR_HEIGHT, rect.right, TOOLBAR_HEIGHT + RULER_HEIGHT );
+	return m_ruler.Create( rulerRect, this, RULER_CONTROL );
+}
+
+BOOL CRulerRichEditCtrl::CreateRTFControl( BOOL autohscroll )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::CreateRTFControl
+	Description :	Creates the embedded RTF-control.
+	Access :		Private
+					
+	Return :		BOOL				-	"TRUE" if created ok.
+	Parameters :	BOOL autohscroll	-	"TRUE" if the RTF-control
+											should have the
+											"ES_AUTOHSCROLL" style
+											set.
+
+	Usage :			Called during control creation
+
+   ============================================================*/
+{
+	BOOL result = FALSE;
+
+	CRect rect;
+	GetClientRect( rect );
+
+	int top = TOOLBAR_HEIGHT + RULER_HEIGHT;
+	CRect rtfRect( 0, top, rect.right, rect.bottom );
+	DWORD style = ES_NOHIDESEL|WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|ES_WANTRETURN|ES_MULTILINE;
+	if( autohscroll )
+		style |= ES_AUTOHSCROLL;
+
+	if( m_rtf.Create( style, rtfRect, this ) )
+	{
+		// Setting up default tab stops
+ 		ParaFormat para( PFM_TABSTOPS );
+ 		para.cTabCount = MAX_TAB_STOPS;
+ 		for( int t = 0; t < MAX_TAB_STOPS ; t++ )
+ 			para.rgxTabs[ t ] = 640 * ( t + 1 );
+ 
+ 		m_rtf.SetParaFormat( para );
+ 
+ 		// Setting default character format
+ 		CharFormat	cf;
+ 		cf.dwMask = CFM_SIZE | CFM_FACE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_LINK;
+ 		cf.yHeight = CGetSetOptions::GetEditorDefaultFontSize() * 20;
+ 		cf.dwEffects = 0;
+ 		lstrcpy( cf.szFaceName, _T( "Segoe UI" ) );
+ 		m_rtf.SendMessage(EM_SETCHARFORMAT, 0, (LPARAM)&cf);
+
+#ifndef SES_HYPERLINKTOOLTIPS
+#define SES_HYPERLINKTOOLTIPS   8
+#endif
+#ifndef SES_NOFOCUSLINKNOTIFY
+#define SES_NOFOCUSLINKNOTIFY   32
+#endif 
+
+		DWORD style = SES_HYPERLINKTOOLTIPS | SES_NOFOCUSLINKNOTIFY;
+		m_rtf.SendMessage(EM_SETEDITSTYLE, style, style);
+
+		m_rtf.SendMessage(EM_AUTOURLDETECT, TRUE, 0);
+ 
+ 		// Set the internal tabs array
+ 		SetTabStops( ( LPLONG ) ( para.rgxTabs ), MAX_TAB_STOPS );
+ 
+ 		m_rtf.SetEventMask( m_rtf.GetEventMask() | ENM_SELCHANGE | ENM_SCROLL | ENM_CHANGE | ENM_LINK );
+ 		SetReadOnly( GetReadOnly() ); 
+
+		result = TRUE;
+	}
+
+	return result;
+}
+
+void CRulerRichEditCtrl::CreateMargins()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::CreateMargins
+	Description :	Sets the margins for the subcontrols and 
+					the RTF-control edit rect.
+	Access :		Private
+
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called during control creation.
+
+   ============================================================*/
+{
+	// Set up edit rect margins
+	int scmargin = 4;
+	CRect rc;
+	m_rtf.GetClientRect( rc );
+
+	rc.top = scmargin;
+	rc.left = scmargin * 2;
+	rc.right -= scmargin * 2;
+
+	m_rtf.SetRect( rc );
+
+	// Get the diff between the window- and client 
+	// rect of the RTF-control. This gives the actual 
+	// size of the RTF-control border.
+	CRect	r1;
+	CRect	r2;
+
+	m_rtf.GetWindowRect( r1 );
+	m_rtf.GetClientRect( r2 );
+	m_rtf.ClientToScreen( r2 );
+
+	// Create the margin for the toolbar 
+	// controls and the ruler.
+	m_margin = scmargin * 2 + r2.left - r1.left;
+	m_ruler.SetMargin( m_margin );
+}
+
+BEGIN_MESSAGE_MAP(CRulerRichEditCtrl, CWnd)
+	//{{AFX_MSG_MAP(CRulerRichEditCtrl)
+	ON_WM_PAINT()
+	ON_WM_ERASEBKGND()
+	ON_WM_SIZE()
+	ON_MESSAGE( WM_SETTEXT, OnSetText )
+	ON_MESSAGE( WM_GETTEXT, OnGetText  )
+	ON_MESSAGE( WM_GETTEXTLENGTH, OnGetTextLength )
+	ON_BN_CLICKED(BUTTON_FONT, OnButtonFont)
+	ON_BN_CLICKED(BUTTON_COLOR, OnButtonColor)
+	ON_BN_CLICKED(BUTTON_BOLD, OnButtonBold)
+	ON_BN_CLICKED(BUTTON_ITALIC, OnButtonItalic)
+	ON_BN_CLICKED(BUTTON_UNDERLINE, OnButtonUnderline)
+	ON_BN_CLICKED(BUTTON_LEFTALIGN, OnButtonLeftAlign)
+	ON_BN_CLICKED(BUTTON_CENTERALIGN, OnButtonCenterAlign)
+	ON_BN_CLICKED(BUTTON_RIGHTALIGN, OnButtonRightAlign)
+	ON_BN_CLICKED(BUTTON_INDENT, OnButtonIndent)
+	ON_BN_CLICKED(BUTTON_OUTDENT, OnButtonOutdent)
+	ON_BN_CLICKED(BUTTON_BULLET, OnButtonBullet)
+	ON_BN_CLICKED(ID_BUTTONWRAP, OnButtonWrap)
+	ON_WM_SETFOCUS()
+	ON_REGISTERED_MESSAGE(urm_RULERACTION, OnTrackRuler)
+	ON_REGISTERED_MESSAGE(urm_GETSCROLLPOS, OnGetScrollPos)
+	ON_REGISTERED_MESSAGE(urm_SETCURRENTFONTNAME, OnSetCurrentFontName)
+	ON_REGISTERED_MESSAGE(urm_SETCURRENTFONTSIZE, OnSetCurrentFontSize)
+	ON_NOTIFY(EN_LINK, RTF_CONTROL, OnLink)
+	ON_REGISTERED_MESSAGE(urm_SETCURRENTFONTCOLOR, OnSetCurrentFontColor)
+	//}}AFX_MSG_MAP
+	ON_WM_KEYDOWN()
+END_MESSAGE_MAP()
+
+void CRulerRichEditCtrl::OnLink(NMHDR* pnm, LRESULT* pResult)
+{
+	ENLINK* pnml = reinterpret_cast<ENLINK*>(pnm);
+
+	if (pnml->msg == WM_LBUTTONDOWN ||
+		(pnml->msg == WM_KEYDOWN && pnml->wParam == VK_RETURN))
+	{
+		CString url;
+		m_rtf.GetTextRange(pnml->chrg.cpMin, pnml->chrg.cpMax, url);
+		CHyperLink::GotoURL(url, SW_SHOW);
+
+		*pResult = 1; // message handled
+	}
+
+	*pResult = 0;  // enable default processing
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEditCtrl message handlers
+
+void CRulerRichEditCtrl::OnPaint() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnPaint
+	Description :	Paints the ruler.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC. 
+
+   ============================================================*/
+{
+
+	CPaintDC mainDC(this);
+	UpdateTabStops();
+
+}
+
+BOOL CRulerRichEditCtrl::OnEraseBkgnd( CDC* /*pDC*/ ) 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnEraseBkgnd
+	Description :	Returns "TRUE" to avoid flicker.
+	Access :		Protected
+					
+	Return :		BOOL		-	Always "TRUE",
+	Parameters :	CDC* pDC	-	Not used
+					
+	Usage :			Called from MFC. 
+
+   ============================================================*/
+{
+	
+	return TRUE;
+
+}
+
+BOOL CRulerRichEditCtrl::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult ) 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnNotify
+	Description :	Called as the RTF-control is updated or 
+					the selection changes.
+	Access :		Protected
+					
+	Return :		BOOL				-	From base class
+	Parameters :	WPARAM wParam		-	Control ID
+					LPARAM lParam		-	Not interested
+					LRESULT* pResult	-	Not interested
+					
+	Usage :			Called from MFC. We must check the control 
+					every time the selection changes or the 
+					contents are changed, as the cursor might 
+					have entered a new paragraph, with new tab 
+					and/or font settings.
+
+   ============================================================*/
+{
+
+	if( wParam == RTF_CONTROL )
+	{
+
+		// Update the toolbar
+		UpdateToolbarButtons();
+
+		// Update ruler
+		CRect rect;
+		GetClientRect( rect );
+		rect.top = TOOLBAR_HEIGHT;
+		rect.bottom = rect.top + RULER_HEIGHT;
+
+		RedrawWindow( rect );
+
+	}
+	
+	return CWnd::OnNotify( wParam, lParam, pResult );
+
+}
+
+void CRulerRichEditCtrl::OnSize( UINT nType, int cx, int cy ) 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnSize
+	Description :	We resize the embedded RTF-control.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	UINT nType	-	Not interested
+					int cx		-	New width
+					int cy		-	New height
+					
+	Usage :			Called from MFC. 
+
+   ============================================================*/
+{
+
+	CWnd::OnSize( nType, cx, cy );
+	
+	if( m_rtf.m_hWnd )
+	{
+
+		LayoutControls( cx, cy );
+
+	}
+	
+}
+
+void CRulerRichEditCtrl::OnSetFocus( CWnd* pOldWnd ) 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnSetFocus
+	Description :	We handle over the focus to the embedded 
+					RTF-control.
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	CWnd* pOldWnd	-	Not used
+					
+	Usage :			Called from MFC. 
+
+   ============================================================*/
+{
+
+	CWnd::OnSetFocus( pOldWnd );
+	m_rtf.SetFocus();
+	
+}
+
+LRESULT CRulerRichEditCtrl::OnGetScrollPos(WPARAM, LPARAM)
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnGetScrollPos
+	Description :	The function handles the registered message 
+					"urm_GETSCROLLPOS", that is sent from the 
+					ruler to get the current scroll position 
+					of the embedded RTF-control.
+	Access :		Protected
+
+	Return :		LRESULT		-	Current scroll pos
+	Parameters :	WPARAM mode	-	Not used
+					LPARAM pt	-	Not used
+					
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	return m_rtf.GetScrollPos( SB_HORZ );
+
+}
+
+LRESULT CRulerRichEditCtrl::OnTrackRuler(WPARAM mode, LPARAM pt)
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnTrackRuler
+	Description :	The function handles the registered message 
+					"urm_RULERACTION", that is sent from the 
+					mouse handling mappings in the ruler control.
+					The function handles dragging of tabulator 
+					points in the ruler.
+	Access :		Protected
+					
+	Return :		LRESULT		-	Not used
+	Parameters :	WPARAM mode	-	The type of mouse operation, 
+									"DOWN", "MOVE" or "UP"
+					LPARAM pt	-	Cursor point for the cursor.
+					
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	CPoint* point = ( CPoint* ) pt;
+	int toolbarHeight = 0;
+	if( m_showToolbar )
+		toolbarHeight = TOOLBAR_HEIGHT;
+
+	switch( mode )
+	{
+		case DOWN:
+			// The left mouse button is clicked
+			{
+				// Check if we clicked on a tab-marker.
+				int pos = m_rtf.GetScrollPos( SB_HORZ );
+				m_movingtab = -1;
+				CRect hitRect;
+				int y = RULER_HEIGHT - 9;
+				for( int t = 0 ; t < MAX_TAB_STOPS ; t++ )
+				{
+					int x = m_tabs[ t ] + m_margin - pos;
+					hitRect.SetRect( x - 2, y - 1, x + 3, y + 3 );
+					if( hitRect.PtInRect( *point ) )
+					{
+						// Yes, we did.
+						m_movingtab = t;
+
+						// Calc offset, as the hit area is wider than
+						// the 1 pixel tab line
+						m_offset = point->x - ( m_tabs[ t ] + m_margin - pos );
+					}
+				}
+
+				if( m_movingtab != -1 )
+				{
+
+					// We did click in a tab marker.
+					// Start dragging
+
+					// Find initial ruler position
+					m_rulerPosition = point->x - m_offset;
+					CRect rect;
+					GetClientRect( rect );
+
+					// Draw a new ruler line
+					CClientDC dc( this );
+					dc.SelectObject( &m_pen );
+					dc.SetROP2( R2_XORPEN );
+
+					dc.MoveTo( m_rulerPosition, toolbarHeight + 3 );
+					dc.LineTo( m_rulerPosition, rect.Height() );
+
+					dc.SelectStockObject( BLACK_PEN );
+
+				}
+			}
+			break;
+
+		case MOVE:
+			// The mouse is moved
+			{
+				if( m_movingtab != -1 )
+				{
+					CRect rect;
+					GetClientRect( rect );
+					CClientDC dc( this );
+
+					// Erase previous line
+					dc.SelectObject( &m_pen );
+					dc.SetROP2( R2_XORPEN );
+
+					dc.MoveTo( m_rulerPosition, toolbarHeight + 3 );
+					dc.LineTo( m_rulerPosition, rect.Height() );
+
+					// Set up new line
+					// Calc min and max. We can not place this marker 
+					// before the previous or after the next. Neither 
+					// can we move the marker outside the ruler.
+					int pos = m_rtf.GetScrollPos( SB_HORZ );
+					int min = m_margin + m_offset;
+					if( m_movingtab > 0 )
+						min = ( m_tabs[ m_movingtab - 1 ] + m_margin - pos ) + 3 + m_offset;
+
+					int max = rect.Width() - 5 + m_offset;
+					if( m_movingtab < m_tabs.GetUpperBound() )
+						max = ( m_tabs[ m_movingtab + 1 ] + m_margin - pos ) - 3 + m_offset;
+					max = min( max, rect.Width() - 5 + m_offset );
+
+					// Set new positions
+					m_rulerPosition = max( min, point->x - m_offset );
+					m_rulerPosition = min( m_rulerPosition, max );
+
+					// Draw the new line
+					dc.MoveTo( m_rulerPosition, toolbarHeight + 3 );
+					dc.LineTo( m_rulerPosition, rect.Height() );
+
+					dc.SelectStockObject( BLACK_PEN );
+
+				}
+			}
+			break;
+
+		case UP:
+			// The mouse button is released
+			{
+				if( m_movingtab != -1 )
+				{
+
+					// Set new value for tab position
+					int pos = m_rtf.GetScrollPos( SB_HORZ );
+					m_tabs[ m_movingtab ] = m_rulerPosition - m_margin + pos - m_offset;
+
+					// Get the current tabstops, as we
+					// must set all tabs in one operation
+					ParaFormat para( PFM_TABSTOPS );
+					para.cTabCount = MAX_TAB_STOPS;
+					m_rtf.GetParaFormat( para );
+
+					// Convert current position to twips
+					double twip = ( double )m_physicalInch / 1440;
+					int tabpos = m_tabs[ m_movingtab ];
+					tabpos = ( int ) ( ( double ) tabpos / twip +.5 );
+					para.rgxTabs[ m_movingtab ] = tabpos; 
+
+					// Set tabs to control
+					m_rtf.SetParaFormat( para );
+
+					// Erase the ruler
+					m_ruler.RedrawWindow();
+					m_rtf.RedrawWindow();
+
+					m_movingtab = -1;
+					m_rtf.SetFocus();
+
+				}
+			}
+			break;
+	}
+
+	return 0;
+
+}
+
+LRESULT CRulerRichEditCtrl::OnSetText( WPARAM wParam, LPARAM lParam )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnSetText
+	Description :	The function handles the "WM_SETTEXT" 
+					message. The handler sets the text in the 
+					RTF-control
+	Access :		Protected
+										
+	Return :		LRESULT			-	From the control
+	Parameters :	WPARAM wParam	-	Passed on
+					LPARAM lParam	-	Passed on
+					
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+	
+	return m_rtf.SendMessage( WM_SETTEXT, wParam, lParam );
+
+}
+
+LRESULT CRulerRichEditCtrl::OnGetText( WPARAM wParam, LPARAM lParam )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnGetText
+	Description :	The function handles the "WM_GETTEXT" 
+					message. The handler gets the text from the 
+					RTF-control
+	Access :		Protected
+										
+	Return :		LRESULT			-	From the control
+	Parameters :	WPARAM wParam	-	Passed on
+					LPARAM lParam	-	Passed on
+					
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	return m_rtf.SendMessage( WM_GETTEXT, wParam, lParam );
+
+}
+
+LRESULT CRulerRichEditCtrl::OnGetTextLength( WPARAM wParam, LPARAM lParam )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnGetTextLength
+	Description :	The function handles the "WM_GETTEXTLENGTH" 
+					message. The handler gets the length of 
+					the text in the RTF-control
+	Access :		Protected
+										
+	Return :		LRESULT			-	From the control
+	Parameters :	WPARAM wParam	-	Passed on
+					LPARAM lParam	-	Passed on
+					
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	return m_rtf.GetTextLength();
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEditCtrl public implementation
+
+CString CRulerRichEditCtrl::GetRTF()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::GetRTF
+	Description :	Returns the contents of the control as RTF.
+	Access :		Public
+					
+	Return :		CString	-	The RTF-contents of the control.
+	Parameters :	none
+
+	Usage :			Call this function to get a char buffer 
+					with the contents of the embedded RTF-
+					control.
+
+   ============================================================*/
+{
+
+	CString* str = new CString;
+	EDITSTREAM	es;
+	es.dwCookie = (DWORD_PTR) str;
+	es.pfnCallback = StreamOut;
+	m_rtf.StreamOut( SF_RTF, es );
+
+	CString output( *str );
+	delete str;
+
+	return output;
+
+}
+
+void CRulerRichEditCtrl::SetRTF( const CString& rtf )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetRTF
+	Description :	Set the contents of the embedded RTF-
+					control from rtf.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	const CString& rtf	-	The rtf-contents to 
+											set.
+					
+	Usage :			Call this function to set the RTF-contents 
+					of the control.
+
+   ============================================================*/
+{
+
+	CString* str = new CString( rtf );
+
+	EDITSTREAM	es;
+	es.dwCookie = (DWORD_PTR) str;
+	es.pfnCallback = StreamIn;
+	m_rtf.StreamIn( SF_RTF, es );
+
+	delete str;
+}
+
+void CRulerRichEditCtrl::SetText(CString sText)
+{
+	// Read the text in
+	EDITSTREAM es;
+	es.dwError = 0;
+//	es.pfnCallback = StreamIn;
+#ifdef _UNICODE
+	CString cs;
+	es.dwCookie = (DWORD_PTR) &cs;
+#else
+	es.dwCookie = (DWORD_PTR) &sText;
+	m_rtf.StreamIn(SF_TEXT, es);	// Do it.
+#endif
+	
+
+#ifdef _UNICODE
+	SETTEXTEX stex;
+	stex.flags = ST_SELECTION | ST_KEEPUNDO;
+	stex.codepage = 1200;  // Unicode code page(set SETTEXTEX documentation)
+	m_rtf.SendMessage(EM_SETTEXTEX, (WPARAM)&stex, (LPARAM)sText.GetBuffer(sText.GetLength())); 
+	sText.ReleaseBuffer();
+#endif
+}
+
+CString CRulerRichEditCtrl::GetText()
+{
+	CString sText;
+
+#ifdef _UNICODE
+  	GETTEXTEX stex;
+  	stex.codepage = 1200;  // Unicode code page(set SETTEXTEX documentation)
+	stex.flags = GT_USECRLF;
+  
+  	int nSize = m_rtf.GetTextLength();
+  	//increase the size incase of unicode text
+  	nSize += 50;
+  	nSize = nSize * sizeof(WCHAR);
+  	stex.cb = nSize;
+  
+  	TCHAR *pText = new TCHAR[nSize];
+  	if(pText)
+  	{
+  		m_rtf.SendMessage(EM_GETTEXTEX, (WPARAM)&stex, (LPARAM)pText); 
+  		sText = pText;
+  
+  		delete []pText;
+  		pText = NULL;
+  	}
+#else
+	// Stream out here.
+	EDITSTREAM es;
+	es.dwError = 0;
+	es.pfnCallback = StreamOut;		// Set the callback
+	es.dwCookie = (DWORD_PTR) &sText;	// so sRTF receives the string
+	m_rtf.StreamOut(SF_TEXT, es);			// Call CRichEditCtrl::StreamOut to get the string.
+#endif
+
+	return sText;
+}
+
+BOOL CRulerRichEditCtrl::Save( CString& filename )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::Save
+	Description :	Saves the contents to the file filename. 
+					If filename is empty, a file dialog will 
+					be displayed and the selected name will be 
+					returned in the "CString".
+	Access :		Public
+					
+	Return :		BOOL				-	"TRUE" if the file 
+											was saved.
+	Parameters :	CString& filename	-	The file name to save 
+											to. Can be empty.
+					
+	Usage :			Call to save the contents of the embedded 
+					RTF-control do a file.
+
+   ============================================================*/
+{
+
+	BOOL result = TRUE;
+
+	CString* str = new CString;
+
+	EDITSTREAM	es;
+	es.dwCookie = (DWORD_PTR) str;
+	es.pfnCallback = StreamOut;
+	m_rtf.StreamOut( SF_RTF, es );
+
+	CTextFile f( _T( "rtf" ) );
+	result = f.WriteTextFile( filename, *str );
+
+	delete str;
+	return result;
+
+}
+
+BOOL CRulerRichEditCtrl::Load( CString& filename )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::Load
+	Description :	Loads the embedded RTF-control with the 
+					contents from the file filename. 
+					If filename is empty, a file dialog will 
+					be displayed and the selected name will be 
+					returned in the "CString".
+	Access :		Public
+					
+	Return :		BOOL				-	"TRUE" if the file 
+											was loaded.
+	Parameters :	CString& filename	-	File name to load 
+											from. Can be empty.
+					
+	Usage :			Call to load an RTF-file to the control.
+
+   ============================================================*/
+{
+
+	BOOL result = TRUE;
+
+	CString* str = new CString;
+	CTextFile f( _T( "rtf" ) );
+	result = f.ReadTextFile( filename, *str );
+	if( result )
+	{
+		EDITSTREAM	es;
+		es.dwCookie = (DWORD_PTR) str;
+//		es.pfnCallback = StreamIn;
+		m_rtf.StreamIn( SF_RTF, es );
+	}
+
+	delete str;
+	return result;
+
+}
+
+void CRulerRichEditCtrl::SetMode( int mode )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetMode
+	Description :	Sets the internal mode, that is, if the 
+					ruler should display inches or centimeters.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	int mode	-	Mode to use, "MODE_INCH" or 
+									"MODE_METRIC" (default)
+					
+	Usage :			Call to change the mode.
+
+   ============================================================*/
+{
+
+	m_ruler.SetMode( mode );
+
+}
+
+int CRulerRichEditCtrl::GetMode() const
+/* ============================================================
+	Function :		CRulerRichEditCtrl::GetMode
+	Description :	Gets the mode, that is, either "MODE_INCH" or 
+					"MODE_METRIC", that is used to draw the ruler.
+	Access :		Public         
+					
+	Return :		int		-	The mode, either "MODE_INCH" or 
+								"MODE_METRIC"
+	Parameters :	none
+
+	Usage :			Call to get the current mode.
+
+   ============================================================*/
+{
+
+	return m_ruler.GetMode();
+
+}
+
+CRichEditCtrl& CRulerRichEditCtrl::GetRichEditCtrl()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::GetRichEditCtrl
+	Description :	Returns an alias to the embedded RTF-control.
+	Access :		Public
+					
+	Return :		CRichEditCtrl&	-	An alias to the rtf-control
+	Parameters :	none
+
+	Usage :			Call to access the RTF-control directly.
+
+   ============================================================*/
+{
+
+	return m_rtf;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEditCtrl toolbar button handlers
+
+void CRulerRichEditCtrl::OnButtonFont() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonFont
+	Description :	Button handler for the Font button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	DoFont();
+
+
+}
+
+void CRulerRichEditCtrl::OnButtonColor() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonColor
+	Description :	Button handler for the Color button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoColor();
+
+}
+
+void CRulerRichEditCtrl::OnButtonBold() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonBold
+	Description :	Button handler for the Bold button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoBold();
+
+}
+
+void CRulerRichEditCtrl::OnButtonWrap()
+{
+	DoWrap();
+}
+
+void CRulerRichEditCtrl::OnButtonItalic() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonItalic
+	Description :	Button handler for the Italic button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoItalic();
+
+}
+
+void CRulerRichEditCtrl::OnButtonUnderline() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonUnderline
+	Description :	Button handler for the Underline button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoUnderline();
+
+}
+
+void CRulerRichEditCtrl::OnButtonLeftAlign() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonLeftAlign
+	Description :	Button handler for the Left aligned button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoLeftAlign();
+
+}
+
+void CRulerRichEditCtrl::OnButtonCenterAlign() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonCenterAlign
+	Description :	Button handler for the Center button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoCenterAlign();
+
+}
+
+void CRulerRichEditCtrl::OnButtonRightAlign() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonRightAlign
+	Description :	Button handler for the Right-aligned button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoRightAlign();
+
+}
+
+void CRulerRichEditCtrl::OnButtonIndent() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonIndent
+	Description :	Button handler for the Indent button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoIndent();
+
+}
+
+void CRulerRichEditCtrl::OnButtonOutdent() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonOutdent
+	Description :	Button handler for the outdent button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoOutdent();
+
+}
+
+void CRulerRichEditCtrl::OnButtonBullet() 
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnButtonBullet
+	Description :	Button handler for the Bullet button
+	Access :		Protected
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Called from MFC.
+
+   ============================================================*/
+{
+
+	DoBullet();
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEditCtrl private helpers
+
+void CRulerRichEditCtrl::SetTabStops( LPLONG tabs, int size )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetTabStops
+	Description :	Set the tab stops in the internal tab stop 
+					list from the RTF-control, converting the 
+					twip values to physical pixels.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	LPLONG tabs	-	A pointer to an array of 
+									"LONG" twip values.
+					int size	-	The size of "tabs"
+					
+	Usage :			Call to set the tab list.
+
+   ============================================================*/
+{
+
+	m_tabs.RemoveAll();
+
+	double twip = ( double )m_physicalInch / 1440;
+	for( int t = 0 ; t < size ; t++ )
+	{
+		// Convert from twips to pixels
+		int tabpos = *( tabs + t );
+		tabpos = ( int ) ( ( double ) tabpos * twip +.5 );
+		m_tabs.Add( tabpos );
+
+	}
+
+	m_ruler.SetTabStops( m_tabs );
+}
+
+void CRulerRichEditCtrl::UpdateTabStops()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::UpdateTabStops
+	Description :	Sets the tabs in the internal tab stop 
+					list, converting the twip physical (pixel) 
+					position to twip values.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to refresh the tab list from the RTF-
+					control. Called from the "OnPaint" handler.
+
+   ============================================================*/
+{
+
+	ParaFormat para( PFM_TABSTOPS );
+	m_rtf.GetParaFormat( para );
+	SetTabStops( (LPLONG)(para.rgxTabs), MAX_TAB_STOPS );
+
+}
+
+void CRulerRichEditCtrl::UpdateToolbarButtons()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::UpdateToolbarButtons
+	Description :	Updates the toolbar button, by getting 
+					formatting information from the currently 
+					selected text in the embedded RTF-control.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call as the selection changes in the 
+					RTF-control
+
+   ============================================================*/
+{
+	if( m_showToolbar && m_toolbar.m_hWnd )
+	{
+		CharFormat	cf;
+		cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
+		m_rtf.SendMessage( EM_GETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+
+		ParaFormat para( PFM_ALIGNMENT | PFM_NUMBERING );
+		m_rtf.GetParaFormat( para );
+
+		// Style
+		m_toolbar.SetState( BUTTON_BOLD, TBSTATE_ENABLED | ( ( cf.dwEffects & CFE_BOLD )  ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( BUTTON_ITALIC, TBSTATE_ENABLED | ( ( cf.dwEffects & CFE_ITALIC )  ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( BUTTON_UNDERLINE, TBSTATE_ENABLED | ( ( cf.dwEffects & CFM_UNDERLINE )  ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( BUTTON_LEFTALIGN, TBSTATE_ENABLED | ( para.wAlignment == PFA_LEFT ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( BUTTON_CENTERALIGN, TBSTATE_ENABLED | ( para.wAlignment == PFA_CENTER ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( BUTTON_RIGHTALIGN, TBSTATE_ENABLED | ( para.wAlignment == PFA_RIGHT ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( BUTTON_BULLET, TBSTATE_ENABLED | ( para.wNumbering ? TBSTATE_CHECKED : 0 ) );
+		m_toolbar.SetState( ID_BUTTONWRAP, TBSTATE_ENABLED | ( m_bInWrapMode ? TBSTATE_CHECKED : 0 ));
+
+		if( cf.dwMask & CFM_FACE )
+			m_toolbar.SetFontName( CString( cf.szFaceName ) );
+
+		if( cf.dwMask & CFM_SIZE )
+			m_toolbar.SetFontSize( cf.yHeight / 20 );
+
+		if( cf.dwMask & CFM_COLOR )
+			m_toolbar.SetFontColor( cf.crTextColor );
+	}
+}
+
+void CRulerRichEditCtrl::SetEffect( int mask, int effect )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetEffect
+	Description :	Sets the effect (bold, italic and/or 
+					underline) for the currently selected text 
+					in the embedded RTF-control.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	int mask	-	What effects are valid. See 
+									the documentation for 
+									"CHARFORMAT".
+					int effect	-	What effects to set. See the 
+									documentation for "CHARFORMAT".
+					
+	Usage :			Called internally from button handlers
+
+   ============================================================*/
+{
+
+	CharFormat cf;
+	cf.dwMask = mask;
+	cf.dwEffects = effect;
+
+	m_rtf.SendMessage( EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+	m_rtf.SetFocus();
+
+}
+
+void CRulerRichEditCtrl::SetAlignment( int alignment )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetAlignment
+	Description :	Sets the alignment for the currently 
+					selected text in the embedded RTF-control.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	int alignment	-	Alignment to set. See
+										documentation for
+										"PARAFORMAT"
+					
+	Usage :			Called internally from button handlers
+
+   ============================================================*/
+{
+
+	ParaFormat	para( PFM_ALIGNMENT );
+	para.wAlignment = ( WORD ) alignment;
+
+	m_rtf.SetParaFormat( para );
+	UpdateToolbarButtons();
+	m_rtf.SetFocus();
+
+}
+
+// Virtual interface
+void CRulerRichEditCtrl::DoFont()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoFont
+	Description :	Externally accessible member to set the
+					font of the control
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to set the font of the selected text.
+
+   ============================================================*/
+{
+
+	// Get the current font
+	LOGFONT	lf;
+	ZeroMemory( &lf, sizeof( LOGFONT ) );
+	CharFormat	cf;
+	m_rtf.SendMessage( EM_GETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+	int height;
+
+	// Creating a LOGFONT from the current font settings
+
+	// Font
+	if( cf.dwMask & CFM_FACE )
+		lstrcpy( lf.lfFaceName, cf.szFaceName );
+
+	if( cf.dwMask & CFM_SIZE )
+	{
+		double twip = ( double )m_physicalInch / 1440;
+		height = cf.yHeight;
+		height = -( int ) ( ( double ) height * twip +.5 );
+		lf.lfHeight = height;
+
+	}
+
+	// Effects
+	if( cf.dwMask & CFM_BOLD )
+	{
+		if( cf.dwEffects & CFE_BOLD )
+			lf.lfWeight = FW_BOLD;
+		else
+			lf.lfWeight = FW_NORMAL;
+	}
+
+	if( cf.dwMask & CFM_ITALIC )
+		if( cf.dwEffects & CFE_ITALIC )
+			lf.lfItalic = TRUE;
+
+	if( cf.dwMask & CFM_UNDERLINE )
+		if( cf.dwEffects & CFE_UNDERLINE )
+			lf.lfUnderline = TRUE;
+
+	// Show font dialog
+	CFontDialog	dlg(&lf);
+	if(dlg.DoModal() == IDOK)
+	{
+		// Apply new font
+		cf.yHeight = dlg.GetSize() * 2;
+		lstrcpy(cf.szFaceName, dlg.GetFaceName());
+
+		cf.dwMask = CFM_FACE | CFM_SIZE;
+		cf.dwEffects = 0;
+
+		if( dlg.IsBold() )
+		{
+			cf.dwMask |= CFM_BOLD;
+			cf.dwEffects |= CFE_BOLD;
+		}
+
+		if( dlg.IsItalic() )
+		{
+			cf.dwMask |= CFM_ITALIC;
+			cf.dwEffects |= CFE_ITALIC;
+		}
+
+		if( dlg.IsUnderline() )
+		{
+			cf.dwMask |= CFM_UNDERLINE;
+			cf.dwEffects |= CFE_UNDERLINE;
+		}
+
+		m_rtf.SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
+
+		m_rtf.SetFocus();
+	}
+}
+
+void CRulerRichEditCtrl::SetCurrentFontName( const CString& font )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetCurrentFontName
+	Description :	Changes the font of the selected text in 
+					the editor to "font".
+	Access :		Public
+					
+	Return :		void
+	Parameters :	const CString& font	-	Font name of font 
+											to change to.
+
+	Usage :			Call to set the font of the selected text 
+					in the editor.
+
+   ============================================================*/
+{
+	CharFormat	cf;
+	cf.dwMask = CFM_FACE;
+
+	lstrcpy( cf.szFaceName, font );
+
+	m_rtf.SendMessage( EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+}
+
+void CRulerRichEditCtrl::SetCurrentFontSize( int size )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetCurrentFontSize
+	Description :	Changes the size of the selected text in 
+					the editor to "size" (measured in 
+					typographical points).
+	Access :		Public
+					
+	Return :		void
+	Parameters :	int size	-	New size in typographical 
+									points
+
+	Usage :			Call to change the size of the selected 
+					text.
+
+   ============================================================*/
+{
+	CharFormat	cf;
+	cf.dwMask = CFM_SIZE;
+	cf.yHeight = size * 20;
+
+	m_rtf.SendMessage( EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+}
+
+void CRulerRichEditCtrl::SetCurrentFontColor( COLORREF color )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetCurrentFontSize
+	Description :	Changes the color of the selected text in 
+					the editor to "color".
+	Access :		Public
+					
+	Return :		void
+	Parameters :	COLORREF color	-	New color
+
+	Usage :			Call to change the color of the selected 
+					text.
+
+   ============================================================*/
+{
+
+	CharFormat	cf;
+	cf.dwMask = CFM_COLOR;
+	cf.crTextColor = color;
+
+	m_rtf.SendMessage( EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+
+}
+
+LRESULT CRulerRichEditCtrl::OnSetCurrentFontName( WPARAM font, LPARAM )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnSetCurrentFontName
+	Description :	Handler for the registered message 
+					"urm_SETCURRENTFONTNAME", called when the 
+					font name is changed from the toolbar.
+	Access :		Protected
+
+	Return :		LRESULT		-	Not used
+	Parameters :	WPARAM font	-	Pointer to the new font name
+					LPARAM		-	Not used
+
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	CString fnt( ( LPCTSTR ) font );
+	SetCurrentFontName( fnt );
+
+	return 0;
+	
+}
+
+LRESULT CRulerRichEditCtrl::OnSetCurrentFontSize(WPARAM, LPARAM size)
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnSetCurrentFontSize
+	Description :	Handler for the registered message 
+					"urm_SETCURRENTFONTSIZE", called when the 
+					font size is changed from the toolbar.
+	Access :		Protected
+
+	Return :		LRESULT		-	Not used
+	Parameters :	WPARAM		-	Not used
+					LPARAM size	-	New font size in typographical 
+									points of the selected text
+
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	SetCurrentFontSize((int)size);
+	return 0;
+	
+}
+
+LRESULT CRulerRichEditCtrl::OnSetCurrentFontColor(WPARAM, LPARAM color)
+/* ============================================================
+	Function :		CRulerRichEditCtrl::OnSetCurrentFontColor
+	Description :	Handler for the registered message 
+					"urm_SETCURRENTFONTCOLOR", called when the 
+					font color is changed from the toolbar.
+	Access :		Protected
+
+	Return :		LRESULT		-	Not used
+	Parameters :	WPARAM		-	Not used
+					LPARAM		-	New color of the selected 
+									text
+
+	Usage :			Called from MFC
+
+   ============================================================*/
+{
+
+	SetCurrentFontColor( ( COLORREF ) color );
+	return 0;
+	
+}
+
+void CRulerRichEditCtrl::DoColor()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoColor
+	Description :	Externally accessible member to set the
+					color of the selected text
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to set the color of the selected text.
+
+   ============================================================*/
+{
+
+	// Get the current color
+	COLORREF	clr( RGB( 0, 0, 0 ) );
+	CharFormat	cf;
+	m_rtf.SendMessage( EM_GETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+	if( cf.dwMask & CFM_COLOR )
+		clr = cf.crTextColor;
+
+	// Display color selection dialog
+	CColorDialog dlg( clr );
+	if( dlg.DoModal() == IDOK )
+	{
+		// Apply new color
+		cf.dwMask = CFM_COLOR;
+		cf.dwEffects = 0;
+		cf.crTextColor = dlg.GetColor();
+
+		m_rtf.SendMessage( EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
+
+	}
+
+	m_rtf.SetFocus();
+
+}
+
+void CRulerRichEditCtrl::DoWrap()
+{
+	if(m_bInWrapMode)
+	{
+		// Turn off word wrap.
+		m_rtf.SetTargetDevice(NULL, 1);
+		m_bInWrapMode = false;
+	}
+	else
+	{
+		// Turn on word wrap.
+		m_rtf.SetTargetDevice(NULL, 0); 
+		m_bInWrapMode = true;
+	}
+
+	g_Opt.SetEditWordWrap(m_bInWrapMode);
+
+	m_toolbar.CheckButton(ID_BUTTONWRAP, m_bInWrapMode);
+}
+
+void CRulerRichEditCtrl::DoBold()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoBold
+	Description :	Externally accessible member to set/unset
+					the selected text to/from bold
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to toggle the selected text to/from 
+					bold.
+
+   ============================================================*/
+{
+	m_toolbar.CheckButton( BUTTON_BOLD, !m_toolbar.IsButtonChecked( BUTTON_BOLD ) );
+
+	int effect = 0;
+	if( m_toolbar.IsButtonChecked( BUTTON_BOLD ) )
+		effect = CFE_BOLD;
+
+	SetEffect( CFM_BOLD, effect );
+}
+
+void CRulerRichEditCtrl::DoItalic()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoItalic
+	Description :	Externally accessible member to set/unset
+					the selected text to/from italic
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to toggle the selected text to/from 
+					italic.
+
+   ===========================================================  =*/
+{
+
+	m_toolbar.CheckButton( BUTTON_ITALIC, !m_toolbar.IsButtonChecked( BUTTON_ITALIC ) );
+
+	int effect = 0;
+	if( m_toolbar.IsButtonChecked( BUTTON_ITALIC ) )
+		effect = CFE_ITALIC;
+
+	SetEffect( CFM_ITALIC, effect );
+}
+
+void CRulerRichEditCtrl::DoUnderline()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoUnderline
+	Description :	Externally accessible member to set/unset
+					the selected text to/from underline
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to toggle the selected text to/from 
+					underlined.
+
+   ============================================================*/
+{
+
+	m_toolbar.CheckButton( BUTTON_UNDERLINE, !m_toolbar.IsButtonChecked( BUTTON_UNDERLINE ) );
+
+	int effect = 0;
+	if( m_toolbar.IsButtonChecked( BUTTON_UNDERLINE ) )
+		effect = CFE_UNDERLINE;
+
+	SetEffect( CFM_UNDERLINE, effect );
+}
+
+void CRulerRichEditCtrl::DoLeftAlign()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoLeftAlign
+	Description :	Externally accessible member to set the
+					selected text to left aligned.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to left-align the selected text
+
+   ============================================================*/
+{
+
+	if( !m_toolbar.IsButtonChecked( BUTTON_LEFTALIGN ) )
+		SetAlignment( PFA_LEFT );
+}
+
+void CRulerRichEditCtrl::DoCenterAlign()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoCenterAlign
+	Description :	Externally accessible member to set the
+					selected text to center aligned
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to center-align the selected text
+
+   ============================================================*/
+{
+	if( !m_toolbar.IsButtonChecked( BUTTON_CENTERALIGN ) )
+		SetAlignment( PFA_CENTER );
+}
+
+void CRulerRichEditCtrl::DoRightAlign()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoRightAlign
+	Description :	Externally accessible member to set the
+					selected text to right aligned
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to right-align the selected text
+
+   ============================================================*/
+{
+	if( !m_toolbar.IsButtonChecked( BUTTON_RIGHTALIGN ) )
+		SetAlignment( PFA_RIGHT );
+}
+
+void CRulerRichEditCtrl::DoIndent()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoIndent
+	Description :	Externally accessible member to indent the
+					selected text to the next tab position
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to indent the selected text
+
+   ============================================================*/
+{
+	// Get current indent
+	ParaFormat	para( PFM_STARTINDENT | PFM_TABSTOPS );
+	m_rtf.GetParaFormat( para );
+	int newindent = para.dxStartIndent;
+
+	// Find next larger tab
+	for( int t = MAX_TAB_STOPS - 1 ; t >= 0 ; t-- )
+	{
+
+		if( para.rgxTabs[ t ] > para.dxStartIndent )
+			newindent = para.rgxTabs[ t ];
+
+	}
+
+	if( newindent != para.dxStartIndent )
+	{
+
+		// Set indent to this value
+		para.dwMask = PFM_STARTINDENT | PFM_OFFSET;
+		para.dxStartIndent = newindent;
+		para.dxOffset = newindent;
+
+		m_rtf.SetParaFormat( para );
+
+	}
+
+	m_rtf.SetFocus();
+}
+
+void CRulerRichEditCtrl::DoOutdent()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoOutdent
+	Description :	Externally accessible member to outdent the
+					selected text to the previous tab position
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to outdent the selected text
+
+   ============================================================*/
+{
+	// Get the current indent, if any
+	ParaFormat	para( PFM_STARTINDENT | PFM_TABSTOPS );
+	m_rtf.GetParaFormat( para );
+	int newindent = 0;
+
+	// Find closest smaller tab
+	for( int t = 0 ; t < MAX_TAB_STOPS ; t++ )
+		if( para.rgxTabs[ t ] < para.dxStartIndent )
+			newindent = para.rgxTabs[ t ];
+
+	// Set indent to this value or 0 if none
+	para.dwMask = PFM_STARTINDENT | PFM_OFFSET;
+	para.dxStartIndent = newindent;
+	para.dxOffset = newindent;
+
+	m_rtf.SetParaFormat( para );
+	m_rtf.SetFocus();
+}
+
+void CRulerRichEditCtrl::DoBullet()
+/* ============================================================
+	Function :		CRulerRichEditCtrl::DoBullet
+	Description :	Externally accessible member to set the
+					selected text to bulleted
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+					
+	Usage :			Call to set the selected text to bulleted.
+
+   ============================================================*/
+{
+	m_toolbar.CheckButton( BUTTON_BULLET, !m_toolbar.IsButtonChecked( BUTTON_BULLET ) );
+
+	ParaFormat	para( PFM_NUMBERING );
+	if( m_toolbar.IsButtonChecked( BUTTON_BULLET ) )
+		para.wNumbering = PFN_BULLET;
+	else
+		para.wNumbering = 0;
+
+	m_rtf.SetParaFormat( para );
+	m_rtf.SetFocus();
+}
+
+void CRulerRichEditCtrl::ShowToolbar( BOOL show )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::ShowToolbar
+	Description :	Shows or hides the toolbar
+	Access :		Public
+					
+	Return :		void
+	Parameters :	BOOL show	-	"TRUE" to show
+					
+	Usage :			Call to show or hide the toolbar subcontrol
+
+   ============================================================*/
+{
+	m_showToolbar = show;
+
+	if( m_hWnd )
+	{
+		if( show )
+			m_toolbar.ShowWindow( SW_SHOW );
+		else
+			m_toolbar.ShowWindow( SW_HIDE );
+
+		CRect rect;
+		GetClientRect( rect );
+		LayoutControls( rect.Width(), rect.Height() );
+	}
+}
+
+void CRulerRichEditCtrl::ShowRuler( BOOL show )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::ShowRuler
+	Description :	Shows or hides the ruler
+	Access :		Public
+					
+	Return :		void
+	Parameters :	BOOL show	-	"TRUE" to show
+					
+	Usage :			Call to show or hide the ruler subcontrol
+
+   ============================================================*/
+{
+	m_showRuler = show;
+
+	if( m_hWnd )
+	{
+		if( show )
+			m_ruler.ShowWindow( SW_SHOW );
+		else
+			m_ruler.ShowWindow( SW_HIDE );
+
+		CRect rect;
+		GetClientRect( rect );
+		LayoutControls( rect.Width(), rect.Height() );
+	}
+}
+
+void CRulerRichEditCtrl::LayoutControls( int width, int height )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::LayoutControls
+	Description :	Lays out the sub-controls depending on 
+					visibility.
+	Access :		Private
+					
+	Return :		void
+	Parameters :	int width	-	Width of control
+					int height	-	Height of control
+					
+	Usage :			Called internally to lay out the controls
+
+   ============================================================*/
+{
+	int toolbarHeight = 0;
+	if( m_showToolbar )
+		toolbarHeight = TOOLBAR_HEIGHT;
+	int rulerHeight = 0;
+	if( m_showRuler )
+		rulerHeight = RULER_HEIGHT;
+
+	m_toolbar.MoveWindow( 0, 0, width, toolbarHeight );
+	m_ruler.MoveWindow( 0, toolbarHeight, width, rulerHeight );
+
+	int top = toolbarHeight + rulerHeight;
+	CRect rect( 0, top, width, height );
+	m_rtf.MoveWindow( rect );
+}
+
+BOOL CRulerRichEditCtrl::IsToolbarVisible() const
+/* ============================================================
+	Function :		CRulerRichEditCtrl::IsToolbarVisible
+	Description :	Returns if the toolbar is visible or not
+	Access :		Public
+					
+	Return :		BOOL	-	"TRUE" if visible
+	Parameters :	none
+					
+	Usage :			Call to get the visibility of the toolbar
+
+   ============================================================*/
+{
+	return m_showToolbar;
+}
+
+BOOL CRulerRichEditCtrl::IsRulerVisible() const
+/* ============================================================
+	Function :		CRulerRichEditCtrl::IsRulerVisible
+	Description :	Returns if the ruler is visible or not
+	Access :		Public
+					
+	Return :		BOOL	-	"TRUE" if visible
+	Parameters :	none
+					
+	Usage :			Call to get the visibility of the ruler
+
+   ============================================================*/
+{
+	return m_showRuler;
+}
+
+void CRulerRichEditCtrl::SetReadOnly( BOOL readOnly )
+/* ============================================================
+	Function :		CRulerRichEditCtrl::SetReadOnly
+	Description :	Sets the control to read only or not.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	BOOL readOnly	-	New read only state
+					
+	Usage :			Call to set the read only state of the 
+					control
+
+   ============================================================*/
+{
+	if( m_rtf.m_hWnd )
+		m_rtf.SetReadOnly( readOnly );
+
+	m_readOnly = readOnly;
+}
+
+BOOL CRulerRichEditCtrl::GetReadOnly() const
+/* ============================================================
+	Function :		CRulerRichEditCtrl::GetReadOnly
+	Description :	Returns if the control is read only or not
+	Access :		Public
+					
+	Return :		BOOL	-	"TRUE" if read only
+	Parameters :	none
+					
+	Usage :			Call to get the read only-state of the 
+					control
+
+   ============================================================*/
+{
+	return m_readOnly;
+}
+
+BOOL CRulerRichEditCtrl::PreTranslateMessage(MSG* pMsg)
+{
+	if(pMsg->message == WM_KEYDOWN)
+	{
+		switch(pMsg->wParam)
+		{
+		case 'X':
+			if(CONTROL_PRESSED)
+			{
+				m_rtf.Cut();
+				return TRUE;
+			}
+			break;
+
+		case 'C':
+			if(CONTROL_PRESSED)
+			{
+				m_rtf.Copy();
+				return TRUE;
+			}
+			break;
+
+		case 'V':
+			if(CONTROL_PRESSED)
+			{
+				m_rtf.Paste();
+				return TRUE;
+			}
+			break;
+		case 'I':
+			if(CONTROL_PRESSED)
+			{
+				DoItalic();
+				return TRUE;
+			}
+			break;
+		case 'B':
+			if(CONTROL_PRESSED)
+			{
+				DoBold();
+				return TRUE;
+			}
+			break;
+		case 'U':
+			if(CONTROL_PRESSED)
+			{
+				DoUnderline();
+				return TRUE;
+			}
+			break;
+		case 'Z':
+			if(CONTROL_PRESSED)
+			{
+				m_rtf.Undo();
+				return TRUE;
+			}
+			break;
+		case 'Y':
+			if(CONTROL_PRESSED)
+			{
+				m_rtf.Redo();
+				return TRUE;
+			}
+			break;
+		case 'W':
+			if(CONTROL_PRESSED)
+			{
+				DoWrap();
+				return TRUE;
+			}
+			break;
+		}
+	}
+
+	return CWnd::PreTranslateMessage(pMsg);
+}

+ 174 - 0
src/RulerRichEditCtrl/RulerRichEditCtrl.h

@@ -0,0 +1,174 @@
+#if !defined(AFX_RULERRICHEDITCTRL_H__4CD13283_82E4_484A_83B4_DBAD5B64F17C__INCLUDED_)
+#define AFX_RULERRICHEDITCTRL_H__4CD13283_82E4_484A_83B4_DBAD5B64F17C__INCLUDED_
+
+#include "RulerRichEdit.h"
+#include "RRECRuler.h"
+#include "RRECToolbar.h"
+
+#include "ids.h"
+
+#include "../RichEditCtrlEx.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Helper structs
+
+#ifdef _UNICODE
+struct CharFormat : public CHARFORMATW
+#else
+struct CharFormat : public CHARFORMAT
+#endif
+{
+	CharFormat()
+	{
+		memset( this, 0, sizeof ( CharFormat ) );
+		cbSize = sizeof( CharFormat );
+	};
+
+};
+
+struct ParaFormat : public PARAFORMAT
+{
+	ParaFormat( DWORD mask )
+	{
+		memset( this, 0, sizeof ( ParaFormat ) );
+		cbSize = sizeof( ParaFormat );
+		dwMask = mask;
+	}
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CRulerRichEditCtrl window
+
+class CRulerRichEditCtrl : public CWnd
+{
+
+public:
+// Construction/creation/destruction
+	CRulerRichEditCtrl();
+	virtual ~CRulerRichEditCtrl();
+	virtual BOOL Create( DWORD dwStyle, const RECT &rect, CWnd* pParentWnd, UINT nID, BOOL autohscroll = FALSE );
+
+// Attributes
+	void	SetMode( int mode );
+	int		GetMode() const;
+
+	void ShowToolbar( BOOL show = TRUE );
+	void ShowRuler( BOOL show = TRUE );
+
+	BOOL IsToolbarVisible() const;
+	BOOL IsRulerVisible() const;
+
+	CRichEditCtrl& GetRichEditCtrl( );
+
+// Implementation
+	CString GetRTF();
+	void	SetRTF( const CString& rtf );
+	void	SetText(CString sText);
+	CString GetText();
+	BOOL	Save( CString& filename );
+	BOOL	Load( CString& filename );
+
+	void SetReadOnly( BOOL readOnly );
+	BOOL GetReadOnly() const;
+
+// Formatting
+	virtual void DoFont();
+	virtual void DoColor();
+	virtual void DoBold();
+	virtual void DoItalic();
+	virtual void DoUnderline();
+	virtual void DoLeftAlign();
+	virtual void DoCenterAlign();
+	virtual void DoRightAlign();
+	virtual void DoIndent();
+	virtual void DoOutdent();
+	virtual void DoBullet();
+	virtual void DoWrap();
+
+	void SetCurrentFontName( const CString& font );
+	void SetCurrentFontSize( int points );
+	void SetCurrentFontColor( COLORREF color );
+
+// Overrides
+	//{{AFX_VIRTUAL(CRulerRichEditCtrl)
+	protected:
+	virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
+	//}}AFX_VIRTUAL
+
+protected:
+// Message handlers
+	//{{AFX_MSG(CRulerRichEditCtrl)
+	afx_msg void OnPaint();
+	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnButtonFont();
+	afx_msg void OnButtonColor();
+	afx_msg void OnButtonBold();
+	afx_msg void OnButtonItalic();
+	afx_msg void OnButtonUnderline();
+	afx_msg void OnButtonLeftAlign();
+	afx_msg void OnButtonCenterAlign();
+	afx_msg void OnButtonRightAlign();
+	afx_msg void OnButtonIndent();
+	afx_msg void OnButtonOutdent();
+	afx_msg void OnButtonBullet();
+	afx_msg void OnButtonWrap();
+	afx_msg void OnSetFocus(CWnd* pOldWnd);
+	afx_msg LRESULT OnSetText (WPARAM wParam, LPARAM lParam);
+	afx_msg LRESULT OnGetText (WPARAM wParam, LPARAM lParam);
+	afx_msg LRESULT OnGetTextLength (WPARAM wParam, LPARAM lParam);
+	afx_msg void OnLink(NMHDR* pnm, LRESULT* pResult);
+	//}}AFX_MSG
+
+	LRESULT OnTrackRuler(WPARAM mode, LPARAM pt);
+	LRESULT OnGetScrollPos(WPARAM, LPARAM);
+	LRESULT OnSetCurrentFontName(WPARAM font, LPARAM size);
+	LRESULT OnSetCurrentFontSize(WPARAM font, LPARAM size);
+	LRESULT OnSetCurrentFontColor(WPARAM font, LPARAM size);
+
+	DECLARE_MESSAGE_MAP()
+
+protected:
+	// Internal data
+	int				m_rulerPosition;	// The x-position of the ruler line when dragging a tab
+	CPen			m_pen;				// The pen to use for drawing the XORed ruler line
+
+	CDWordArray		m_tabs;				// An array containing the tab-positions in device pixels
+	int				m_margin;			// The margin to use for the ruler and buttons
+
+	int				m_physicalInch;		// The number of pixels for an inch on screen
+	int				m_movingtab;		// The tab-position being moved, or -1 if none
+	int				m_offset;			// Internal offset of the tab-marker being moved.
+
+	BOOL			m_showToolbar;
+	BOOL			m_showRuler;
+	BOOL			m_readOnly;
+
+	BOOL			m_bInWrapMode;
+
+	// Sub-controls
+	CRulerRichEdit	m_rtf;
+	CRRECToolbar	m_toolbar;
+	CRRECRuler		m_ruler;
+
+	// Private helpers
+	void	SetTabStops( LPLONG tabs, int size );
+	void	UpdateTabStops();
+
+	BOOL	CreateToolbar();
+	BOOL	CreateRuler();
+	BOOL	CreateRTFControl( BOOL autohscroll );
+	void	CreateMargins();
+
+	void	UpdateToolbarButtons();
+
+	void	SetEffect( int mask, int effect );
+	void	SetAlignment( int alignment );
+
+	void	LayoutControls( int width, int height );
+
+public:
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+};
+
+#endif // !defined(AFX_RULERRICHEDITCTRL_H__4CD13283_82E4_484A_83B4_DBAD5B64F17C__INCLUDED_)

+ 40 - 0
src/RulerRichEditCtrl/RulerRichEditCtrl.rc

@@ -0,0 +1,40 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar resources
+//
+
+#include "ids.h"
+
+TOOLBAR_CONTROL         BITMAP  DISCARDABLE     "src\\RulerRichEditCtrl\\toolbar.bmp"
+
+TOOLBAR_CONTROL TOOLBAR DISCARDABLE  16, 15
+BEGIN
+    BUTTON      BUTTON_FONT
+	SEPARATOR
+	SEPARATOR
+	SEPARATOR
+	SEPARATOR
+	SEPARATOR
+	SEPARATOR
+	SEPARATOR
+    BUTTON      BUTTON_BOLD
+    BUTTON      BUTTON_ITALIC
+    BUTTON      BUTTON_UNDERLINE
+    SEPARATOR
+    BUTTON      BUTTON_LEFTALIGN
+    BUTTON      BUTTON_CENTERALIGN
+    BUTTON      BUTTON_RIGHTALIGN
+    SEPARATOR
+    BUTTON      BUTTON_INDENT
+    BUTTON      BUTTON_OUTDENT
+    BUTTON      BUTTON_BULLET
+    BUTTON      ID_BUTTONWRAP
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    STRING_COLOR            "Color"
+    STRING_DEFAULT          "Automatic"
+    STRING_CUSTOM           "More Colours..."
+END
+

+ 137 - 0
src/RulerRichEditCtrl/SizeComboBox.cpp

@@ -0,0 +1,137 @@
+/* ==========================================================================
+	File :			SizeComboBox.cpp
+
+	Class :			CSizeComboBox
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+					Iain Clarke
+
+	Date :			2005-05-06
+
+	Purpose :		"CSizeComboBox" is derived from "CComboBox" and is a simple 
+					combobox for displaying a selection of font sizes.
+
+	Description :	Simpel derived class with members to fill the box and 
+					select an entry by contents instead of index.
+
+	Usage :			Create as any combobox, and call "FillCombo" to fill 
+					the control with a selection of font sizes. Call 
+					"SelectFontSize" to select an entry by content.
+
+   ========================================================================*/
+#include "stdafx.h"
+#include "SizeComboBox.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CSizeComboBox
+
+CSizeComboBox::CSizeComboBox()
+/* ============================================================
+	Function :		CSizeComboBox::CSizeComboBox
+	Description :	ctor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+CSizeComboBox::~CSizeComboBox()
+/* ============================================================
+	Function :		CSizeComboBox::~CSizeComboBox
+	Description :	dtor
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			
+
+   ============================================================*/
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CSizeComboBox, CComboBox)
+	//{{AFX_MSG_MAP(CSizeComboBox)
+		// NOTE - the ClassWizard will add and remove mapping macros here.
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CSizeComboBox message handlers
+
+void CSizeComboBox::SelectSize( int size )
+/* ============================================================
+	Function :		CSizeComboBox::SelectSize
+	Description :	Selects the entry corresponding to "size", 
+					if it exists in the list.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	int size	-	Size to select
+
+	Usage :			Call to select an entry by value in the 
+					combo list.
+
+   ============================================================*/
+{
+	CString sz;
+	sz.Format( _T( "%d" ), size );
+
+	int max = GetCount();
+	for( int t = 0 ; t < max ; t++ )
+	{
+		CString data;
+		GetLBText( t, data );
+		if( data == sz )
+		{
+			SetCurSel( t );
+			return;
+		}
+	}
+}
+
+void CSizeComboBox::FillCombo()
+/* ============================================================
+	Function :		CSizeComboBox::FillCombo
+	Description :	Fills the combo box with a fairly standard 
+					selection of font sizes.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	none
+
+	Usage :			Call to fill the combobox.
+
+   ============================================================*/
+{
+
+	AddString( _T( "8" ) );
+	AddString( _T( "9" ) );
+	AddString( _T( "10" ) );
+	AddString( _T( "11" ) );
+	AddString( _T( "12" ) );
+	AddString( _T( "14" ) );
+	AddString( _T( "16" ) );
+	AddString( _T( "18" ) );
+	AddString( _T( "20" ) );
+	AddString( _T( "22" ) );
+	AddString( _T( "24" ) );
+	AddString( _T( "26" ) );
+	AddString( _T( "28" ) );
+	AddString( _T( "36" ) );
+	AddString( _T( "48" ) );
+	AddString( _T( "72" ) );
+
+}

+ 50 - 0
src/RulerRichEditCtrl/SizeComboBox.h

@@ -0,0 +1,50 @@
+#if !defined(AFX_SIZECOMBOBOX_H__8A810F5D_013B_43FE_9821_505F4AAF12D6__INCLUDED_)
+#define AFX_SIZECOMBOBOX_H__8A810F5D_013B_43FE_9821_505F4AAF12D6__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// SizeComboBox.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CSizeComboBox window
+
+class CSizeComboBox : public CComboBox
+{
+// Construction
+public:
+	CSizeComboBox();
+
+// Attributes
+public:
+
+// Operations
+public:
+	void FillCombo();
+	void SelectSize( int size );
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CSizeComboBox)
+	//}}AFX_VIRTUAL
+
+// Implementation
+public:
+	virtual ~CSizeComboBox();
+
+	// Generated message map functions
+protected:
+	//{{AFX_MSG(CSizeComboBox)
+		// NOTE - the ClassWizard will add and remove member functions here.
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_SIZECOMBOBOX_H__8A810F5D_013B_43FE_9821_505F4AAF12D6__INCLUDED_)

+ 406 - 0
src/RulerRichEditCtrl/StdGrfx.cpp

@@ -0,0 +1,406 @@
+/* ==========================================================================
+	File :			StdGrfx.cpp
+
+	Class :			CStdGrfx
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+
+	Date :			2004-03-31
+
+	Purpose :		Static graphics helper class. Will return pens and 
+					brushes in the current Windows colors. Will also draw 
+					some different kind of boxes, as they seem not to 
+					scale well. Neither does these, but better than 
+					Draw3dFrame et. al.
+
+	Description :	
+
+	Usage :			
+
+   ========================================================================*/
+#include "stdafx.h"
+#include "stdgrfx.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+CPen	CStdGrfx::s_shadowPen;
+CPen	CStdGrfx::s_darkshadowPen;
+CPen	CStdGrfx::s_lightPen;
+CPen	CStdGrfx::s_highlightPen;
+CPen	CStdGrfx::s_dialogPen;
+CPen	CStdGrfx::s_windowPen;
+CPen	CStdGrfx::s_scrollPen;
+CBrush	CStdGrfx::s_dialogBrush;
+CBrush	CStdGrfx::s_backgroundBrush;
+CBrush	CStdGrfx::s_windowBrush;
+CBrush	CStdGrfx::s_scrollBrush;
+
+/////////////////////////////////////////////////////////////////////////////
+// CStdGrfx
+//
+
+CPen* CStdGrfx::shadowPen()
+/* ============================================================
+	Function :		CStdGrfx::shadowPen
+	Description :	Returns a pen with the current 3d shadow 
+					color (dark gray).
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_shadowPen.m_hObject == NULL )
+		CStdGrfx::s_shadowPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_3DSHADOW ) );
+	return &CStdGrfx::s_shadowPen;
+
+}
+
+CPen* CStdGrfx::darkshadowPen()
+/* ============================================================
+	Function :		CStdGrfx::darkshadowPen
+	Description :	Returns a pen with the current 3d dark 
+					shadow color (black).
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_darkshadowPen.m_hObject == NULL )
+		CStdGrfx::s_darkshadowPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_3DDKSHADOW ) );
+	return &CStdGrfx::s_darkshadowPen;
+
+}
+
+CPen* CStdGrfx::lightPen()
+/* ============================================================
+	Function :		CStdGrfx::lightPen
+	Description :	Returns a pen with the current 3d light 
+					color (light gray).
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_lightPen.m_hObject == NULL )
+		CStdGrfx::s_lightPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_3DLIGHT ) );
+	return &CStdGrfx::s_lightPen;
+
+}
+
+CPen* CStdGrfx::highlightPen()
+/* ============================================================
+	Function :		CStdGrfx::highlightPen
+	Description :	Returns a pen with the current 3d highligh 
+					color (white).
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_highlightPen.m_hObject == NULL )
+		CStdGrfx::s_highlightPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_3DHILIGHT ) );
+	return &CStdGrfx::s_highlightPen;
+
+}
+
+CPen* CStdGrfx::dialogPen()
+/* ============================================================
+	Function :		CStdGrfx::dialogPen
+	Description :	Returns a pen with the current dialog
+					background color
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_dialogPen.m_hObject == NULL )
+		CStdGrfx::s_dialogPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_3DFACE ) );
+	return &CStdGrfx::s_dialogPen;
+
+}
+
+CPen* CStdGrfx::windowPen()
+/* ============================================================
+	Function :		CStdGrfx::windowPen
+	Description :	Returns a pen with the current window 
+					background color
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_windowPen.m_hObject == NULL )
+		CStdGrfx::s_windowPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_WINDOW ) );
+	return &CStdGrfx::s_windowPen;
+
+}
+
+CPen* CStdGrfx::scrollPen()
+/* ============================================================
+	Function :		CStdGrfx::scrollPen
+	Description :	Returns a pen with the current scrollbar
+					background color
+	Access :		Public
+					
+	Return :		CPen*	-	The pen
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_scrollPen.m_hObject == NULL )
+		CStdGrfx::s_scrollPen.CreatePen( PS_SOLID, 0, ::GetSysColor( COLOR_SCROLLBAR ) );
+	return &CStdGrfx::s_scrollPen;
+
+}
+
+CBrush* CStdGrfx::dialogBrush()
+/* ============================================================
+	Function :		CStdGrfx::dialogBrush
+	Description :	Returns a brush with the current dialog 
+					color.
+	Access :		Public
+					
+	Return :		CBrush*	-	The brush
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_dialogBrush.m_hObject == NULL )
+		CStdGrfx::s_dialogBrush.CreateSolidBrush( ::GetSysColor( COLOR_3DFACE ) );
+	return &CStdGrfx::s_dialogBrush;
+
+}
+
+CBrush* CStdGrfx::windowBrush()
+/* ============================================================
+	Function :		CStdGrfx::windowBrush
+	Description :	Returns a brush with the current window 
+					color.
+	Access :		Public
+					
+	Return :		CBrush*	-	The brush
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_windowBrush.m_hObject == NULL )
+		CStdGrfx::s_windowBrush.CreateSolidBrush( ::GetSysColor( COLOR_WINDOW ) );
+	return &CStdGrfx::s_windowBrush;
+
+}
+
+CBrush* CStdGrfx::scrollBrush()
+/* ============================================================
+	Function :		CStdGrfx::scrollBrush
+	Description :	Returns a brush with the current scrollbar
+					color.
+	Access :		Public
+					
+	Return :		CBrush*	-	The brush
+	Parameters :	none
+
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	if( CStdGrfx::s_scrollBrush.m_hObject == NULL )
+		CStdGrfx::s_scrollBrush.CreateSolidBrush( ::GetSysColor( COLOR_SCROLLBAR ) );
+	return &CStdGrfx::s_scrollBrush;
+
+}
+
+void CStdGrfx::drawframed3dBox( CDC* dc, CRect rect )
+/* ============================================================
+	Function :		CStdGrfx::drawframed3dBox
+	Description :	Draws a 3d rect with a black frame.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	CDC* dc		-	The "CDC" to draw to
+					CRect rect	-	The rectangle to draw
+					
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	dc->SelectObject( CStdGrfx::darkshadowPen() );
+	dc->SelectObject( CStdGrfx::dialogBrush() );
+
+	dc->Rectangle( rect );
+	rect.InflateRect( -1, -1 );
+	CStdGrfx::draw3dFrame( dc, rect );
+
+	dc->SelectStockObject( BLACK_PEN );
+	dc->SelectStockObject( WHITE_BRUSH );
+
+}
+
+void CStdGrfx::drawsunkenframed3dWindow( CDC* dc, CRect rect )
+/* ============================================================
+	Function :		CStdGrfx::drawsunkenframed3dWindow
+	Description :	Draws a sunken 3d rect with a black frame.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	CDC* dc		-	The "CDC" to draw to
+					CRect rect	-	The rectangle to draw
+					
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	dc->SelectObject( CStdGrfx::windowPen() );
+	dc->SelectObject( CStdGrfx::windowBrush() );
+
+	dc->Rectangle( rect );
+	CStdGrfx::drawdoublesunken3dFrame( dc, rect );
+
+	dc->SelectStockObject( BLACK_PEN );
+	dc->SelectStockObject( WHITE_BRUSH );
+
+}
+
+void CStdGrfx::draw3dFrame( CDC* dc, CRect rect )
+/* ============================================================
+	Function :		CStdGrfx::draw3dFrame
+	Description :	Draws a 3d rect.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	CDC* dc		-	The "CDC" to draw to
+					CRect rect	-	The rectangle to draw
+					
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	rect.InflateRect( 0, 0, -1, -1 );
+
+	dc->SelectObject( CStdGrfx::highlightPen() );
+	dc->MoveTo( rect.left, rect.bottom );
+	dc->LineTo( rect.left, rect.top );
+	dc->LineTo( rect.right , rect.top );
+	dc->SelectObject( CStdGrfx::shadowPen() );
+	dc->LineTo( rect.right , rect.bottom );
+	dc->LineTo( rect.left, rect.bottom );
+
+	dc->SelectStockObject( BLACK_PEN );
+
+}
+
+void CStdGrfx::drawsunken3dFrame( CDC* dc, CRect rect )
+/* ============================================================
+	Function :		CStdGrfx::drawsunken3dFrame
+	Description :	Draws a sunken 3d rect.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	CDC* dc		-	The "CDC" to draw to
+					CRect rect	-	The rectangle to draw
+					
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	rect.InflateRect( 0, 0, -1, -1 );
+
+	dc->SelectObject( CStdGrfx::shadowPen() );
+	dc->MoveTo( rect.left, rect.bottom );
+	dc->LineTo( rect.left, rect.top );
+	dc->LineTo( rect.right , rect.top );
+	dc->SelectObject( CStdGrfx::highlightPen() );
+	dc->LineTo( rect.right , rect.bottom );
+	dc->LineTo( rect.left, rect.bottom );
+
+	dc->SelectStockObject( BLACK_PEN );
+
+}
+
+void CStdGrfx::drawdoublesunken3dFrame( CDC* dc, CRect rect )
+/* ============================================================
+	Function :		CStdGrfx::drawdoublesunken3dFrame
+	Description :	Draws a double sunken 3d rect.
+	Access :		Public
+					
+	Return :		void
+	Parameters :	CDC* dc		-	The "CDC" to draw to
+					CRect rect	-	The rectangle to draw
+					
+	Usage :			Static function.
+
+   ============================================================*/
+{
+
+	rect.InflateRect( 0, 0, -1, -1 );
+
+	dc->SelectObject( CStdGrfx::shadowPen() );
+	dc->MoveTo( rect.left, rect.bottom );
+	dc->LineTo( rect.left, rect.top );
+	dc->LineTo( rect.right , rect.top );
+
+	dc->SelectObject( CStdGrfx::highlightPen() );
+	dc->LineTo( rect.right , rect.bottom );
+	dc->LineTo( rect.left, rect.bottom );
+
+	rect.InflateRect( -1, -1 );
+
+	dc->SelectObject( CStdGrfx::darkshadowPen() );
+	dc->MoveTo( rect.left, rect.bottom );
+	dc->LineTo( rect.left, rect.top );
+	dc->LineTo( rect.right , rect.top );
+
+	dc->SelectObject( CStdGrfx::lightPen() );
+	dc->LineTo( rect.right , rect.bottom );
+	dc->LineTo( rect.left, rect.bottom );
+
+	dc->SelectStockObject( BLACK_PEN );
+
+}

+ 41 - 0
src/RulerRichEditCtrl/StdGrfx.h

@@ -0,0 +1,41 @@
+#ifndef _STDGRFX_H_
+#define _STDGRFX_H_
+
+class CStdGrfx {
+
+public:
+
+	static CPen* shadowPen();
+	static CPen* darkshadowPen();
+	static CPen* lightPen();
+	static CPen* highlightPen();
+	static CPen* dialogPen();
+	static CPen* windowPen();
+	static CPen* scrollPen();
+	static CBrush* dialogBrush();
+	static CBrush* windowBrush();
+	static CBrush* scrollBrush();
+
+	static void drawframed3dBox( CDC* dc, CRect rect );
+	static void draw3dFrame( CDC* dc, CRect rect );
+	static void drawsunken3dFrame( CDC* dc, CRect rect );
+	static void drawdoublesunken3dFrame( CDC* dc, CRect rect );
+	static void drawsunkenframed3dWindow( CDC* dc, CRect rect );
+
+private:
+
+	static CPen s_shadowPen;
+	static CPen s_darkshadowPen;
+	static CPen s_lightPen;
+	static CPen s_highlightPen;
+	static CPen s_dialogPen;
+	static CPen s_windowPen;
+	static CPen s_scrollPen;
+	static CBrush s_dialogBrush;
+	static CBrush s_backgroundBrush;
+	static CBrush s_windowBrush;
+	static CBrush s_scrollBrush;
+
+};
+
+#endif // _STDGRFX_H_

+ 737 - 0
src/RulerRichEditCtrl/TextFile/TextFile.cpp

@@ -0,0 +1,737 @@
+/* ==========================================================================
+	CTextFile
+
+	Author :		Johan Rosengren, Abstrakt Mekanik AB
+
+	Date :			2004-03-22
+
+	Purpose :		The class is a helper-package for text files and 
+					windows. It allows loading and saving text files in a 
+					single operation, as well as getting text to and 
+					from edit- and listboxes. If an empty filename is given 
+					as a parameter to a call, the standard file dialog will 
+					be displayed, to let the user select a file.
+					Error handling is managed internally, and the different 
+					API-functions return a BOOL to signal success or 
+					failure. In case of failure, FALSE returned, the member 
+					function GetErrorMessage can be called to retrieve a 
+					CString with the error message.
+					If this string is empty, the file selection was aborted 
+					in the case of an empty input name.
+   ========================================================================
+					14/4 2005	Added Dave Pritchards class CStdioFileEx
+								for MBCS/UNICODE-support.
+   ========================================================================*/
+
+#include "stdafx.h"
+#include "TextFile.h"
+#include "../External/StdioFileEx.h"
+
+////////////////////////////////////////
+// CTextFile construction/destruction
+
+CTextFile::CTextFile( const CString& ext, const CString& eol )
+/* ============================================================
+	Function :		CTextFile::CTextFile
+	Description :	constructor
+					
+	Return :		void
+	Parameters :	const CString& ext	-	Standard extension 
+											to use in case no 
+											file name is given.
+					const CString& eol	-	The end-of-line to 
+											use. Defaults to 
+											"\r\n".
+
+   ============================================================*/
+{
+
+	m_extension = ext;
+	m_eol = eol;
+
+}
+
+CTextFile::~CTextFile()
+/* ============================================================
+	Function :		CTextFile::~CTextFile
+	Description :	destructor
+					
+	Return :		void
+	Parameters :	none
+
+   ============================================================*/
+{
+}
+
+////////////////////////////////////////
+// CTextFile operations
+//
+
+BOOL CTextFile::ReadTextFile( CString& filename, CStringArray& contents )
+/* ============================================================
+	Function :		CTextFile::ReadTextFile
+	Description :	Will read the contents of the file filename 
+					into the CStringArray contents, one line 
+					from the file at a time.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+
+	Return :		BOOL					-	TRUE if OK. 
+												GetErrorMessage 
+												will contain errors.
+	Parameters :	CString& filename		-	file to read from
+					CStringArray& contents	-	will be filled 
+												with the contents 
+												of the file
+
+   ============================================================*/
+{
+
+	ClearError();
+	BOOL result = TRUE;
+
+	if( filename.IsEmpty() )
+		result = GetFilename( FALSE, filename );
+
+	if( result )
+	{
+		CStdioFileEx file;
+		CFileException feError;
+
+		if( file.Open( filename, CFile::modeRead , &feError ) )
+		{
+
+			contents.RemoveAll();
+
+			CString line;
+			while( file.ReadString( line ) )
+				contents.Add( line );
+
+			file.Close();
+
+		}
+		else
+		{
+
+			TCHAR	errBuff[256];
+			feError.GetErrorMessage( errBuff, 256 );
+			m_error = errBuff;
+			result = FALSE;
+
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::ReadTextFile( CString& filename, CString& contents )
+/* ============================================================
+	Function :		CTextFile::ReadTextFile
+	Description :	Will read the contents of the file filename 
+					into contents.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					
+	Return :		BOOL				-	TRUE if OK. 
+											GetErrorMessage will 
+											contain errors.
+	Parameters :	CString& filename	-	file to read from
+					CString& contents	-	will be filled with 
+											the contents of the 
+											file
+
+   ============================================================*/
+{
+
+	contents = _T( "" );
+
+	// Error handling
+	ClearError();
+
+	CStdioFileEx file;
+	CFileException feError;
+	BOOL result = TRUE;
+
+	if( filename.IsEmpty() )
+		result = GetFilename( FALSE, filename );
+
+	if( result )
+	{
+
+		// Reading the file
+		if( file.Open( filename, CFile::modeRead | CFile::typeText, &feError ) )
+		{
+
+			CString line;
+			while( file.ReadString( line ) )
+				contents += line + m_eol;
+
+			file.Close();
+
+		}
+		else
+		{
+
+			// Setting error message
+			TCHAR	errBuff[256];
+			feError.GetErrorMessage( errBuff, 256 );
+			m_error = errBuff;
+			result = FALSE;
+
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::WriteTextFile( CString& filename, const CStringArray& contents )
+/* ============================================================
+	Function :		CTextFile::WriteTextFile
+	Description :	Writes contents to filename. Will create 
+					the file if it doesn't already exist, 
+					overwrite it otherwise.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					
+	Return :		BOOL							-	TRUE if OK. 
+														GetErrorMessage 
+														will return 
+														errors
+	Parameters :	CString& filename				-	file to 
+														write to
+					conste CStringArray& contents	-	contents 
+														to write
+
+   ============================================================*/
+{
+
+	// Error handling
+	ClearError();
+
+	CStdioFileEx file;
+	CFileException feError;
+	BOOL result = TRUE;
+
+	if(filename.IsEmpty())
+		result = GetFilename(TRUE, filename);
+
+	if(result)
+	{
+		// Write file
+		if(file.Open( filename, CFile::modeWrite | CFile::modeCreate , &feError))
+		{
+			INT_PTR max = contents.GetSize();
+			for(int t = 0 ; t < max ; t++)
+			{
+				file.WriteString(contents[t] + m_eol);
+			}
+
+			file.Close();
+		}
+		else
+		{
+			// Set error message
+			TCHAR	errBuff[256];
+			feError.GetErrorMessage(errBuff, 256);
+			m_error = errBuff;
+			result = FALSE;
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::WriteTextFile( CString& filename, const CString& contents )
+/* ============================================================
+	Function :		CTextFile::WriteTextFile
+	Description :	Writes contents to filename. Will create 
+					the file if it doesn't already exist, 
+					overwrite it otherwise.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					
+	Return :		BOOL					-	TRUE if OK. 
+												GetErrorMessage 
+												will return 
+												errors
+	Parameters :	CString& filename		-	file to write to
+					const CString& contents	-	contents to write
+
+   ============================================================*/
+{
+
+	// Error checking
+	ClearError();
+
+	CStdioFileEx file;
+	CFileException feError;
+	BOOL result = TRUE;
+
+	if( filename.IsEmpty() )
+		result = GetFilename( TRUE, filename );
+
+	if( result )
+	{
+		// Write the file
+		if( file.Open( filename, CFile::modeWrite | CFile::modeCreate , &feError ) ) 
+		{
+
+			file.WriteString( contents );
+			file.Close();
+
+		}
+		else
+		{
+
+			// Set error message
+			TCHAR	errBuff[256];
+			feError.GetErrorMessage( errBuff, 256 );
+			m_error = errBuff;
+			result = FALSE;
+
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::AppendFile( CString& filename, const CString& contents )
+/* ============================================================
+	Function :		CTextFile::AppendFile
+	Description :	Appends contents to filename. Will create 
+					the file if it doesn't already exist.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					AppendFile will not add eols.
+					
+	Return :		BOOL					-	TRUE if OK. 
+												GetErrorMessage 
+												will return errors
+	Parameters :	CString& filename		-	file to write to
+					const CString& contents	-	contents to write
+
+   ============================================================*/
+{
+
+	CFile file;
+	CFileException feError;
+	BOOL result = TRUE;
+
+	if( filename.IsEmpty() )
+		result = GetFilename( TRUE, filename );
+
+	if( result )
+	{
+		// Write the file
+		if( file.Open( filename, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate, &feError ) ) 
+		{
+
+			file.SeekToEnd();
+			file.Write( contents, contents.GetLength() );
+			file.Close();
+
+		}
+		else
+		{
+
+			// Set error message
+			TCHAR	errBuff[256];
+			feError.GetErrorMessage( errBuff, 256 );
+			m_error = errBuff;
+			result = FALSE;
+
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::AppendFile( CString& filename, const CStringArray& contents )
+/* ============================================================
+	Function :		CTextFile::AppendFile
+	Description :	Appends contents to filename. Will create 
+					the file if it doesn't already exist.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					
+	Return :		BOOL					-	TRUE if OK. 
+												GetErrorMessage 
+												will return 
+												errors
+	Parameters :	CString& filename		-	file to write to
+					CStringArray contents	-	contents to write
+
+   ============================================================*/
+{
+
+	CStdioFileEx file;
+	CFileException feError;
+	BOOL result = TRUE;
+
+	if(filename.IsEmpty())
+		result = GetFilename(TRUE, filename);
+
+	if(result)
+	{
+		// Write the file
+		if(file.Open(filename, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate, &feError)) 
+		{
+
+			file.SeekToEnd();
+
+			INT_PTR max = contents.GetSize();
+			for(int t = 0 ; t < max ; t++)
+				file.WriteString(contents[t] + m_eol);
+
+			file.Close();
+		}
+		else
+		{
+			// Set error message
+			TCHAR errBuff[256];
+			feError.GetErrorMessage(errBuff, 256);
+			m_error = errBuff;
+			result = FALSE;
+
+		}
+	}
+
+	return result;
+
+}
+
+////////////////////////////////////////
+// Window operations
+//
+
+BOOL CTextFile::Load( CString& filename, CEdit* edit )
+/* ============================================================
+	Function :		CTextFile::Load
+	Description :	Loads a text file from filename to the 
+					CEdit edit.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					No translation of eols will be made.
+
+	Return :		BOOL				-	FALSE if failure. 
+											GetErrorMessage will 
+											return the error.
+	Parameters :	CString& filename	-	name of file to load
+					CEdit* edit			-	pointer to CEdit to 
+											set text to
+
+   ============================================================*/
+{
+	BOOL result = FALSE;
+
+	// Error checking
+	if( ValidParam( edit ) )
+	{
+		CString contents;
+		if( ReadTextFile( filename, contents ) )
+		{
+
+			edit->SetWindowText( contents );
+			result = TRUE;
+
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::Load( CString& filename, CListBox* list )
+/* ============================================================
+	Function :		CTextFile::Load
+	Description :	Loads a text file from filename to the 
+					CListBox list.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+
+	Return :		BOOL				-	FALSE if failure. 
+											GetErrorMessage will 
+											return the error.
+	Parameters :	CString& filename	-	name of file to load
+					CListBox* list		-	pointer to CListBox 
+											to set text to
+
+   ============================================================*/
+{
+	BOOL result = FALSE;
+
+	// Error checking
+	if(ValidParam(list))
+	{
+
+		// Read the file
+		CStringArray contents;
+		if(ReadTextFile( filename, contents))
+		{
+			// Set to listbox
+			INT_PTR max = contents.GetSize();
+			for(int t = 0 ; t < max ; t++)
+			{
+				if(contents[t].GetLength())
+				{
+					list->AddString(contents[t]);
+				}
+			}
+
+			result = TRUE;
+		}
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::Save( CString& filename, CEdit* edit )
+/* ============================================================
+	Function :		CTextFile::Save
+	Description :	Saves the contents of the CEdit edit to the 
+					file filename. The file will be created or 
+					overwritten.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					Note that the eol-öarkers from the editbox
+					will be used.
+
+	Return :		BOOL				-	FALSE if failure. 
+											GetErrorMessage will 
+											return the error.
+	Parameters :	CString& filename	-	name of file to save 
+											to. Will be 
+											overwritten
+					CEdit* edit			-	pointer to CEdit to 
+											get text from
+
+   ============================================================*/
+{
+	BOOL result = FALSE;
+
+	// Error checking
+	if( ValidParam( edit ) )
+	{
+
+		// Get text
+		CString contents;
+		edit->GetWindowText( contents );
+
+		// Write file
+		if( WriteTextFile( filename, contents ) )
+			result = TRUE;
+
+	}
+
+	return result;
+
+}
+
+BOOL CTextFile::Save( CString& filename, CListBox* list )
+/* ============================================================
+	Function :		CTextFile::Save
+	Description :	Saves the contents of the CListBox list to 
+					the file filename. The file will be created 
+					or overwritten.
+					If filename is empty, the standard file 
+					dialog will be displayed, and - if OK is 
+					selected - filename will contain the 
+					selected filename on return.
+					
+	Return :		BOOL				-	FALSE if failure. 
+											GetErrorMessage will 
+											return the error.
+	Parameters :	CString& filename	-	name of file to save 
+											to. Will be 
+											overwritten
+					CListBox* list		-	pointer to CListBox 
+											to get text from
+
+   ============================================================*/
+{
+	BOOL result = FALSE;
+
+	// Error checking
+	if( ValidParam( list ) )
+	{
+
+		// Get listbox contents
+		CStringArray contents;
+		int max = list->GetCount();
+		for( int t = 0; t < max ; t++ )
+		{
+
+			CString line;
+			list->GetText( t, line );
+			contents.Add( line );
+
+		}
+
+		// Write file
+		if( WriteTextFile( filename, contents ) )
+			result = TRUE;
+
+	}
+
+	return result;
+
+}
+
+////////////////////////////////////////
+// Error handling
+//
+
+CString CTextFile::GetErrorMessage()
+/* ============================================================
+	Function :		CTextFile::GetErrorMessage
+	Description :	Retrieves the error message. Should be 
+					called after any of the file operations 
+					returns FALSE and the file name is not 
+					empty.
+
+	Return :		CString	-	The current error string
+	Parameters :	none
+
+   ============================================================*/
+{
+
+	return m_error;
+
+}
+
+////////////////////////////////////////
+// Private functions
+//
+
+void CTextFile::ClearError()
+/* ============================================================
+	Function :		CTextFile::ClearError
+	Description :	Clears the internal error string. Should 
+					be called first by all functions setting 
+					the error message string.
+
+	Return :		void
+	Parameters :	none
+
+   ============================================================*/
+{
+
+	m_error = _T( "" );
+
+}
+
+BOOL CTextFile::ValidParam( CWnd* wnd )
+/* ============================================================
+	Function :		CTextFile::ValidParam
+	Description :	Used to check parameters of the Save/Load
+					functions. The pointer to the window must 
+					be valid and the window itself must exist.
+					
+	Return :		BOOL			-	FALSE if any parameter 
+										was invalid
+	Parameters :	CWnd* wnd		-	a window pointer, that 
+										must be valid, to a 
+										window
+
+   ============================================================*/
+{
+
+	ClearError();
+	BOOL result = TRUE;
+
+	if( wnd == NULL )
+	{
+
+		ASSERT( FALSE );
+		result = FALSE;
+
+	}
+
+	if( !IsWindow( wnd->m_hWnd ) )
+	{
+
+		ASSERT( FALSE );
+		result = FALSE;
+
+	}
+
+	if( !result )
+		m_error = _T( "Bad Window handle as parameter" );
+
+	return result;
+
+}
+
+BOOL CTextFile::GetFilename( BOOL save, CString& filename )
+/* ============================================================
+	Function :		CTextFile::GetFilename
+	Description :	The function will display a standard file 
+					dialog. If the instance is created with an 
+					extension, the extension will be used to 
+					filter files.
+					
+	Return :		BOOL				-	TRUE if a file was 
+											selected
+	Parameters :	BOOL save			-	TRUE if the file 
+											should be saved.
+					CString& filename	-	Placeholder for the 
+											selected filename
+
+   ============================================================*/
+{
+	CString filter;
+	CString extension = GetExtension();
+	if( extension.GetLength() )
+		filter = extension + _T( "-files (*." + extension + ")|*." ) + extension + _T( "|All Files (*.*)|*.*||" );
+
+	BOOL result = FALSE;
+	CFileDialog dlg( !save, extension, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, filter );
+
+	if( dlg.DoModal() == IDOK )
+	{
+		filename = dlg.GetPathName();
+		result = TRUE;
+	}
+
+	return result;
+
+}
+
+CString CTextFile::GetExtension()
+/* ============================================================
+	Function :		CTextFile::GetExtension
+	Description :	An accessor for the m_extension field.
+					
+	Return :		CString	-	the extension.
+	Parameters :	none
+
+   ============================================================*/
+{
+
+	return m_extension;
+
+}

+ 50 - 0
src/RulerRichEditCtrl/TextFile/TextFile.h

@@ -0,0 +1,50 @@
+#ifndef _TEXTFILE_H_
+#define _TEXTFILE_H_
+
+class CTextFile
+{
+
+public:
+
+	// ctor( s )
+	CTextFile( const CString& ext = _T( "" ), const CString& eol = _T( "\r\n" ) );
+
+	// dtor
+	virtual ~CTextFile();
+
+	// File operations
+	BOOL	ReadTextFile( CString& filename, CStringArray& contents );
+	BOOL	ReadTextFile( CString& filename, CString& contents );
+
+	BOOL	WriteTextFile( CString& filename, const CStringArray& contents );
+	BOOL	WriteTextFile( CString& filename, const CString& contents );
+
+	BOOL	AppendFile( CString& filename, const CString& contents );
+	BOOL	AppendFile( CString& filename, const CStringArray& contents );
+
+	// Window operations
+	BOOL	Load( CString& filename, CEdit* edit );
+	BOOL	Load( CString& filename, CListBox* list );
+	BOOL	Save( CString& filename, CEdit* edit );
+	BOOL	Save( CString& filename, CListBox* list );
+
+	// Error handling
+	CString GetErrorMessage();
+
+protected:
+
+	virtual BOOL GetFilename( BOOL save, CString& filename );
+	CString GetExtension();
+
+private:
+
+	CString m_error;
+	CString m_extension;
+	CString m_eol;
+
+	void	ClearError();
+	BOOL	ValidParam( CWnd* wnd );
+
+};
+
+#endif // _TEXTFILE_H_

+ 59 - 0
src/RulerRichEditCtrl/ids.h

@@ -0,0 +1,59 @@
+//
+// Assorted defines and ids for the RulerRichEditCtrl
+//
+
+#ifndef _IDS_H_
+#define _IDS_H_
+
+// Some measures
+#define TOOLBAR_HEIGHT		28
+#define RULER_HEIGHT		26
+#define TOP_HEIGHT			RULER_HEIGHT + TOOLBAR_HEIGHT
+#define FONT_COMBO_WIDTH	128
+#define SIZE_COMBO_WIDTH	48
+#define COMBO_HEIGHT		128
+#define COMBO_WIDTH			48
+#define COLOR_WIDTH			64
+
+#define FONT_NAME_POS		2
+#define FONT_SIZE_POS		4
+#define FONT_COLOR_POS		6
+
+// Measures for the ruler
+#define MODE_INCH	0
+#define MODE_METRIC	1
+
+// ID of sub-controls
+#define TOOLBAR_CONTROL	10
+#define RULER_CONTROL	11
+#define RTF_CONTROL		12
+
+// Toolbar buttons
+#define BUTTON_FONT			20
+#define BUTTON_COLOR		21
+#define BUTTON_BOLD			22
+#define BUTTON_ITALIC		23
+#define BUTTON_UNDERLINE	24
+#define BUTTON_LEFTALIGN	25
+#define BUTTON_CENTERALIGN	26
+#define BUTTON_RIGHTALIGN	27
+#define BUTTON_INDENT		28
+#define BUTTON_OUTDENT		29
+#define BUTTON_BULLET		30
+
+#define DROPDOWN_FONT		31
+#define DROPDOWN_SIZE		32
+
+#define STRING_COLOR		33
+#define STRING_DEFAULT		34
+#define STRING_CUSTOM		35
+#define ID_BUTTONWRAP		36
+
+// Mouse handling
+extern UINT urm_RULERACTION;
+extern UINT urm_GETSCROLLPOS;
+#define UP		0
+#define DOWN	1
+#define MOVE	2
+
+#endif // _IDS_H_

BIN
src/RulerRichEditCtrl/toolbar.bmp