From: Takashi Sawanaka Date: Sat, 1 Oct 2022 14:15:38 +0000 (+0900) Subject: Allow saving of left-side and right-side description and window type in the project... X-Git-Tag: 2.16.24+jp-1~5^2~35 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=c3bab2c1e96ddfef65e6ccaaec30c1e5ab5a3b84;p=winmerge-jp%2Fwinmerge-jp.git Allow saving of left-side and right-side description and window type in the project file. --- diff --git a/Src/DirDoc.h b/Src/DirDoc.h index ed072e616..b4543b5e9 100644 --- a/Src/DirDoc.h +++ b/Src/DirDoc.h @@ -72,6 +72,7 @@ public: void InitCompare(const PathContext & paths, bool bRecursive, CTempPathContext *); void DiffThreadCallback(int& state); void Rescan(); + String GetDescription(int nIndex) const { return m_strDesc[nIndex]; }; bool GetReadOnly(int nIndex) const; const bool *GetReadOnly(void) const; void SetReadOnly(int nIndex, bool bReadOnly); diff --git a/Src/HexMergeDoc.h b/Src/HexMergeDoc.h index 5a56cb8eb..54b2c6e9d 100644 --- a/Src/HexMergeDoc.h +++ b/Src/HexMergeDoc.h @@ -83,7 +83,7 @@ public: bool OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[]); void MoveOnLoad(int nPane = -1, int nLineIndex = -1); void CheckFileChanged(void) override; - String GetDescription(int pane) const { return m_strDesc[pane]; }; + String GetDescription(int pane) const override { return m_strDesc[pane]; }; void SetDescription(int pane, const String& strDesc) { m_strDesc[pane] = strDesc; }; void SaveAs(int nBuffer, bool packing = true) { DoFileSaveAs(nBuffer, packing); } private: diff --git a/Src/IMergeDoc.h b/Src/IMergeDoc.h index d8ab0e629..a03293f6c 100644 --- a/Src/IMergeDoc.h +++ b/Src/IMergeDoc.h @@ -27,6 +27,7 @@ struct IMergeDoc : public IMDITab virtual const PrediffingInfo *GetPrediffer() const = 0; virtual int GetFileCount() const = 0; virtual String GetPath(int pane) const = 0; + virtual String GetDescription(int pane) const = 0; virtual bool GetReadOnly(int pane) const = 0; }; diff --git a/Src/ImgMergeFrm.h b/Src/ImgMergeFrm.h index 80777ec6c..2a05afa27 100644 --- a/Src/ImgMergeFrm.h +++ b/Src/ImgMergeFrm.h @@ -71,7 +71,7 @@ public: bool IsModified() const; IMergeDoc::FileChange IsFileChangedOnDisk(int pane) const; void CheckFileChanged(void) override; - String GetDescription(int pane) const { return m_strDesc[pane]; } + String GetDescription(int pane) const override { return m_strDesc[pane]; } static bool IsLoadable(); // Attributes diff --git a/Src/MainFrm.cpp b/Src/MainFrm.cpp index b40996d0d..94fe8c0ce 100644 --- a/Src/MainFrm.cpp +++ b/Src/MainFrm.cpp @@ -1143,8 +1143,6 @@ void CMainFrame::OnOptions() // Update all dirdoc settings for (auto pDirDoc : GetAllDirDocs()) pDirDoc->RefreshOptions(); - for (auto pOpenDoc : GetAllOpenDocs()) - pOpenDoc->RefreshOptions(); for (auto pHexMergeDoc : GetAllHexMergeDocs()) pHexMergeDoc->RefreshOptions(); for (auto pImgMergeFrame : GetAllImgMergeFrames()) @@ -2350,7 +2348,7 @@ void CMainFrame::OnSaveProject() CFrameWnd * pFrame = GetActiveFrame(); FRAMETYPE frame = pFrame ? GetFrameType(pFrame) : FRAME_OTHER; - if (frame == FRAME_FILE || frame == FRAME_HEXFILE || frame == FRAME_IMGFILE) + if (frame == FRAME_FILE || frame == FRAME_HEXFILE || frame == FRAME_IMGFILE || frame == FRAME_WEBPAGE) { if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc()) { @@ -2358,12 +2356,29 @@ void CMainFrame::OnSaveProject() for (int pane = 0; pane < pMergeDoc->GetFileCount(); ++pane) { pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pMergeDoc->GetReadOnly(pane) ? FFILEOPEN_READONLY : 0); - paths.SetPath(pane, pMergeDoc->GetPath(pane)); + paths.SetPath(pane, pMergeDoc->GetPath(pane), false); + pOpenDoc->m_strDesc[pane] = pMergeDoc->GetDescription(pane); } pOpenDoc->m_files = paths; pOpenDoc->m_bRecurse = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS); pOpenDoc->m_strExt = theApp.GetGlobalFileFilter()->GetFilterNameOrMask(); pOpenDoc->m_strUnpackerPipeline = pMergeDoc->GetUnpacker()->GetPluginPipeline(); + switch (frame) + { + case FRAME_FILE: + pOpenDoc->m_nWindowType = (static_cast(pMergeDoc)->GetEnableTableEditing().value_or(false) ? + ID_MERGE_COMPARE_TABLE : ID_MERGE_COMPARE_TEXT) - ID_MERGE_COMPARE_TEXT + 1; + break; + case FRAME_HEXFILE: + pOpenDoc->m_nWindowType = ID_MERGE_COMPARE_HEX - ID_MERGE_COMPARE_TEXT + 1; + break; + case FRAME_IMGFILE: + pOpenDoc->m_nWindowType = ID_MERGE_COMPARE_IMAGE - ID_MERGE_COMPARE_TEXT + 1; + break; + case FRAME_WEBPAGE: + pOpenDoc->m_nWindowType = ID_MERGE_COMPARE_WEBPAGE - ID_MERGE_COMPARE_TEXT + 1; + break; + } } } else if (frame == FRAME_FOLDER) @@ -2378,6 +2393,7 @@ void CMainFrame::OnSaveProject() { pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pDoc->GetReadOnly(pane) ? FFILEOPEN_READONLY : 0); pOpenDoc->m_files.SetPath(pane, paths::AddTrailingSlash(ctxt.GetNormalizedPath(pane))); + pOpenDoc->m_strDesc[pane] = pDoc->GetDescription(pane); } pOpenDoc->m_bRecurse = ctxt.m_bRecursive; pOpenDoc->m_strExt = static_cast(ctxt.m_piFilterGlobal)->GetFilterNameOrMask(); @@ -2886,6 +2902,7 @@ void CMainFrame::OnToolbarButtonDropDown(NMHDR* pNMHDR, LRESULT* pResult) } *pResult = 0; } + void CMainFrame::OnDiffWhitespace(UINT nID) { GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_WHITESPACE, nID - ID_DIFF_OPTIONS_WHITESPACE_COMPARE); diff --git a/Src/Merge.cpp b/Src/Merge.cpp index 1109fb9a7..2d18622fb 100644 --- a/Src/Merge.cpp +++ b/Src/Merge.cpp @@ -1296,7 +1296,11 @@ bool CMergeApp::LoadAndOpenProjectFile(const String& sProject, const String& sRe pInfoUnpacker.reset(new PackingInfo(projItem.GetUnpacker())); if (projItem.HasPrediffer()) pInfoPrediffer.reset(new PrediffingInfo(projItem.GetPrediffer())); + int nID = 0; + if (projItem.HasWindowType()) + nID = ID_MERGE_COMPARE_TEXT + projItem.GetWindowType() - 1; + String strDesc[3]; DWORD dwFlags[3] = { static_cast(tFiles.GetPath(0).empty() ? FFILEOPEN_NONE : FFILEOPEN_PROJECT), static_cast(tFiles.GetPath(1).empty() ? FFILEOPEN_NONE : FFILEOPEN_PROJECT), @@ -1304,10 +1308,14 @@ bool CMergeApp::LoadAndOpenProjectFile(const String& sProject, const String& sRe }; if (bLeftReadOnly) dwFlags[0] |= FFILEOPEN_READONLY; + if (projItem.HasLeftDesc()) + strDesc[0] = projItem.GetLeftDesc(); if (tFiles.GetSize() == 2) { if (bRightReadOnly) dwFlags[1] |= FFILEOPEN_READONLY; + if (projItem.HasRightDesc()) + strDesc[1] = projItem.GetRightDesc(); } else { @@ -1315,6 +1323,10 @@ bool CMergeApp::LoadAndOpenProjectFile(const String& sProject, const String& sRe dwFlags[1] |= FFILEOPEN_READONLY; if (bRightReadOnly) dwFlags[2] |= FFILEOPEN_READONLY; + if (projItem.HasMiddleDesc()) + strDesc[1] = projItem.GetMiddleDesc(); + if (projItem.HasRightDesc()) + strDesc[2] = projItem.GetRightDesc(); } GetOptionsMgr()->Set(OPT_CMP_INCLUDE_SUBDIRS, bRecursive); @@ -1346,8 +1358,8 @@ bool CMergeApp::LoadAndOpenProjectFile(const String& sProject, const String& sRe pOpenFolderParams->m_hiddenItems = projItem.GetHiddenItems(); } - rtn &= GetMainFrame()->DoFileOrFolderOpen(&tFiles, dwFlags, nullptr, sReportFile, bRecursive, - nullptr, pInfoUnpacker.get(), pInfoPrediffer.get(), 0, pOpenFolderParams.get()); + rtn &= GetMainFrame()->DoFileOrFolderOpen(&tFiles, dwFlags, strDesc, sReportFile, bRecursive, + nullptr, pInfoUnpacker.get(), pInfoPrediffer.get(), nID, pOpenFolderParams.get()); } AddToRecentProjectsMRU(sProject.c_str()); diff --git a/Src/MergeDoc.h b/Src/MergeDoc.h index 94f7684bb..6c533418a 100644 --- a/Src/MergeDoc.h +++ b/Src/MergeDoc.h @@ -243,7 +243,7 @@ public: void ClearSyncPoints(); bool HasSyncPoints(); std::vector > GetSyncPointList(); - String GetDescription(int pane) const { return m_strDesc[pane]; } + String GetDescription(int pane) const override { return m_strDesc[pane]; } void SetDescription(int pane, const String& sText) { m_strDesc[pane] = sText; } // Overrides diff --git a/Src/OpenDoc.cpp b/Src/OpenDoc.cpp index fc4800233..31c646e38 100644 --- a/Src/OpenDoc.cpp +++ b/Src/OpenDoc.cpp @@ -15,6 +15,7 @@ IMPLEMENT_DYNCREATE(COpenDoc, CDocument) COpenDoc::COpenDoc() : m_bRecurse(false) , m_dwFlags() +, m_nWindowType(-1) { PackingInfo infoHandler; m_strUnpackerPipeline = GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED) ? infoHandler.GetPluginPipeline() : _T(""); diff --git a/Src/OpenDoc.h b/Src/OpenDoc.h index a3c1895ae..a8866c475 100644 --- a/Src/OpenDoc.h +++ b/Src/OpenDoc.h @@ -23,6 +23,8 @@ public: bool m_bRecurse; String m_strExt; String m_strUnpackerPipeline; + String m_strDesc[3]; + int m_nWindowType; std::vector m_hiddenItems; protected: diff --git a/Src/OpenView.cpp b/Src/OpenView.cpp index 45b0e4c6a..ac86afdf8 100644 --- a/Src/OpenView.cpp +++ b/Src/OpenView.cpp @@ -897,10 +897,14 @@ void COpenView::OnSaveProject() if (!m_strPath[0].empty()) projItem.SetLeft(m_strPath[0], &m_bReadOnly[0]); + if (!GetDocument()->m_strDesc[0].empty()) + projItem.SetLeftDesc(GetDocument()->m_strDesc[0]); if (m_strPath[2].empty()) { if (!m_strPath[1].empty()) projItem.SetRight(m_strPath[1], &m_bReadOnly[1]); + if (!GetDocument()->m_strDesc[1].empty()) + projItem.SetRightDesc(GetDocument()->m_strDesc[1]); } else { @@ -908,6 +912,10 @@ void COpenView::OnSaveProject() projItem.SetMiddle(m_strPath[1], &m_bReadOnly[1]); if (!m_strPath[2].empty()) projItem.SetRight(m_strPath[2], &m_bReadOnly[2]); + if (!GetDocument()->m_strDesc[1].empty()) + projItem.SetMiddleDesc(GetDocument()->m_strDesc[1]); + if (!GetDocument()->m_strDesc[2].empty()) + projItem.SetRightDesc(GetDocument()->m_strDesc[2]); } if (bSaveFileFilter && !m_strExt.empty()) { @@ -926,6 +934,8 @@ void COpenView::OnSaveProject() projItem.SetSubfolders(m_bRecurse); if (bSaveUnpackerPlugin && !m_strUnpackerPipeline.empty()) projItem.SetUnpacker(m_strUnpackerPipeline); + if (GetDocument()->m_nWindowType != -1) + projItem.SetWindowType(GetDocument()->m_nWindowType); if (bSaveCompareOptions) { diff --git a/Src/ProjectFile.cpp b/Src/ProjectFile.cpp index 350608ce6..f045de6c1 100755 --- a/Src/ProjectFile.cpp +++ b/Src/ProjectFile.cpp @@ -35,6 +35,9 @@ const char Paths_element_name[] = "paths"; const char Left_element_name[] = "left"; const char Middle_element_name[] = "middle"; const char Right_element_name[] = "right"; +const char Left_desc_element_name[] = "left-desc"; +const char Middle_desc_element_name[] = "middle-desc"; +const char Right_desc_element_name[] = "right-desc"; const char Filter_element_name[] = "filter"; const char Subfolders_element_name[] = "subfolders"; const char Left_ro_element_name[] = "left-readonly"; @@ -42,6 +45,7 @@ const char Middle_ro_element_name[] = "middle-readonly"; const char Right_ro_element_name[] = "right-readonly"; const char Unpacker_element_name[] = "unpacker"; const char Prediffer_element_name[] = "prediffer"; +const char Window_type_element_name[] = "window-type"; const char White_spaces_element_name[] = "white-spaces"; const char Ignore_blank_lines_element_name[] = "ignore-blank-lines"; const char Ignore_case_element_name[] = "ignore-case"; @@ -123,6 +127,21 @@ public: currentItem.m_paths.SetRight(currentItem.m_paths.GetRight() + xmlch2tstr(ch + start, length), false); currentItem.m_bHasRight = true; } + else if (nodename == Left_desc_element_name) + { + currentItem.m_leftDesc += xmlch2tstr(ch + start, length); + currentItem.m_bHasLeftDesc = true; + } + else if (nodename == Middle_desc_element_name) + { + currentItem.m_middleDesc += xmlch2tstr(ch + start, length); + currentItem.m_bHasMiddleDesc = true; + } + else if (nodename == Right_desc_element_name) + { + currentItem.m_rightDesc += xmlch2tstr(ch + start, length); + currentItem.m_bHasRightDesc = true; + } else if (nodename == Filter_element_name) { currentItem.m_filter += xmlch2tstr(ch + start, length); @@ -155,6 +174,11 @@ public: currentItem.m_prediffer += xmlch2tstr(ch + start, length); currentItem.m_bHasPrediffer = true; } + else if (nodename == Window_type_element_name) + { + currentItem.m_nWindowType = atoi(token.c_str()); + currentItem.m_bHasWindowType = true; + } else if (nodename == White_spaces_element_name) { currentItem.m_nIgnoreWhite = atoi(token.c_str()); @@ -223,14 +247,19 @@ const String ProjectFile::PROJECTFILE_EXT = toTString("WinMerge"); : m_bHasLeft(false) , m_bHasMiddle(false) , m_bHasRight(false) +, m_bHasLeftDesc(false) +, m_bHasMiddleDesc(false) +, m_bHasRightDesc(false) , m_bHasFilter(false) , m_bHasSubfolders(false) , m_bHasUnpacker(false) , m_bHasPrediffer(false) +, m_bHasWindowType(false) , m_subfolders(-1) , m_bLeftReadOnly(false) , m_bMiddleReadOnly(false) , m_bRightReadOnly(false) +, m_nWindowType(-1) , m_bHasIgnoreWhite(false) , m_nIgnoreWhite(0) , m_bHasIgnoreBlankLines(false) @@ -387,6 +416,12 @@ bool ProjectFile::Save(const String& path) const writeElement(writer, Middle_element_name, toUTF8(item.m_paths.GetMiddle())); if (!item.m_paths.GetRight().empty()) writeElement(writer, Right_element_name, toUTF8(item.m_paths.GetRight())); + if (!item.m_leftDesc.empty()) + writeElement(writer, Left_desc_element_name, toUTF8(item.m_leftDesc)); + if (!item.m_middleDesc.empty()) + writeElement(writer, Middle_desc_element_name, toUTF8(item.m_middleDesc)); + if (!item.m_rightDesc.empty()) + writeElement(writer, Right_desc_element_name, toUTF8(item.m_rightDesc)); if (item.m_bSaveFilter && !item.m_filter.empty()) writeElement(writer, Filter_element_name, toUTF8(item.m_filter)); if (item.m_bSaveSubfolders) @@ -399,6 +434,8 @@ bool ProjectFile::Save(const String& path) const writeElement(writer, Unpacker_element_name, toUTF8(item.m_unpacker)); if (!item.m_prediffer.empty()) writeElement(writer, Prediffer_element_name, toUTF8(item.m_prediffer)); + if (item.m_nWindowType != -1) + writeElement(writer, Window_type_element_name, std::to_string(item.m_nWindowType)); if (item.m_bSaveIgnoreWhite) writeElement(writer, White_spaces_element_name, std::to_string(item.m_nIgnoreWhite)); if (item.m_bSaveIgnoreBlankLines) diff --git a/Src/ProjectFile.h b/Src/ProjectFile.h index 8bebb617e..3089074c6 100755 --- a/Src/ProjectFile.h +++ b/Src/ProjectFile.h @@ -18,10 +18,14 @@ public: bool HasLeft() const; bool HasMiddle() const; bool HasRight() const; + bool HasLeftDesc() const; + bool HasMiddleDesc() const; + bool HasRightDesc() const; bool HasFilter() const; bool HasSubfolders() const; bool HasUnpacker() const; bool HasPrediffer() const; + bool HasWindowType() const; bool HasIgnoreWhite() const; bool HasIgnoreBlankLines() const; bool HasIgnoreCase() const; @@ -33,15 +37,19 @@ public: bool HasHiddenItems() const; String GetLeft(bool * pReadOnly = nullptr) const; + String GetLeftDesc() const; bool GetLeftReadOnly() const; String GetMiddle(bool * pReadOnly = nullptr) const; + String GetMiddleDesc() const; bool GetMiddleReadOnly() const; String GetRight(bool * pReadOnly = nullptr) const; + String GetRightDesc() const; bool GetRightReadOnly() const; String GetFilter() const; int GetSubfolders() const; String GetUnpacker() const; String GetPrediffer() const; + int GetWindowType() const; int GetIgnoreWhite() const; bool GetIgnoreBlankLines() const; bool GetIgnoreCase() const; @@ -55,10 +63,14 @@ public: void SetLeft(const String& sLeft, const bool * pReadOnly = nullptr); void SetMiddle(const String& sMiddle, const bool * pReadOnly = nullptr); void SetRight(const String& sRight, const bool * pReadOnly = nullptr); + void SetLeftDesc(const String& sLeftDesc); + void SetMiddleDesc(const String& sMiddleDesc); + void SetRightDesc(const String& sRightDesc); void SetFilter(const String& sFilter); void SetSubfolders(bool bSubfolder); void SetUnpacker(const String& sUnpacker); void SetPrediffer(const String& sPrediffer); + void SetWindowType(int nWindowType); void SetIgnoreWhite(int nIgnoreWhite); void SetIgnoreBlankLines(bool bIgnoreBlankLines); void SetIgnoreCase(bool bIgnoreCase); @@ -90,6 +102,12 @@ private: bool m_bHasLeft; /**< Has left path? */ bool m_bHasMiddle; /**< Has middle path? */ bool m_bHasRight; /**< Has right path? */ + String m_leftDesc; + String m_middleDesc; + String m_rightDesc; + bool m_bHasLeftDesc; /**< Has left description? */ + bool m_bHasMiddleDesc; /**< Has middle description? */ + bool m_bHasRightDesc; /**< Has right description? */ bool m_bHasFilter; /**< Has filter? */ String m_filter; /**< Filter name or mask */ bool m_bHasSubfolders; /**< Has subfolders? */ @@ -101,6 +119,8 @@ private: String m_unpacker; /**< Unpacker name or pipeline */ bool m_bHasPrediffer; /**< Has prediffer? */ String m_prediffer; /**< Prediffer name or pipeline */ + bool m_bHasWindowType; /**< Has window type? */ + int m_nWindowType; /**< The value of the window type */ bool m_bHasIgnoreWhite; /**< Has "Whitespaces" setting? */ int m_nIgnoreWhite; /**< The value of the "Whitespaces" setting */ bool m_bHasIgnoreBlankLines; /**< Has "Ignore blank lines" setting? */ @@ -179,6 +199,86 @@ inline bool ProjectFileItem::HasRight() const } /** + * @brief Returns if left description is defined in project file. + * @return true if project file has left description. + */ +inline bool ProjectFileItem::HasLeftDesc() const +{ + return m_bHasLeftDesc; +} + +/** + * @brief Returns if middle description is defined. + */ +inline bool ProjectFileItem::HasMiddleDesc() const +{ + return m_bHasMiddleDesc; +} + +/** + * @brief Returns if right description is defined in project file. + * @return true if project file has right description. + */ +inline bool ProjectFileItem::HasRightDesc() const +{ + return m_bHasRightDesc; +} + +/** + * @brief Returns left description. + * @return left description. + */ +inline String ProjectFileItem::GetLeftDesc() const +{ + return m_leftDesc; +} + +/** + * @brief Returns middle description. + * @return middle description. + */ +inline String ProjectFileItem::GetMiddleDesc() const +{ + return m_middleDesc; +} + +/** + * @brief Returns right description. + * @return right description. + */ +inline String ProjectFileItem::GetRightDesc() const +{ + return m_rightDesc; +} + +/** + * @brief Set left description. + * @param [in] sDesc New left description. + */ +inline void ProjectFileItem::SetLeftDesc(const String& sDesc) +{ + m_leftDesc = sDesc; +} + +/** + * @brief Set middle description. + * @param [in] sDesc New middle description. + */ +inline void ProjectFileItem::SetMiddleDesc(const String& sDesc) +{ + m_middleDesc = sDesc; +} + +/** + * @brief Set right description. + * @param [in] sDesc New right description. + */ +inline void ProjectFileItem::SetRightDesc(const String& sDesc) +{ + m_rightDesc = sDesc; +} + +/** * @brief Returns if filter is defined in project file. * @return true if project file has filter. */ @@ -215,6 +315,15 @@ inline bool ProjectFileItem::HasPrediffer() const } /** + * @brief Returns if window type is defined in projectfile. + * @return true if project file has window type definition. + */ +inline bool ProjectFileItem::HasWindowType() const +{ + return m_bHasWindowType; +} + +/** * @brief Returns if "Whitespaces" setting is defined in projectfile. * @return true if project file has "Whitespaces" setting definition. */ @@ -394,6 +503,24 @@ inline void ProjectFileItem::SetPrediffer(const String& sPrediffer) } /** + * @brief Returns window type + * @return Window type + */ +inline int ProjectFileItem::GetWindowType() const +{ + return m_nWindowType; +} + +/** + * @brief Set window type + * @param [in] nWindowType New window type to set. + */ +inline void ProjectFileItem::SetWindowType(int nWindowType) +{ + m_nWindowType = nWindowType; +} + +/** * @brief Returns the value of the "Whitespaces" setting. * @return The value of the "Whitespaces" setting */ diff --git a/Src/WebPageDiffFrm.h b/Src/WebPageDiffFrm.h index 88f1cd482..4fd4c4b0b 100644 --- a/Src/WebPageDiffFrm.h +++ b/Src/WebPageDiffFrm.h @@ -67,7 +67,7 @@ public: CString GetTooltipString() const override; IMergeDoc::FileChange IsFileChangedOnDisk(int pane) const; void CheckFileChanged(void) override; - String GetDescription(int pane) const { return m_strDesc[pane]; } + String GetDescription(int pane) const override { return m_strDesc[pane]; } static bool IsLoadable(); static bool MatchURLPattern(const String& url);