浏览代码

Bug 2125: Displaying current version on the New version notification + Contents of About dialog can be copied to the clipboard using Ctrl+C

https://winscp.net/tracker/2125

Source commit: 50ae66db19efb1f756ea809faad22a8067d53bff
Martin Prikryl 3 年之前
父节点
当前提交
e78ae6176e

+ 115 - 0
source/core/Common.cpp

@@ -3224,6 +3224,116 @@ UnicodeString __fastcall FormatDateTimeSpan(const UnicodeString TimeFormat, TDat
   return Result;
 }
 //---------------------------------------------------------------------------
+UnicodeString FormatRelativeTime(const TDateTime & ANow, const TDateTime & AThen, bool DateOnly)
+{
+  UnicodeString Result;
+  if (DateOnly)
+  {
+    if (IsSameDay(AThen, ANow - 1))
+    {
+      Result = LoadStrPart(TIME_RELATIVE, 3);
+    }
+    else if (IsSameDay(AThen, ANow))
+    {
+      Result = LoadStrPart(TIME_RELATIVE, 2);
+    }
+  }
+
+  if (Result.IsEmpty())
+  {
+    int Part, Num;
+
+    Num = YearsBetween(ANow, AThen);
+    if (Num > 1)
+    {
+      Part = 18;
+    }
+    else if (Num == 1)
+    {
+      Part = 17;
+    }
+    else
+    {
+      Num = MonthsBetween(ANow, AThen);
+      if (Num > 1)
+      {
+        Part = 16;
+      }
+      else if (Num == 1)
+      {
+        Part = 15;
+      }
+      else
+      {
+        Num = DaysBetween(ANow, AThen);
+        if (Num > 1)
+        {
+          Part = 12;
+        }
+        else if (Num == 1)
+        {
+          Part = 11;
+        }
+        else
+        {
+          Num = static_cast<int>(HoursBetween(ANow, AThen));
+          if (Num > 1)
+          {
+            Part = 10;
+          }
+          else if (Num == 1)
+          {
+            Part = 9;
+          }
+          else
+          {
+            Num = static_cast<int>(MinutesBetween(ANow, AThen));
+            if (Num > 1)
+            {
+              Part = 8;
+            }
+            else if (Num == 1)
+            {
+              Part = 7;
+            }
+            else
+            {
+              Num = static_cast<int>(SecondsBetween(ANow, AThen));
+              if (Num > 1)
+              {
+                Part = 6;
+              }
+              else if (Num == 1)
+              {
+                Part = 5;
+              }
+              else if (Num == 0)
+              {
+                Part = 1;
+              }
+              else
+              {
+                DebugFail();
+                Part = -1;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    if (DebugAlwaysTrue(Part >= 0))
+    {
+      Result = FORMAT(LoadStrPart(TIME_RELATIVE, Part), (abs(Num)));
+    }
+    else
+    {
+      Result = FormatDateTime(L"ddddd", AThen);
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 UnicodeString __fastcall ExtractFileBaseName(const UnicodeString & Path)
 {
   return ChangeFileExt(ExtractFileName(Path), L"");
@@ -4347,3 +4457,8 @@ void NotImplemented()
   DebugFail();
   throw Exception(L"Not implemented");
 }
+//---------------------------------------------------------------------------
+UnicodeString GetDividerLine()
+{
+  return UnicodeString::StringOfChar(L'-', 27);
+}

+ 2 - 0
source/core/Common.h

@@ -194,6 +194,7 @@ void SetStringValueEvenIfEmpty(TStrings * Strings, const UnicodeString & Name, c
 UnicodeString __fastcall GetAncestorProcessName(int Levels = 1);
 UnicodeString GetAncestorProcessNames();
 void NotImplemented();
+UnicodeString GetDividerLine();
 //---------------------------------------------------------------------------
 struct TSearchRecSmart : public TSearchRec
 {
@@ -267,6 +268,7 @@ int __fastcall TimeToMSec(TDateTime T);
 int __fastcall TimeToSeconds(TDateTime T);
 int __fastcall TimeToMinutes(TDateTime T);
 UnicodeString __fastcall FormatDateTimeSpan(const UnicodeString TimeFormat, TDateTime DateTime);
+UnicodeString FormatRelativeTime(const TDateTime & ANow, const TDateTime & AThen, bool DateOnly);
 TStrings * TlsCipherList();
 //---------------------------------------------------------------------------
 template<class MethodT>

+ 57 - 26
source/core/Configuration.cpp

@@ -17,6 +17,7 @@
 #include <shlobj.h>
 #include <System.IOUtils.hpp>
 #include <System.StrUtils.hpp>
+#include <System.DateUtils.hpp>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -1000,38 +1001,40 @@ bool __fastcall TConfiguration::GetIsUnofficial()
   #endif
 }
 //---------------------------------------------------------------------------
-UnicodeString __fastcall TConfiguration::GetVersionStr()
+UnicodeString TConfiguration::FormatVersionStr(bool Verbose)
 {
   TGuard Guard(FCriticalSection);
   try
   {
     UnicodeString BuildStr;
-    if (!IsUnofficial)
-    {
-      BuildStr = LoadStr(VERSION_BUILD);
-    }
-    else
+    if (!IsUnofficial || Verbose)
     {
-      #ifdef _DEBUG
-      BuildStr = LoadStr(VERSION_DEBUG_BUILD);
-      #else
-      BuildStr = LoadStr(VERSION_DEV_BUILD);
-      #endif
-    }
+      if (!IsUnofficial)
+      {
+        BuildStr = LoadStr(VERSION_BUILD);
+      }
+      else
+      {
+        #ifdef _DEBUG
+        BuildStr = LoadStr(VERSION_DEBUG_BUILD);
+        #else
+        BuildStr = LoadStr(VERSION_DEV_BUILD);
+        #endif
+      }
 
-    int Build = LOWORD(FixedApplicationInfo->dwFileVersionLS);
-    if (Build > 0)
-    {
-      BuildStr += L" " + IntToStr(Build);
+      int Build = LOWORD(FixedApplicationInfo->dwFileVersionLS);
+      if (Build > 0)
+      {
+        BuildStr += L" " + IntToStr(Build);
+      }
     }
 
-    UnicodeString BuildDate = __DATE__;
-    UnicodeString MonthStr = CutToChar(BuildDate, L' ', true);
+    UnicodeString BuildDateStr = __DATE__;
+    UnicodeString MonthStr = CutToChar(BuildDateStr, L' ', true);
     int Month = ParseShortEngMonthName(MonthStr);
-    int Day = StrToInt(CutToChar(BuildDate, L' ', true));
-    int Year = StrToInt(Trim(BuildDate));
-    UnicodeString DateStr = FORMAT(L"%d-%2.2d-%2.2d", (Year, Month, Day));
-    AddToList(BuildStr, DateStr, L" ");
+    int Day = StrToInt(CutToChar(BuildDateStr, L' ', true));
+    int Year = StrToInt(Trim(BuildDateStr));
+    TDateTime BuildDate = EncodeDateVerbose(static_cast<Word>(Year), static_cast<Word>(Month), static_cast<Word>(Day));
 
     UnicodeString FullVersion = Version;
 
@@ -1043,11 +1046,34 @@ UnicodeString __fastcall TConfiguration::GetVersionStr()
       FullVersion += L" " + AReleaseType;
     }
 
-    UnicodeString Result = FMTLOAD(VERSION2, (FullVersion, BuildStr));
+    UnicodeString Fmt;
+    UnicodeString DateStr;
+    if (Verbose)
+    {
+      DateStr = FormatDateTime(L"yyyy-mm-dd", BuildDate);
+      Fmt = LoadStr(VERSION2);
+    }
+    else
+    {
+      TDateTime ANow = Now();
+      if (BuildDate < ANow)
+      {
+        DateStr = FormatRelativeTime(ANow, BuildDate, true);
+      }
+      else
+      {
+        DateStr = FormatDateTime(L"ddddd", BuildDate);
+      }
+
+      Fmt = L"%s (%s)";
+    }
+    AddToList(BuildStr, DateStr, L" ");
+    UnicodeString Result = FORMAT(Fmt, (FullVersion, BuildStr));
 
-    #ifndef BUILD_OFFICIAL
-    Result += L" " + LoadStr(VERSION_DONT_DISTRIBUTE);
-    #endif
+    if (!IsUnofficial && Verbose)
+    {
+      Result += L" " + LoadStr(VERSION_DONT_DISTRIBUTE);
+    }
 
     return Result;
   }
@@ -1057,6 +1083,11 @@ UnicodeString __fastcall TConfiguration::GetVersionStr()
   }
 }
 //---------------------------------------------------------------------------
+UnicodeString __fastcall TConfiguration::GetVersionStr()
+{
+  return FormatVersionStr(true);
+}
+//---------------------------------------------------------------------------
 UnicodeString __fastcall TConfiguration::GetFileVersion(const UnicodeString & FileName)
 {
   UnicodeString Result;

+ 1 - 0
source/core/Configuration.h

@@ -270,6 +270,7 @@ public:
   bool RegistryPathExists(const UnicodeString & RegistryPath);
   bool HasLocalPortNumberLimits();
   virtual UnicodeString TemporaryDir(bool Mask = false) = 0;
+  UnicodeString FormatVersionStr(bool IncludeOfficialBuildNumber);
 
   TStoredSessionList * __fastcall SelectFilezillaSessionsForImport(
     TStoredSessionList * Sessions, UnicodeString & Error);

+ 40 - 0
source/forms/About.cpp

@@ -397,3 +397,43 @@ void __fastcall TAboutDialog::Dispatch(void * Message)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TAboutDialog::FormKeyDown(TObject *, WORD & Key, TShiftState Shift)
+{
+  if ((Key == L'C') && Shift.Contains(ssCtrl))
+  {
+    if ((FThirdPartyWebBrowser != NULL) &&
+        FThirdPartyWebBrowser->Focused() &&
+        (FThirdPartyWebBrowser->SelLength() > 0))
+    {
+      // Let the browser handle the Ctrl+C
+    }
+    else
+    {
+      UnicodeString Text =
+        ApplicationLabel->Caption + sLineBreak +
+        VersionLabel->Caption + sLineBreak +
+        sLineBreak +
+        WinSCPCopyrightLabel->Caption + sLineBreak +
+        HomepageLabel->Caption + sLineBreak +
+        sLineBreak +
+        ProductSpecificMessageLabel->Caption + sLineBreak +
+        ForumUrlLabel->Caption + sLineBreak;
+
+      UnicodeString ThirdPartyText;
+      if ((FThirdPartyWebBrowser != NULL) &&
+          CopyTextFromBrowser(FThirdPartyWebBrowser, ThirdPartyText))
+      {
+        Text +=
+          sLineBreak +
+          GetDividerLine() + sLineBreak +
+          Label3->Caption + sLineBreak +
+          ThirdPartyText + sLineBreak;
+      }
+
+      TInstantOperationVisualizer Visualizer;
+      CopyToClipboard(Text);
+      Key = 0;
+    }
+  }
+}
+//---------------------------------------------------------------------------

+ 2 - 0
source/forms/About.dfm

@@ -10,8 +10,10 @@ object AboutDialog: TAboutDialog
   ClientWidth = 410
   Color = clBtnFace
   ParentFont = True
+  KeyPreview = True
   OldCreateOrder = True
   Position = poOwnerFormCenter
+  OnKeyDown = FormKeyDown
   DesignSize = (
     410
     501)

+ 1 - 0
source/forms/About.h

@@ -39,6 +39,7 @@ __published:
   void __fastcall OKButtonMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
           int X, int Y);
   void __fastcall IconPaintBoxPaint(TObject *Sender);
+  void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
 private:
   TConfiguration * FConfiguration;
   TNotifyEvent FOnRegistrationLink;

+ 5 - 11
source/forms/MessageDlg.cpp

@@ -209,7 +209,7 @@ UnicodeString __fastcall TMessageForm::GetFormText()
 {
   UnicodeString DividerLine, ButtonCaptions;
 
-  DividerLine = UnicodeString::StringOfChar(L'-', 27) + sLineBreak;
+  DividerLine = GetDividerLine() + sLineBreak;
   for (int i = 0; i < ComponentCount - 1; i++)
   {
     if (dynamic_cast<TButton*>(Components[i]) != NULL)
@@ -224,19 +224,13 @@ UnicodeString __fastcall TMessageForm::GetFormText()
   {
     MoreMessages = MessageMemo->Text + DividerLine;
   }
-  else if (MessageBrowser != NULL)
+  else if ((MessageBrowser != NULL) && CopyTextFromBrowser(MessageBrowser, MoreMessages))
   {
-    MessageBrowser->SelectAll();
-    MessageBrowser->CopyToClipBoard();
-    if (NonEmptyTextFromClipboard(MoreMessages))
+    if (!EndsStr(sLineBreak, MoreMessages))
     {
-      if (!EndsStr(sLineBreak, MoreMessages))
-      {
-        MoreMessages += sLineBreak;
-      }
-      MoreMessages += DividerLine;
+      MoreMessages += sLineBreak;
     }
-    MessageBrowser->DoCommand(L"UNSELECT");
+    MoreMessages += DividerLine;
   }
   UnicodeString MessageCaption = NormalizeNewLines(MessageText);
   UnicodeString Result = FORMAT(L"%s%s%s%s%s%s%s%s%s%s%s", (DividerLine, Caption, sLineBreak,

+ 1 - 0
source/resource/TextsCore.h

@@ -496,6 +496,7 @@
 #define COPY_INFO_EXCLUDE_EMPTY_DIRS 566
 #define PUBLIC_KEY_UPLOADED     567
 #define PUBLIC_KEY_PERMISSIONS  568
+#define TIME_RELATIVE           569
 
 #define CORE_VARIABLE_STRINGS   600
 #define PUTTY_BASED_ON          601

+ 1 - 0
source/resource/TextsCore1.rc

@@ -465,6 +465,7 @@ BEGIN
   COPY_INFO_EXCLUDE_EMPTY_DIRS, "Exclude empty directories"
   PUBLIC_KEY_UPLOADED, "**Public key \"%s\" was installed.**\n\nYou can now login to the server using the key pair."
   PUBLIC_KEY_PERMISSIONS, "Though potentially wrong permissions of \"%s\" file and/or its parent folder were detected. Please check them."
+  TIME_RELATIVE, "just now|today|yesterday|tomorrow|one second ago|%d seconds ago|one minute ago|%d minutes ago|one hour ago|%d hours ago|one day ago|%d days ago|one week ago|%d weeks ago|one month ago|%d months ago|one year ago|%d years ago"
 
   CORE_VARIABLE_STRINGS, "CORE_VARIABLE"
   PUTTY_BASED_ON, "SSH and SCP code based on PuTTY %s"

+ 1 - 0
source/resource/TextsWin.h

@@ -309,6 +309,7 @@
 #define STORE_MIGRATION_LEARN   1596
 #define MESSAGE_DISMISS         1597
 #define USAGE_PASSWORDSFROMFILES 1598
+#define UPDATE_CURRENT          1599
 
 #define WIN_FORMS_STRINGS       1600
 #define COPY_FILE               1605

+ 1 - 0
source/resource/TextsWin1.rc

@@ -314,6 +314,7 @@ BEGIN
         STORE_MIGRATION_LEARN, "Learn about transitioning from classic installation."
         MESSAGE_DISMISS, "Dismiss"
         USAGE_PASSWORDSFROMFILES, "Read all passwords from files"
+        UPDATE_CURRENT, "Your version: %s"
 
         WIN_FORMS_STRINGS, "WIN_FORMS_STRINGS"
         COPY_FILE, "%s file '%s' to %s:"

+ 9 - 0
source/windows/GUITools.cpp

@@ -1444,6 +1444,15 @@ void HideBrowserScrollbars(TWebBrowserEx * WebBrowser)
   }
 }
 //---------------------------------------------------------------------------
+bool CopyTextFromBrowser(TWebBrowserEx * WebBrowser, UnicodeString & Text)
+{
+  WebBrowser->SelectAll();
+  WebBrowser->CopyToClipBoard();
+  bool Result = NonEmptyTextFromClipboard(Text);
+  WebBrowser->DoCommand(L"UNSELECT");
+  return Result;
+}
+//---------------------------------------------------------------------------
 UnicodeString GenerateAppHtmlPage(TFont * Font, TPanel * Parent, const UnicodeString & Body, bool Seamless)
 {
   UnicodeString Result =

+ 1 - 0
source/windows/GUITools.h

@@ -58,6 +58,7 @@ void __fastcall NavigateBrowserToUrl(TWebBrowserEx * WebBrowser, const UnicodeSt
 void ReadyBrowserForStreaming(TWebBrowserEx * WebBrowser);
 void WaitBrowserToIdle(TWebBrowserEx * WebBrowser);
 void HideBrowserScrollbars(TWebBrowserEx * WebBrowser);
+bool CopyTextFromBrowser(TWebBrowserEx * WebBrowser, UnicodeString & Text);
 UnicodeString GenerateAppHtmlPage(TFont * Font, TPanel * Parent, const UnicodeString & Body, bool Seamless);
 void LoadBrowserDocument(TWebBrowserEx * WebBrowser, const UnicodeString & Document);
 TComponent * __fastcall FindComponentRecursively(TComponent * Root, const UnicodeString & Name);

+ 5 - 0
source/windows/Setup.cpp

@@ -1659,6 +1659,11 @@ bool __fastcall CheckForUpdates(bool CachedResults)
       Message += L"\n\n" +
         FMTLOAD(UPDATE_NEXT, (FormatDateTime("ddddd", Updates.LastCheck + Updates.Period)));
     }
+    else if (New)
+    {
+      UnicodeString Version = Configuration->FormatVersionStr(false);
+      Message += L"\n\n" + FMTLOAD(UPDATE_CURRENT, (Version));
+    }
 
     int Answers = qaOK |
       // show "what's new" button only when change list URL was not provided in results