Browse Source

Bug 1580: Support wider range of Unicode characters in internal editor

https://winscp.net/tracker/1580

Source commit: 162b977f3987658f9e124db1d6694b309feaa02a
Martin Prikryl 8 years ago
parent
commit
a24965c84e

+ 22 - 46
source/forms/Editor.cpp

@@ -144,10 +144,10 @@ int __fastcall TPreambleFilteringFileStream::Write(
   EXCEPTION;
 }
 //---------------------------------------------------------------------------
-class TRichEdit20 : public TRichEdit
+class TEditorRichEdit : public TNewRichEdit
 {
 public:
-  virtual __fastcall TRichEdit20(TComponent * AOwner);
+  virtual __fastcall TEditorRichEdit(TComponent * AOwner);
 
   bool __fastcall LoadFromStream(TStream * Stream, TEncoding * Encoding, bool & EncodingError);
 
@@ -166,7 +166,6 @@ protected:
   friend unsigned long __stdcall StreamLoad(DWORD_PTR Cookie, unsigned char * Buff, long Read, long * WasRead);
 
   virtual void __fastcall CreateParams(TCreateParams & Params);
-  virtual void __fastcall DestroyWnd();
   void __fastcall Dispatch(void * Message);
   bool __fastcall GetCanRedo();
   void __fastcall SetTabSize(unsigned int TabSize);
@@ -187,8 +186,8 @@ private:
   TColor FFontColor;
 };
 //---------------------------------------------------------------------------
-__fastcall TRichEdit20::TRichEdit20(TComponent * AOwner) :
-  TRichEdit(AOwner),
+__fastcall TEditorRichEdit::TEditorRichEdit(TComponent * AOwner) :
+  TNewRichEdit(AOwner),
   FLibrary(0),
   FTabSize(0),
   FWordWrap(true),
@@ -197,7 +196,7 @@ __fastcall TRichEdit20::TRichEdit20(TComponent * AOwner) :
 {
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::ApplyFont()
+void __fastcall TEditorRichEdit::ApplyFont()
 {
   std::unique_ptr<TFont> NewFont(new TFont());
   TWinConfiguration::RestoreFont(FFontConfiguration, NewFont.get());
@@ -214,7 +213,7 @@ void __fastcall TRichEdit20::ApplyFont()
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::SetFormat(
+void __fastcall TEditorRichEdit::SetFormat(
   const TFontConfiguration & FontConfiguration, TColor FontColor, unsigned int TabSize,
   bool AWordWrap)
 {
@@ -258,14 +257,14 @@ void __fastcall TRichEdit20::SetFormat(
 
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::ResetFormat()
+void __fastcall TEditorRichEdit::ResetFormat()
 {
   // tabs are paragraph attributes, which default values cannot be set,
   // so we need to reapply them after loading file
   SetTabSize(FTabSize);
 }
 //---------------------------------------------------------------------------
-int __fastcall TRichEdit20::FindText(const UnicodeString SearchStr, int StartPos,
+int __fastcall TEditorRichEdit::FindText(const UnicodeString SearchStr, int StartPos,
   int /*Length*/, TSearchTypes Options, bool Down)
 {
   ::FINDTEXTEX Find;
@@ -282,28 +281,15 @@ int __fastcall TRichEdit20::FindText(const UnicodeString SearchStr, int StartPos
   return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::Redo()
+void __fastcall TEditorRichEdit::Redo()
 {
   SendMessage(Handle, EM_REDO, 0, 0);
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::CreateParams(TCreateParams & Params)
+void __fastcall TEditorRichEdit::CreateParams(TCreateParams & Params)
 {
-  UnicodeString RichEditModuleName(L"RICHED20.DLL");
-  long int OldError;
+  TNewRichEdit::CreateParams(Params);
 
-  OldError = SetErrorMode(SEM_NOOPENFILEERRORBOX);
-  FLibrary = LoadLibrary(RichEditModuleName.c_str());
-  SetErrorMode(OldError);
-
-  // No fallback, RichEdit 2.0 is available since Windows NT/98
-  if (FLibrary == 0)
-  {
-    throw Exception(FORMAT(L"Cannot load %s", (RichEditModuleName)));
-  }
-
-  TCustomMemo::CreateParams(Params);
-  CreateSubClass(Params, RICHEDIT_CLASS);
   Params.Style = Params.Style |
     (HideScrollBars ? 0 : ES_DISABLENOSCROLL) |
     (HideSelection ? 0 : ES_NOHIDESEL);
@@ -311,17 +297,7 @@ void __fastcall TRichEdit20::CreateParams(TCreateParams & Params)
     ~(CS_HREDRAW | CS_VREDRAW);
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::DestroyWnd()
-{
-  TRichEdit::DestroyWnd();
-
-  if (DebugAlwaysTrue(FLibrary != 0))
-  {
-    FreeLibrary(FLibrary);
-  }
-}
-//---------------------------------------------------------------------------
-void __fastcall TRichEdit20::WMPaste()
+void __fastcall TEditorRichEdit::WMPaste()
 {
   // override default pasting to prevent inserting formatted text (RTF).
   const wchar_t * Text = NULL;
@@ -396,7 +372,7 @@ static int __fastcall AdjustLineBreaks(unsigned char * Dest, const TBytes & Sour
 struct TStreamLoadInfo
 {
   TRichEditStreamInfo * StreamInfo;
-  TRichEdit20 * RichEdit;
+  TEditorRichEdit * RichEdit;
 };
 //---------------------------------------------------------------------------
 // VCLCOPY Vcl.ComCtrls.pas,
@@ -410,7 +386,7 @@ static unsigned long __stdcall StreamLoad(DWORD_PTR Cookie, unsigned char * Buff
   return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::EMStreamIn(TMessage & Message)
+void __fastcall TEditorRichEdit::EMStreamIn(TMessage & Message)
 {
   TEditStream * EditStream = reinterpret_cast<TEditStream *>(Message.LParam);
   EditStream->pfnCallback = &::StreamLoad;
@@ -418,10 +394,10 @@ void __fastcall TRichEdit20::EMStreamIn(TMessage & Message)
   LoadInfo.StreamInfo = reinterpret_cast<TRichEditStreamInfo *>(EditStream->dwCookie);
   LoadInfo.RichEdit = this;
   EditStream->dwCookie = reinterpret_cast<DWORD_PTR>(&LoadInfo);
-  TRichEdit::Dispatch(&Message);
+  TNewRichEdit::Dispatch(&Message);
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::Dispatch(void * Message)
+void __fastcall TEditorRichEdit::Dispatch(void * Message)
 {
   TMessage * M = static_cast<TMessage *>(Message);
   switch (M->Msg)
@@ -435,17 +411,17 @@ void __fastcall TRichEdit20::Dispatch(void * Message)
       break;
 
     default:
-      TRichEdit::Dispatch(Message);
+      TNewRichEdit::Dispatch(Message);
       break;
   }
 }
 //---------------------------------------------------------------------------
-bool __fastcall TRichEdit20::GetCanRedo()
+bool __fastcall TEditorRichEdit::GetCanRedo()
 {
   return (SendMessage(Handle, EM_CANREDO, 0, 0) != 0);
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit20::SetTabSize(unsigned int TabSize)
+void __fastcall TEditorRichEdit::SetTabSize(unsigned int TabSize)
 {
   DebugAssert(TabSize > 0);
 
@@ -490,7 +466,7 @@ void __fastcall TRichEdit20::SetTabSize(unsigned int TabSize)
   SendMessage(Handle, EM_EXSETSEL, 0, (LPARAM)&CharRange);
 }
 //---------------------------------------------------------------------------
-bool __stdcall TRichEdit20::StreamLoad(
+bool __stdcall TEditorRichEdit::StreamLoad(
   TRichEditStreamInfo * StreamInfo, unsigned char * Buff, long Read, long & WasRead)
 {
   WasRead = 0;
@@ -583,7 +559,7 @@ bool __stdcall TRichEdit20::StreamLoad(
   return Result;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TRichEdit20::LoadFromStream(TStream * Stream, TEncoding * Encoding, bool & EncodingError)
+bool __fastcall TEditorRichEdit::LoadFromStream(TStream * Stream, TEncoding * Encoding, bool & EncodingError)
 {
   FStreamLoadEncodingError = false;
   FStreamLoadError = false;
@@ -669,7 +645,7 @@ __fastcall TEditorForm::TEditorForm(TComponent* Owner)
     // Based on TEncoding.GetANSI.
     FAnsiEncoding = new TMBCSEncoding(GetACP(), MB_ERR_INVALID_CHARS, 0);
   }
-  EditorMemo = new TRichEdit20(this);
+  EditorMemo = new TEditorRichEdit(this);
   EditorMemo->Parent = this;
   EditorMemo->Align = alClient;
   EditorMemo->HideSelection = false;

+ 2 - 2
source/forms/Editor.h

@@ -26,7 +26,7 @@
 #include "TBXExtItems.hpp"
 #include <Vcl.AppEvnts.hpp>
 //---------------------------------------------------------------------------
-class TRichEdit20;
+class TEditorRichEdit;
 //---------------------------------------------------------------------------
 class TEditorForm : public TForm
 {
@@ -106,7 +106,7 @@ private:
   TFindDialog * FFindDialog;
   TReplaceDialog * FReplaceDialog;
   bool FCloseAnnounced;
-  TRichEdit20 * EditorMemo;
+  TEditorRichEdit * EditorMemo;
   bool FFormRestored;
   UnicodeString FWindowParams;
   unsigned int FInstance;

+ 33 - 71
source/forms/GenerateUrl.cpp

@@ -11,6 +11,7 @@
 #include <PuttyTools.h>
 #include <TextsWin.h>
 #include <ProgParams.h>
+#include <GUITools.h>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #ifndef NO_RESOURCES
@@ -36,58 +37,29 @@ void __fastcall DoGenerateTransferCodeDialog(
   Dialog->Execute();
 }
 //---------------------------------------------------------------------------
-// Rich edit 4.1 supports "Friendly name hyperlinks"
-class TRichEdit41 : public TRichEdit
+class TRichEditWithLinks : public TNewRichEdit
 {
 public:
-  virtual __fastcall TRichEdit41(TComponent * AOwner);
+  virtual __fastcall TRichEditWithLinks(TComponent * AOwner);
 
 protected:
   virtual void __fastcall CreateWnd();
-  virtual void __fastcall CreateParams(TCreateParams & Params);
-  virtual void __fastcall DestroyWnd();
   void __fastcall Dispatch(void * Message);
-
-private:
-  HINSTANCE FLibrary;
 };
 //---------------------------------------------------------------------------
-__fastcall TRichEdit41::TRichEdit41(TComponent * AOwner) :
-  TRichEdit(AOwner),
-  FLibrary(0)
-{
-}
-//---------------------------------------------------------------------------
-void __fastcall TRichEdit41::CreateParams(TCreateParams & Params)
+__fastcall TRichEditWithLinks::TRichEditWithLinks(TComponent * AOwner) :
+  TNewRichEdit(AOwner)
 {
-  UnicodeString RichEditModuleName(L"MSFTEDIT.DLL");
-  long int OldError;
-
-  OldError = SetErrorMode(SEM_NOOPENFILEERRORBOX);
-  FLibrary = LoadLibrary(RichEditModuleName.c_str());
-  SetErrorMode(OldError);
-
-  TCustomMemo::CreateParams(Params);
-  // Should not happen as
-  if (FLibrary != 0)
-  {
-    // MSDN says that we should use MSFTEDIT_CLASS to load Rich Edit 4.1:
-    // https://msdn.microsoft.com/en-us/library/windows/desktop/bb787873.aspx
-    // But MSFTEDIT_CLASS is defined as "RICHEDIT50W",
-    // so not sure what version we are loading.
-    // Seem to work on Windows XP SP3.
-    CreateSubClass(Params, MSFTEDIT_CLASS);
-  }
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit41::CreateWnd()
+void __fastcall TRichEditWithLinks::CreateWnd()
 {
-  TRichEdit::CreateWnd();
+  TNewRichEdit::CreateWnd();
   int Mask = SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
   SendMessage(Handle, EM_SETEVENTMASK, 0, Mask | ENM_LINK);
 }
 //---------------------------------------------------------------------------
-void __fastcall TRichEdit41::Dispatch(void * AMessage)
+void __fastcall TRichEditWithLinks::Dispatch(void * AMessage)
 {
   TMessage & Message = *reinterpret_cast<TMessage *>(AMessage);
   if (Message.Msg == CN_NOTIFY)
@@ -110,21 +82,11 @@ void __fastcall TRichEdit41::Dispatch(void * AMessage)
         }
       }
     }
-    TRichEdit::Dispatch(AMessage);
+    TNewRichEdit::Dispatch(AMessage);
   }
   else
   {
-    TRichEdit::Dispatch(AMessage);
-  }
-}
-//---------------------------------------------------------------------------
-void __fastcall TRichEdit41::DestroyWnd()
-{
-  TRichEdit::DestroyWnd();
-
-  if (FLibrary != 0)
-  {
-    FreeLibrary(FLibrary);
+    TNewRichEdit::Dispatch(AMessage);
   }
 }
 //---------------------------------------------------------------------------
@@ -191,20 +153,20 @@ __fastcall TGenerateUrlDialog::TGenerateUrlDialog(
   FPath = Path;
   FChanging = false;
 
-  FResultMemo41 = new TRichEdit41(this);
-  FResultMemo41->Parent = ResultMemo->Parent;
-  FResultMemo41->SetBounds(ResultMemo->Left, ResultMemo->Top, ResultMemo->Width, ResultMemo->Height);
-  FResultMemo41->Anchors = ResultMemo->Anchors;
-  FResultMemo41->BevelInner = ResultMemo->BevelInner;
-  FResultMemo41->BevelOuter = ResultMemo->BevelOuter;
-  FResultMemo41->BorderStyle = ResultMemo->BorderStyle;
-  FResultMemo41->PopupMenu = ResultMemo->PopupMenu;
-  FResultMemo41->TabOrder = ResultMemo->TabOrder;
-  FResultMemo41->PlainText = false;
-  FResultMemo41->WantReturns = false; // affects Esc too, what we want
+  FResultMemoWithLinks = new TRichEditWithLinks(this);
+  FResultMemoWithLinks->Parent = ResultMemo->Parent;
+  FResultMemoWithLinks->SetBounds(ResultMemo->Left, ResultMemo->Top, ResultMemo->Width, ResultMemo->Height);
+  FResultMemoWithLinks->Anchors = ResultMemo->Anchors;
+  FResultMemoWithLinks->BevelInner = ResultMemo->BevelInner;
+  FResultMemoWithLinks->BevelOuter = ResultMemo->BevelOuter;
+  FResultMemoWithLinks->BorderStyle = ResultMemo->BorderStyle;
+  FResultMemoWithLinks->PopupMenu = ResultMemo->PopupMenu;
+  FResultMemoWithLinks->TabOrder = ResultMemo->TabOrder;
+  FResultMemoWithLinks->PlainText = false;
+  FResultMemoWithLinks->WantReturns = false; // affects Esc too, what we want
   ResultMemo->Visible = false;
 
-  ReadOnlyControl(FResultMemo41);
+  ReadOnlyControl(FResultMemoWithLinks);
 }
 //---------------------------------------------------------------------------
 bool __fastcall TGenerateUrlDialog::IsFileUrl()
@@ -771,11 +733,11 @@ void __fastcall TGenerateUrlDialog::UpdateControls()
 
     if (FixedWidth)
     {
-      FResultMemo41->Font->Name = CustomWinConfiguration->DefaultFixedWidthFontName;
+      FResultMemoWithLinks->Font->Name = CustomWinConfiguration->DefaultFixedWidthFontName;
     }
     else
     {
-      FResultMemo41->Font->Name = Font->Name;
+      FResultMemoWithLinks->Font->Name = Font->Name;
     }
 
     if (!CounterName.IsEmpty() && !(*Counted))
@@ -796,21 +758,21 @@ void __fastcall TGenerateUrlDialog::UpdateControls()
        RtfColorEntry(0x993333) + // command-line argument (reddish)
        RtfColorEntry(0x808080) + // script command (gray)
       L"}\n"
-       "{\\fonttbl{\\f0\\fnil\\fcharset0 " + FResultMemo41->Font->Name + L";}}\n"
-       "\\f0\\fs" + IntToStr(FResultMemo41->Font->Size * 2) + L" " +
+       "{\\fonttbl{\\f0\\fnil\\fcharset0 " + FResultMemoWithLinks->Font->Name + L";}}\n"
+       "\\f0\\fs" + IntToStr(FResultMemoWithLinks->Font->Size * 2) + L" " +
        Result +
       "}";
 
-    FResultMemo41->WordWrap = WordWrap;
-    FResultMemo41->ScrollBars = WordWrap ? ssVertical : ssBoth;
+    FResultMemoWithLinks->WordWrap = WordWrap;
+    FResultMemoWithLinks->ScrollBars = WordWrap ? ssVertical : ssBoth;
 
     std::unique_ptr<TMemoryStream> Stream(new TMemoryStream());
     UTF8String ResultUtf = Result;
     Stream->Write(ResultUtf.c_str(), ResultUtf.Length());
     Stream->Position = 0;
 
-    FResultMemo41->Perform(WM_VSCROLL, SB_TOP, 0);
-    FResultMemo41->Lines->LoadFromStream(Stream.get(), TEncoding::UTF8);
+    FResultMemoWithLinks->Perform(WM_VSCROLL, SB_TOP, 0);
+    FResultMemoWithLinks->Lines->LoadFromStream(Stream.get(), TEncoding::UTF8);
   }
 }
 //---------------------------------------------------------------------------
@@ -932,12 +894,12 @@ void __fastcall TGenerateUrlDialog::ClipboardButtonClick(TObject * /*Sender*/)
 {
   TInstantOperationVisualizer Visualizer;
 
-  // Cannot read the text from FResultMemo41->Lines as TRichEdit (as opposite to TMemo)
+  // Cannot read the text from FResultMemoWithLinks->Lines as TRichEdit (as opposite to TMemo)
   // breaks wrapped lines
-  UnicodeString Text = FResultMemo41->Text;
+  UnicodeString Text = FResultMemoWithLinks->Text;
   UnicodeString EOL = sLineBreak;
   int P = Pos(EOL, Text);
-  // Trim the EOL of the only string, what CopyToClipboard(FResultMemo41->Lines) would have done.
+  // Trim the EOL of the only string, what CopyToClipboard(FResultMemoWithLinks->Lines) would have done.
   // It probably never happens as rich edit does not return EOL on the last line.
   if (DebugAlwaysFalse(P == Text.Length() - EOL.Length() + 1))
   {

+ 1 - 3
source/forms/GenerateUrl.h

@@ -15,8 +15,6 @@
 #include <Vcl.StdActns.hpp>
 #include <WinInterface.h>
 //---------------------------------------------------------------------------
-class TRichEdit41;
-//---------------------------------------------------------------------------
 class TGenerateUrlDialog : public TForm
 {
 __published:
@@ -57,7 +55,7 @@ private:
   std::unique_ptr<TStrings> FPaths;
   bool FPathsSample;
   bool FChanging;
-  TRichEdit41 * FResultMemo41;
+  TRichEdit * FResultMemoWithLinks;
   bool FTransfer;
   bool FToRemote;
   bool FMove;

+ 42 - 0
source/windows/GUITools.cpp

@@ -1522,3 +1522,45 @@ void __fastcall TScreenTipHintWindow::Paint()
     DrawText(Canvas->Handle, FLongHint.c_str(), -1, &Rect, Flags);
   }
 }
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+__fastcall TNewRichEdit::TNewRichEdit(TComponent * AOwner) :
+  TRichEdit(AOwner),
+  FLibrary(0)
+{
+}
+//---------------------------------------------------------------------------
+void __fastcall TNewRichEdit::CreateParams(TCreateParams & Params)
+{
+  UnicodeString RichEditModuleName(L"MSFTEDIT.DLL");
+  long int OldError;
+
+  OldError = SetErrorMode(SEM_NOOPENFILEERRORBOX);
+  FLibrary = LoadLibrary(RichEditModuleName.c_str());
+  SetErrorMode(OldError);
+
+  // No fallback, MSFTEDIT.DLL is available since Windows XP
+  // https://blogs.msdn.microsoft.com/murrays/2006/10/13/richedit-versions/
+  if (FLibrary == 0)
+  {
+    throw Exception(FORMAT(L"Cannot load %s", (RichEditModuleName)));
+  }
+
+  TCustomMemo::CreateParams(Params);
+  // MSDN says that we should use MSFTEDIT_CLASS to load Rich Edit 4.1:
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/bb787873.aspx
+  // But MSFTEDIT_CLASS is defined as "RICHEDIT50W",
+  // so not sure what version we are loading.
+  // Seem to work on Windows XP SP3.
+  CreateSubClass(Params, MSFTEDIT_CLASS);
+}
+//---------------------------------------------------------------------------
+void __fastcall TNewRichEdit::DestroyWnd()
+{
+  TRichEdit::DestroyWnd();
+
+  if (FLibrary != 0)
+  {
+    FreeLibrary(FLibrary);
+  }
+}

+ 15 - 0
source/windows/GUITools.h

@@ -149,6 +149,21 @@ private:
   TControl * __fastcall GetHintControl(void * Data);
 };
 //---------------------------------------------------------------------------
+// Newer version rich edit that supports "Friendly name hyperlinks" and
+// allows wider range of Unicode characters: https://stackoverflow.com/q/47433656/850848
+class TNewRichEdit : public TRichEdit
+{
+public:
+  virtual __fastcall TNewRichEdit(TComponent * AOwner);
+
+protected:
+  virtual void __fastcall CreateParams(TCreateParams & Params);
+  virtual void __fastcall DestroyWnd();
+
+private:
+  HINSTANCE FLibrary;
+};
+//---------------------------------------------------------------------------
 extern const UnicodeString PageantTool;
 extern const UnicodeString PuttygenTool;
 //---------------------------------------------------------------------------