1 // PanelListNotify.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "resource.h"
6 
7 #include "../../../Common/IntToString.h"
8 #include "../../../Common/StringConvert.h"
9 
10 #include "../../../Windows/PropVariant.h"
11 #include "../../../Windows/PropVariantConv.h"
12 
13 #include "../Common/PropIDUtils.h"
14 #include "../../PropID.h"
15 
16 #include "App.h"
17 #include "Panel.h"
18 #include "FormatUtils.h"
19 
20 using namespace NWindows;
21 
22 /* Unicode characters for space:
23 0x009C STRING TERMINATOR
24 0x00B7 Middle dot
25 0x237D Shouldered open box
26 0x2420 Symbol for space
27 0x2422 Blank symbol
28 0x2423 Open box
29 */
30 
31 #define SPACE_REPLACE_CHAR (wchar_t)(0x2423)
32 #define SPACE_TERMINATOR_CHAR (wchar_t)(0x9C)
33 
34 #define INT_TO_STR_SPEC(v) \
35   while (v >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(v % 10)); v /= 10; } \
36   *s++ = (unsigned char)('0' + (unsigned)v);
37 
ConvertSizeToString(UInt64 val,wchar_t * s)38 static void ConvertSizeToString(UInt64 val, wchar_t *s) throw()
39 {
40   unsigned char temp[32];
41   unsigned i = 0;
42 
43   if (val <= (UInt32)0xFFFFFFFF)
44   {
45     UInt32 val32 = (UInt32)val;
46     INT_TO_STR_SPEC(val32)
47   }
48   else
49   {
50     INT_TO_STR_SPEC(val)
51   }
52 
53   if (i < 3)
54   {
55     if (i != 0)
56     {
57       *s++ = temp[(size_t)i - 1];
58       if (i == 2)
59         *s++ = temp[0];
60     }
61     *s = 0;
62     return;
63   }
64 
65   unsigned r = i % 3;
66   if (r != 0)
67   {
68     s[0] = temp[--i];
69     if (r == 2)
70       s[1] = temp[--i];
71     s += r;
72   }
73 
74   do
75   {
76     s[0] = ' ';
77     s[1] = temp[(size_t)i - 1];
78     s[2] = temp[(size_t)i - 2];
79     s[3] = temp[(size_t)i - 3];
80     s += 4;
81   }
82   while (i -= 3);
83 
84   *s = 0;
85 }
86 
87 UString ConvertSizeToString(UInt64 value);
ConvertSizeToString(UInt64 value)88 UString ConvertSizeToString(UInt64 value)
89 {
90   wchar_t s[32];
91   ConvertSizeToString(value, s);
92   return s;
93 }
94 
GetHex_Upper(unsigned v)95 static inline unsigned GetHex_Upper(unsigned v)
96 {
97   return (v < 10) ? ('0' + v) : ('A' + (v - 10));
98 }
99 
GetHex_Lower(unsigned v)100 static inline unsigned GetHex_Lower(unsigned v)
101 {
102   return (v < 10) ? ('0' + v) : ('a' + (v - 10));
103 }
104 
105 /*
106 static void HexToString(char *dest, const Byte *data, UInt32 size)
107 {
108   for (UInt32 i = 0; i < size; i++)
109   {
110     unsigned b = data[i];
111     dest[0] = GetHex((b >> 4) & 0xF);
112     dest[1] = GetHex(b & 0xF);
113     dest += 2;
114   }
115   *dest = 0;
116 }
117 */
118 
119 bool IsSizeProp(UINT propID) throw();
IsSizeProp(UINT propID)120 bool IsSizeProp(UINT propID) throw()
121 {
122   switch (propID)
123   {
124     case kpidSize:
125     case kpidPackSize:
126     case kpidNumSubDirs:
127     case kpidNumSubFiles:
128     case kpidOffset:
129     case kpidLinks:
130     case kpidNumBlocks:
131     case kpidNumVolumes:
132     case kpidPhySize:
133     case kpidHeadersSize:
134     case kpidTotalSize:
135     case kpidFreeSpace:
136     case kpidClusterSize:
137     case kpidNumErrors:
138     case kpidNumStreams:
139     case kpidNumAltStreams:
140     case kpidAltStreamsSize:
141     case kpidVirtualSize:
142     case kpidUnpackSize:
143     case kpidTotalPhySize:
144     case kpidTailSize:
145     case kpidEmbeddedStubSize:
146       return true;
147   }
148   return false;
149 }
150 
151 
152 
153 /*
154 #include <stdio.h>
155 
156 UInt64 GetCpuTicks()
157 {
158     #ifdef _WIN64
159       return __rdtsc();
160     #else
161       UInt32 lowVal, highVal;
162       __asm RDTSC;
163       __asm mov lowVal, EAX;
164       __asm mov highVal, EDX;
165       return ((UInt64)highVal << 32) | lowVal;
166     #endif
167 }
168 
169 UInt32 g_NumGroups;
170 UInt64 g_start_tick;
171 UInt64 g_prev_tick;
172 DWORD g_Num_SetItemText;
173 UInt32 g_NumMessages;
174 */
175 
SetItemText(LVITEMW & item)176 LRESULT CPanel::SetItemText(LVITEMW &item)
177 {
178   if (_dontShowMode)
179     return 0;
180   UInt32 realIndex = GetRealIndex(item);
181 
182   // g_Num_SetItemText++;
183 
184   /*
185   if ((item.mask & LVIF_IMAGE) != 0)
186   {
187     bool defined  = false;
188     CComPtr<IFolderGetSystemIconIndex> folderGetSystemIconIndex;
189     _folder.QueryInterface(&folderGetSystemIconIndex);
190     if (folderGetSystemIconIndex)
191     {
192       folderGetSystemIconIndex->GetSystemIconIndex(index, &item.iImage);
193       defined = (item.iImage > 0);
194     }
195     if (!defined)
196     {
197       NCOM::CPropVariant prop;
198       _folder->GetProperty(index, kpidAttrib, &prop);
199       UINT32 attrib = 0;
200       if (prop.vt == VT_UI4)
201         attrib = prop.ulVal;
202       else if (IsItemFolder(index))
203         attrib |= FILE_ATTRIBUTE_DIRECTORY;
204       if (_currentFolderPrefix.IsEmpty())
205         throw 1;
206       else
207         item.iImage = _extToIconMap.GetIconIndex(attrib, GetSystemString(GetItemName(index)));
208     }
209     // item.iImage = 1;
210   }
211   */
212 
213   if ((item.mask & LVIF_TEXT) == 0)
214     return 0;
215 
216   LPWSTR text = item.pszText;
217 
218   if (item.cchTextMax > 0)
219     text[0] = 0;
220 
221   if (item.cchTextMax <= 1)
222     return 0;
223 
224   const CPropColumn &property = _visibleColumns[item.iSubItem];
225   PROPID propID = property.ID;
226 
227   if (realIndex == kParentIndex_UInt32)
228   {
229     if (propID == kpidName)
230     {
231       if (item.cchTextMax > 2)
232       {
233         text[0] = '.';
234         text[1] = '.';
235         text[2] = 0;
236       }
237     }
238     return 0;
239   }
240 
241   /*
242   // List-view in report-view in Windows 10 is slow (50+ ms) for page change.
243   // that code shows the time of page reload for items
244   // if you know how to improve the speed of list view refresh, notify 7-Zip developer
245 
246   // if (propID == 2000)
247   // if (propID == kpidName)
248   {
249     // debug column;
250     // DWORD dw = GetCpuTicks();
251     UInt64 dw = GetCpuTicks();
252     UInt64 deltaLast = dw - g_prev_tick;
253     #define conv_ticks(t) ((unsigned)((t) / 100000))
254     if (deltaLast > 1000u * 1000 * 1000)
255     {
256       UInt64 deltaFull = g_prev_tick - g_start_tick;
257       char s[128];
258       sprintf(s, "%d", conv_ticks(deltaFull));
259       OutputDebugStringA(s);
260       g_start_tick = dw;
261       g_NumGroups++;
262     }
263     g_prev_tick = dw;
264     UString u;
265     char s[128];
266     UInt64 deltaFull = dw - g_start_tick;
267     // for (int i = 0; i < 100000; i++)
268     sprintf(s, "%d %d %d-%d ", g_NumMessages, g_Num_SetItemText, g_NumGroups, conv_ticks(deltaFull));
269     // sprintf(s, "%d-%d ", g_NumGroups, conv_ticks(deltaFull));
270     u = s;
271     lstrcpyW(text, u.Ptr());
272     text += u.Len();
273 
274     // dw = GetCpuTicks();
275     // deltaFull = dw - g_prev_tick;
276     // sprintf(s, "-%d ", conv_ticks(deltaFull));
277     // u = s;
278     // lstrcpyW(text, u.Ptr());
279     // text += u.Len();
280 
281     if (propID != kpidName)
282       return 0;
283   }
284   */
285 
286 
287   if (property.IsRawProp)
288   {
289     const void *data;
290     UInt32 dataSize;
291     UInt32 propType;
292     RINOK(_folderRawProps->GetRawProp(realIndex, propID, &data, &dataSize, &propType));
293     unsigned limit = item.cchTextMax - 1;
294     if (dataSize == 0)
295     {
296       text[0] = 0;
297       return 0;
298     }
299 
300     if (propID == kpidNtReparse)
301     {
302       UString s;
303       ConvertNtReparseToString((const Byte *)data, dataSize, s);
304       if (!s.IsEmpty())
305       {
306         unsigned i;
307         for (i = 0; i < limit; i++)
308         {
309           wchar_t c = s[i];
310           if (c == 0)
311             break;
312           text[i] = c;
313         }
314         text[i] = 0;
315         return 0;
316       }
317     }
318     else if (propID == kpidNtSecure)
319     {
320       AString s;
321       ConvertNtSecureToString((const Byte *)data, dataSize, s);
322       if (!s.IsEmpty())
323       {
324         unsigned i;
325         for (i = 0; i < limit; i++)
326         {
327           wchar_t c = (Byte)s[i];
328           if (c == 0)
329             break;
330           text[i] = c;
331         }
332         text[i] = 0;
333         return 0;
334       }
335     }
336     {
337       const unsigned kMaxDataSize = 64;
338       if (dataSize > kMaxDataSize)
339       {
340         char temp[32];
341         MyStringCopy(temp, "data:");
342         ConvertUInt32ToString(dataSize, temp + 5);
343         unsigned i;
344         for (i = 0; i < limit; i++)
345         {
346           wchar_t c = (Byte)temp[i];
347           if (c == 0)
348             break;
349           text[i] = c;
350         }
351         text[i] = 0;
352       }
353       else
354       {
355         if (dataSize > limit)
356           dataSize = limit;
357         WCHAR *dest = text;
358         const bool needUpper = (dataSize <= 8)
359             && (propID == kpidCRC || propID == kpidChecksum);
360         for (UInt32 i = 0; i < dataSize; i++)
361         {
362           unsigned b = ((const Byte *)data)[i];
363           if (needUpper)
364           {
365             dest[0] = (WCHAR)GetHex_Upper((b >> 4) & 0xF);
366             dest[1] = (WCHAR)GetHex_Upper(b & 0xF);
367           }
368           else
369           {
370             dest[0] = (WCHAR)GetHex_Lower((b >> 4) & 0xF);
371             dest[1] = (WCHAR)GetHex_Lower(b & 0xF);
372           }
373           dest += 2;
374         }
375         *dest = 0;
376       }
377     }
378     return 0;
379   }
380   /*
381   {
382     NCOM::CPropVariant prop;
383     if (propID == kpidType)
384       string = GetFileType(index);
385     else
386     {
387       HRESULT result = m_ArchiveFolder->GetProperty(index, propID, &prop);
388       if (result != S_OK)
389       {
390         // PrintMessage("GetPropertyValue error");
391         return 0;
392       }
393       string = ConvertPropertyToString(prop, propID, false);
394     }
395   }
396   */
397   // const NFind::CFileInfo &aFileInfo = m_Files[index];
398 
399   NCOM::CPropVariant prop;
400   /*
401   bool needRead = true;
402   if (propID == kpidSize)
403   {
404     CComPtr<IFolderGetItemFullSize> getItemFullSize;
405     if (_folder.QueryInterface(&getItemFullSize) == S_OK)
406     {
407       if (getItemFullSize->GetItemFullSize(index, &prop) == S_OK)
408         needRead = false;
409     }
410   }
411   if (needRead)
412   */
413 
414   if (item.cchTextMax < 32)
415     return 0;
416 
417   if (propID == kpidName)
418   {
419     if (_folderGetItemName)
420     {
421       const wchar_t *name = NULL;
422       unsigned nameLen = 0;
423       _folderGetItemName->GetItemName(realIndex, &name, &nameLen);
424 
425       if (name)
426       {
427         unsigned dest = 0;
428         unsigned limit = item.cchTextMax - 1;
429 
430         for (unsigned i = 0; dest < limit;)
431         {
432           wchar_t c = name[i++];
433           if (c == 0)
434             break;
435           text[dest++] = c;
436 
437           if (c != ' ')
438           {
439             if (c != 0x202E) // RLO
440               continue;
441             text[(size_t)dest - 1] = '_';
442             continue;
443           }
444 
445           if (name[i] != ' ')
446             continue;
447 
448           unsigned t = 1;
449           for (; name[i + t] == ' '; t++);
450 
451           if (t >= 4 && dest + 4 < limit)
452           {
453             text[dest++] = '.';
454             text[dest++] = '.';
455             text[dest++] = '.';
456             text[dest++] = ' ';
457             i += t;
458           }
459         }
460 
461         if (dest == 0)
462           text[dest++]= '_';
463 
464         #ifdef _WIN32
465         else if (text[(size_t)dest - 1] == ' ')
466         {
467           if (dest < limit)
468             text[dest++] = SPACE_TERMINATOR_CHAR;
469           else
470             text[dest - 1] = SPACE_REPLACE_CHAR;
471         }
472         #endif
473 
474         text[dest] = 0;
475         // OutputDebugStringW(text);
476         return 0;
477       }
478     }
479   }
480 
481   if (propID == kpidPrefix)
482   {
483     if (_folderGetItemName)
484     {
485       const wchar_t *name = NULL;
486       unsigned nameLen = 0;
487       _folderGetItemName->GetItemPrefix(realIndex, &name, &nameLen);
488       if (name)
489       {
490         unsigned dest = 0;
491         unsigned limit = item.cchTextMax - 1;
492         for (unsigned i = 0; dest < limit;)
493         {
494           wchar_t c = name[i++];
495           if (c == 0)
496             break;
497           text[dest++] = c;
498         }
499         text[dest] = 0;
500         return 0;
501       }
502     }
503   }
504 
505   HRESULT res = _folder->GetProperty(realIndex, propID, &prop);
506 
507   if (res != S_OK)
508   {
509     MyStringCopy(text, L"Error: ");
510     // s = UString("Error: ") + HResultToMessage(res);
511   }
512   else if ((prop.vt == VT_UI8 || prop.vt == VT_UI4 || prop.vt == VT_UI2) && IsSizeProp(propID))
513   {
514     UInt64 v = 0;
515     ConvertPropVariantToUInt64(prop, v);
516     ConvertSizeToString(v, text);
517   }
518   else if (prop.vt == VT_BSTR)
519   {
520     unsigned limit = item.cchTextMax - 1;
521     const wchar_t *src = prop.bstrVal;
522     unsigned i;
523     for (i = 0; i < limit; i++)
524     {
525       wchar_t c = src[i];
526       if (c == 0) break;
527       if (c == 0xA) c = ' ';
528       if (c == 0xD) c = ' ';
529       text[i] = c;
530     }
531     text[i] = 0;
532   }
533   else
534   {
535     char temp[64];
536     ConvertPropertyToShortString2(temp, prop, propID, _timestampLevel);
537     unsigned i;
538     unsigned limit = item.cchTextMax - 1;
539     for (i = 0; i < limit; i++)
540     {
541       wchar_t c = (Byte)temp[i];
542       if (c == 0)
543         break;
544       text[i] = c;
545     }
546     text[i] = 0;
547   }
548 
549   return 0;
550 }
551 
552 #ifndef UNDER_CE
553 extern DWORD g_ComCtl32Version;
554 #endif
555 
OnItemChanged(NMLISTVIEW * item)556 void CPanel::OnItemChanged(NMLISTVIEW *item)
557 {
558   int index = (int)item->lParam;
559   if (index == kParentIndex)
560     return;
561   bool oldSelected = (item->uOldState & LVIS_SELECTED) != 0;
562   bool newSelected = (item->uNewState & LVIS_SELECTED) != 0;
563   // Don't change this code. It works only with such check
564   if (oldSelected != newSelected)
565     _selectedStatusVector[index] = newSelected;
566 }
567 
568 extern bool g_LVN_ITEMACTIVATE_Support;
569 
OnNotifyActivateItems()570 void CPanel::OnNotifyActivateItems()
571 {
572   bool alt = IsKeyDown(VK_MENU);
573   bool ctrl = IsKeyDown(VK_CONTROL);
574   bool shift = IsKeyDown(VK_SHIFT);
575   if (!shift && alt && !ctrl)
576     Properties();
577   else
578     OpenSelectedItems(!shift || alt || ctrl);
579 }
580 
OnNotifyList(LPNMHDR header,LRESULT & result)581 bool CPanel::OnNotifyList(LPNMHDR header, LRESULT &result)
582 {
583   switch (header->code)
584   {
585     case LVN_ITEMCHANGED:
586     {
587       if (_enableItemChangeNotify)
588       {
589         if (!_mySelectMode)
590           OnItemChanged((LPNMLISTVIEW)header);
591 
592         // Post_Refresh_StatusBar();
593         /* 9.26: we don't call Post_Refresh_StatusBar.
594            it was very slow if we select big number of files
595            and then clead slection by selecting just new file.
596            probably it called slow Refresh_StatusBar for each item deselection.
597            I hope Refresh_StatusBar still will be called for each key / mouse action.
598         */
599       }
600       return false;
601     }
602     /*
603 
604     case LVN_ODSTATECHANGED:
605       {
606       break;
607       }
608     */
609 
610     case LVN_GETDISPINFOW:
611     {
612       LV_DISPINFOW *dispInfo = (LV_DISPINFOW *)header;
613 
614       //is the sub-item information being requested?
615 
616       if ((dispInfo->item.mask & LVIF_TEXT) != 0 ||
617           (dispInfo->item.mask & LVIF_IMAGE) != 0)
618         SetItemText(dispInfo->item);
619       {
620         // 20.03:
621         result = 0;
622         return true;
623         // old 7-Zip:
624         // return false;
625       }
626     }
627     case LVN_KEYDOWN:
628     {
629       LPNMLVKEYDOWN keyDownInfo = LPNMLVKEYDOWN(header);
630       bool boolResult = OnKeyDown(keyDownInfo, result);
631       switch (keyDownInfo->wVKey)
632       {
633         case VK_CONTROL:
634         case VK_SHIFT:
635         case VK_MENU:
636           break;
637         default:
638           Post_Refresh_StatusBar();
639       }
640       return boolResult;
641     }
642 
643     case LVN_COLUMNCLICK:
644       OnColumnClick(LPNMLISTVIEW(header));
645       return false;
646 
647     case LVN_ITEMACTIVATE:
648       if (g_LVN_ITEMACTIVATE_Support)
649       {
650         OnNotifyActivateItems();
651         return false;
652       }
653       break;
654     case NM_DBLCLK:
655     case NM_RETURN:
656       if (!g_LVN_ITEMACTIVATE_Support)
657       {
658         OnNotifyActivateItems();
659         return false;
660       }
661       break;
662 
663     case NM_RCLICK:
664       Post_Refresh_StatusBar();
665       break;
666 
667     /*
668       return OnRightClick((LPNMITEMACTIVATE)header, result);
669     */
670       /*
671       case NM_CLICK:
672       SendRefreshStatusBarMessage();
673       return 0;
674 
675         // TODO : Handler default action...
676         return 0;
677         case LVN_ITEMCHANGED:
678         {
679         NMLISTVIEW *pNMLV = (NMLISTVIEW *) lpnmh;
680         SelChange(pNMLV);
681         return TRUE;
682         }
683         case NM_SETFOCUS:
684         return onSetFocus(NULL);
685         case NM_KILLFOCUS:
686         return onKillFocus(NULL);
687       */
688     case NM_CLICK:
689     {
690       // we need SetFocusToList, if we drag-select items from other panel.
691       SetFocusToList();
692       Post_Refresh_StatusBar();
693       if (_mySelectMode)
694         #ifndef UNDER_CE
695         if (g_ComCtl32Version >= MAKELONG(71, 4))
696         #endif
697           OnLeftClick((MY_NMLISTVIEW_NMITEMACTIVATE *)header);
698       return false;
699     }
700     case LVN_BEGINLABELEDITW:
701       result = OnBeginLabelEdit((LV_DISPINFOW *)header);
702       return true;
703     case LVN_ENDLABELEDITW:
704       result = OnEndLabelEdit((LV_DISPINFOW *)header);
705       return true;
706 
707     case NM_CUSTOMDRAW:
708     {
709       if (_mySelectMode || (_markDeletedItems && _thereAreDeletedItems))
710         return OnCustomDraw((LPNMLVCUSTOMDRAW)header, result);
711       break;
712     }
713     case LVN_BEGINDRAG:
714     {
715       OnDrag((LPNMLISTVIEW)header);
716       Post_Refresh_StatusBar();
717       break;
718     }
719     // case LVN_BEGINRDRAG:
720   }
721   return false;
722 }
723 
OnCustomDraw(LPNMLVCUSTOMDRAW lplvcd,LRESULT & result)724 bool CPanel::OnCustomDraw(LPNMLVCUSTOMDRAW lplvcd, LRESULT &result)
725 {
726   switch (lplvcd->nmcd.dwDrawStage)
727   {
728   case CDDS_PREPAINT :
729     result = CDRF_NOTIFYITEMDRAW;
730     return true;
731 
732   case CDDS_ITEMPREPAINT:
733     /*
734     SelectObject(lplvcd->nmcd.hdc,
735     GetFontForItem(lplvcd->nmcd.dwItemSpec,
736     lplvcd->nmcd.lItemlParam) );
737     lplvcd->clrText = GetColorForItem(lplvcd->nmcd.dwItemSpec,
738     lplvcd->nmcd.lItemlParam);
739     lplvcd->clrTextBk = GetBkColorForItem(lplvcd->nmcd.dwItemSpec,
740     lplvcd->nmcd.lItemlParam);
741     */
742     int realIndex = (int)lplvcd->nmcd.lItemlParam;
743     lplvcd->clrTextBk = _listView.GetBkColor();
744     if (_mySelectMode)
745     {
746       if (realIndex != kParentIndex && _selectedStatusVector[realIndex])
747        lplvcd->clrTextBk = RGB(255, 192, 192);
748     }
749 
750     if (_markDeletedItems && _thereAreDeletedItems)
751     {
752       if (IsItem_Deleted(realIndex))
753         lplvcd->clrText = RGB(255, 0, 0);
754     }
755     // lplvcd->clrText = RGB(0, 0, 0);
756     // result = CDRF_NEWFONT;
757     result = CDRF_NOTIFYITEMDRAW;
758     return true;
759 
760     // return false;
761     // return true;
762     /*
763     case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
764     if (lplvcd->iSubItem == 0)
765     {
766     // lplvcd->clrText = RGB(255, 0, 0);
767     lplvcd->clrTextBk = RGB(192, 192, 192);
768     }
769     else
770     {
771     lplvcd->clrText = RGB(0, 0, 0);
772     lplvcd->clrTextBk = RGB(255, 255, 255);
773     }
774     return true;
775     */
776 
777         /* At this point, you can change the background colors for the item
778         and any subitems and return CDRF_NEWFONT. If the list-view control
779         is in report mode, you can simply return CDRF_NOTIFYSUBITEMREDRAW
780         to customize the item's subitems individually */
781   }
782   return false;
783 }
784 
Refresh_StatusBar()785 void CPanel::Refresh_StatusBar()
786 {
787   /*
788   g_name_cnt++;
789   char s[256];
790   sprintf(s, "g_name_cnt = %8d", g_name_cnt);
791   OutputDebugStringA(s);
792   */
793   // DWORD dw = GetTickCount();
794 
795   CRecordVector<UInt32> indices;
796   GetOperatedItemIndices(indices);
797 
798   wchar_t temp[32];
799   ConvertUInt32ToString(indices.Size(), temp);
800   wcscat(temp, L" / ");
801   ConvertUInt32ToString(_selectedStatusVector.Size(), temp + wcslen(temp));
802 
803   // UString s1 = MyFormatNew(g_App.LangString_N_SELECTED_ITEMS, NumberToString(indices.Size()));
804   // UString s1 = MyFormatNew(IDS_N_SELECTED_ITEMS, NumberToString(indices.Size()));
805   _statusBar.SetText(0, MyFormatNew(g_App.LangString_N_SELECTED_ITEMS, temp));
806   // _statusBar.SetText(0, MyFormatNew(IDS_N_SELECTED_ITEMS, NumberToString(indices.Size())));
807 
808   wchar_t selectSizeString[32];
809   selectSizeString[0] = 0;
810 
811   if (indices.Size() > 0)
812   {
813     // for (unsigned ttt = 0; ttt < 1000; ttt++) {
814     UInt64 totalSize = 0;
815     FOR_VECTOR (i, indices)
816       totalSize += GetItemSize(indices[i]);
817     ConvertSizeToString(totalSize, selectSizeString);
818     // }
819   }
820   _statusBar.SetText(1, selectSizeString);
821 
822   int focusedItem = _listView.GetFocusedItem();
823   wchar_t sizeString[32];
824   sizeString[0] = 0;
825   wchar_t dateString[32];
826   dateString[0] = 0;
827   if (focusedItem >= 0 && _listView.GetSelectedCount() > 0)
828   {
829     int realIndex = GetRealItemIndex(focusedItem);
830     if (realIndex != kParentIndex)
831     {
832       ConvertSizeToString(GetItemSize(realIndex), sizeString);
833       NCOM::CPropVariant prop;
834       if (_folder->GetProperty(realIndex, kpidMTime, &prop) == S_OK)
835       {
836         char dateString2[32];
837         dateString2[0] = 0;
838         ConvertPropertyToShortString2(dateString2, prop, kpidMTime);
839         for (unsigned i = 0;; i++)
840         {
841           char c = dateString2[i];
842           dateString[i] = (Byte)c;
843           if (c == 0)
844             break;
845         }
846       }
847     }
848   }
849   _statusBar.SetText(2, sizeString);
850   _statusBar.SetText(3, dateString);
851 
852   // _statusBar.SetText(4, nameString);
853   // _statusBar2.SetText(1, MyFormatNew(L"{0} bytes", NumberToStringW(totalSize)));
854   // }
855   /*
856   dw = GetTickCount() - dw;
857   sprintf(s, "status = %8d ms", dw);
858   OutputDebugStringA(s);
859   */
860 }
861