From 3af691e54211738563cbb65cbef5b73bf1086007 Mon Sep 17 00:00:00 2001 From: Johannes Schultz Date: Mon, 25 Nov 2024 17:32:01 +0000 Subject: [PATCH] [Imp] Quick Start: Allow multi selection in file list. git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@22302 56274372-70c3-4bfc-bfc3-4c3a0b034d27 --- mptrack/QuickStartDialog.cpp | 134 +++++++++++++++++++++-------------- mptrack/QuickStartDialog.h | 7 +- mptrack/mptrack.rc | 24 ++++--- 3 files changed, 95 insertions(+), 70 deletions(-) diff --git a/mptrack/QuickStartDialog.cpp b/mptrack/QuickStartDialog.cpp index 64360ae4b2..b30956f9ab 100644 --- a/mptrack/QuickStartDialog.cpp +++ b/mptrack/QuickStartDialog.cpp @@ -33,6 +33,7 @@ BEGIN_MESSAGE_MAP(QuickStartDlg, ResizableDialog) ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &QuickStartDlg::OnOpenFile) ON_NOTIFY(NM_RCLICK, IDC_LIST1, &QuickStartDlg::OnRightClickFile) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &QuickStartDlg::OnItemChanged) END_MESSAGE_MAP() @@ -41,7 +42,6 @@ void QuickStartDlg::DoDataExchange(CDataExchange *pDX) ResizableDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_BUTTON1, m_newButton); 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 +51,6 @@ QuickStartDlg::QuickStartDlg(const std::vector &templates, cons { m_newButton.SetAccessibleText(_T("New Module")); m_openButton.SetAccessibleText(_T("Open Module")); - m_closeButton.SetAccessibleText(_T("Close")); Create(IDD_QUICKSTART, parent); m_groupsEnabled = m_list.EnableGroupView(); @@ -98,11 +97,11 @@ QuickStartDlg::QuickStartDlg(const std::vector &templates, cons 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); m_list.SetRedraw(TRUE); m_list.Invalidate(FALSE); + OnItemChanged(nullptr, nullptr); UpdateHeight(); } @@ -133,10 +132,6 @@ void QuickStartDlg::OnDPIChanged() m_list.SetColumnWidth(0, LVSCW_AUTOSIZE); m_list.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER); - m_buttonFont.DeleteObject(); - m_buttonFont.CreateFont(mpt::saturate_round(14 * scaling), 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, _T("Marlett")); - m_closeButton.SetFont(&m_buttonFont); - m_bmpNew.DeleteObject(); m_bmpOpen.DeleteObject(); @@ -158,30 +153,20 @@ void QuickStartDlg::OnDPIChanged() } -INT_PTR QuickStartDlg::OnToolHitTest(CPoint point, TOOLINFO *pTI) const -{ - auto result = ResizableDialog::OnToolHitTest(point, pTI); - if(result == IDCANCEL) - { - // MFC will free() the text - mpt::tstring text = _T("Close"); - TCHAR *textP = static_cast(calloc(text.size() + 1, sizeof(TCHAR))); - std::copy(text.begin(), text.end(), textP); - pTI->lpszText = textP; - } - return result; -} - - 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); + const int curSel = m_list.GetSelectionMark(); + const int selItem = std::clamp(curSel + (pMsg->wParam == VK_UP ? -1 : 1), 0, m_list.GetItemCount() - 1); + if(curSel != selItem) + { + if(curSel != -1) + m_list.SetItemState(curSel, 0, LVIS_SELECTED); + m_list.SetItemState(selItem, LVIS_SELECTED, LVIS_SELECTED); + m_list.SetSelectionMark(selItem); + } return TRUE; } @@ -203,7 +188,7 @@ void QuickStartDlg::UpdateHeight() 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); + const int maxHeight = std::max(HighDPISupport::ScalePixels(154, m_hWnd), Util::muldiv(parentRect.bottom, 9, 10)); LimitMax(windowRect.bottom, maxHeight); HighDPISupport::AdjustWindowRectEx(windowRect, GetStyle(), FALSE, GetExStyle(), GetDPI()); SetWindowPos(nullptr, 0, 0, windowRect.Width(), windowRect.Height(), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); @@ -232,22 +217,23 @@ void QuickStartDlg::OnOpen() void QuickStartDlg::OnOK() { - if(const CWnd *focus = GetFocus(); focus == &m_list || focus == &m_find) - OnOpenFile(nullptr, nullptr); + OnOpenFile(nullptr, nullptr); } void QuickStartDlg::OnRemoveMRUItem() { - const int index = m_list.GetSelectionMark(); - if(index < 0) - return; - auto &mruFiles= TrackerSettings::Instance().mruFiles; - if(static_cast(index) >= mruFiles.size()) - return; - mruFiles.erase(mruFiles.begin() + index); - m_paths[GetItemGroup(index)][GetItemIndex(index)] = {}; - m_list.DeleteItem(index); + auto &mruFiles = TrackerSettings::Instance().mruFiles; + int i = -1; + while((i = m_list.GetNextItem(i, LVNI_SELECTED | LVNI_ALL)) != -1) + { + if(GetItemGroup(i) != 0) + continue; + mruFiles.erase(mruFiles.begin() + i); + m_paths[0][GetItemIndex(i)] = {}; + m_list.DeleteItem(i); + i--; + } CMainFrame::GetMainFrame()->UpdateMRUList(); } @@ -268,15 +254,18 @@ void QuickStartDlg::OnRemoveAllMRUItems() void QuickStartDlg::OnUpdateFilter() { m_list.SetRedraw(FALSE); + LPARAM highlight = LPARAM(-1); + if(int sel = m_list.GetSelectionMark(); sel != -1) + highlight = m_list.GetItemData(sel); m_list.DeleteAllItems(); CString filter; m_find.GetWindowText(filter); - UpdateFileList(filter); + UpdateFileList(highlight, filter); m_list.SetRedraw(TRUE); } -void QuickStartDlg::UpdateFileList(CString filter) +void QuickStartDlg::UpdateFileList(LPARAM highlight, CString filter) { const bool applyFilter = !filter.IsEmpty(); if(applyFilter) @@ -293,6 +282,7 @@ void QuickStartDlg::UpdateFileList(CString filter) lvi.iImage = TIMAGE_MODULE_FILE; lvi.iIndent = 0; + bool highlightFound = false; int itemId = -1; for(size_t groupId = 0; groupId < m_paths.size(); groupId++) { @@ -307,29 +297,57 @@ void QuickStartDlg::UpdateFileList(CString filter) lvi.pszText = const_cast(filename.c_str()); m_list.InsertItem(&lvi); m_list.SetItemText(itemId, 1, m_paths[groupId][i].GetDirectoryWithDrive().AsNative().c_str()); + if(lvi.lParam == highlight) + { + m_list.SetItemState(itemId, LVIS_SELECTED, LVIS_SELECTED); + m_list.SetSelectionMark(itemId); + highlightFound = true; + } } } + if(!highlightFound) + { + m_list.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED); + m_list.SetSelectionMark(0); + } } 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][index]; - CDocument *doc = nullptr; - if(group != 1) - doc = theApp.OpenDocumentFile(path.ToCString()); - else - doc = theApp.OpenTemplateFile(path); - - if(!doc) + struct OpenItem { - m_list.DeleteItem(i); - m_paths[group][i] = {}; + const mpt::PathString path; // Must create copy because dialog will get destroyed after successfully loading the first file + const int item; + const int group; + const size_t index; + }; + std::vector files; + int i = -1; + while((i = m_list.GetNextItem(i, LVNI_SELECTED | LVNI_ALL)) != -1) + { + const int group = GetItemGroup(i); + const size_t index = GetItemIndex(i); + const auto &path = m_paths[group][index]; + files.push_back({path, i, group, index}); + } + bool success = false; + for(const auto &item : files) + { + if(item.group != 1) + success |= (theApp.OpenDocumentFile(item.path.ToCString()) != nullptr); + else + success |= (theApp.OpenTemplateFile(item.path) != nullptr); + } + + if(!success) + { + // If at least one item managed to load, the dialog will now be destroyed, and there are no items to delete from the list + for(auto it = files.rbegin(); it != files.rend(); it++) + { + m_paths[it->group][it->index] = {}; + m_list.DeleteItem(it->item); + } } } @@ -352,4 +370,10 @@ void QuickStartDlg::OnRightClickFile(NMHDR *nmhdr, LRESULT *) menu.DestroyMenu(); } + +void QuickStartDlg::OnItemChanged(NMHDR *, LRESULT *) +{ + GetDlgItem(IDOK)->EnableWindow(m_list.GetSelectedCount() != 0 ? TRUE : FALSE); +} + OPENMPT_NAMESPACE_END diff --git a/mptrack/QuickStartDialog.h b/mptrack/QuickStartDialog.h index af7a8f5a5d..2a804c4217 100644 --- a/mptrack/QuickStartDialog.h +++ b/mptrack/QuickStartDialog.h @@ -30,12 +30,11 @@ class QuickStartDlg : public ResizableDialog void DoDataExchange(CDataExchange *pDX) override; 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 = {}); + void UpdateFileList(LPARAM highlight = LPARAM(-1), CString filter = {}); size_t GetItemIndex(int index) const { return static_cast(m_list.GetItemData(index) & 0x00FF'FFFF); } int GetItemGroup(int index) const { return static_cast(m_list.GetItemData(index) >> 24); } @@ -47,13 +46,13 @@ class QuickStartDlg : public ResizableDialog afx_msg void OnUpdateFilter(); afx_msg void OnOpenFile(NMHDR *, LRESULT *); afx_msg void OnRightClickFile(NMHDR *, LRESULT *); + afx_msg void OnItemChanged(NMHDR *, LRESULT *); DECLARE_MESSAGE_MAP() CListCtrlEx m_list; CEdit m_find; - AccessibleButton m_newButton, m_openButton, m_closeButton; - CFont m_buttonFont; + AccessibleButton m_newButton, m_openButton; CBitmap m_bmpNew, m_bmpOpen; std::array, 3> m_paths; CSize m_prevSize; diff --git a/mptrack/mptrack.rc b/mptrack/mptrack.rc index 056556ca40..d41781e3a3 100644 --- a/mptrack/mptrack.rc +++ b/mptrack/mptrack.rc @@ -2304,17 +2304,18 @@ BEGIN LTEXT "Warning: Only samples are affected by these settings! Modifying them will change the balance between samples and plugins in songs with mix levels set to 1.17RC2 or earlier in the Song Properties! ",IDC_STATIC,12,222,252,30 END -IDD_QUICKSTART DIALOGEX 0, 0, 332, 185 +IDD_QUICKSTART DIALOGEX 0, 0, 332, 211 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD | WS_CLIPSIBLINGS EXSTYLE WS_EX_CONTROLPARENT | WS_EX_NOACTIVATE FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - PUSHBUTTON " &New Module",IDC_BUTTON1,6,6,90,30 - PUSHBUTTON " &Open Module",IDC_BUTTON2,108,6,90,30 - LTEXT "F&ind:",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 + PUSHBUTTON " &New Module",IDC_BUTTON1,10,9,90,30 + PUSHBUTTON " &Open Module",IDC_BUTTON2,108,9,90,30 + LTEXT "F&ind:",IDC_STATIC,10,48,30,8 + EDITTEXT IDC_EDIT1,36,45,285,14,ES_AUTOHSCROLL + CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,10,65,311,109 + PUSHBUTTON "&Close",IDCANCEL,261,184,60,18,BS_VCENTER + DEFPUSHBUTTON "Open &Selected",IDOK,195,184,60,18 END @@ -2583,10 +2584,10 @@ BEGIN IDD_QUICKSTART, DIALOG BEGIN - LEFTMARGIN, 6 + LEFTMARGIN, 10 RIGHTMARGIN, 321 - TOPMARGIN, 6 - BOTTOMMARGIN, 174 + TOPMARGIN, 9 + BOTTOMMARGIN, 202 END END #endif // APSTUDIO_INVOKED @@ -2752,7 +2753,8 @@ BEGIN 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 100, 100, - 100, 0, 0, 0 + 100, 100, 0, 0, + 100, 100, 0, 0 END