BOOLEAN PhCmForwardSort( _In_ PPH_TREENEW_NODE *Nodes, _In_ ULONG NumberOfNodes, _In_ ULONG SortColumn, _In_ PH_SORT_ORDER SortOrder, _In_ PPH_CM_MANAGER Manager ) { PH_TREENEW_COLUMN tnColumn; PPH_CM_COLUMN column; PH_CM_SORT_CONTEXT sortContext; if (SortColumn < Manager->MinId) return FALSE; if (!TreeNew_GetColumn(Manager->Handle, SortColumn, &tnColumn)) return TRUE; column = tnColumn.Context; if (!column->SortFunction) return TRUE; sortContext.SortFunction = column->SortFunction; sortContext.SubId = column->SubId; sortContext.Context = column->Context; sortContext.PostSortFunction = Manager->PostSortFunction; sortContext.SortOrder = SortOrder; qsort_s(Nodes, NumberOfNodes, sizeof(PVOID), PhCmpSortFunction, &sortContext); return TRUE; }
VOID PhMapDisplayIndexTreeNew( __in HWND TreeNewHandle, __out_opt PULONG *DisplayToId, __out_opt PWSTR **DisplayToText, __out PULONG NumberOfColumns ) { PPH_TREENEW_COLUMN fixedColumn; ULONG numberOfColumns; PULONG displayToId; PWSTR *displayToText; ULONG i; PH_TREENEW_COLUMN column; fixedColumn = TreeNew_GetFixedColumn(TreeNewHandle); numberOfColumns = TreeNew_GetVisibleColumnCount(TreeNewHandle); displayToId = PhAllocate(numberOfColumns * sizeof(ULONG)); if (fixedColumn) { TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns - 1, displayToId + 1); displayToId[0] = fixedColumn->Id; } else { TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns, displayToId); } if (DisplayToText) { displayToText = PhAllocate(numberOfColumns * sizeof(PWSTR)); for (i = 0; i < numberOfColumns; i++) { if (TreeNew_GetColumn(TreeNewHandle, displayToId[i], &column)) { displayToText[i] = column.Text; } } *DisplayToText = displayToText; } if (DisplayToId) *DisplayToId = displayToId; else PhFree(displayToId); *NumberOfColumns = numberOfColumns; }
PPH_STRING PhCmSaveSettingsEx( _In_ HWND TreeNewHandle, _In_opt_ PPH_CM_MANAGER Manager, _In_ ULONG Flags, _Out_opt_ PPH_STRING *SortSettings ) { PH_STRING_BUILDER stringBuilder; ULONG i = 0; ULONG count = 0; ULONG total; ULONG increment; PH_TREENEW_COLUMN column; total = TreeNew_GetColumnCount(TreeNewHandle); if (TreeNew_GetFixedColumn(TreeNewHandle)) increment = 1; // the first normal column should have a display index that starts with 1, for compatibility else increment = 0; PhInitializeStringBuilder(&stringBuilder, 100); while (count < total) { if (TreeNew_GetColumn(TreeNewHandle, i, &column)) { if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) { if (column.Visible) { if (!Manager || i < Manager->MinId) { PhAppendFormatStringBuilder( &stringBuilder, L"%u,%u,%u|", i, column.Fixed ? 0 : column.DisplayIndex + increment, column.Width ); } else { PPH_CM_COLUMN cmColumn; cmColumn = column.Context; PhAppendFormatStringBuilder( &stringBuilder, L"+%s+%u,%u,%u|", cmColumn->Plugin->Name.Buffer, cmColumn->SubId, column.DisplayIndex + increment, column.Width ); } } } else { if (!Manager || i < Manager->MinId) { PhAppendFormatStringBuilder( &stringBuilder, L"%u,,%u|", i, column.Width ); } else { PPH_CM_COLUMN cmColumn; cmColumn = column.Context; PhAppendFormatStringBuilder( &stringBuilder, L"+%s+%u,,%u|", cmColumn->Plugin->Name.Buffer, cmColumn->SubId, column.Width ); } } count++; } i++; } if (stringBuilder.String->Length != 0) PhRemoveEndStringBuilder(&stringBuilder, 1); if (SortSettings) { ULONG sortColumn; PH_SORT_ORDER sortOrder; if (TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder)) { if (sortOrder != NoSortOrder) { if (!Manager || sortColumn < Manager->MinId) { *SortSettings = PhFormatString(L"%u,%u", sortColumn, sortOrder); } else { PH_TREENEW_COLUMN column; PPH_CM_COLUMN cmColumn; if (TreeNew_GetColumn(TreeNewHandle, sortColumn, &column)) { cmColumn = column.Context; *SortSettings = PhFormatString(L"+%s+%u,%u", cmColumn->Plugin->Name.Buffer, cmColumn->SubId, sortOrder); } else { *SortSettings = PhReferenceEmptyString(); } } } else { *SortSettings = PhCreateString(L"0,0"); } } else { *SortSettings = PhReferenceEmptyString(); } } return PhFinalStringBuilderString(&stringBuilder); }
BOOLEAN PhCmLoadSettingsEx( _In_ HWND TreeNewHandle, _In_opt_ PPH_CM_MANAGER Manager, _In_ ULONG Flags, _In_ PPH_STRINGREF Settings, _In_opt_ PPH_STRINGREF SortSettings ) { BOOLEAN result = FALSE; PH_STRINGREF columnPart; PH_STRINGREF remainingColumnPart; PH_STRINGREF valuePart; PH_STRINGREF subPart; ULONG64 integer; ULONG total; BOOLEAN hasFixedColumn; ULONG count; ULONG i; PPH_HASHTABLE columnHashtable; PH_HASHTABLE_ENUM_CONTEXT enumContext; PPH_KEY_VALUE_PAIR pair; LONG orderArray[PH_CM_ORDER_LIMIT]; LONG maxOrder; if (Settings->Length != 0) { columnHashtable = PhCreateSimpleHashtable(20); remainingColumnPart = *Settings; while (remainingColumnPart.Length != 0) { PPH_TREENEW_COLUMN column; ULONG id; ULONG displayIndex; ULONG width; PhSplitStringRefAtChar(&remainingColumnPart, '|', &columnPart, &remainingColumnPart); if (columnPart.Length != 0) { // Id PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); if (valuePart.Length == 0) goto CleanupExit; if (valuePart.Buffer[0] == '+') { PH_STRINGREF pluginName; ULONG subId; PPH_CM_COLUMN cmColumn; // This is a plugin-owned column. if (!Manager) continue; if (!PhEmParseCompoundId(&valuePart, &pluginName, &subId)) continue; cmColumn = PhCmFindColumn(Manager, &pluginName, subId); if (!cmColumn) continue; // can't find the column, skip this part id = cmColumn->Id; } else { if (!PhStringToInteger64(&valuePart, 10, &integer)) goto CleanupExit; id = (ULONG)integer; } // Display Index PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) { if (valuePart.Length == 0 || !PhStringToInteger64(&valuePart, 10, &integer)) goto CleanupExit; displayIndex = (ULONG)integer; } else { if (valuePart.Length != 0) goto CleanupExit; displayIndex = -1; } // Width if (columnPart.Length == 0 || !PhStringToInteger64(&columnPart, 10, &integer)) goto CleanupExit; width = (ULONG)integer; column = PhAllocate(sizeof(PH_TREENEW_COLUMN)); column->Id = id; column->DisplayIndex = displayIndex; column->Width = width; PhAddItemSimpleHashtable(columnHashtable, (PVOID)column->Id, column); } } TreeNew_SetRedraw(TreeNewHandle, FALSE); // Set visibility and width. i = 0; count = 0; total = TreeNew_GetColumnCount(TreeNewHandle); hasFixedColumn = !!TreeNew_GetFixedColumn(TreeNewHandle); memset(orderArray, 0, sizeof(orderArray)); maxOrder = 0; while (count < total) { PH_TREENEW_COLUMN setColumn; PPH_TREENEW_COLUMN *columnPtr; if (TreeNew_GetColumn(TreeNewHandle, i, &setColumn)) { columnPtr = (PPH_TREENEW_COLUMN *)PhFindItemSimpleHashtable(columnHashtable, (PVOID)i); if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) { if (columnPtr) { setColumn.Visible = TRUE; setColumn.Width = (*columnPtr)->Width; TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE | TN_COLUMN_WIDTH, &setColumn); if (!setColumn.Fixed) { // For compatibility reasons, normal columns have their display indicies stored // one higher than usual (so they start from 1, not 0). Fix that here. if (hasFixedColumn && (*columnPtr)->DisplayIndex != 0) (*columnPtr)->DisplayIndex--; if ((*columnPtr)->DisplayIndex < PH_CM_ORDER_LIMIT) { orderArray[(*columnPtr)->DisplayIndex] = i; if ((ULONG)maxOrder < (*columnPtr)->DisplayIndex + 1) maxOrder = (*columnPtr)->DisplayIndex + 1; } } } else if (!setColumn.Fixed) // never hide the fixed column { setColumn.Visible = FALSE; TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &setColumn); } } else { if (columnPtr) { setColumn.Width = (*columnPtr)->Width; TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_WIDTH, &setColumn); } } count++; } i++; } if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) { // Set the order array. TreeNew_SetColumnOrderArray(TreeNewHandle, maxOrder, orderArray); } TreeNew_SetRedraw(TreeNewHandle, TRUE); result = TRUE; CleanupExit: PhBeginEnumHashtable(columnHashtable, &enumContext); while (pair = PhNextEnumHashtable(&enumContext)) PhFree(pair->Value); PhDereferenceObject(columnHashtable); } // Load sort settings. if (SortSettings && SortSettings->Length != 0) { PhSplitStringRefAtChar(SortSettings, ',', &valuePart, &subPart); if (valuePart.Length != 0 && subPart.Length != 0) { ULONG sortColumn; PH_SORT_ORDER sortOrder; sortColumn = -1; if (valuePart.Buffer[0] == '+') { PH_STRINGREF pluginName; ULONG subId; PPH_CM_COLUMN cmColumn; if ( Manager && PhEmParseCompoundId(&valuePart, &pluginName, &subId) && (cmColumn = PhCmFindColumn(Manager, &pluginName, subId)) ) { sortColumn = cmColumn->Id; } } else { PhStringToInteger64(&valuePart, 10, &integer); sortColumn = (ULONG)integer; } PhStringToInteger64(&subPart, 10, &integer); sortOrder = (PH_SORT_ORDER)integer; if (sortColumn != -1 && sortOrder <= DescendingSortOrder) { TreeNew_SetSort(TreeNewHandle, sortColumn, sortOrder); } } } return result; }
BOOLEAN PhCmForwardMessage( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, _In_opt_ PVOID Parameter1, _In_opt_ PVOID Parameter2, _In_ PPH_CM_MANAGER Manager ) { PH_PLUGIN_TREENEW_MESSAGE pluginMessage; PPH_PLUGIN plugin; if (Message == TreeNewDestroying) return FALSE; switch (Message) { case TreeNewGetCellText: { PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; PH_TREENEW_COLUMN tnColumn; PPH_CM_COLUMN column; if (getCellText->Id < Manager->MinId) return FALSE; if (!TreeNew_GetColumn(hwnd, getCellText->Id, &tnColumn)) return FALSE; column = tnColumn.Context; pluginMessage.SubId = column->SubId; pluginMessage.Context = column->Context; plugin = column->Plugin; } break; case TreeNewCustomDraw: { PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; PPH_CM_COLUMN column; if (customDraw->Column->Id < Manager->MinId) return FALSE; column = customDraw->Column->Context; pluginMessage.SubId = column->SubId; pluginMessage.Context = column->Context; plugin = column->Plugin; } break; case TreeNewColumnResized: { PPH_TREENEW_COLUMN tlColumn = Parameter1; PPH_CM_COLUMN column; if (tlColumn->Id < Manager->MinId) return FALSE; column = tlColumn->Context; pluginMessage.SubId = column->SubId; pluginMessage.Context = column->Context; plugin = column->Plugin; } break; default: { // Some plugins want to be notified about all messages. if (Manager->NotifyList) { ULONG i; for (i = 0; i < Manager->NotifyList->Count; i++) { plugin = Manager->NotifyList->Items[i]; pluginMessage.TreeNewHandle = hwnd; pluginMessage.Message = Message; pluginMessage.Parameter1 = Parameter1; pluginMessage.Parameter2 = Parameter2; pluginMessage.SubId = 0; pluginMessage.Context = NULL; PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); } } } return FALSE; } pluginMessage.TreeNewHandle = hwnd; pluginMessage.Message = Message; pluginMessage.Parameter1 = Parameter1; pluginMessage.Parameter2 = Parameter2; PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); return TRUE; }
VOID PhpEnsureValidSortColumnTreeNew( __inout HWND TreeNewHandle, __in ULONG DefaultSortColumn, __in PH_SORT_ORDER DefaultSortOrder ) { ULONG sortColumn; PH_SORT_ORDER sortOrder; // Make sure the column we're sorting by is actually visible, and if not, don't sort anymore. TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder); if (sortOrder != NoSortOrder) { PH_TREENEW_COLUMN column; TreeNew_GetColumn(TreeNewHandle, sortColumn, &column); if (!column.Visible) { if (DefaultSortOrder != NoSortOrder) { // Make sure the default sort column is visible. TreeNew_GetColumn(TreeNewHandle, DefaultSortColumn, &column); if (!column.Visible) { ULONG maxId; ULONG id; BOOLEAN found; // Use the first visible column. maxId = TreeNew_GetMaxId(TreeNewHandle); id = 0; found = FALSE; while (id <= maxId) { if (TreeNew_GetColumn(TreeNewHandle, id, &column)) { if (column.Visible) { DefaultSortColumn = id; found = TRUE; break; } } id++; } if (!found) { DefaultSortColumn = 0; DefaultSortOrder = NoSortOrder; } } } TreeNew_SetSort(TreeNewHandle, DefaultSortColumn, DefaultSortOrder); } } }
INT_PTR CALLBACK PhpColumnsDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ) { PCOLUMNS_DIALOG_CONTEXT context = NULL; if (uMsg == WM_INITDIALOG) { context = (PCOLUMNS_DIALOG_CONTEXT)lParam; SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); } else { context = (PCOLUMNS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); } if (!context) return FALSE; switch (uMsg) { case WM_INITDIALOG: { ULONG count; ULONG total; ULONG i; PPH_LIST displayOrderList; context->InactiveList = GetDlgItem(hwndDlg, IDC_INACTIVE); context->ActiveList = GetDlgItem(hwndDlg, IDC_ACTIVE); if (context->Type == PH_CONTROL_TYPE_TREE_NEW) { PH_TREENEW_COLUMN column; count = 0; total = TreeNew_GetColumnCount(context->ControlHandle); i = 0; displayOrderList = PhCreateList(total); while (count < total) { if (TreeNew_GetColumn(context->ControlHandle, i, &column)) { PPH_TREENEW_COLUMN copy; if (column.Fixed) { i++; total--; continue; } copy = PhAllocateCopy(&column, sizeof(PH_TREENEW_COLUMN)); PhAddItemList(context->Columns, copy); count++; if (column.Visible) { PhAddItemList(displayOrderList, copy); } else { ListBox_AddString(context->InactiveList, column.Text); } } i++; } qsort(displayOrderList->Items, displayOrderList->Count, sizeof(PVOID), PhpColumnsCompareDisplayIndexTn); } for (i = 0; i < displayOrderList->Count; i++) { if (context->Type == PH_CONTROL_TYPE_TREE_NEW) { PPH_TREENEW_COLUMN copy = displayOrderList->Items[i]; ListBox_AddString(context->ActiveList, copy->Text); } } PhDereferenceObject(displayOrderList); SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); } break; case WM_DESTROY: { ULONG i; for (i = 0; i < context->Columns->Count; i++) PhFree(context->Columns->Items[i]); RemoveProp(hwndDlg, PhMakeContextAtom()); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); break; case IDOK: { #define ORDER_LIMIT 100 PPH_LIST activeList; ULONG activeCount; ULONG i; INT orderArray[ORDER_LIMIT]; INT maxOrder; memset(orderArray, 0, sizeof(orderArray)); maxOrder = 0; activeCount = ListBox_GetCount(context->ActiveList); activeList = PhCreateList(activeCount); for (i = 0; i < activeCount; i++) PhAddItemList(activeList, PhGetListBoxString(context->ActiveList, i)); if (context->Type == PH_CONTROL_TYPE_TREE_NEW) { // Apply visiblity settings. TreeNew_SetRedraw(context->ControlHandle, FALSE); for (i = 0; i < context->Columns->Count; i++) { PPH_TREENEW_COLUMN column = context->Columns->Items[i]; ULONG index; index = IndexOfStringInList(activeList, column->Text); column->Visible = index != -1; column->DisplayIndex = index; // the active list box order is the actual display order TreeNew_SetColumn(context->ControlHandle, TN_COLUMN_FLAG_VISIBLE, column); } // Do a second pass to create the order array. This is because the ViewIndex of each column // were unstable in the previous pass since we were both adding and removing columns. for (i = 0; i < context->Columns->Count; i++) { PPH_TREENEW_COLUMN column = context->Columns->Items[i]; PH_TREENEW_COLUMN tempColumn; if (column->Visible) { if (column->DisplayIndex < ORDER_LIMIT) { TreeNew_GetColumn(context->ControlHandle, column->Id, &tempColumn); orderArray[column->DisplayIndex] = tempColumn.s.ViewIndex; if ((ULONG)maxOrder < column->DisplayIndex + 1) maxOrder = column->DisplayIndex + 1; } } } // Apply display order. TreeNew_SetColumnOrderArray(context->ControlHandle, maxOrder, orderArray); TreeNew_SetRedraw(context->ControlHandle, TRUE); PhDereferenceObject(activeList); InvalidateRect(context->ControlHandle, NULL, FALSE); } EndDialog(hwndDlg, IDOK); } break; case IDC_INACTIVE: { switch (HIWORD(wParam)) { case LBN_DBLCLK: { SendMessage(hwndDlg, WM_COMMAND, IDC_SHOW, 0); } break; case LBN_SELCHANGE: { INT sel = ListBox_GetCurSel(context->InactiveList); EnableWindow(GetDlgItem(hwndDlg, IDC_SHOW), sel != -1); } break; } } break; case IDC_ACTIVE: { switch (HIWORD(wParam)) { case LBN_DBLCLK: { SendMessage(hwndDlg, WM_COMMAND, IDC_HIDE, 0); } break; case LBN_SELCHANGE: { INT sel = ListBox_GetCurSel(context->ActiveList); INT count = ListBox_GetCount(context->ActiveList); EnableWindow(GetDlgItem(hwndDlg, IDC_HIDE), sel != -1 && count != 1); EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0 && sel != -1); EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1 && sel != -1); } break; } } break; case IDC_SHOW: { INT sel; INT count; PPH_STRING string; sel = ListBox_GetCurSel(context->InactiveList); count = ListBox_GetCount(context->InactiveList); if (string = PhGetListBoxString(context->InactiveList, sel)) { ListBox_DeleteString(context->InactiveList, sel); ListBox_AddString(context->ActiveList, string->Buffer); PhDereferenceObject(string); count--; if (sel >= count - 1) sel = count - 1; ListBox_SetCurSel(context->InactiveList, sel); SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); } } break; case IDC_HIDE: { INT sel; INT count; PPH_STRING string; sel = ListBox_GetCurSel(context->ActiveList); count = ListBox_GetCount(context->ActiveList); if (count != 1) { if (string = PhGetListBoxString(context->ActiveList, sel)) { ListBox_DeleteString(context->ActiveList, sel); ListBox_AddString(context->InactiveList, string->Buffer); PhDereferenceObject(string); count--; if (sel >= count - 1) sel = count - 1; ListBox_SetCurSel(context->ActiveList, sel); SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); } } } break; case IDC_MOVEUP: { INT sel; INT count; PPH_STRING string; sel = ListBox_GetCurSel(context->ActiveList); count = ListBox_GetCount(context->ActiveList); if (sel != 0) { if (string = PhGetListBoxString(context->ActiveList, sel)) { ListBox_DeleteString(context->ActiveList, sel); ListBox_InsertString(context->ActiveList, sel - 1, string->Buffer); PhDereferenceObject(string); sel -= 1; ListBox_SetCurSel(context->ActiveList, sel); EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0); EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1); } } } break; case IDC_MOVEDOWN: { INT sel; INT count; PPH_STRING string; sel = ListBox_GetCurSel(context->ActiveList); count = ListBox_GetCount(context->ActiveList); if (sel != count - 1) { if (string = PhGetListBoxString(context->ActiveList, sel)) { ListBox_DeleteString(context->ActiveList, sel); ListBox_InsertString(context->ActiveList, sel + 1, string->Buffer); PhDereferenceObject(string); sel += 1; ListBox_SetCurSel(context->ActiveList, sel); EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0); EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1); } } } break; } } break; } return FALSE; }