Process Hacker
chdlg.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * choice dialog
4  *
5  * Copyright (C) 2010-2013 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phapp.h>
24 #include <settings.h>
25 #include <windowsx.h>
26 
27 typedef struct _CHOICE_DIALOG_CONTEXT
28 {
29  PWSTR Title;
30  PWSTR Message;
31  PWSTR *Choices;
32  ULONG NumberOfChoices;
33  PWSTR Option;
34  ULONG Flags;
35  PPH_STRING *SelectedChoice;
36  PBOOLEAN SelectedOption;
37  PWSTR SavedChoicesSettingName;
38 
39  HWND ComboBoxHandle;
41 
42 INT_PTR CALLBACK PhpChoiceDlgProc(
43  _In_ HWND hwndDlg,
44  _In_ UINT uMsg,
45  _In_ WPARAM wParam,
46  _In_ LPARAM lParam
47  );
48 
56  _In_ HWND ParentWindowHandle,
57  _In_ PWSTR Title,
58  _In_ PWSTR Message,
59  _In_opt_ PWSTR *Choices,
60  _In_opt_ ULONG NumberOfChoices,
61  _In_opt_ PWSTR Option,
62  _In_ ULONG Flags,
63  _Inout_ PPH_STRING *SelectedChoice,
64  _Inout_opt_ PBOOLEAN SelectedOption,
65  _In_opt_ PWSTR SavedChoicesSettingName
66  )
67 {
68  CHOICE_DIALOG_CONTEXT context;
69 
70  context.Title = Title;
71  context.Message = Message;
72  context.Choices = Choices;
73  context.NumberOfChoices = NumberOfChoices;
74  context.Option = Option;
75  context.Flags = Flags;
76  context.SelectedChoice = SelectedChoice;
77  context.SelectedOption = SelectedOption;
78  context.SavedChoicesSettingName = SavedChoicesSettingName;
79 
80  return DialogBoxParam(
82  MAKEINTRESOURCE(IDD_CHOOSE),
83  ParentWindowHandle,
85  (LPARAM)&context
86  ) == IDOK;
87 }
88 
89 INT_PTR CALLBACK PhpChoiceDlgProc(
90  _In_ HWND hwndDlg,
91  _In_ UINT uMsg,
92  _In_ WPARAM wParam,
93  _In_ LPARAM lParam
94  )
95 {
96  switch (uMsg)
97  {
98  case WM_INITDIALOG:
99  {
101  ULONG type;
102  SIZE_T i;
103  HWND comboBoxHandle;
104  HWND checkBoxHandle;
105  RECT checkBoxRect;
106  RECT rect;
107  ULONG diff;
108 
109  SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
110  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
111 
112  SetWindowText(hwndDlg, context->Title);
113  SetWindowText(GetDlgItem(hwndDlg, IDC_MESSAGE), context->Message);
114 
115  type = context->Flags & PH_CHOICE_DIALOG_TYPE_MASK;
116 
117  // Select the control to show, depending on the type. This is
118  // because it is impossible to change the style of the combo box
119  // after it is created.
120  switch (type)
121  {
123  comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICEUSER);
124  ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICEUSER), SW_SHOW);
125  break;
127  comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICESIMPLE);
128  ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICESIMPLE), SW_SHOW);
129 
130  // Disable combo box features since it isn't a combo box.
131  context->SavedChoicesSettingName = NULL;
132  break;
134  default:
135  comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICE);
136  ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICE), SW_SHOW);
137  break;
138  }
139 
140  context->ComboBoxHandle = comboBoxHandle;
141 
142  checkBoxHandle = GetDlgItem(hwndDlg, IDC_OPTION);
143 
144  if (type == PH_CHOICE_DIALOG_PASSWORD)
145  {
146  // Nothing
147  }
148  else if (type == PH_CHOICE_DIALOG_USER_CHOICE && context->SavedChoicesSettingName)
149  {
150  PPH_STRING savedChoices = PhGetStringSetting(context->SavedChoicesSettingName);
151  ULONG_PTR indexOfDelim;
152  PPH_STRING savedChoice;
153 
154  i = 0;
155 
156  // Split the saved choices using the delimiter.
157  while (i < savedChoices->Length / 2)
158  {
159  // BUG BUG BUG - what if the user saves "\s"?
160  indexOfDelim = PhFindStringInString(savedChoices, i, L"\\s");
161 
162  if (indexOfDelim == -1)
163  indexOfDelim = savedChoices->Length / 2;
164 
165  savedChoice = PhSubstring(savedChoices, i, indexOfDelim - i);
166 
167  if (savedChoice->Length != 0)
168  {
169  PPH_STRING unescaped;
170 
171  unescaped = PhUnescapeStringForDelimiter(savedChoice, '\\');
172  ComboBox_InsertString(comboBoxHandle, -1, unescaped->Buffer);
173  PhDereferenceObject(unescaped);
174  }
175 
176  PhDereferenceObject(savedChoice);
177 
178  i = indexOfDelim + 2;
179  }
180 
181  PhDereferenceObject(savedChoices);
182  }
183  else
184  {
185  for (i = 0; i < context->NumberOfChoices; i++)
186  {
187  ComboBox_AddString(comboBoxHandle, context->Choices[i]);
188  }
189 
190  context->SavedChoicesSettingName = NULL; // make sure we don't try to save the choices
191  }
192 
193  if (type == PH_CHOICE_DIALOG_PASSWORD)
194  {
195  if (*context->SelectedChoice)
196  SetWindowText(comboBoxHandle, (*context->SelectedChoice)->Buffer);
197 
198  Edit_SetSel(comboBoxHandle, 0, -1);
199  }
200  else if (type == PH_CHOICE_DIALOG_USER_CHOICE || type == PH_CHOICE_DIALOG_CHOICE)
201  {
202  // If we failed to choose a default choice based on what was specified,
203  // select the first one if possible, or set the text directly.
204  if (!(*context->SelectedChoice) || PhSelectComboBoxString(
205  comboBoxHandle, (*context->SelectedChoice)->Buffer, FALSE) == CB_ERR)
206  {
207  if (type == PH_CHOICE_DIALOG_USER_CHOICE && *context->SelectedChoice)
208  {
209  SetWindowText(comboBoxHandle, (*context->SelectedChoice)->Buffer);
210  }
211  else if (type == PH_CHOICE_DIALOG_CHOICE && context->NumberOfChoices != 0)
212  {
213  ComboBox_SetCurSel(comboBoxHandle, 0);
214  }
215  }
216 
217  if (type == PH_CHOICE_DIALOG_USER_CHOICE)
218  ComboBox_SetEditSel(comboBoxHandle, 0, -1);
219  }
220 
221  if (context->Option)
222  {
223  SetWindowText(checkBoxHandle, context->Option);
224 
225  if (context->SelectedOption)
226  Button_SetCheck(checkBoxHandle, *context->SelectedOption ? BST_CHECKED : BST_UNCHECKED);
227  }
228  else
229  {
230  // Hide the check box and move the buttons up.
231 
232  ShowWindow(checkBoxHandle, SW_HIDE);
233  GetWindowRect(checkBoxHandle, &checkBoxRect);
234  MapWindowPoints(NULL, hwndDlg, (POINT *)&checkBoxRect, 2);
235  GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rect);
236  MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2);
237  diff = rect.top - checkBoxRect.top;
238 
239  // OK
240  rect.top -= diff;
241  rect.bottom -= diff;
242  SetWindowPos(GetDlgItem(hwndDlg, IDOK), NULL, rect.left, rect.top,
243  rect.right - rect.left, rect.bottom - rect.top,
244  SWP_NOACTIVATE | SWP_NOZORDER);
245 
246  // Cancel
247  GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rect);
248  MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2);
249  rect.top -= diff;
250  rect.bottom -= diff;
251  SetWindowPos(GetDlgItem(hwndDlg, IDCANCEL), NULL, rect.left, rect.top,
252  rect.right - rect.left, rect.bottom - rect.top,
253  SWP_NOACTIVATE | SWP_NOZORDER);
254 
255  // Window
256  GetWindowRect(hwndDlg, &rect);
257  rect.bottom -= diff;
258  SetWindowPos(hwndDlg, NULL, rect.left, rect.top,
259  rect.right - rect.left, rect.bottom - rect.top,
260  SWP_NOACTIVATE | SWP_NOZORDER);
261  }
262 
263  SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)comboBoxHandle, TRUE);
264  }
265  break;
266  case WM_DESTROY:
267  {
268  RemoveProp(hwndDlg, PhMakeContextAtom());
269  }
270  break;
271  case WM_COMMAND:
272  {
273  switch (LOWORD(wParam))
274  {
275  case IDCANCEL:
276  EndDialog(hwndDlg, IDCANCEL);
277  break;
278  case IDOK:
279  {
280  PCHOICE_DIALOG_CONTEXT context = (PCHOICE_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
281  PPH_STRING selectedChoice;
282 
283  if ((context->Flags & PH_CHOICE_DIALOG_TYPE_MASK) != PH_CHOICE_DIALOG_PASSWORD)
284  {
285  selectedChoice = PhAutoDereferenceObject(PhGetWindowText(context->ComboBoxHandle));
286  *context->SelectedChoice = selectedChoice;
287  }
288  else
289  {
290  // Password values are never auto-dereferenced.
291  selectedChoice = PhGetWindowText(context->ComboBoxHandle);
292  *context->SelectedChoice = selectedChoice;
293  }
294 
295  if (context->Option && context->SelectedOption)
296  *context->SelectedOption = Button_GetCheck(GetDlgItem(hwndDlg, IDC_OPTION)) == BST_CHECKED;
297 
298  if (context->SavedChoicesSettingName)
299  {
300  PH_STRING_BUILDER savedChoices;
301  ULONG i;
302  ULONG choicesToSave = PH_CHOICE_DIALOG_SAVED_CHOICES;
303  PPH_STRING choice;
304  PPH_STRING escaped;
305 
306  PhInitializeStringBuilder(&savedChoices, 100);
307 
308  // Push the selected choice to the top, then save the others.
309 
310  if (selectedChoice->Length != 0)
311  {
312  escaped = PhEscapeStringForDelimiter(selectedChoice, '\\');
313  PhAppendStringBuilder(&savedChoices, &escaped->sr);
314  PhDereferenceObject(escaped);
315  PhAppendStringBuilder2(&savedChoices, L"\\s");
316  }
317 
318  for (i = 1; i < choicesToSave; i++)
319  {
320  choice = PhGetComboBoxString(context->ComboBoxHandle, i - 1);
321 
322  if (!choice)
323  break;
324 
325  // Don't save the choice if it's the same as the one
326  // entered by the user (since we already saved it above).
327  if (PhEqualString(choice, selectedChoice, FALSE))
328  {
329  PhDereferenceObject(choice);
330  choicesToSave++; // useless for now, but may be needed in the future
331  continue;
332  }
333 
334  escaped = PhEscapeStringForDelimiter(choice, '\\');
335  PhAppendStringBuilder(&savedChoices, &escaped->sr);
336  PhDereferenceObject(escaped);
337  PhDereferenceObject(choice);
338 
339  PhAppendStringBuilder2(&savedChoices, L"\\s");
340  }
341 
342  if (PhEndsWithString2(savedChoices.String, L"\\s", FALSE))
343  PhRemoveEndStringBuilder(&savedChoices, 2);
344 
345  PhSetStringSetting2(context->SavedChoicesSettingName, &savedChoices.String->sr);
346  PhDeleteStringBuilder(&savedChoices);
347  }
348 
349  EndDialog(hwndDlg, IDOK);
350  }
351  break;
352  }
353  }
354  break;
355  }
356 
357  return FALSE;
358 }