1 // SystemPage.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/MyWindows.h"
6
7 #if defined(__MINGW32__) || defined(__MINGW64__)
8 #include <shlobj.h>
9 #else
10 #include <ShlObj.h>
11 #endif
12
13 #include "../../../Common/Defs.h"
14 #include "../../../Common/StringConvert.h"
15
16 #include "../../../Windows/DLL.h"
17 #include "../../../Windows/ErrorMsg.h"
18
19 #include "HelpUtils.h"
20 #include "IFolder.h"
21 #include "LangUtils.h"
22 #include "PropertyNameRes.h"
23 #include "SystemPage.h"
24 #include "SystemPageRes.h"
25
26 using namespace NWindows;
27
28 #ifndef _UNICODE
29 extern bool g_IsNT;
30 #endif
31
32 #ifdef Z7_LANG
33 static const UInt32 kLangIDs[] =
34 {
35 IDT_SYSTEM_ASSOCIATE
36 };
37 #endif
38
39 #define kSystemTopic "FM/options.htm#system"
40
GetString() const41 CSysString CModifiedExtInfo::GetString() const
42 {
43 const char *s;
44 if (State == kExtState_7Zip)
45 s = "7-Zip";
46 else if (State == kExtState_Clear)
47 s = "";
48 else if (Other7Zip)
49 s = "[7-Zip]";
50 else
51 return ProgramKey;
52 return CSysString (s);
53 }
54
55
AddIcon(const UString & iconPath,int iconIndex)56 int CSystemPage::AddIcon(const UString &iconPath, int iconIndex)
57 {
58 if (iconPath.IsEmpty())
59 return -1;
60 if (iconIndex == -1)
61 iconIndex = 0;
62
63 HICON hicon;
64
65 #ifdef UNDER_CE
66 ExtractIconExW(iconPath, iconIndex, NULL, &hicon, 1);
67 if (!hicon)
68 #else
69 // we expand path from REG_EXPAND_SZ registry item.
70 UString path;
71 const DWORD size = MAX_PATH + 10;
72 const DWORD needLen = ::ExpandEnvironmentStringsW(iconPath, path.GetBuf(size + 2), size);
73 path.ReleaseBuf_CalcLen(size);
74 if (needLen == 0 || needLen >= size)
75 path = iconPath;
76 const UINT num = ExtractIconExW(path, iconIndex, NULL, &hicon, 1);
77 if (num != 1 || !hicon)
78 #endif
79 return -1;
80
81 _imageList.AddIcon(hicon);
82 DestroyIcon(hicon);
83 return (int)(_numIcons++);
84 }
85
86
RefreshListItem(unsigned group,unsigned listIndex)87 void CSystemPage::RefreshListItem(unsigned group, unsigned listIndex)
88 {
89 const CAssoc &assoc = _items[GetRealIndex(listIndex)];
90 _listView.SetSubItem(listIndex, group + 1, assoc.Pair[group].GetString());
91 LVITEMW newItem;
92 memset(&newItem, 0, sizeof(newItem));
93 newItem.iItem = (int)listIndex;
94 newItem.mask = LVIF_IMAGE;
95 newItem.iImage = assoc.GetIconIndex();
96 _listView.SetItem(&newItem);
97 }
98
99
ChangeState(unsigned group,const CUIntVector & indices)100 void CSystemPage::ChangeState(unsigned group, const CUIntVector &indices)
101 {
102 if (indices.IsEmpty())
103 return;
104
105 bool thereAreClearItems = false;
106 unsigned counters[3] = { 0, 0, 0 };
107
108 unsigned i;
109 for (i = 0; i < indices.Size(); i++)
110 {
111 const CModifiedExtInfo &mi = _items[GetRealIndex(indices[i])].Pair[group];
112 int state = kExtState_7Zip;
113 if (mi.State == kExtState_7Zip)
114 state = kExtState_Clear;
115 else if (mi.State == kExtState_Clear)
116 {
117 thereAreClearItems = true;
118 if (mi.Other)
119 state = kExtState_Other;
120 }
121 counters[state]++;
122 }
123
124 int state = kExtState_Clear;
125 if (counters[kExtState_Other] != 0)
126 state = kExtState_Other;
127 else if (counters[kExtState_7Zip] != 0)
128 state = kExtState_7Zip;
129
130 for (i = 0; i < indices.Size(); i++)
131 {
132 unsigned listIndex = indices[i];
133 CAssoc &assoc = _items[GetRealIndex(listIndex)];
134 CModifiedExtInfo &mi = assoc.Pair[group];
135 bool change = false;
136
137 switch (state)
138 {
139 case kExtState_Clear: change = true; break;
140 case kExtState_Other: change = mi.Other; break;
141 default: change = !(mi.Other && thereAreClearItems); break;
142 }
143
144 if (change)
145 {
146 mi.State = state;
147 RefreshListItem(group, listIndex);
148 }
149 }
150
151 _needSave = true;
152 Changed();
153 }
154
155
OnInit()156 bool CSystemPage::OnInit()
157 {
158 _needSave = false;
159
160 #ifdef Z7_LANG
161 LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs));
162 #endif
163
164 _listView.Attach(GetItem(IDL_SYSTEM_ASSOCIATE));
165 _listView.SetUnicodeFormat();
166 DWORD newFlags = LVS_EX_FULLROWSELECT;
167 _listView.SetExtendedListViewStyle(newFlags, newFlags);
168
169 _numIcons = 0;
170 _imageList.Create(16, 16, ILC_MASK | ILC_COLOR32, 0, 0);
171
172 _listView.SetImageList(_imageList, LVSIL_SMALL);
173
174 _listView.InsertColumn(0, LangString(IDS_PROP_FILE_TYPE), 80);
175
176 UString s;
177
178 #if NUM_EXT_GROUPS == 1
179 s = "Program";
180 #else
181 #ifndef UNDER_CE
182 const unsigned kSize = 256;
183 BOOL res;
184
185 DWORD size = kSize;
186
187 #ifndef _UNICODE
188 if (!g_IsNT)
189 {
190 AString s2;
191 res = GetUserNameA(s2.GetBuf(size), &size);
192 s2.ReleaseBuf_CalcLen(MyMin((unsigned)size, kSize));
193 s = GetUnicodeString(s2);
194 }
195 else
196 #endif
197 {
198 res = GetUserNameW(s.GetBuf(size), &size);
199 s.ReleaseBuf_CalcLen(MyMin((unsigned)size, kSize));
200 }
201
202 if (!res)
203 #endif
204 s = "Current User";
205 #endif
206
207 LV_COLUMNW ci;
208 ci.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
209 ci.cx = 152;
210 ci.fmt = LVCFMT_CENTER;
211 ci.pszText = s.Ptr_non_const();
212 ci.iSubItem = 1;
213 _listView.InsertColumn(1, &ci);
214
215 #if NUM_EXT_GROUPS > 1
216 {
217 LangString(IDS_SYSTEM_ALL_USERS, s);
218 ci.pszText = s.Ptr_non_const();
219 ci.iSubItem = 2;
220 _listView.InsertColumn(2, &ci);
221 }
222 #endif
223
224 _extDB.Read();
225 _items.Clear();
226
227 FOR_VECTOR (i, _extDB.Exts)
228 {
229 const CExtPlugins &extInfo = _extDB.Exts[i];
230
231 LVITEMW item;
232 item.iItem = (int)i;
233 item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
234 item.lParam = (LPARAM)i;
235 item.iSubItem = 0;
236 // ListView always uses internal iImage that is 0 by default?
237 // so we always use LVIF_IMAGE.
238 item.iImage = -1;
239 item.pszText = extInfo.Ext.Ptr_non_const();
240
241 CAssoc assoc;
242 const CPluginToIcon &plug = extInfo.Plugins[0];
243 assoc.SevenZipImageIndex = AddIcon(plug.IconPath, plug.IconIndex);
244
245 CSysString texts[NUM_EXT_GROUPS];
246 unsigned g;
247 for (g = 0; g < NUM_EXT_GROUPS; g++)
248 {
249 CModifiedExtInfo &mi = assoc.Pair[g];
250 mi.ReadFromRegistry(GetHKey(g), GetSystemString(extInfo.Ext));
251 mi.SetState(plug.IconPath);
252 mi.ImageIndex = AddIcon(mi.IconPath, mi.IconIndex);
253 texts[g] = mi.GetString();
254 }
255 item.iImage = assoc.GetIconIndex();
256 const int itemIndex = _listView.InsertItem(&item);
257 for (g = 0; g < NUM_EXT_GROUPS; g++)
258 _listView.SetSubItem((unsigned)itemIndex, 1 + g, texts[g]);
259 _items.Add(assoc);
260 }
261
262 if (_listView.GetItemCount() > 0)
263 _listView.SetItemState(0, LVIS_FOCUSED, LVIS_FOCUSED);
264
265 return CPropertyPage::OnInit();
266 }
267
268
GetProgramCommand()269 static UString GetProgramCommand()
270 {
271 UString s ('\"');
272 s += fs2us(NDLL::GetModuleDirPrefix());
273 s += "7zFM.exe\" \"%1\"";
274 return s;
275 }
276
277
OnApply()278 LONG CSystemPage::OnApply()
279 {
280 if (!_needSave)
281 return PSNRET_NOERROR;
282
283 const UString command = GetProgramCommand();
284
285 LONG res = 0;
286
287 FOR_VECTOR (listIndex, _extDB.Exts)
288 {
289 unsigned realIndex = GetRealIndex(listIndex);
290 const CExtPlugins &extInfo = _extDB.Exts[realIndex];
291 CAssoc &assoc = _items[realIndex];
292
293 for (unsigned g = 0; g < NUM_EXT_GROUPS; g++)
294 {
295 CModifiedExtInfo &mi = assoc.Pair[g];
296 HKEY key = GetHKey(g);
297
298 if (mi.OldState != mi.State)
299 {
300 LONG res2 = 0;
301
302 if (mi.State == kExtState_7Zip)
303 {
304 UString title = extInfo.Ext;
305 title += " Archive";
306 const CPluginToIcon &plug = extInfo.Plugins[0];
307 res2 = NRegistryAssoc::AddShellExtensionInfo(key, GetSystemString(extInfo.Ext),
308 title, command, plug.IconPath, plug.IconIndex);
309 }
310 else if (mi.State == kExtState_Clear)
311 res2 = NRegistryAssoc::DeleteShellExtensionInfo(key, GetSystemString(extInfo.Ext));
312
313 if (res == 0)
314 res = res2;
315 if (res2 == 0)
316 mi.OldState = mi.State;
317
318 mi.State = mi.OldState;
319 RefreshListItem(g, listIndex);
320 }
321 }
322 }
323
324 #ifndef UNDER_CE
325 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
326 #endif
327
328 WasChanged = true;
329
330 _needSave = false;
331
332 if (res != 0)
333 MessageBoxW(*this, NError::MyFormatMessage(res), L"7-Zip", MB_ICONERROR);
334
335 return PSNRET_NOERROR;
336 }
337
338
OnNotifyHelp()339 void CSystemPage::OnNotifyHelp()
340 {
341 ShowHelpWindow(kSystemTopic);
342 }
343
344
OnButtonClicked(unsigned buttonID,HWND buttonHWND)345 bool CSystemPage::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
346 {
347 switch (buttonID)
348 {
349 /*
350 case IDC_SYSTEM_SELECT_ALL:
351 _listView.SelectAll();
352 return true;
353 */
354 case IDB_SYSTEM_CURRENT:
355 case IDB_SYSTEM_ALL:
356 ChangeState(buttonID == IDB_SYSTEM_CURRENT ? 0 : 1);
357 return true;
358 }
359 return CPropertyPage::OnButtonClicked(buttonID, buttonHWND);
360 }
361
362
OnNotify(UINT controlID,LPNMHDR lParam)363 bool CSystemPage::OnNotify(UINT controlID, LPNMHDR lParam)
364 {
365 if (lParam->hwndFrom == HWND(_listView))
366 {
367 switch (lParam->code)
368 {
369 case NM_RETURN:
370 {
371 ChangeState(0);
372 return true;
373 }
374
375 case NM_CLICK:
376 {
377 #ifdef UNDER_CE
378 NMLISTVIEW *item = (NMLISTVIEW *)lParam;
379 #else
380 NMITEMACTIVATE *item = (NMITEMACTIVATE *)lParam;
381 if (item->uKeyFlags == 0)
382 #endif
383 {
384 if (item->iItem >= 0)
385 {
386 // unsigned realIndex = GetRealIndex(item->iItem);
387 if (item->iSubItem >= 1 && item->iSubItem <= 2)
388 {
389 CUIntVector indices;
390 indices.Add((unsigned)item->iItem);
391 ChangeState(item->iSubItem < 2 ? 0 : 1, indices);
392 }
393 }
394 }
395 break;
396 }
397
398 case LVN_KEYDOWN:
399 {
400 if (OnListKeyDown(LPNMLVKEYDOWN(lParam)))
401 return true;
402 break;
403 }
404
405 /*
406 case NM_RCLICK:
407 case NM_DBLCLK:
408 case LVN_BEGINRDRAG:
409 // PostMessage(kRefreshpluginsListMessage, 0);
410 PostMessage(kUpdateDatabase, 0);
411 break;
412 */
413 }
414 }
415 return CPropertyPage::OnNotify(controlID, lParam);
416 }
417
418
ChangeState(unsigned group)419 void CSystemPage::ChangeState(unsigned group)
420 {
421 CUIntVector indices;
422
423 int itemIndex = -1;
424 while ((itemIndex = _listView.GetNextSelectedItem(itemIndex)) != -1)
425 indices.Add((unsigned)itemIndex);
426
427 if (indices.IsEmpty())
428 FOR_VECTOR (i, _items)
429 indices.Add(i);
430
431 ChangeState(group, indices);
432 }
433
434
OnListKeyDown(LPNMLVKEYDOWN keyDownInfo)435 bool CSystemPage::OnListKeyDown(LPNMLVKEYDOWN keyDownInfo)
436 {
437 bool ctrl = IsKeyDown(VK_CONTROL);
438 bool alt = IsKeyDown(VK_MENU);
439
440 if (alt)
441 return false;
442
443 if ((ctrl && keyDownInfo->wVKey == 'A')
444 || (!ctrl && keyDownInfo->wVKey == VK_MULTIPLY))
445 {
446 _listView.SelectAll();
447 return true;
448 }
449
450 switch (keyDownInfo->wVKey)
451 {
452 case VK_SPACE:
453 case VK_ADD:
454 case VK_SUBTRACT:
455 case VK_SEPARATOR:
456 case VK_DIVIDE:
457
458 #ifndef UNDER_CE
459 case VK_OEM_PLUS:
460 case VK_OEM_MINUS:
461 #endif
462
463 if (!ctrl)
464 {
465 ChangeState(keyDownInfo->wVKey == VK_SPACE ? 0 : 1);
466 return true;
467 }
468 break;
469 }
470
471 return false;
472 }
473