Make "Window->Windows...->Sort Tabs" more user friendly

Closes #3248
This commit is contained in:
AngryGamer 2017-05-05 17:30:15 -07:00 committed by Don HO
parent 76395bb5e5
commit ca3c2a6016
2 changed files with 140 additions and 49 deletions

View File

@ -74,8 +74,8 @@ inline static BOOL ModifyStyleEx(HWND hWnd, DWORD dwRemove, DWORD dwAdd) {
struct NumericStringEquivalence struct NumericStringEquivalence
{ {
bool operator()(const TCHAR* s1, const TCHAR* s2) const int operator()(const TCHAR* s1, const TCHAR* s2) const
{ return numstrcmp(s1, s2) < 0; } { return numstrcmp(s1, s2); }
static inline int numstrcmp_get(const TCHAR **str, int *length) static inline int numstrcmp_get(const TCHAR **str, int *length)
{ {
const TCHAR *p = *str; const TCHAR *p = *str;
@ -143,28 +143,36 @@ struct BufferEquivalent
} }
bool compare(int i1, int i2) const bool compare(int i1, int i2) const
{
if (_iColumn >= 0 && _iColumn <= 2)
{ {
BufferID bid1 = _pTab->getBufferByIndex(i1); BufferID bid1 = _pTab->getBufferByIndex(i1);
BufferID bid2 = _pTab->getBufferByIndex(i2); BufferID bid2 = _pTab->getBufferByIndex(i2);
Buffer * b1 = MainFileManager->getBufferByID(bid1); Buffer * b1 = MainFileManager->getBufferByID(bid1);
Buffer * b2 = MainFileManager->getBufferByID(bid2); Buffer * b2 = MainFileManager->getBufferByID(bid2);
if (_iColumn == 0) if (_iColumn == 0)
{ {
const TCHAR *s1 = b1->getFileName(); const TCHAR *s1 = b1->getFileName();
const TCHAR *s2 = b2->getFileName(); const TCHAR *s2 = b2->getFileName();
return _strequiv(s1, s2); int result = _strequiv(s1, s2);
}
else if (_iColumn == 1) if (result != 0) // default to filepath sorting when equivalent
{ return result < 0;
const TCHAR *s1 = b1->getFullPathName();
const TCHAR *s2 = b2->getFullPathName();
return _strequiv(s1, s2); //we can compare the full path to sort on directory, since after sorting directories sorting files is the second thing to do (if directories are the same that is)
} }
else if (_iColumn == 2) else if (_iColumn == 2)
{ {
auto t1 = b1->getLangType(); auto t1 = b1->getLangType();
auto t2 = b2->getLangType(); auto t2 = b2->getLangType();
return (t1 < t2); // yeah should be the name
if (t1 != t2) // default to filepath sorting when equivalent
return (t1 < t2);
}
// _iColumn == 1
const TCHAR *s1 = b1->getFullPathName();
const TCHAR *s2 = b2->getFullPathName();
return _strequiv(s1, s2) < 0; //we can compare the full path to sort on directory, since after sorting directories sorting files is the second thing to do (if directories are the same that is)
} }
return false; return false;
} }
@ -255,9 +263,21 @@ INT_PTR CALLBACK WindowsDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lPa
} }
case IDC_WINDOWS_SORT: case IDC_WINDOWS_SORT:
{ {
// they never set a column to sort by, so assume they wanted filename
if (_currentColumn == -1)
{
_currentColumn = 0;
_reverseSort = false;
_lastSort = _currentColumn;
updateColumnNames();
doColumnSort();
}
doSortToTabs(); doSortToTabs();
_isSorted = false;
updateButtonState(); // must re-sort because tab indexes changed
doColumnSort();
break; break;
} }
} }
@ -344,31 +364,21 @@ INT_PTR CALLBACK WindowsDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lPa
NMLISTVIEW *pNMLV = (NMLISTVIEW *)pNMHDR; NMLISTVIEW *pNMLV = (NMLISTVIEW *)pNMHDR;
if (pNMLV->iItem == -1) if (pNMLV->iItem == -1)
{ {
bool reverse = false; _currentColumn = pNMLV->iSubItem;
int iColumn = pNMLV->iSubItem;
if (_lastSort == iColumn) if (_lastSort == _currentColumn)
{ {
reverse = true; _reverseSort = true;
_lastSort = -1; _lastSort = -1;
} }
else else
{ {
_lastSort = iColumn; _reverseSort = false;
_lastSort = _currentColumn;
} }
size_t i;
size_t n = _idxMap.size();
vector<int> sortMap;
sortMap.resize(n);
for (i = 0; i < n; ++i)
sortMap[_idxMap[i]] = ListView_GetItemState(_hList, i, LVIS_SELECTED);
stable_sort(_idxMap.begin(), _idxMap.end(), BufferEquivalent(_pTab, iColumn, reverse)); updateColumnNames();
for (i = 0; i < n; ++i) doColumnSort();
ListView_SetItemState(_hList, i, sortMap[_idxMap[i]] ? LVIS_SELECTED : 0, LVIS_SELECTED);
::InvalidateRect(_hList, &_rc, FALSE);
_isSorted = true;
updateButtonState();
} }
return TRUE; return TRUE;
} }
@ -403,6 +413,26 @@ INT_PTR CALLBACK WindowsDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lPa
return MyBaseClass::run_dlgProc(message, wParam, lParam); return MyBaseClass::run_dlgProc(message, wParam, lParam);
} }
void WindowsDlg::doColumnSort()
{
if (_currentColumn == -1)
return;
size_t i;
size_t n = _idxMap.size();
vector<int> sortMap;
sortMap.resize(n);
for (i = 0; i < n; ++i)
sortMap[_idxMap[i]] = ListView_GetItemState(_hList, i, LVIS_SELECTED);
stable_sort(_idxMap.begin(), _idxMap.end(), BufferEquivalent(_pTab, _currentColumn, _reverseSort));
for (i = 0; i < n; ++i)
ListView_SetItemState(_hList, i, sortMap[_idxMap[i]] ? LVIS_SELECTED : 0, LVIS_SELECTED);
::InvalidateRect(_hList, &_rc, FALSE);
updateButtonState();
}
void WindowsDlg::updateButtonState() void WindowsDlg::updateButtonState()
{ {
@ -422,7 +452,7 @@ void WindowsDlg::updateButtonState()
else else
EnableWindow(GetDlgItem(_hSelf, IDOK), FALSE); EnableWindow(GetDlgItem(_hSelf, IDOK), FALSE);
} }
EnableWindow(GetDlgItem(_hSelf, IDC_WINDOWS_SORT), _isSorted); EnableWindow(GetDlgItem(_hSelf, IDC_WINDOWS_SORT), TRUE);
} }
int WindowsDlg::doDialog(TiXmlNodeA *dlgNode) int WindowsDlg::doDialog(TiXmlNodeA *dlgNode)
@ -482,7 +512,9 @@ BOOL WindowsDlg::onInitDialog()
// save min size for OK/Cancel buttons // save min size for OK/Cancel buttons
_szMinButton = RectToSize(_winMgr.GetRect(IDOK)); _szMinButton = RectToSize(_winMgr.GetRect(IDOK));
_szMinListCtrl = RectToSize(_winMgr.GetRect(IDC_WINDOWS_LIST)); _szMinListCtrl = RectToSize(_winMgr.GetRect(IDC_WINDOWS_LIST));
_currentColumn = -1;
_lastSort = -1; _lastSort = -1;
_reverseSort = false;
_winMgr.CalcLayout(_hSelf); _winMgr.CalcLayout(_hSelf);
_winMgr.SetWindowPositions(_hSelf); _winMgr.SetWindowPositions(_hSelf);
@ -492,25 +524,27 @@ BOOL WindowsDlg::onInitDialog()
DWORD exStyle = ListView_GetExtendedListViewStyle(_hList); DWORD exStyle = ListView_GetExtendedListViewStyle(_hList);
exStyle |= LVS_EX_HEADERDRAGDROP|LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER; exStyle |= LVS_EX_HEADERDRAGDROP|LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER;
ListView_SetExtendedListViewStyle(_hList, exStyle); ListView_SetExtendedListViewStyle(_hList, exStyle);
RECT rc; RECT rc;
GetClientRect(_hList, &rc); GetClientRect(_hList, &rc);
LONG width = rc.right - rc.left; LONG width = rc.right - rc.left;
LVCOLUMN lvColumn; LVCOLUMN lvColumn;
memset(&lvColumn, 0, sizeof(lvColumn)); memset(&lvColumn, 0, sizeof(lvColumn));
lvColumn.mask = LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM|LVCF_FMT; lvColumn.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
lvColumn.fmt = LVCFMT_LEFT; lvColumn.fmt = LVCFMT_LEFT;
lvColumn.pszText = TEXT("Name");
lvColumn.cx = width/4; lvColumn.pszText = TEXT("\u21F5 Name");
lvColumn.cx = width / 4;
SendMessage(_hList, LVM_INSERTCOLUMN, 0, LPARAM(&lvColumn)); SendMessage(_hList, LVM_INSERTCOLUMN, 0, LPARAM(&lvColumn));
lvColumn.pszText = TEXT("Path"); lvColumn.pszText = TEXT("\u21F5 Path");
lvColumn.cx = 300; lvColumn.cx = 300;
SendMessage(_hList, LVM_INSERTCOLUMN, 1, LPARAM(&lvColumn)); SendMessage(_hList, LVM_INSERTCOLUMN, 1, LPARAM(&lvColumn));
lvColumn.fmt = LVCFMT_CENTER; lvColumn.fmt = LVCFMT_CENTER;
lvColumn.pszText = TEXT("Type"); lvColumn.pszText = TEXT("\u21F5 Type");
lvColumn.cx = 40; lvColumn.cx = 50;
SendMessage(_hList, LVM_INSERTCOLUMN, 2, LPARAM(&lvColumn)); SendMessage(_hList, LVM_INSERTCOLUMN, 2, LPARAM(&lvColumn));
fitColumnsToSize(); fitColumnsToSize();
@ -529,6 +563,60 @@ BOOL WindowsDlg::onInitDialog()
return TRUE; return TRUE;
} }
void WindowsDlg::updateColumnNames()
{
LVCOLUMN lvColumn;
memset(&lvColumn, 0, sizeof(lvColumn));
lvColumn.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
lvColumn.fmt = LVCFMT_LEFT;
if (_currentColumn != 0)
{
lvColumn.pszText = TEXT("\u21F5 Name");
}
else if (_reverseSort)
{
lvColumn.pszText = TEXT("\u25B3 Name");
}
else
{
lvColumn.pszText = TEXT("\u25BD Name");
}
lvColumn.cx = static_cast<int>(SendMessage(_hList, LVM_GETCOLUMNWIDTH, 0, 0));
SendMessage(_hList, LVM_SETCOLUMN, 0, LPARAM(&lvColumn));
if (_currentColumn != 1)
{
lvColumn.pszText = TEXT("\u21F5 Path");
}
else if (_reverseSort)
{
lvColumn.pszText = TEXT("\u25B3 Path");
}
else
{
lvColumn.pszText = TEXT("\u25BD Path");
}
lvColumn.cx = static_cast<int>(SendMessage(_hList, LVM_GETCOLUMNWIDTH, 1, 0));
SendMessage(_hList, LVM_SETCOLUMN, 1, LPARAM(&lvColumn));
lvColumn.fmt = LVCFMT_CENTER;
if (_currentColumn != 2)
{
lvColumn.pszText = TEXT("\u21F5 Type");
}
else if (_reverseSort)
{
lvColumn.pszText = TEXT("\u25B3 Type");
}
else
{
lvColumn.pszText = TEXT("\u25BD Type");
}
lvColumn.cx = static_cast<int>(SendMessage(_hList, LVM_GETCOLUMNWIDTH, 2, 0));
SendMessage(_hList, LVM_SETCOLUMN, 2, LPARAM(&lvColumn));
}
void WindowsDlg::onSize(UINT nType, int cx, int cy) void WindowsDlg::onSize(UINT nType, int cx, int cy)
{ {
MyBaseClass::onSize(nType, cx, cy); MyBaseClass::onSize(nType, cx, cy);

View File

@ -82,6 +82,7 @@ protected :
virtual void onGetMinMaxInfo(MINMAXINFO* lpMMI); virtual void onGetMinMaxInfo(MINMAXINFO* lpMMI);
virtual LRESULT onWinMgr(WPARAM wp, LPARAM lp); virtual LRESULT onWinMgr(WPARAM wp, LPARAM lp);
virtual void destroy(); virtual void destroy();
void updateColumnNames();
void fitColumnsToSize(); void fitColumnsToSize();
void resetSelection(); void resetSelection();
void doSave(); void doSave();
@ -89,6 +90,7 @@ protected :
void doSortToTabs(); void doSortToTabs();
void updateButtonState(); void updateButtonState();
void activateCurrent(); void activateCurrent();
void doColumnSort();
HWND _hList = nullptr; HWND _hList = nullptr;
static RECT _lastKnownLocation; static RECT _lastKnownLocation;
@ -96,8 +98,9 @@ protected :
SIZE _szMinListCtrl; SIZE _szMinListCtrl;
DocTabView *_pTab = nullptr; DocTabView *_pTab = nullptr;
std::vector<int> _idxMap; std::vector<int> _idxMap;
int _currentColumn = -1;
int _lastSort = -1; int _lastSort = -1;
bool _isSorted = false; bool _reverseSort = false;
TiXmlNodeA *_dlgNode = nullptr; TiXmlNodeA *_dlgNode = nullptr;
private: private: