| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051 | unit NortonLikeListView;interfaceuses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,  ComCtrls, ListViewColProperties, CommCtrl, Menus;type  TCustomNortonLikeListView = class;  TSelectMode = (smAll, smNone, smInvert);  TNortonLikeMode = (nlOn, nlOff, nlKeyboard);  TSelectMethod = (smNoneYet, smMouse, smKeyboard);  TCustomNortonLikeListView = class(TCustomListView)  private    { Private declarations }    FColProperties: TCustomListViewColProperties;    FDontSelectItem: Boolean;    FDontUnSelectItem: Boolean;    FSelCount: Integer;    FNortonLike: TNortonLikeMode;    FLastDeletedItem: TListItem; // aby sme nepocitali smazany item 2x    FFocusingItem: Boolean;    FManageSelection: Boolean;    FForceUpdateOnItemUnfocus: Boolean;    FFirstSelected: Integer;    FLastSelected: Integer;    FFocused: TDateTime;    FIgnoreSetFocusFrom: THandle;    FSelectingImplicitly: Boolean;    FAnyAndAllSelectedImplicitly: Boolean;    FLButtonDownShiftState: TShiftState;    FLButtonDownPos: TPoint;    FLastSelectMethod: TSelectMethod;    procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;    procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;    procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;    procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;    procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;    procedure WMChar(var Message: TWMChar); message WM_CHAR;    procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY;    procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;    procedure LVMEditLabel(var Message: TMessage); message LVM_EDITLABEL;    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;    procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY;    function GetMarkedCount: Integer;    function GetMarkedFile: TListItem;    procedure ItemSelected(Item: TListItem; Index: Integer);    procedure ItemUnselected(Item: TListItem; Index: Integer);    procedure SelectAll(Mode: TSelectMode; Exclude: TListItem); reintroduce; overload;  protected    { Protected declarations }    FClearingItems: Boolean;    FUpdatingSelection: Integer;    FNextCharToIgnore: Word;    FHeaderHandle: HWND;    procedure CreateWnd; override;    procedure DestroyWnd; override;    procedure BeginSelectionUpdate; virtual;    procedure EndSelectionUpdate; virtual;    function CanChangeSelection(Item: TListItem; Select: Boolean): Boolean; virtual;    procedure ClearItems; virtual;    procedure ItemsReordered;    procedure Delete(Item: TListItem); override;    function ExCanChange(Item: TListItem; Change: Integer;      NewState, OldState: Word): Boolean; dynamic;    procedure InsertItem(Item: TListItem); override;    function NewColProperties: TCustomListViewColProperties; virtual; abstract;    procedure FocusSomething; virtual;    function EnableDragOnClick: Boolean; virtual;    function GetItemFromHItem(const Item: TLVItem): TListItem;    function GetValid: Boolean; virtual;    function GetSelCount: Integer; override;    procedure DDBeforeDrag;    function CanEdit(Item: TListItem): Boolean; override;    function GetPopupMenu: TPopupMenu; override;    procedure ChangeScale(M, D: Integer); override;  public    { Public declarations }    constructor Create(AOwner: TComponent); override;    destructor Destroy; override;    function ClosestUnselected(Item: TListItem): TListItem;    procedure SelectAll(Mode: TSelectMode); reintroduce; overload;    procedure SelectCurrentItem(FocusNext: Boolean);    function GetNextItem(StartItem: TListItem; Direction: TSearchDirection;      States: TItemStates): TListItem;    procedure MakeProgressVisible(Item: TListItem);    procedure FocusItem(Item: TListItem);    property ColProperties: TCustomListViewColProperties read FColProperties write FColProperties stored False;    property MultiSelect default True;    property NortonLike: TNortonLikeMode read FNortonLike write FNortonLike default nlOn;    property MarkedCount: Integer read GetMarkedCount;    property MarkedFile: TListItem read GetMarkedFile;    property Valid: Boolean read GetValid;    property LastSelectMethod: TSelectMethod read FLastSelectMethod;  end;implementationuses  PasTools, Types;  { TCustomNortonLikeListView }constructor TCustomNortonLikeListView.Create(AOwner: TComponent);begin  inherited Create(AOwner);  FSelCount := 0;  FFirstSelected := -1;  FLastSelected := -1;  FClearingItems := False;  MultiSelect := True;  FDontSelectItem := False;  FDontUnSelectItem := False;  FNortonLike := nlOn;  FColProperties := NewColProperties;  FLastDeletedItem := nil;  FUpdatingSelection := 0;  FFocusingItem := False;  FLastSelectMethod := smNoneYet;  // On Windows Vista, native GetNextItem for selection stops working once we  // disallow deselecting any item (see ExCanChange).  // So we need to manage selection state ourselves  // cannot use Win32MajorVersion as it is affected by compatibility mode and  // the bug is present even in compatibility mode  FManageSelection := IsVistaHard;  FFocused := 0;  FIgnoreSetFocusFrom := INVALID_HANDLE_VALUE;  // On Windows 7 we have to force item update when it looses focus,  // otherwise some remnants of focus rectangle remain  // Doing the same on WinXP makes list view down from the item flicker,  // so we avoid this there.  // Not sure about Vista  FForceUpdateOnItemUnfocus := IsWin7;  FNextCharToIgnore := 0;end;destructor TCustomNortonLikeListView.Destroy;begin  FColProperties.Free;  inherited;end;procedure TCustomNortonLikeListView.ItemSelected(Item: TListItem; Index: Integer);begin  Inc(FSelCount);  if FSelectingImplicitly and (FSelCount = 1) then  begin    FAnyAndAllSelectedImplicitly := True;  end    else  if not FSelectingImplicitly then  begin    FAnyAndAllSelectedImplicitly := False;  end;  if FManageSelection then  begin    if Index < 0 then      Index := Item.Index;    if FSelCount = 1 then    begin      Assert(FFirstSelected < 0);      FFirstSelected := Index;      Assert(FLastSelected < 0);      FLastSelected := Index;    end      else    begin      // if reference is not assigned, do not assign it as we      // cannot be sure that the item is actually first/last      if (FFirstSelected >= 0) and (Index < FFirstSelected) then        FFirstSelected := Index;      if (FLastSelected >= 0) and (Index > FLastSelected) then        FLastSelected := Index;    end;  end;end;procedure TCustomNortonLikeListView.ItemUnselected(Item: TListItem; Index: Integer);begin  Dec(FSelCount);  if (FSelCount = 0) or (not FSelectingImplicitly) then  begin    FAnyAndAllSelectedImplicitly := False;  end;  if FManageSelection then  begin    if Index < 0 then      Index := Item.Index;    if FFirstSelected = Index then    begin      if FSelCount = 1 then        FFirstSelected := FLastSelected // may be -1      else        FFirstSelected := -1;    end;    if FLastSelected = Index then    begin      if FSelCount = 1 then        FLastSelected := FFirstSelected // may be -1      else        FLastSelected := -1;    end;  end;end;procedure TCustomNortonLikeListView.Delete(Item: TListItem);var  Index: Integer;begin  if (FLastDeletedItem <> Item) and (not FClearingItems) then  begin    Index := Item.Index;    if Item.Selected then      ItemUnselected(Item, Index);    if FManageSelection then    begin      if (FLastSelected >= 0) and (Index <= FLastSelected) then        Dec(FLastSelected);      if (FFirstSelected >= 0) and (Index <= FFirstSelected) then        Dec(FFirstSelected);    end;  end;  FLastDeletedItem := Item;  inherited;  FLastDeletedItem := nil;end;function TCustomNortonLikeListView.ExCanChange(Item: TListItem; Change: Integer;  NewState, OldState: Word): Boolean;begin  Assert(Assigned(Item));  Result := True;  if (Change = LVIF_STATE) and     ((((OldState and LVIS_SELECTED) < (NewState and LVIS_SELECTED)) and       (FDontSelectItem or (not CanChangeSelection(Item, True)))) or      (((OldState and LVIS_SELECTED) > (NewState and LVIS_SELECTED)) and       (FDontUnSelectItem or (not CanChangeSelection(Item, False))))) then  begin    if (OldState or LVIS_SELECTED) <> (NewState or LVIS_SELECTED) then    begin      ListView_SetItemState(Handle, Item.Index, NewState,        (NewState or OldState) - LVIS_SELECTED);    end;    Result := False;  end;end;function TCustomNortonLikeListView.CanChangeSelection(Item: TListItem;  Select: Boolean): Boolean;begin  Result := True;end;procedure TCustomNortonLikeListView.ClearItems;begin  Items.BeginUpdate;  try    FClearingItems := True;    Items.Clear;  finally    FSelCount := 0;    if FManageSelection then    begin      FFirstSelected := -1;      FLastSelected := -1;    end;    FClearingItems := False;    Items.EndUpdate;  end;end; { ClearItems }procedure TCustomNortonLikeListView.ItemsReordered;begin  if FManageSelection then  begin    FFirstSelected := -1;    FLastSelected := -1;  end;end;function TCustomNortonLikeListView.ClosestUnselected(Item: TListItem): TListItem;var  Index: Integer;begin  if Assigned(Item) and (Item.Selected or ((NortonLike <> nlOff) and (SelCount = 0))) then  begin    Index := Item.Index + 1;    while (Index < Items.Count) and Items[Index].Selected do Inc(Index);    if (Index >= Items.Count) or Items[Index].Selected then    begin      Index := Item.Index - 1;      while (Index >= 0) and Items[Index].Selected do Dec(Index);    end;    if (Index >= 0) and (Index < Items.Count) and (not Items[Index].Selected) then      Result := Items[Index]    else      Result := nil;  end    else Result := Item;end;function TCustomNortonLikeListView.GetPopupMenu: TPopupMenu;begin  // While editing pretend that we do not have a popup menu.  // Otherwise Ctrl+V is swallowed by the TWinControl.CNKeyDown,  // when it finds out (TWinControl.IsMenuKey) that there's a command with Ctrl+V shortcut in the list view context menu  // (the "paste" file action)  if IsEditing then  begin    Result := nil;  end    else  begin    Result := inherited;  end;end;procedure TCustomNortonLikeListView.WMNotify(var Message: TWMNotify);var  HDNotify: PHDNotify;begin  if (FHeaderHandle <> 0) and (Message.NMHdr^.hWndFrom = FHeaderHandle) then  begin    HDNotify := PHDNotify(Message.NMHdr);    // Disallow resizing of "invisible" (width=0) columns.    // (We probably get only Unicode versions of the messages here as    // controls are created as Unicode by VCL)    case HDNotify.Hdr.code of      HDN_BEGINTRACKA, HDN_TRACKA, HDN_BEGINTRACKW, HDN_TRACKW:        if not ColProperties.Visible[HDNotify.Item] then        begin          Message.Result := 1;          Exit;        end;      // We won't get here when user tries to resize the column by mouse,      // as that's prevented above.      // But we get here when other methods are used      // (the only we know about atm is Ctrl-+ shortcut)      // We are getting this notification also when control is being setup,      // with mask including also other fields, not just HDI_WIDTH.      // While it does not seem to hurt to swallow even those messages,      // not sure it's good thing to do, so we swallow width-only messages only.      // That's why there's "= HDI_WIDTH" not "and HDI_WIDTH <> 0".      HDN_ITEMCHANGINGA, HDN_ITEMCHANGINGW:        if (HDNotify.PItem.Mask = HDI_WIDTH) and           (HDNotify.PItem.cxy <> 0) and           (not ColProperties.Visible[HDNotify.Item]) then        begin          Message.Result := 1;          Exit;        end;    end;  end;  inherited;end;procedure TCustomNortonLikeListView.DDBeforeDrag;begin  FDontSelectItem := False;  FDontUnSelectItem := False;end;procedure TCustomNortonLikeListView.CNNotify(var Message: TWMNotify);var  Item: TListItem;begin  with Message do    case NMHdr^.code of      LVN_ITEMCHANGING:        with PNMListView(NMHdr)^ do        begin          Item := Items[iItem];          if Valid and (not FClearingItems) and (Item <> FLastDeletedItem) and             ((not CanChange(Item, uChanged)) or              (not ExCanChange(Item, uChanged, uNewState, uOldState)))              then          begin            Result := 1;          end;        end;      LVN_ITEMCHANGED:        begin          with PNMListView(NMHdr)^ do          begin            Item := Items[iItem];            if Valid and (not FClearingItems) and               (uChanged = LVIF_STATE) and (Item <> FLastDeletedItem) then            begin              if FForceUpdateOnItemUnfocus and                 (NortonLike <> nlOff) and                 ((uOldState and LVIS_FOCUSED) > (uNewState and LVIS_FOCUSED)) then              begin                // force update, otherwise some remnants of focus rectangle remain                Item.Update;              end;              if (uOldState and LVIS_SELECTED) <> (uNewState and LVIS_SELECTED) then              begin                if (uOldState and LVIS_SELECTED) <> 0 then                begin                  ItemUnselected(Item, iItem);                end                  else                begin                  ItemSelected(Item, iItem);                end;              end;            end;          end;          inherited;        end;      LVN_ENDLABELEDIT:        begin          FIgnoreSetFocusFrom := ListView_GetEditControl(Handle);          inherited;        end;      else        begin          inherited;        end;    end;end;procedure TCustomNortonLikeListView.SelectCurrentItem(FocusNext: Boolean);var  Item: TListItem;begin  Item := ItemFocused;  if Item = nil then Item := Items[0];  Item.Selected := not Item.Selected;  if FocusNext then  begin    SendMessage(Handle, WM_KEYDOWN, VK_DOWN, LongInt(0));  end;end;procedure TCustomNortonLikeListView.WMKeyDown(var Message: TWMKeyDown);var  PLastSelectMethod: TSelectMethod;  PDontUnSelectItem: Boolean;  PDontSelectItem: Boolean;begin  FNextCharToIgnore := 0;  if (NortonLike <> nlOff) and (Message.CharCode = VK_INSERT) then  begin    if Items.Count > 0 then    begin      PLastSelectMethod := FLastSelectMethod;      FLastSelectMethod := smKeyboard;      try        SelectCurrentItem(True);      finally        FLastSelectMethod := PLastSelectMethod;      end;      Message.Result := 1;    end;  end    else  if Message.CharCode = VK_ADD then  begin    FNextCharToIgnore := Word('+');    inherited;  end    else  if Message.CharCode = VK_SUBTRACT then  begin    FNextCharToIgnore := Word('-');    inherited;  end    else  if Message.CharCode = VK_MULTIPLY then  begin    FNextCharToIgnore := Word('*');    inherited;  end    else  if (NortonLike <> nlOff) and (Message.CharCode in [VK_LEFT, VK_RIGHT]) and     (ViewStyle = vsReport) and     ((GetWindowLong(Handle, GWL_STYLE) and WS_HSCROLL) = 0) then  begin    if Items.Count > 0 then    begin      // do not focus item directly to make later selecting work      if Message.CharCode = VK_LEFT then        SendMessage(Handle, WM_KEYDOWN, VK_HOME, LongInt(0))      else        SendMessage(Handle, WM_KEYDOWN, VK_END, LongInt(0));    end;    Message.Result := 1;  end    else  if (NortonLike <> nlOff) and (Message.CharCode = VK_SPACE) and     ((KeyDataToShiftState(Message.KeyData) * [ssCtrl]) <> []) then  begin    // prevent Ctrl+Space landing in else branch below,    // this can safely get processed by default handler as Ctrl+Space    // toggles only focused item, not affecting others    PLastSelectMethod := FLastSelectMethod;    FLastSelectMethod := smKeyboard;    try      inherited;    finally      FLastSelectMethod := PLastSelectMethod;    end;  end    else  if (Message.CharCode in [VK_SPACE, VK_PRIOR, VK_NEXT, VK_END, VK_HOME, VK_LEFT,    VK_UP, VK_RIGHT, VK_DOWN, VK_SELECT]) then  begin    PLastSelectMethod := FLastSelectMethod;    PDontSelectItem := FDontSelectItem;    PDontUnSelectItem := FDontUnSelectItem;    FLastSelectMethod := smKeyboard;    FDontSelectItem := FDontSelectItem or      ((NortonLike <> nlOff) and       ((KeyDataToShiftState(Message.KeyData) * [ssShift]) = []));    // Note that Space (selecting toggling) is processed by default handler for WM_CHAR,    // otherwise the below condition would prevent unselection    FDontUnSelectItem :=      FDontUnSelectItem or      (NortonLike = nlOn) or      ((NortonLike = nlKeyboard) and (not FAnyAndAllSelectedImplicitly));    try      inherited;    finally      FDontSelectItem := PDontSelectItem;      FDontUnSelectItem := PDontUnSelectItem;      FLastSelectMethod := PLastSelectMethod;    end;  end    else inherited;end;procedure TCustomNortonLikeListView.WMSysCommand(var Message: TWMSysCommand);begin  // Ugly workaround to avoid Windows beeping when Alt+Grey +/- are pressed  // (for (Us)Select File with Same Ext commands)  if (Message.CmdType = SC_KEYMENU) and     ((Message.Key = Word('+')) or (Message.Key = Word('-'))) then  begin    Message.Result := 1;  end    else inherited;end;procedure TCustomNortonLikeListView.WMChar(var Message: TWMChar);var  PLastSelectMethod: TSelectMethod;  PDontUnSelectItem: Boolean;  PDontSelectItem: Boolean;begin  if Message.CharCode = FNextCharToIgnore then  begin    // ugly fix to avoid Windows beeping when these keys are processed by    // WMKeyDown instead of here (WMChar)    Message.Result := 1;  end    else  if (NortonLike <> nlOff) and (Message.CharCode = Byte(' ')) then  begin    if (GetKeyState(VK_CONTROL) >= 0) then    begin      // If not handled in TCustomScpExplorerForm::DirViewKeyPress      if not DoKeyPress(Message) then      begin        if Assigned(ItemFocused) then          ItemFocused.Selected := not ItemFocused.Selected;      end;    end      else inherited;  end    else  begin    PLastSelectMethod := FLastSelectMethod;    PDontSelectItem := FDontSelectItem;    PDontUnSelectItem := FDontUnSelectItem;    FDontSelectItem := FDontSelectItem or (NortonLike <> nlOff);    FLastSelectMethod := smKeyboard;    FDontUnSelectItem :=      FDontUnSelectItem or      (NortonLike = nlOn) or      ((NortonLike = nlKeyboard) and (not FAnyAndAllSelectedImplicitly));    try      inherited;    finally      FLastSelectMethod := PLastSelectMethod;      FDontSelectItem := PDontSelectItem;      FDontUnSelectItem := PDontUnSelectItem;    end;  end;  FNextCharToIgnore := 0;end;procedure TCustomNortonLikeListView.FocusSomething;begin  if Valid and (Items.Count > 0) and not Assigned(ItemFocused) then  begin    if (NortonLike <> nlOff) then SendMessage(Handle, WM_KEYDOWN, VK_DOWN, LongInt(0));    if not Assigned(ItemFocused) then      ItemFocused := Items[0];  end;  if Assigned(ItemFocused) then    ItemFocused.MakeVisible(False);end;function TCustomNortonLikeListView.EnableDragOnClick: Boolean;begin  Result := (not FFocusingItem);end;procedure TCustomNortonLikeListView.FocusItem(Item: TListItem);var  P: TPoint;  PLastSelectMethod: TSelectMethod;  PDontUnSelectItem: Boolean;  PDontSelectItem: Boolean;  WParam: UINT_PTR;  LParam: INT_PTR;begin  // This whole is replacement for mere ItemFocused := Item  // because that does not reset some internal focused pointer,  // causing subsequent Shift-Click selects range from the first item,  // not from focused item.  Item.MakeVisible(False);  Assert(Focused);  if Focused then  begin    P := Item.GetPosition;    PLastSelectMethod := FLastSelectMethod;    PDontSelectItem := FDontSelectItem;    PDontUnSelectItem := FDontUnSelectItem;    FLastSelectMethod := smNoneYet;    FDontSelectItem := True;    FDontUnSelectItem := True;    FFocusingItem := True;    try      // HACK      // WM_LBUTTONDOWN enters loop, waiting for WM_LBUTTONUP,      // so we have to post it in advance to break the loop immediately      // Without MK_CONTROL, if there are more items selected,      // they won't get unselected on subsequent focus change      // (with explorer-style selection).      // And it also makes the click the least obtrusive, affecting the focused      // file only.      WParam := MK_LBUTTON or MK_CONTROL;      LParam := MAKELPARAM(P.X, P.Y);      PostMessage(Handle, WM_LBUTTONUP, WParam, LParam);      SendMessage(Handle, WM_LBUTTONDOWN, WParam, LParam);    finally      FFocusingItem := False;      FLastSelectMethod := PLastSelectMethod;      FDontSelectItem := PDontSelectItem;      FDontUnSelectItem := PDontUnSelectItem;    end;  end;  if ItemFocused <> Item then    ItemFocused := Item;end;procedure TCustomNortonLikeListView.SelectAll(Mode: TSelectMode; Exclude: TListItem);var  Index: Integer;  Item: TListItem;begin  BeginSelectionUpdate;  try    for Index := 0 to Items.Count - 1 do    begin      Item := Items[Index];      if Item <> Exclude then      begin        case Mode of          smAll: Item.Selected := True;          smNone: Item.Selected := False;          smInvert: Item.Selected := not Item.Selected;        end;      end;    end;  finally    EndSelectionUpdate;  end;end;procedure TCustomNortonLikeListView.SelectAll(Mode: TSelectMode);begin  SelectAll(Mode, nil);end;procedure TCustomNortonLikeListView.WMLButtonDown(var Message: TWMLButtonDown);var  PLastSelectMethod: TSelectMethod;  PDontUnSelectItem: Boolean;  PDontSelectItem: Boolean;  PSelectingImplicitly: Boolean;  SelectingImplicitly: Boolean;  Shift: TShiftState;  Item: TListItem;begin  Shift := KeysToShiftState(Message.Keys);  PLastSelectMethod := FLastSelectMethod;  PDontSelectItem := FDontSelectItem;  PDontUnSelectItem := FDontUnSelectItem;  PSelectingImplicitly := FSelectingImplicitly;  FLastSelectMethod := smMouse;  FDontSelectItem := FDontSelectItem or ((NortonLike = nlOn) and ((Shift * [ssCtrl, ssShift]) = []));  FDontUnSelectItem := FDontUnSelectItem or ((NortonLike = nlOn) and ((Shift * [ssCtrl]) = []));  SelectingImplicitly := ((Shift * [ssCtrl, ssShift]) = []);  if SelectingImplicitly and (NortonLike = nlKeyboard) then  begin    // in general, when clicking, we clear selection only after mouse button is released,    // from within WMLButtonUp, so we know we are not starting dragging,    // so we do not want to clear the selection.    // on the other hand, when clicking outside of the selection,    // we want to explicitly clear the selection, no matter what    Item := GetItemAt(Message.XPos, Message.YPos);    if (Item = nil) or (not Item.Selected) then      SelectAll(smNone);  end;  FSelectingImplicitly := FSelectingImplicitly or SelectingImplicitly;  FLButtonDownShiftState := Shift;  FLButtonDownPos := Point(Message.XPos, Message.YPos);  try    inherited;  finally    FLastSelectMethod := PLastSelectMethod;    FDontSelectItem := PDontSelectItem;    FDontUnSelectItem := PDontUnSelectItem;    FSelectingImplicitly := PSelectingImplicitly;  end;end;procedure TCustomNortonLikeListView.WMRButtonDown(var Message: TWMRButtonDown);var  PLastSelectMethod: TSelectMethod;  PDontUnSelectItem: Boolean;  PDontSelectItem: Boolean;  PSelectingImplicitly: Boolean;  SelectingImplicitly: Boolean;  Shift: TShiftState;begin  Shift := KeysToShiftState(Message.Keys);  PLastSelectMethod := FLastSelectMethod;  PDontSelectItem := FDontSelectItem;  PDontUnSelectItem := FDontUnSelectItem;  PSelectingImplicitly := FSelectingImplicitly;  FLastSelectMethod := smMouse;  FDontSelectItem := FDontSelectItem or (NortonLike = nlOn);  FDontUnSelectItem := FDontUnSelectItem or (NortonLike = nlOn);  SelectingImplicitly := ((Shift * [ssCtrl, ssShift]) = []);  // TODO unselect all when clicking outside of selection  // (is not done automatically when focused item is not selected)  FSelectingImplicitly := FSelectingImplicitly or SelectingImplicitly;  try    inherited;  finally    FLastSelectMethod := PLastSelectMethod;    FDontSelectItem := PDontSelectItem;    FDontUnSelectItem := PDontUnSelectItem;    FSelectingImplicitly := PSelectingImplicitly;  end;end;procedure TCustomNortonLikeListView.WMLButtonUp(var Message: TWMLButtonUp);var  SelectingImplicitly: Boolean;  Shift: TShiftState;begin  // Workaround  // For some reason Message.Keys is always 0 here,  // so we use shift state from the LButtonDown as a workaround  Shift := KeysToShiftState(Message.Keys);  SelectingImplicitly :=    ((Shift * [ssCtrl, ssShift]) = []) and    ((FLButtonDownShiftState * [ssCtrl, ssShift]) = []);  if SelectingImplicitly and (csClicked in ControlState) and     (Abs(FLButtonDownPos.X - Message.XPos) <= 4) and     (Abs(FLButtonDownPos.Y - Message.YPos) <= 4) then  begin    SelectAll(smNone, ItemFocused);    // Because condition in ItemSelected is not triggered as we first select    // the new item and then unselect the previous.    // This probably means that we can get rid of the code in ItemSelected.    FAnyAndAllSelectedImplicitly := True;  end;  inherited;end;function TCustomNortonLikeListView.GetMarkedFile: TListItem;begin  if Assigned(Selected) then Result := Selected    else  if Assigned(ItemFocused) and (NortonLike <> nlOff) then Result := ItemFocused    else Result := nil;end;function TCustomNortonLikeListView.GetNextItem(StartItem: TListItem;  Direction: TSearchDirection; States: TItemStates): TListItem;var  Start, Index, First, Last: Integer;begin  if not FManageSelection then  begin    Result := inherited GetNextItem(StartItem, Direction, States);  end    else  begin    Assert(Direction = sdAll);    if States = [isSelected] then    begin      if FSelCount = 0 then      begin        Result := nil      end        else      if (not Assigned(StartItem)) and (FFirstSelected >= 0) then      begin        Result := Items[FFirstSelected]      end        else      begin        if Assigned(StartItem) then          Start := StartItem.Index        else          Start := -1;        if (FFirstSelected >= 0) and (Start < FFirstSelected) then          First := FFirstSelected        else          First := Start + 1;        if FLastSelected >= 0 then          Last := FLastSelected        else          Last := Items.Count - 1;        if Start > Last then        begin          Result := nil;        end          else        begin          Index := First;          while (Index <= Last) and (not (Items[Index].Selected)) do          begin            Inc(Index);          end;          if Index > Last then          begin            Result := nil;            if Assigned(StartItem) and StartItem.Selected then            begin              Assert((FLastSelected < 0) or (FLastSelected = Start));              FLastSelected := Start;            end;          end            else          begin            Result := Items[Index];            Assert(Result.Selected);            if not Assigned(StartItem) then            begin              Assert((FFirstSelected < 0) or (FFirstSelected = Index));              FFirstSelected := Index;            end;          end;        end;      end;    end      else    if States = [isCut] then    begin      Result := inherited GetNextItem(StartItem, Direction, States);    end      else    if States = [] then    begin      if Assigned(StartItem) then        Start := StartItem.Index      else        Start := -1;      Inc(Start);      if Start < Items.Count then        Result := Items[Start]      else        Result := nil;    end      else    begin      Assert(False);      Result := nil;    end;  end;end;function TCustomNortonLikeListView.GetSelCount: Integer;begin  Result := FSelCount;end;procedure TCustomNortonLikeListView.InsertItem(Item: TListItem);begin  inherited;  if Item.Selected then    ItemSelected(Item, -1);end;function TCustomNortonLikeListView.GetItemFromHItem(const Item: TLVItem): TListItem;begin  with Item do    if (state and LVIF_PARAM) <> 0 then Result := Pointer(lParam)      else Result := Items[iItem];end;function TCustomNortonLikeListView.GetMarkedCount: Integer;begin  if (SelCount > 0) or (NortonLike = nlOff) then Result := SelCount    else  if Assigned(ItemFocused) then Result := 1    else Result := 0;end;function TCustomNortonLikeListView.GetValid: Boolean;begin  // Note that TCustomDirView::GetValid don't inherit  // this method because of optimalization  Result := (not (csDestroying in ComponentState)) and (not FClearingItems);end;procedure TCustomNortonLikeListView.BeginSelectionUpdate;begin  // Higher value is probably some nesting error  Assert(FUpdatingSelection in [0..4]);  Inc(FUpdatingSelection);end; { BeginUpdatingSelection }procedure TCustomNortonLikeListView.EndSelectionUpdate;begin  Assert(FUpdatingSelection > 0);  Dec(FUpdatingSelection);end; { EndUpdatingSelection }procedure TCustomNortonLikeListView.CreateWnd;begin  try    Assert(ColProperties <> nil);    inherited;    FHeaderHandle := ListView_GetHeader(Handle);    ColProperties.ListViewWndCreated;  finally  end;end;procedure TCustomNortonLikeListView.DestroyWnd;begin  ColProperties.ListViewWndDestroying;  try    inherited;  finally    ColProperties.ListViewWndDestroyed;  end;end;procedure TCustomNortonLikeListView.LVMEditLabel(var Message: TMessage);begin  // explicitly requesting editing (e.g. F2),  // so we do not care anymore when the view was focused  FFocused := 0;  inherited;end;function TCustomNortonLikeListView.CanEdit(Item: TListItem): Boolean;var  N: TDateTime;  Delta: Double;begin  N := Now;  Result := inherited CanEdit(Item);  if Result and (FFocused > 0) then  begin    Delta := N - FFocused;    // it takes little more than 500ms to trigger editing after click    Result := Delta > (750.0/MSecsPerDay);  end;  FFocused := 0;end;procedure TCustomNortonLikeListView.WMSetFocus(var Message: TWMSetFocus);begin  inherited;  if Message.FocusedWnd <> FIgnoreSetFocusFrom then    FFocused := Now;end;procedure TCustomNortonLikeListView.CMWantSpecialKey(var Message: TCMWantSpecialKey);begin  inherited;  if IsEditing and (Message.CharCode = VK_TAB) then    Message.Result := 1;end;procedure TCustomNortonLikeListView.MakeProgressVisible(Item: TListItem);var  DisplayRect: TRect;begin  if ViewStyle = vsReport then  begin    DisplayRect := Item.DisplayRect(drBounds);    if DisplayRect.Bottom > ClientHeight then    begin      Scroll(0, Item.Top - TopItem.Top);    end;  end;  Item.MakeVisible(False);end;procedure TCustomNortonLikeListView.ChangeScale(M, D: Integer);begin  if M <> D then  begin    // When font is scaled, while the control is being re-created, previous font is restored once    // read from the persistence data in TCustomListView.CreateWnd.    // Requiring handle, makes sure the re-create phase is closed.    // We could limit impact by checking ControlHasRecreationPersistenceData,    // but for now, we actually prefer larger impact to test this change better.    HandleNeeded;  end;  inherited;end;end.
 |