xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/SystemPage.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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