home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 21 / IOPROG_21.ISO / SOFT / DDLB.ZIP / DragDropListBox.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-11-19  |  26.1 KB  |  770 lines

  1. {***************************************************************
  2.  *
  3.  * Unit Name: DragDropListBox
  4.  * Version  : 2.8
  5.  * Date     : 1998/11/18
  6.  * Purpose  : Enhanced Listbox that show the bitmap items, allow 
  7.  *              drag-drop operation to change the order of Listbox 
  8.  *              items and move items between Listboxes. 
  9.  * Author   : Vincent Huang
  10.  * E-Mail   : mac@lion.syscom.com.tw
  11.  * History  :
  12.  *        1.5 First release.
  13.  *        2.0 Allow drag-drop among multiple TDragDropListBox.
  14.  *            Property 'EnableDragDrop' is renamed to 'DragDropEnabled'.
  15.  *            Property 'DragDropInsert' added.
  16.  *            Property 'DragDropDelete' added.
  17.  *        2.5 Implement the bimap cursor using TImageList.BeginDrag.
  18.  *        2.6 Fix bug when the drag out another DragDropListBox.
  19.  *        2.7 Utilize TDragControlObject to handle bitmap cursor.
  20.  *            Miltiple selection support.
  21.  *            Event OnGetDragImageList added, allow user to specify the
  22.  *              custommized drag image.
  23.  *            Method InsertItems added, allow trigger items moving between
  24.  *              listboxes via program.
  25.  *        2.8 Add support for showing bitmap in front of ListBox Item using
  26.  *              TImageList. Use it just like the way you use TTreeview.
  27.  *            PS: the feature is added because one Delphi component web site
  28.  *                  add this feature to version 1.5, though it's never
  29.  *                  supported. Now, you really got it. :)
  30.  *                If there is no feature request, there will be only new
  31.  *                  bug-fixed version from now on. To get new copy, you may
  32.  *                  check the Delphi Super Page. If you e-mail me, I'll add
  33.  *                  you to my TDragDropListBox mailing-list. :)
  34.  * Comments : For use with Delphi 2,3,4 and C++ Builder 3.
  35.  *            This component is absolutely free. if you use this
  36.  *              component, please e-mail me with any kind of
  37.  *              comments about this component. I'll inform you about
  38.  *              the new version info when avaiable.
  39.  * Usage    : set the 'DragDropEnable' to True and it works for you.
  40.  *            If DragDropInsert is False, the new item won't be added
  41.  *              and the insert position line won't be drawn.
  42.  *            If DragDropDelete is False, the dragged item won't be
  43.  *              deleted after dropped.
  44.  *            To show the dragging image on other control, you have to
  45.  *              add the following line in FormShow or somewhere else before
  46.  *              the drag event happens.
  47.  *
  48.  *              CONTROL_NAME.ControlStyle :=
  49.  *                      CONTROL_NAME.ControlStyle + [csDisplayDragImage];
  50.  *
  51.  * Bugs     : Most of the time, this component works fine. In one of
  52.  *              my project, it crash the Delphi 4.02 in debug mode
  53.  *              when the drag start but still works fine at run-time
  54.  *              (non-debug mode). It crashed at line 8005 of Controls.pas
  55.  *              while calling ImageList_DragEnter. :( I'm almost sure it's
  56.  *              a bug of either Microsoft's comctl32.dll or Delphi 4.02,
  57.  *              not mine. :)
  58.  * PS       : This component is to my beloved Daphne, though for
  59.  *              some reason we can't be together.
  60.  *
  61.  ****************************************************************}
  62.  
  63. unit DragDropListBox;
  64.  
  65. interface
  66.  
  67. uses
  68.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  69.   StdCtrls, extctrls
  70. {$IFDEF VER120}
  71.   ,imgList
  72. {$ENDIF}
  73.   ;
  74. type
  75.   TScrollDriection = (sdNone, sdUp, sdDown); // automatically scolling direction
  76.  
  77. {$IFDEF VER120}
  78.   TGetDragImageListEvent = procedure (Sender: TObject; var ImageList: TDragImageList; var Index, HotSpotX, HotSpotY: Integer) of Object;
  79. {$ELSE}
  80.   TGetDragImageListEvent = procedure (Sender: TObject; var ImageList: TCustomImageList; var Index, HotSpotX, HotSpotY: Integer) of Object;
  81. {$ENDIF}
  82.   TGetIntegerEvent = procedure (Sender: TObject; Index: Integer; var ImageIndex: Integer) of Object;
  83.  
  84.   // Delphi 2 made a mistake in TDragControlObject declearation. In line 244 to 252 of controls.pas,
  85.   // the protected and public section should be exchanged. To keep compatible with Delphi 2, This
  86.   // class inherited from TDragControlObject is used to override the member visibility bug.
  87.   THackDragListBoxObject = class(TDragControlObject);
  88.  
  89.   TDragDropListBox = class(TCustomListBox)
  90.   private
  91.     FDragDropEnabled: Boolean;                    // is the drag-drop functionality enabled ?
  92.     FDragDropInsert: Boolean;                     // does this DragDropListBox allow insert ?
  93.     FDragDropDelete: Boolean;                     // does this DragDropListBox allow dlete  ?
  94.     FNewItemIndex: Integer;                       // insert position caculated from the mouse position
  95.     FImages: TCustomImageList;                    // provide the item bitmap
  96.     FImageChangeLink: TChangeLink;
  97.     FTimer: TTimer;                               // automatically scroll mechanism
  98.     FScroll: TScrollDriection;                    // scroll direction of automatically scrolling
  99.     FDragSource: TObject;                         // used in Timer.OnTrigger to identify the DragSource
  100.     DragControlObject: THackDragListBoxObject;        // the DragObject used in this component
  101. {$IFDEF VER120}
  102.     FDragImage: TDragImageList;                   // the drag bitmap
  103. {$ELSE}
  104.     FDragImage: TCustomImageList;                 // the drag bitmap
  105. {$ENDIF}
  106.     FFreeDragImageListNeeded: Boolean;
  107.     FOnGetDragImageList: TGetDragImageListEvent;  // allow user to provide the drag image
  108.     FOnGetImageIndex: TGetIntegerEvent;           // allow user to specify item bitmap if ImageList provided
  109.     procedure DrawLine;                           // draw or clear the insert position line
  110. {$IFDEF VER120}
  111.     procedure DefaultGetDragImage(var ImageList: TDragImageList; var Index, HotSpotX, HotSpotY: Integer);
  112. {$ELSE}
  113.     procedure DefaultGetDragImage(var ImageList: TCustomImageList; var Index, HotSpotX, HotSpotY: Integer);
  114. {$ENDIF}
  115.     procedure ImageListChange(Sender: TObject);
  116.     procedure SetImages(Value: TCustomImageList);
  117.     procedure TimerTriggered(Sender: TObject);    // perform the automatically scrolling
  118.   protected
  119.     procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
  120.     procedure DoStartDrag(var DragObject: TDragObject); override;
  121.     procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override;
  122.     procedure DoEndDrag(Target: TObject; X, Y: Integer); override;
  123. {$IFDEF VER120}
  124.     function GetDragImages: TDragImageList; override;
  125. {$ELSE}
  126.     function GetDragImages: TCustomImageList; override;
  127. {$ENDIF}
  128.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  129.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  130.     procedure SetImageList;
  131.   public
  132.     constructor Create(AOwner: TComponent); override;
  133.     destructor Destroy; override;
  134.     procedure DragDrop(Source: TObject; X, Y: Integer); override;
  135.     procedure InsertItems(SourceBox: TDragDropListBox; PosIndex: Integer);
  136.   published
  137.     property DragDropDelete:  Boolean read FDragDropDelete  write FDragDropDelete  default False;
  138.     property DragDropEnabled: Boolean read FDragDropEnabled write FDragDropEnabled default False;
  139.     property DragDropInsert:  Boolean read FDragDropInsert  write FDragDropInsert  default False;
  140.     property Images: TCustomImageList read FImages write SetImages;
  141.     property OnGetDragImageList: TGetDragImageListEvent read FOnGetDragImageList write FOnGetDragImageList;
  142.     property OnGetImageIndex: TGetIntegerEvent read FOnGetImageIndex write FOnGetImageIndex;
  143. // the following lines are copyright of Inprise Inc.
  144. {$IFDEF VER120}    // Delphi 4
  145.     property Align;
  146.     property Anchors;
  147.     property BiDiMode;
  148.     property BorderStyle;
  149.     property Color;
  150.     property Columns;
  151.     property Constraints;
  152.     property Ctl3D;
  153.     property DragCursor;
  154.     property DragKind;
  155.     property DragMode;
  156.     property Enabled;
  157.     property ExtendedSelect;
  158.     property Font;
  159.     property ImeMode;
  160.     property ImeName;
  161.     property IntegralHeight;
  162.     property ItemHeight;
  163.     property Items;
  164.     property MultiSelect;
  165.     property ParentBiDiMode;
  166.     property ParentColor;
  167.     property ParentCtl3D;
  168.     property ParentFont;
  169.     property ParentShowHint;
  170.     property PopupMenu;
  171.     property ShowHint;
  172.     property Sorted;
  173.     property Style;
  174.     property TabOrder;
  175.     property TabStop;
  176.     property TabWidth;
  177.     property Visible;
  178.     property OnClick;
  179.     property OnDblClick;
  180.     property OnDragDrop;
  181.     property OnDragOver;
  182.     property OnDrawItem;
  183.     property OnEndDock;
  184.     property OnEndDrag;
  185.     property OnEnter;
  186.     property OnExit;
  187.     property OnKeyDown;
  188.     property OnKeyPress;
  189.     property OnKeyUp;
  190.     property OnMeasureItem;
  191.     property OnMouseDown;
  192.     property OnMouseMove;
  193.     property OnMouseUp;
  194.     property OnStartDock;
  195.     property OnStartDrag;
  196. {$ENDIF}
  197. {$IFDEF VER110}    // C++ Builder 3
  198.     property Align;
  199.     property BorderStyle;
  200.     property Color;
  201.     property Columns;
  202.     property Ctl3D;
  203.     property DragCursor;
  204.     property DragMode;
  205.     property Enabled;
  206.     property ExtendedSelect;
  207.     property Font;
  208.     property ImeMode;
  209.     property ImeName;
  210.     property IntegralHeight;
  211.     property ItemHeight;
  212.     property Items;
  213.     property MultiSelect;
  214.     property ParentColor;
  215.     property ParentCtl3D;
  216.     property ParentFont;
  217.     property ParentShowHint;
  218.     property PopupMenu;
  219.     property ShowHint;
  220.     property Sorted;
  221.     property Style;
  222.     property TabOrder;
  223.     property TabStop;
  224.     property TabWidth;
  225.     property Visible;
  226.     property OnClick;
  227.     property OnDblClick;
  228.     property OnDragDrop;
  229.     property OnDragOver;
  230.     property OnDrawItem;
  231.     property OnEndDrag;
  232.     property OnEnter;
  233.     property OnExit;
  234.     property OnKeyDown;
  235.     property OnKeyPress;
  236.     property OnKeyUp;
  237.     property OnMeasureItem;
  238.     property OnMouseDown;
  239.     property OnMouseMove;
  240.     property OnMouseUp;
  241.     property OnStartDrag;
  242. {$ENDIF}
  243. {$IFDEF VER100}    // Delphi 3
  244.     property Align;
  245.     property BorderStyle;
  246.     property Color;
  247.     property Columns;
  248.     property Ctl3D;
  249.     property DragCursor;
  250.     property DragMode;
  251.     property Enabled;
  252.     property ExtendedSelect;
  253.     property Font;
  254.     property ImeMode;
  255.     property ImeName;
  256.     property IntegralHeight;
  257.     property ItemHeight;
  258.     property Items;
  259.     property MultiSelect;
  260.     property ParentColor;
  261.     property ParentCtl3D;
  262.     property ParentFont;
  263.     property ParentShowHint;
  264.     property PopupMenu;
  265.     property ShowHint;
  266.     property Sorted;
  267.     property Style;
  268.     property TabOrder;
  269.     property TabStop;
  270.     property TabWidth;
  271.     property Visible;
  272.     property OnClick;
  273.     property OnDblClick;
  274.     property OnDragDrop;
  275.     property OnDragOver;
  276.     property OnDrawItem;
  277.     property OnEndDrag;
  278.     property OnEnter;
  279.     property OnExit;
  280.     property OnKeyDown;
  281.     property OnKeyPress;
  282.     property OnKeyUp;
  283.     property OnMeasureItem;
  284.     property OnMouseDown;
  285.     property OnMouseMove;
  286.     property OnMouseUp;
  287.     property OnStartDrag;
  288. {$ENDIF}
  289. {$IFDEF VER90}    // Delphi 2
  290.     property Align;
  291.     property BorderStyle;
  292.     property Color;
  293.     property Columns;
  294.     property Ctl3D;
  295.     property DragCursor;
  296.     property DragMode;
  297.     property Enabled;
  298.     property ExtendedSelect;
  299.     property Font;
  300.     property IntegralHeight;
  301.     property ItemHeight;
  302.     property Items;
  303.     property MultiSelect;
  304.     property ParentColor;
  305.     property ParentCtl3D;
  306.     property ParentFont;
  307.     property ParentShowHint;
  308.     property PopupMenu;
  309.     property ShowHint;
  310.     property Sorted;
  311.     property Style;
  312.     property TabOrder;
  313.     property TabStop;
  314.     property TabWidth;
  315.     property Visible;
  316.     property OnClick;
  317.     property OnDblClick;
  318.     property OnDragDrop;
  319.     property OnDragOver;
  320.     property OnDrawItem;
  321.     property OnEndDrag;
  322.     property OnEnter;
  323.     property OnExit;
  324.     property OnKeyDown;
  325.     property OnKeyPress;
  326.     property OnKeyUp;
  327.     property OnMeasureItem;
  328.     property OnMouseDown;
  329.     property OnMouseMove;
  330.     property OnMouseUp;
  331.     property OnStartDrag;
  332. {$ENDIF}
  333. // the above lines are copyright of Inprise Inc.
  334.   end;
  335.  
  336. procedure Register;
  337.  
  338. implementation
  339. uses Math, Commctrl;
  340.  
  341. procedure Register;
  342. begin
  343.   RegisterComponents('VincentSoft', [TDragDropListBox]);
  344. end;
  345.  
  346. {$IFNDEF VER120}    // not Delphi 4
  347. function Max(A,B: Integer): Integer;
  348. begin
  349.   if A > B then
  350.     Result := A
  351.   else
  352.     Result := B;
  353. end;
  354.  
  355. function Min(A,B: Integer): Integer;
  356. begin
  357.   if A < B then
  358.     Result := A
  359.   else
  360.     Result := B;
  361. end;
  362. {$ENDIF}
  363.  
  364. constructor TDragDropListBox.Create(AOwner: TComponent);
  365. begin
  366.   inherited Create(AOwner);
  367.   FImages := nil;
  368.   FImageChangeLink := TChangeLink.Create;
  369.   FImageChangeLink.OnChange := ImageListChange;
  370.   if not (csDesigning in ComponentState) then
  371.   begin
  372.     ControlStyle := ControlStyle + [csDisplayDragImage];
  373.     DragControlObject := THackDragListBoxObject.Create(Self);
  374.     FFreeDragImageListNeeded := False;
  375.     FDragImage := nil;
  376.     FNewItemIndex := Low(Integer);
  377.     FTimer := TTimer.Create(AOwner);
  378.     FTimer.Enabled := False;
  379.     FTimer.OnTimer := TimerTriggered;
  380.     FTimer.Interval := 250;
  381.   end;
  382. end;
  383.  
  384. destructor TDragDropListBox.Destroy;
  385. begin
  386.   if Assigned(FTimer) then
  387.     FTimer.Free;
  388.   if Assigned(DragControlObject) then
  389.   begin
  390.     DragControlObject.Free;
  391.     DragControlObject := nil;
  392.   end;
  393.   inherited Destroy;
  394. end;
  395.  
  396. procedure TDragDropListBox.Notification(AComponent: TComponent; Operation: TOperation);
  397. begin
  398.   inherited Notification(AComponent, Operation);
  399.   if Operation = opRemove then
  400.     if AComponent = Images then Images := nil;
  401. end;
  402.  
  403. { ** Bitmap Item that Utilize the ImageList ** }
  404.  
  405. {  }
  406. procedure TDragDropListBox.SetImages(Value: TCustomImageList);
  407. begin
  408.   if Images <> nil then
  409.     Images.UnRegisterChanges(FImageChangeLink);
  410.   FImages := Value;
  411.   if Images <> nil then
  412.   begin
  413.     Images.RegisterChanges(FImageChangeLink);
  414.     Images.FreeNotification(Self)
  415.   end;
  416.   SetImageList;
  417.   if (Images <> nil) and (Style=lbStandard) then
  418.     Style := lbOwnerDrawFixed;
  419. end;
  420.  
  421. {  }
  422. procedure TDragDropListBox.SetImageList;
  423. begin
  424.   Refresh;
  425. end;
  426.  
  427. {  }
  428. procedure TDragDropListBox.ImageListChange(Sender: TObject);
  429. begin
  430.   SetImageList;
  431. end;
  432.  
  433. { draw the ListBox item }
  434. procedure TDragDropListBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  435. var
  436. {$IFDEF VER120}
  437.   Flags: Longint;
  438. {$ENDIF}
  439.   ItemBitmapIndex: Integer;
  440. begin
  441.   if Assigned(OnDrawItem) then
  442.     OnDrawItem(Self, Index, Rect, State)
  443.   else
  444.   begin
  445.     if Assigned(FImages) then
  446.     begin
  447.       Canvas.FillRect(Rect);
  448.       if Index < Items.Count then
  449.       begin
  450.         ItemBitmapIndex := -1;
  451.         // draw bitmap
  452.         if Assigned(OnGetImageIndex) then
  453.           OnGetImageIndex(Self, Index, ItemBitmapIndex);
  454.         FImages.Draw(Canvas, Rect.Left+2, Rect.Top+((Rect.Bottom-Rect.Top-TImageList(FImages).Height) div 2), ItemBitmapIndex);
  455.         Rect.Left := Rect.Left+(2+TImageList(FImages).Width);
  456.         // draw text
  457. {$IFDEF VER120}
  458.         Flags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER);
  459.         if not UseRightToLeftAlignment then
  460.           Inc(Rect.Left, 2)
  461.         else
  462.           Dec(Rect.Right, 2);
  463.         DrawText(Canvas.Handle, PChar(Items[Index]), Length(Items[Index]), Rect,
  464.           Flags);
  465. {$ELSE}
  466.         Canvas.FillRect(Rect);
  467.         if Index < Items.Count then
  468.           Canvas.TextOut(Rect.Left + 2, Rect.Top+((Rect.Bottom-Rect.Top-Canvas.TextHeight(Items[Index])) div 2), Items[Index]);
  469. {$ENDIF}
  470.       end;
  471.     end
  472.     else
  473.       inherited;
  474.   end;
  475. end;
  476.  
  477. { ** Drag and Drop Support that Utilize the TDragControlObject ** }
  478.  
  479. { draw or clear the position line }
  480. procedure TDragDropListBox.DrawLine;
  481. var
  482.   PenMode: TPenMode;
  483.   PenWidth: Integer;
  484. begin
  485.   if (FNewItemIndex<>Low(Integer))  // initial status, draw line here is meangless
  486.     and DragDropInsert then     // insert not allowed, draw line here is meangless
  487.   begin
  488.     PenWidth := Canvas.Pen.Width;
  489.     PenMode := Canvas.Pen.Mode;
  490.     try
  491.       Canvas.Pen.Width := 2;
  492.       Canvas.Pen.Mode := pmNot;
  493.       Canvas.PolyLine([Point(0, FNewItemIndex), Point(Width, FNewItemIndex)]);
  494.     finally
  495.       Canvas.Pen.Width := PenWidth;
  496.       Canvas.Pen.Mode := PenMode;
  497.     end;
  498.   end;
  499. end;
  500.  
  501. { using Timer to perform the automatically scrolling }
  502. procedure TDragDropListBox.TimerTriggered(Sender: TObject);
  503. var
  504.   NewTopIndex : Integer;
  505. begin
  506.   // calculate the new TopIndex of ListBox
  507.   case FScroll of
  508.     sdDown: NewTopIndex := Min(TopIndex+1, Items.Count-(Height div ItemHeight));
  509.     sdUp:   NewTopIndex := Max(TopIndex-1, 0);
  510.   else
  511.     Exit;
  512.   end;
  513.   // perform the scrolling
  514.   if NewTopIndex <> TopIndex then
  515.   begin
  516.     // do automatically scrolling
  517.     TDragDropListBox(FDragSource).DragControlObject.HideDragImage;
  518.     DrawLine;                 // clear insert position Line
  519.     TopIndex := NewTopIndex;  // scroll
  520.     DrawLine;                 // draw insert position line
  521.     TDragDropListBox(FDragSource).DragControlObject.ShowDragImage;
  522.   end;
  523. end;
  524.  
  525. {  }
  526. {$IFDEF VER120}
  527. function TDragDropListBox.GetDragImages: TDragImageList;
  528. {$ELSE}
  529. function TDragDropListBox.GetDragImages: TCustomImageList;
  530. {$ENDIF}
  531. begin
  532.   Result := FDragImage;
  533. end;
  534.  
  535. {}
  536. {$IFDEF VER120}
  537. procedure TDragDropListBox.DefaultGetDragImage(var ImageList: TDragImageList; var Index, HotSpotX, HotSpotY: Integer);
  538. {$ELSE}
  539. procedure TDragDropListBox.DefaultGetDragImage(var ImageList: TCustomImageList; var Index, HotSpotX, HotSpotY: Integer);
  540. {$ENDIF}
  541. var
  542.   SelectedIndex : Integer;
  543.   RectItem: TRect;
  544.   ItemSelected : Boolean;
  545.   Bitmap: TBitmap;
  546. begin
  547.   // Get the listbox item bitmap
  548.   RectItem := ItemRect(ItemIndex);
  549.   RectItem := Rect(RectItem.Left+1, RectItem.Top+1, RectItem.Right-1, RectItem.Bottom-1);
  550.   Bitmap := TBitmap.Create;
  551.   try
  552.     Bitmap.Height := RectItem.Bottom-RectItem.Top;
  553.     Bitmap.Width  := RectItem.Right-RectItem.Left;
  554.     // de-hilight the selection temporary to get the normal bitmap
  555.     if MultiSelect then
  556.     begin
  557.       ItemSelected := Selected[ItemIndex];
  558.       Selected[ItemIndex] := False;
  559.       Update;
  560.       Bitmap.Canvas.CopyRect(Bounds(0, 0, Bitmap.Width, Bitmap.Height), Canvas, RectItem);
  561.       Selected[ItemIndex] := ItemSelected;
  562.       Update;
  563.     end
  564.     else
  565.     begin
  566.       SelectedIndex := ItemIndex;
  567.       ItemIndex := -1;
  568.       Bitmap.Canvas.CopyRect(Bounds(0, 0, Bitmap.Width, Bitmap.Height), Canvas, RectItem);
  569.       ItemIndex := SelectedIndex;
  570.     end;
  571.     if Assigned(FDragImage) and (FFreeDragImageListNeeded) then
  572.     begin
  573.       FDragImage.Free;
  574.       FDragImage := nil;
  575.     end;
  576. {$IFDEF VER120}
  577.     ImageList := TDragImageList.CreateSize(Bitmap.Width, Bitmap.Height);
  578. {$ELSE}
  579.     ImageList := TCustomImageList.CreateSize(Bitmap.Width, Bitmap.Height);
  580. {$ENDIF}
  581.     FFreeDragImageListNeeded := True;
  582.     if SelCount > 1 then // if MultiSlect=False, SelCount always return -1
  583.       DragCursor := crMultiDrag
  584.     else
  585.       DragCursor := crDrag;
  586.     ImageList.AddMasked(Bitmap, Color);
  587.     Index := 0;
  588.     HotSpotX := 2;
  589.     HotSpotY := 2;
  590.   finally
  591.     Bitmap.Free;
  592.   end;
  593. end;
  594.  
  595. {  }
  596. procedure TDragDropListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  597. var
  598.   Index, HotSpotX, HotSpotY: Integer;
  599. begin
  600.   inherited;
  601.   if DragDropEnabled and not Dragging and (Button=mbLeft) then
  602.     // there must be some selection to drag
  603.     if (not MultiSelect and (ItemAtPos(Point(X, Y), True)<>-1)) or (MultiSelect and (SelCount>0)) then
  604.       // use DragDetect to avoid the side effect of unnecessary BeginDrag, immediate=False doesn't work. :(
  605.       if DragDetect(Handle, Self.ClientToScreen(Point(X, Y))) then
  606.       begin
  607.         // get DragImageList
  608.         if Assigned(FOnGetDragImageList) then
  609.           FOnGetDragImageList(Self, FDragImage, Index, HotSpotX, HotSpotY)
  610.         else
  611.           DefaultGetDragImage(FDragImage, Index, HotSpotX, HotSpotY);
  612.         if Assigned(FDragImage) then
  613.           FDragImage.SetDragImage(Index, HotSpotX, HotSpotY);
  614.         BeginDrag(False);
  615.       end;
  616. end;
  617.  
  618. {  }
  619. procedure TDragDropListBox.DoStartDrag(var DragObject: TDragObject);
  620. begin
  621.   DragObject := DragControlObject;
  622.   inherited;
  623. end;
  624.  
  625. {  }
  626. procedure TDragDropListBox.DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
  627. var
  628.   ItemIndexY: Integer;
  629.   SourceBox: TDragDropListBox;
  630. begin
  631.   // Trigger user-defined OnDragOver, the following is a modified inherited call
  632.   if (Source is TDragControlObject) then Source := THackDragListBoxObject(Source).Control;
  633.   Accept := (Source is TDragDropListBox);
  634.   if Assigned(OnDragOver) then
  635.     OnDragOver(Self, Source, X, Y, State, Accept);
  636.   // Processing DragOver behavior of TDragDropListBox
  637.   if not ((Source is TDragDropListBox) and DragDropEnabled and TDragDropListBox(Source).DragDropEnabled)then exit;
  638.   if Accept then
  639.   begin
  640.     SourceBox := TDragDropListBox(Source);
  641.     if not SourceBox.DragDropEnabled then exit;
  642.     case State of
  643.     dsDragMove:
  644.       begin
  645.         // set direction of automatically scrolling
  646.         if (Y <= ItemHeight/2) then
  647.           FScroll := sdUp
  648.         else if (Y >= ClientHeight-ItemHeight/2) then
  649.           FScroll := sdDown
  650.         else
  651.           FScroll := sdNone;
  652.         FTimer.Enabled := (DragDropInsert) and (FScroll<>sdNone); // enable automatically scrolling
  653.         ItemIndexY := Min(Round(Y/ItemHeight)*ItemHeight, Items.Count*ItemHeight+1); // calculate new position
  654.         if ItemIndexY <> FNewItemIndex then // if new position changed
  655.         begin
  656.           SourceBox.DragControlObject.HideDragImage;
  657.           DrawLine;                     // clear old line
  658.           FNewItemIndex := ItemIndexY;  // new insert position
  659.           DrawLine;                     // draw new line
  660.           SourceBox.DragControlObject.ShowDragImage;
  661.         end;
  662.      end;
  663.     dsDragLeave:
  664.       begin
  665.         FDragSource := nil;
  666.         FTimer.Enabled := False;            // turn off automatically scrolling
  667.         SourceBox.DragControlObject.HideDragImage;
  668.         DrawLine;                           // clear the new position line
  669.         SourceBox.DragControlObject.ShowDragImage;
  670.       end;
  671.     dsDragEnter:
  672.       begin
  673.         FDragSource := SourceBox;
  674.         FNewItemIndex := Low(Integer);      // initial status
  675.       end;
  676.     end;
  677.   end;
  678. end;
  679.  
  680. {  }
  681. procedure TDragDropListBox.DragDrop(Source: TObject; X, Y: Integer);
  682. var
  683.   NewItemIndex: Integer;
  684. begin
  685.   inherited;  // perform the user-defined OnDragDrop event handler
  686.   if (Source is TDragControlObject) then Source := THackDragListBoxObject(Source).Control;
  687.   if not ((Source is TDragDropListBox) and DragDropEnabled and TDragDropListBox(Source).DragDropEnabled) then exit;
  688.   FTimer.Enabled := False;    // turn off automatically scrolling
  689.   // caculate ItemIndex of the insert position
  690.   if FScroll = sdDown then FNewItemIndex := FNewItemIndex-(ItemHeight div 2); // at the scroll down position, ItemAtPos=-1
  691.   NewItemIndex := ItemAtPos(Point(0, FNewItemIndex), False);
  692.   if FScroll = sdDown then Inc(NewItemIndex);  // at the scroll down position, ItemIndex has to be adjusted
  693.   // perform the insert operation
  694.   try
  695.     InsertItems(TDragDropListBox(Source), NewItemIndex);
  696.   finally
  697.     FNewItemIndex := Low(Integer);
  698.   end;
  699. end;
  700.  
  701. {  }
  702. procedure TDragDropListBox.DoEndDrag(Target: TObject; X, Y: Integer);
  703. begin
  704.   FNewItemIndex := Low(Integer);
  705.   if Assigned(FDragImage) and (FFreeDragImageListNeeded) then
  706.   begin
  707.     FDragImage.Free;
  708.     FDragImage := nil;
  709.   end;
  710.   inherited;  // perform the user-defined OnEndDrag event handler
  711. end;
  712.  
  713. {  }
  714. procedure TDragDropListBox.InsertItems(SourceBox: TDragDropListBox; PosIndex: Integer);
  715. var
  716.   i, SelectItemIndex, DeletedCountBeforeInsertPoint: Integer;
  717.   SelectedItems: TStringList;
  718. begin
  719.   if (SourceBox.MultiSelect and (SourceBox.SelCount<1)) or
  720.      (not SourceBox.MultiSelect and (SourceBox.ItemIndex=-1)) then
  721.     exit;
  722.   DeletedCountBeforeInsertPoint := 0;
  723.   SelectItemIndex := 0;
  724.   SelectedItems := TStringList.Create;
  725.   try
  726.     // build the selection list
  727.     SelectedItems.BeginUpdate;
  728.     for i:=SourceBox.Items.Count-1 downto 0 do
  729.       if SourceBox.Selected[i] then
  730.       begin
  731.         SelectedItems.AddObject(SourceBox.Items[i], SourceBox.Items.Objects[i]);
  732.         if (SourceBox=Self) and (i<PosIndex) then Inc(DeletedCountBeforeInsertPoint);
  733.         if i = SourceBox.ItemIndex then SelectItemIndex := SelectedItems.Count-1;
  734.       end;
  735.     SelectedItems.EndUpdate;
  736.     // insert the selection into target
  737.     if Self.DragDropInsert then
  738.     begin
  739.       Self.Items.BeginUpdate;
  740.       for i:= 0 to SelectedItems.Count-1 do
  741.         Self.Items.InsertObject(PosIndex, SelectedItems.Strings[i], SelectedItems.Objects[i]);
  742.       Self.Items.EndUpdate;
  743.       SourceBox.Items.BeginUpdate;
  744.     end;
  745.     // remove the selection from source
  746.     if SourceBox.DragDropDelete then
  747.     begin
  748.       for i:=SourceBox.Items.Count-1 downto 0 do
  749.         if SourceBox.Selected[i] then
  750.           SourceBox.Items.Delete(i);
  751.       SourceBox.Items.EndUpdate;
  752.     end;
  753.     // set the selection in the target to the new inserted items
  754.     if (Self.DragDropInsert) and (Self.MultiSelect) then
  755.       for i:=0 to Items.Count-1 do
  756.         Self.Selected[i] := (i>=PosIndex-DeletedCountBeforeInsertPoint) and
  757.                             (i<=PosIndex-DeletedCountBeforeInsertPoint+SelectedItems.Count-1);
  758.     // set the current ItemIndex of the source listbox
  759.     if SourceBox.DragDropDelete then
  760.       SourceBox.ItemIndex := Min(SourceBox.ItemIndex, SourceBox.Items.Count-1);
  761.     // set the current ItemIndex of the target listbox to get the correct focused item
  762.     if Self.DragDropInsert then
  763.       Self.ItemIndex := Max(-1, PosIndex-DeletedCountBeforeInsertPoint+SelectedItems.Count-SelectItemIndex-1);
  764.   finally
  765.      SelectedItems.Free;
  766.   end;
  767. end;
  768.  
  769. end.
  770.