From: <sv...@op...> - 2024-11-22 22:56:08
|
Author: sagamusix Date: Fri Nov 22 23:55:56 2024 New Revision: 22262 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=22262 Log: [Imp] Quick Start: Add ability to filter listed files. [Imp] Template and example module lists are now sorted with natural sorting, if available. Modified: trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/QuickStartDialog.cpp trunk/OpenMPT/mptrack/QuickStartDialog.h trunk/OpenMPT/mptrack/View_tre.cpp trunk/OpenMPT/mptrack/View_tre.h trunk/OpenMPT/mptrack/mptrack.rc Modified: trunk/OpenMPT/mptrack/MainFrm.cpp ============================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp Fri Nov 22 08:34:38 2024 (r22261) +++ trunk/OpenMPT/mptrack/MainFrm.cpp Fri Nov 22 23:55:56 2024 (r22262) @@ -618,7 +618,7 @@ } -BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) +BOOL CMainFrame::PreTranslateMessage(MSG *pMsg) { // Right-click menu to disable/enable tree view and main toolbar when right-clicking on either the menu strip or main toolbar if((pMsg->message == WM_RBUTTONUP) || (pMsg->message == WM_NCRBUTTONUP)) @@ -3115,43 +3115,60 @@ HMENU CMainFrame::CreateFileMenu(const size_t maxCount, std::vector<mpt::PathString>& paths, const mpt::PathString &folderName, const uint16 idRangeBegin) { paths.clear(); - HMENU hMenu = ::CreatePopupMenu(); - ASSERT(hMenu != NULL); - if (hMenu != NULL) + + for(size_t i = 0; i < 2; i++) // 0: app items, 1: user items { - UINT_PTR filesAdded = 0; - for(size_t i = 0; i < 2; i++) // 0: app items, 1: user items - { - // To avoid duplicates, check whether app path and config path are the same. - if (i == 1 && mpt::PathCompareNoCase(theApp.GetInstallPath(), theApp.GetConfigPath()) == 0) - break; + // To avoid duplicates, check whether app path and config path are the same. + if(i == 1 && mpt::PathCompareNoCase(theApp.GetInstallPath(), theApp.GetConfigPath()) == 0) + break; - mpt::PathString basePath; - basePath = (i == 0) ? theApp.GetInstallPath() : theApp.GetConfigPath(); - basePath += folderName; - if(!mpt::native_fs{}.is_directory(basePath)) - continue; - - FolderScanner scanner(basePath, FolderScanner::kOnlyFiles); - mpt::PathString fileName; - while(filesAdded < maxCount && scanner.Next(fileName)) - { - paths.push_back(fileName); - CString file = fileName.GetFilename().ToCString(); - file.Replace(_T("&"), _T("&&")); - AppendMenu(hMenu, MF_STRING, idRangeBegin + filesAdded, file); - filesAdded++; - } - } + mpt::PathString basePath; + basePath = (i == 0) ? theApp.GetInstallPath() : theApp.GetConfigPath(); + basePath += folderName; + if(!mpt::native_fs{}.is_directory(basePath)) + continue; + + FolderScanner scanner(basePath, FolderScanner::kOnlyFiles); + mpt::PathString fileName; + while(scanner.Next(fileName)) + { + paths.push_back(fileName); + } + } + DWORD stringCompareFlags = NORM_IGNORECASE | NORM_IGNOREWIDTH; +#if MPT_WINNT_AT_LEAST(MPT_WIN_7) + // Wine does not support natural sorting with SORT_DIGITSASNUMBERS, fall back to normal sorting + if(::CompareString(LOCALE_USER_DEFAULT, stringCompareFlags | SORT_DIGITSASNUMBERS, _T(""), -1, _T(""), -1) == CSTR_EQUAL) + stringCompareFlags |= SORT_DIGITSASNUMBERS; +#endif + std::sort(paths.begin(), paths.end(), [stringCompareFlags](const mpt::PathString &left, const mpt::PathString &right) + { + return ::CompareString(LOCALE_USER_DEFAULT, stringCompareFlags, left.AsNative().c_str(), -1, right.AsNative().c_str(), -1) == CSTR_LESS_THAN; + }); - if(filesAdded == 0) - { - AppendMenu(hMenu, MF_STRING | MF_GRAYED | MF_DISABLED, 0, _T("No items found")); - } else - { - AppendMenu(hMenu, MF_SEPARATOR, 0, 0); - AppendMenu(hMenu, MF_STRING, idRangeBegin + maxCount, _T("&Browse...")); - } + HMENU hMenu = ::CreatePopupMenu(); + MPT_ASSERT(hMenu != nullptr); + if(hMenu == nullptr) + return hMenu; + + UINT_PTR filesAdded = 0; + for(const auto &fileName : paths) + { + CString file = fileName.GetFilename().ToCString(); + file.Replace(_T("&"), _T("&&")); + AppendMenu(hMenu, MF_STRING, idRangeBegin + filesAdded, file); + filesAdded++; + if(filesAdded >= maxCount) + break; + } + + if(filesAdded == 0) + { + AppendMenu(hMenu, MF_STRING | MF_GRAYED | MF_DISABLED, 0, _T("No items found")); + } else + { + AppendMenu(hMenu, MF_SEPARATOR, 0, 0); + AppendMenu(hMenu, MF_STRING, idRangeBegin + maxCount, _T("&Browse...")); } return hMenu; Modified: trunk/OpenMPT/mptrack/QuickStartDialog.cpp ============================================================================== --- trunk/OpenMPT/mptrack/QuickStartDialog.cpp Fri Nov 22 08:34:38 2024 (r22261) +++ trunk/OpenMPT/mptrack/QuickStartDialog.cpp Fri Nov 22 23:55:56 2024 (r22262) @@ -1,7 +1,7 @@ /* * QuickStartDialog.cpp * -------------------- - * Purpose: Dialog to show inside the MDI area when no modules are loaded. + * Purpose: Dialog to show inside the MDI client area when no modules are loaded. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -28,6 +28,8 @@ ON_COMMAND(IDC_BUTTON2, &QuickStartDlg::OnOpen) ON_COMMAND(ID_REMOVE, &QuickStartDlg::OnRemoveMRUItem) ON_COMMAND(ID_REMOVE_ALL, &QuickStartDlg::OnRemoveAllMRUItems) + + ON_EN_CHANGE(IDC_EDIT1, &QuickStartDlg::OnUpdateFilter) ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &QuickStartDlg::OnOpenFile) ON_NOTIFY(NM_RCLICK, IDC_LIST1, &QuickStartDlg::OnRightClickFile) @@ -41,6 +43,7 @@ DDX_Control(pDX, IDC_BUTTON2, m_openButton); DDX_Control(pDX, IDCANCEL, m_closeButton); DDX_Control(pDX, IDC_LIST1, m_list); + DDX_Control(pDX, IDC_EDIT1, m_find); } @@ -51,7 +54,7 @@ m_closeButton.SetAccessibleText(_T("Close")); Create(IDD_QUICKSTART, parent); - const bool groupsEnabled = m_list.EnableGroupView(); + m_groupsEnabled = m_list.EnableGroupView(); m_list.SetRedraw(FALSE); m_list.SetExtendedStyle(m_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT); m_list.InsertColumn(0, _T("File"), LVCFMT_LEFT); @@ -64,11 +67,9 @@ {examples, _T("Example Modules")}, }; static_assert(mpt::array_size<decltype(PathGroups)>::size == mpt::array_size<decltype(m_paths)>::size); - int groupId = -1, itemId = -1; - for(const auto &pathGroup : PathGroups) + for(size_t groupId = 0; groupId < std::size(PathGroups); groupId++) { - ++groupId; - if(groupsEnabled) + if(m_groupsEnabled) { LVGROUP group{}; #if MPT_WINNT_AT_LEAST(MPT_WIN_VISTA) @@ -78,15 +79,15 @@ #endif group.mask = LVGF_HEADER | LVGF_GROUPID; #if defined(UNICODE) - group.pszHeader = const_cast<TCHAR *>(pathGroup.second); + group.pszHeader = const_cast<TCHAR *>(PathGroups[groupId].second); #else - std::wstring titlew = mpt::ToWide(mpt::winstring(pathGroup.second)); + std::wstring titlew = mpt::ToWide(mpt::winstring(PathGroups[groupId].second)); group.pszHeader = const_cast<WCHAR *>(titlew.c_str()); #endif group.cchHeader = 0; group.pszFooter = nullptr; group.cchFooter = 0; - group.iGroupId = groupId; + group.iGroupId = static_cast<int>(groupId); group.stateMask = 0; group.state = 0; group.uAlign = LVGA_HEADER_LEFT; @@ -94,29 +95,9 @@ ListView_SetGroupState(m_list.m_hWnd, group.iGroupId, LVGS_COLLAPSIBLE, LVGS_COLLAPSIBLE); } - LVITEM lvi; - lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; - if(groupsEnabled) - lvi.mask |= LVIF_GROUPID; - lvi.iSubItem = 0; - lvi.state = 0; - lvi.stateMask = 0; - lvi.cchTextMax = 0; - lvi.iImage = TIMAGE_MODULE_FILE; - lvi.iIndent = 0; - - for(const auto &path : pathGroup.first) - { - const auto filename = path.GetFilename().AsNative(); - lvi.iGroupId = groupId; - lvi.iItem = ++itemId; - lvi.lParam = static_cast<LPARAM>(m_paths[groupId].size() | (groupId << 24)); - lvi.pszText = const_cast<TCHAR *>(filename.c_str()); - m_list.InsertItem(&lvi); - m_list.SetItemText(itemId, 1, path.GetDirectoryWithDrive().AsNative().c_str()); - m_paths[groupId].push_back(path); - } + m_paths[groupId] = PathGroups[groupId].first; } + UpdateFileList(); m_list.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED); m_list.SetColumnWidth(0, LVSCW_AUTOSIZE); m_list.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER); @@ -192,8 +173,25 @@ } +BOOL QuickStartDlg::PreTranslateMessage(MSG *pMsg) +{ + // Use up/down keys to navigate in list, even if search field is focussed. This also skips the group headers during navigation + if(pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_UP || pMsg->wParam == VK_DOWN) && GetFocus() == &m_find) + { + m_list.ModifyStyle(0, LVS_SHOWSELALWAYS); + int selItem = std::clamp(m_list.GetSelectionMark() + (pMsg->wParam == VK_UP ? -1 : 1), 0, m_list.GetItemCount() - 1); + m_list.SetItemState(selItem, LVIS_SELECTED, LVIS_SELECTED); + m_list.SetSelectionMark(selItem); + return TRUE; + } + + return ResizableDialog::PreTranslateMessage(pMsg); +} + + void QuickStartDlg::UpdateHeight() { + // Try to make the view tall enough to view the entire list contents, but only up to 90% of the MDI client area CRect listRect, viewRect; m_list.GetClientRect(listRect); m_list.GetViewRect(viewRect); @@ -202,7 +200,7 @@ GetClientRect(windowRect); GetParent()->GetClientRect(parentRect); m_list.GetItemRect(0, itemRect, LVIR_BOUNDS); - viewRect.bottom += itemRect.Height() * 2; + viewRect.bottom += itemRect.Height() * 2; // View height calculation seems to be a bit off and would still cause a vertical scrollbar to appear without this adjustment if(viewRect.bottom > listRect.bottom) windowRect.bottom += viewRect.bottom - listRect.bottom; const int maxHeight = Util::muldiv(parentRect.bottom, 9, 10); @@ -234,7 +232,7 @@ void QuickStartDlg::OnOK() { - if(GetFocus() == &m_list) + if(const CWnd *focus = GetFocus(); focus == &m_list || focus == &m_find) OnOpenFile(nullptr, nullptr); } @@ -249,6 +247,7 @@ return; mruFiles.erase(mruFiles.begin() + index); m_list.DeleteItem(index); + m_paths[GetItemGroup(index)][GetItemIndex(index)] = {}; CMainFrame::GetMainFrame()->UpdateMRUList(); } @@ -266,13 +265,61 @@ } +void QuickStartDlg::OnUpdateFilter() +{ + m_list.SetRedraw(FALSE); + m_list.DeleteAllItems(); + CString filter; + m_find.GetWindowText(filter); + UpdateFileList(filter); + m_list.SetRedraw(TRUE); +} + + +void QuickStartDlg::UpdateFileList(CString filter) +{ + const bool applyFilter = !filter.IsEmpty(); + if(applyFilter) + filter = _T("*") + filter + _T("*"); + + LVITEM lvi; + lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; + if(m_groupsEnabled) + lvi.mask |= LVIF_GROUPID; + lvi.iSubItem = 0; + lvi.state = 0; + lvi.stateMask = 0; + lvi.cchTextMax = 0; + lvi.iImage = TIMAGE_MODULE_FILE; + lvi.iIndent = 0; + + int itemId = -1; + for(size_t groupId = 0; groupId < m_paths.size(); groupId++) + { + lvi.iGroupId = static_cast<int>(groupId); + for(size_t i = 0; i < m_paths[groupId].size(); i++) + { + if(m_paths[groupId][i].empty() || (applyFilter && !PathMatchSpec(m_paths[groupId][i].AsNative().c_str(), filter))) + continue; + const auto filename = m_paths[groupId][i].GetFilename().AsNative(); + lvi.iItem = ++itemId; + lvi.lParam = static_cast<LPARAM>(i | (groupId << 24)); + lvi.pszText = const_cast<TCHAR *>(filename.c_str()); + m_list.InsertItem(&lvi); + m_list.SetItemText(itemId, 1, m_paths[groupId][i].GetDirectoryWithDrive().AsNative().c_str()); + } + } +} + + void QuickStartDlg::OnOpenFile(NMHDR *, LRESULT *) { const int i = m_list.GetSelectionMark(); if(i < 0) return; + const size_t index = GetItemIndex(i); const int group = GetItemGroup(i); - const auto &path = m_paths[group][GetItemIndex(i)]; + const auto &path = m_paths[group][index]; CDocument *doc = nullptr; if(group != 1) doc = theApp.OpenDocumentFile(path.ToCString()); @@ -280,7 +327,10 @@ doc = theApp.OpenTemplateFile(path); if(!doc) + { m_list.DeleteItem(i); + m_paths[group][i] = {}; + } } Modified: trunk/OpenMPT/mptrack/QuickStartDialog.h ============================================================================== --- trunk/OpenMPT/mptrack/QuickStartDialog.h Fri Nov 22 08:34:38 2024 (r22261) +++ trunk/OpenMPT/mptrack/QuickStartDialog.h Fri Nov 22 23:55:56 2024 (r22262) @@ -1,7 +1,7 @@ /* * QuickStartDialog.h * ------------------ - * Purpose: Dialog to show inside the MDI area when no modules are loaded. + * Purpose: Dialog to show inside the MDI client area when no modules are loaded. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -31,9 +31,11 @@ BOOL OnInitDialog() override; void OnDPIChanged() override; INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const override; + BOOL PreTranslateMessage(MSG *pMsg) override; void OnOK() override; void OnCancel() override { DestroyWindow(); } + void UpdateFileList(CString filter = {}); size_t GetItemIndex(int index) const { return static_cast<size_t>(m_list.GetItemData(index) & 0x00FF'FFFF); } int GetItemGroup(int index) const { return static_cast<int>(m_list.GetItemData(index) >> 24); } @@ -42,18 +44,21 @@ afx_msg void OnOpen(); afx_msg void OnRemoveMRUItem(); afx_msg void OnRemoveAllMRUItems(); + afx_msg void OnUpdateFilter(); afx_msg void OnOpenFile(NMHDR *, LRESULT *); afx_msg void OnRightClickFile(NMHDR *, LRESULT *); DECLARE_MESSAGE_MAP() CListCtrlEx m_list; + CEdit m_find; AccessibleButton m_newButton, m_openButton, m_closeButton; CFont m_buttonFont; CBitmap m_bmpNew, m_bmpOpen; std::array<std::vector<mpt::PathString>, 3> m_paths; CSize m_prevSize; int m_prevDPI = 96; + bool m_groupsEnabled = false; }; OPENMPT_NAMESPACE_END Modified: trunk/OpenMPT/mptrack/View_tre.cpp ============================================================================== --- trunk/OpenMPT/mptrack/View_tre.cpp Fri Nov 22 08:34:38 2024 (r22261) +++ trunk/OpenMPT/mptrack/View_tre.cpp Fri Nov 22 23:55:56 2024 (r22262) @@ -179,8 +179,8 @@ #if MPT_WINNT_AT_LEAST(MPT_WIN_7) // Wine does not support natural sorting with SORT_DIGITSASNUMBERS, fall back to normal sorting - if(!::CompareString(LOCALE_USER_DEFAULT, m_stringCompareFlags, _T(""), -1, _T(""), -1)) - m_stringCompareFlags &= ~SORT_DIGITSASNUMBERS; + if(::CompareString(LOCALE_USER_DEFAULT, m_stringCompareFlags | SORT_DIGITSASNUMBERS, _T(""), -1, _T(""), -1) == CSTR_EQUAL) + m_stringCompareFlags |= SORT_DIGITSASNUMBERS; #endif } Modified: trunk/OpenMPT/mptrack/View_tre.h ============================================================================== --- trunk/OpenMPT/mptrack/View_tre.h Fri Nov 22 08:34:38 2024 (r22261) +++ trunk/OpenMPT/mptrack/View_tre.h Fri Nov 22 23:55:56 2024 (r22262) @@ -182,11 +182,7 @@ std::unique_ptr<CDLSBank> m_cachedBank; mpt::PathString m_cachedBankName; -#if MPT_WINNT_AT_LEAST(MPT_WIN_7) - DWORD m_stringCompareFlags = NORM_IGNORECASE | NORM_IGNOREWIDTH | SORT_DIGITSASNUMBERS; -#else DWORD m_stringCompareFlags = NORM_IGNORECASE | NORM_IGNOREWIDTH; -#endif // Instrument library mpt::PathString m_InstrLibPath; // Current path to be explored Modified: trunk/OpenMPT/mptrack/mptrack.rc ============================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc Fri Nov 22 08:34:38 2024 (r22261) +++ trunk/OpenMPT/mptrack/mptrack.rc Fri Nov 22 23:55:56 2024 (r22262) @@ -2311,7 +2311,9 @@ BEGIN PUSHBUTTON " &New Module",IDC_BUTTON1,6,6,90,30 PUSHBUTTON " &Open Module",IDC_BUTTON2,108,6,90,30 - CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,48,315,126 + LTEXT "&Find:",IDC_STATIC,6,45,30,8 + EDITTEXT IDC_EDIT1,36,42,285,14,ES_AUTOHSCROLL + CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,60,315,114 PUSHBUTTON "r",IDCANCEL,308,6,13,12,BS_VCENTER END @@ -2747,6 +2749,8 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 100, 0, 0, 0, 100, 100, 100, 0, 0, 0 END |