Board index » cppbuilder » How to Drag and Drop a TSpeedButton -- Problems!

How to Drag and Drop a TSpeedButton -- Problems!


2004-07-08 08:33:22 AM
cppbuilder34
Hello:
I am writing an application in which there is a form (ChildForm)
in which user can create a button SFastButton (which is derived
from TSpeedButton). I want to allow the user to drag and drop
the button. The button can also be assigned some functions
(for example rigth click the button and choose "make red") which
assigns the function "FuncRed" to the button. when the button
is clicked, "FuncRed" is executed and the color of the ChildForm
changes to Red.
The problem is how can I differentiate between a "click" and a
drag. I have made a very close-to-successful attempt, but it is
not perfect.
Here are the problems:
scenario 1: Say, I right click on the button and assign the
function "FuncRed" to it. Now when I click the button, it stays
in the "pressed" state. A TSpeedButton should not stay in
"pressed" as long as.. 1) it is not in a GroupBox 2) GroupIndex
is 0, 3) Property "Down" is set to false. So, after it is
clicked it should be "un-pressed" again right away, but in my
case it doesn't happen.
Scenario 2: Say I added a new button. I click and drag the button and "drop" it. It gets to the new location, but again,
it stays "pressed."
I want button to be "un-pressed" in both scenarios. I am
including the functions I wrote to handle this.
PLEASE TELL ME IF I AM DOING SOMETHING WRONG OR NEED TO DO
SOMETHING DIFFERENT TO ACHIEVE MY GOAL.
her's my SFastButton dataStructure it is a class defined inside
the class TChilForm
class SFastButton : public TSpeedButton
{
private:
protected:
public:
__property Canvas;
__property DragKind;
__property DragMode;
__property OnEndDrag;
int NumPageNodes;
struct PageNode *HeadPageNode;
// __fastcall virtual ~SFastButton(void);
__fastcall virtual SFastButton(TComponent *AOwner);
// void __fastcall SFastButtonEndDrag(TObject *Sender,
// TObject *Target, int X, int Y);
__published:
};
1) first the form DragDrop and DragOver functions (ignore
comments part. it was out of desperation)
void __fastcall TChildForm::FormDragDrop(TObject *Sender, TObject *Source,
int X, int Y)
{
if(Source->ClassNameIs("SFastButton"))
{
SFastButton *pBtn = dynamic_cast<SFastButton *>(Source);
pBtn->Top = Y;
pBtn->Left = X;
// pBtn->EndDrag(true);
// pBtn->Down = false;
}else if(Source->ClassNameIs("TLabel"))
{
TLabel *pLabel = dynamic_cast<TLabel *>(Source);
pLabel->Top = Y;
pLabel->Left = X;
}else if(Source->ClassNameIs("TPageControl"))
{
TPageControl *pPC = dynamic_cast<TPageControl *>(Source);
pPC->Top = Y;
pPC->Left = X;
}else if(Source->ClassNameIs("TGroupBox"))
{
TGroupBox *pGB = dynamic_cast<TGroupBox *>(Source);
pGB->Top = Y;
pGB->Left = X;
}
else
{
// do nothing
}
}
//-------------------------------------------------------------
void __fastcall TChildForm::FormDragOver(TObject *Sender, TObject *Source,
int X, int Y, TDragState State, bool &Accept)
{
if(Source->ClassNameIs("SFastButton") || Source->ClassNameIs("TLabel")
|| Source->ClassNameIs("TPageControl") || Source->ClassNameIs("TGroupBox"))
{
Accept = true;
}
else
{
Accept = false;
}
}
//-------------------------------------------------------------
2) AddButton Function.
some comments are for testing/trying some out of desperation
void TChildForm::AddButton(void)
{
SFastButton *pSBtn = dynamic_cast<SFastButton*>(new SFastButton(MyActiveControl));
//TODO: see if you shoudl change the owner to "this" JD suggested
pSBtn->Parent = dynamic_cast<TWinControl*>(MyActiveControl);
pSBtn->Left = CompLeft;
pSBtn->Top = CompTop;
// pSBtn->DragKind = dkDrag;
// pSBtn->DragMode = dmManual;
pSBtn->HeadPageNode = NULL;
pSBtn->NumPageNodes = 0;
pSBtn->OnClick = NULL;
// pSBtn->AllowAllUp = true;
// pSBtn->GroupIndex = 2;
if(pSBtn->Parent->ClassNameIs("TGroupBox"))
{
pSBtn->GroupIndex = 1;
}
pSBtn->PopupMenu = this->PopupMenu1;
// pSBtn->OnContextPopup = SButtonContextPopup; // TODO: not possible
pSBtn->OnMouseDown = SpeedButtonMouseDown;
pSBtn->OnMouseUp = SpeedButtonMouseUp;
pSBtn->OnEndDrag = SFastButtonEndDrag;
}
//-----------------------------------------------------------
note: Not sure if I needed following two, but it was part of
the attept to solve my problems.
pSBtn->OnMouseUp = SpeedButtonMouseUp;
pSBtn->OnEndDrag = SFastButtonEndDrag;
3) I am maintaing "ActiveButton" which is type SFastButton
defined in Child.h. Here's MouseDown function
void __fastcall TChildForm::SpeedButtonMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
ActiveButton = dynamic_cast<SFastButton*>(Sender);
if(Button == mbRight)
{
// nothing
} else if(Button == mbLeft)
{
ActiveButton->BeginDrag(false, 2); //TODO; should
} else
{
// nothing
}
}
//------------------------------------------------------------
4) here's MouseUp. I commented it cuz i realized I don't need
it. It was part of trying different things. was trying to
change the button's Down property to fasle, but it was false
already and the button still appears "pressed"!!! also tried
"EndDrag" not sure if I needed to use it at all?
void __fastcall TChildForm::SpeedButtonMouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
/*
ActiveButton = dynamic_cast<SFastButton*>(Sender);
if(Button == mbLeft)
{
ActiveButton->Down = false;
// ActiveButton->EndDrag(true);
}
*/
}
//--------------------------------------
5)
Another desperate effor. tried usig "OnEndDrag" event of
TControl. added the property "EndDrag" in my class SFastButton.
do I really need this? it didn't work anyway.
void __fastcall TChildForm::SFastButtonEndDrag(TObject *Sender,
TObject *Target, int X, int Y)
{
// ActiveButton->EndDrag(true);
ActiveButton->Down = false;
}
//-------------------------------------
I hope this is enoug information to understand the problem and
help. I will truly appreciate your help.
thanks a bunch,
Veebo
 
 

Re:How to Drag and Drop a TSpeedButton -- Problems!

"veebo" < XXXX@XXXXX.COM >wrote:
Quote
[...] I want to allow the user to drag and drop the button.
Well TSpeedButton is a bad choice for that because it doesn't
have all of the events and methods to make it easily accomplished.
Quote
The button can also be assigned some functions (for example
rigth click the button and choose "make red") which assigns
the function "FuncRed" to the button.
As is, right now the user has to right click, select the
function and then (left) click the button again. I think that
that would be better accomplished by using a popup menu with
an option that calls the color dialog and assigns the result
to the form directly.
This only makes sense if the same button is used to set the
color for multiple forms.
Quote
The problem is how can I differentiate between a "click" and a
drag.
Set the DragMode to dmManual instead.
You can modify the following to meet your needs but you'll
have to take care of any DragDrop functions. Don't forget that
just setting the Left and Top isn't enough if you're dragging
an object from one parent to another. You'll also need to
change the control's Parent property.
To use this as is, start a new project, drop a panel on the
form and add a DragOver event for the form and the panel.
Then click File | New | Unit (twice) and add the two new
classes. Click Save and Run.
//-------------------------------------------------------------
#ifndef MainH
#define MainH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//-------------------------------------------------------------
// this is the PButton class's header
#include "PButton.h"
//-------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TPanel *Panel1;
void __fastcall FormDragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept);
void __fastcall Panel1DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept);
private: // User declarations
TPButton* pButton;
void __fastcall SetControlsControlStyle( TControl* Control );
void __fastcall PButtonClick(TObject* Sender);
public: // User declarations
__fastcall TForm1(TComponent* Owner);
__fastcall TForm1::~TForm1();
};
//-------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Main.h"
//-------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// you need this function to allow the drag image to be seen
SetControlsControlStyle( this );
// pass the desired Parent as the owner
pButton = new TPButton( Panel1 /*this*/ );
pButton->Left = 10;
pButton->Top = 10;
pButton->ButtonClick = PButtonClick;
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
//
}
//-------------------------------------------------------------
void __fastcall TForm1::SetControlsControlStyle( TControl* Control )
{
Control->ControlStyle << csDisplayDragImage;
TWinControl* pControl = dynamic_cast<TWinControl*>( Control );
if( pControl )
{
for( int x = 0; x < pControl->ControlCount; ++x )
{
SetControlsControlStyle( pControl->Controls[ x ] );
}
}
}
//-------------------------------------------------------------
void __fastcall TForm1::FormDragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept)
{
Accept = false; //true;
}
//-------------------------------------------------------------
void __fastcall TForm1::Panel1DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept)
{
Accept = true;
}
//-------------------------------------------------------------
void __fastcall TForm1::PButtonClick(TObject* Sender)
{
ShowMessage("Executing the OnClick event");
}
//-------------------------------------------------------------
//the new button class
//-------------------------------------------------------------
#ifndef PButtonH
#define PButtonH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <ExtCtrls.hpp>
//-------------------------------------------------------------
class TPButton : public TPanel
{
protected:
DYNAMIC void __fastcall MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall DoEndDrag(TObject* Target, int X, int Y);
DYNAMIC void __fastcall DoStartDrag(TDragObject* &DragObject);
DYNAMIC void __fastcall DragOver(TObject* Source, int X, int Y, TDragState State, bool &Accept);
private:
typedef TPanel inherited;
typedef void __fastcall (__closure *TButtonClick)(TObject* Sender);
TButtonClick FButtonClick;
TImage* FImage;
int FPixelThreshHold;
TDragObject* FDragObject;
bool FButtonDown;
TPoint FClickPoint;
void __fastcall WndProc( TMessage &Message );
bool __fastcall MouseInButton();
void __fastcall ReleaseButton();
public:
__fastcall TPButton( TComponent* Owner );
__fastcall TPButton::~TPButton();
__published:
__property TButtonClick ButtonClick = { read = FButtonClick, write = FButtonClick };
};
//-------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#pragma hdrstop
#include "PButton.h"
#include "DCObject.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TPButton::TPButton(TComponent* Owner) : TPanel( Owner )
{
Parent = dynamic_cast<TWinControl*>( Owner );
Height = 40;
Width = 40;
Color = clBtnFace;
Caption = "Test";
BevelInner = bvNone;
BevelOuter = bvNone;
BevelWidth = 2;
ControlStyle << csDisplayDragImage;
DragKind = dkDrag;
DragMode = dmManual;
FDragObject = NULL;
DragCursor = crDrag;
FButtonDown = false;
FPixelThreshHold = 3;
FButtonClick = NULL;
FImage = NULL;
}
//-------------------------------------------------------------
__fastcall TPButton::~TPButton()
{
if( FImage ) delete FImage;
}
//-------------------------------------------------------------
void __fastcall TPButton::WndProc(TMessage &Message)
{
if( Message.Msg == CM_MOUSEENTER || Message.Msg == CM_MOUSELEAVE )
{
if( !Dragging() )
{
if( MouseInButton() ) BevelOuter = bvRaised;
else BevelOuter = bvNone;
}
}
inherited::WndProc( Message );
}
//-------------------------------------------------------------
void __fastcall TPButton::MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( Button == mbLeft )
{
BevelOuter = bvLowered;
FButtonDown = true;
if( FImage )
{
FImage->Left = FImage->Left + 1;
FImage->Top = FImage->Top + 1;
}
FClickPoint = Point( X, Y );
}
else if( Button == mbRight )
{
// button was right clicked
}
inherited::MouseDown(Button, Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TPButton::MouseMove(TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) && MouseInButton() )
{
if( (abs(FClickPoint.x - X)>FPixelThreshHold) || (abs(FClickPoint.y - Y)>FPixelThreshHold) )
{
if( FButtonDown ) ReleaseButton();
BevelOuter = bvRaised;
Update();
BeginDrag( true );
BevelOuter = bvNone;
Update();
}
}
inherited::MouseMove(Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TPButton::MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
if( MouseInButton() )
{
if( FButtonDown )
{
ReleaseButton();
BevelOuter = bvRaised;
if( FButtonClick ) FButtonClick( this );
}
}
else BevelOuter = bvNone;
FClickPoint = Point( -1, -1 );
}
inherited::MouseUp(Button, Shift, X, Y);
}
//-------------------------------------------------------------
bool __fastcall TPButton::MouseInButton()
{
RECT Rect;
POINT Point;
::GetWindowRect( Handle, &Rect );
::GetCursorPos( &Point );
if( ::PtInRect(&Rect, Point) ) return true;
return false;
}
//-------------------------------------------------------------
void __fastcall TPButton::ReleaseButton()
{
FButtonDown = false;
if( FImage )
{
FImage->Left = FImage->Left - 1;
FImage->Top = FImage->Top - 1;
}
}
//-------------------------------------------------------------
void __fastcall TPButton::DoStartDrag(TDragObject* &DragObject)
{
TPoint Point;
::GetCursorPos( &Point );
Point = ScreenToClient( Point );
FDragObject = new TDCObject( this, Point.x, Point.y, Color );
DragObject = FDragObject;
inherited::DoStartDrag( DragObject );
}
//-------------------------------------------------------------
void __fastcall TPButton::DoEndDrag(TObject* Target, int X, int Y)
{
delete FDragObject;
FDragObject = NULL;
if( !MouseInButton() ) BevelOuter = bvNone;
inherited::DoEndDrag( Target, X, Y );
}
//-------------------------------------------------------------
void __fastcall TPButton::DragOver(TObject* Source, int X, int Y, TDragState State, bool &Accept)
{
if( this == dynamic_cast<TObject*>(Source) ) Accept = false;
else Accept = true;
inherited::DragOver( Source, X, Y, State, Accept );
}
//-------------------------------------------------------------
// the drag object class
//-------------------------------------------------------------
#ifndef DCObjectH
#define DCObjectH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
//-------------------------------------------------------------
class TDCObject : public TDragControlObject
{
protected:
virtual TDragImageList* __fastcall GetDragImages();
private:
int FX, FY;
TColor FColor;
TDragImageList* FDragImages;
public:
__fastcall TDCObject( TControl* AControl, int X, int Y, TColor AColor );
__fastcall TDCObject::~TDCObject();
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#pragma hdrstop
#include "DCObject.h"
//-------------------------------------------------------------
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TDCObject::TDCObject( TControl* AControl, int X, int Y, TColor AColor ) : TDragControlObject( Control )
{
FX = X;
FY = Y;
FColor = AColor;
Control = AControl;
FDragImages = NULL;
}
//-------------------------------------------------------------
__fastcall TDCObject::~TDCObject()
{
delete FDragImages;
}
//-------------------------------------------------------------
TDragImageList* __fastcall TDCObject::GetDragImages()
{
if( !FDragImages )
{
FDragImages = new TDragImageList( NULL );
Graphics::TBitmap* Bmp = new Graphics::TBitmap;
Bmp->Width = Control->Width;
Bmp->Height = Control->Height;
Bmp->Canvas->Lock();
TWinControl* pControl = dynamic_cast<TWinControl*>( Control );
pControl->PaintTo( Bmp->Canvas->Handle, 0, 0 );
Bmp->Canvas->Unlock();
FDragImages->Width = Control->Width;
FDragImages->Height = Control->Height;
int Index = FDragImages->AddMasked( Bmp, FColor );
FDragImages->SetDragImage( Index, FX, FY );
delete Bmp;
}
return FDragImages;
}
//-------------------------------------------------------------
~ JD
 

Re:How to Drag and Drop a TSpeedButton -- Problems!

-
Quote
void __fastcall TForm1::SetControlsControlStyle( TControl* Control )
{
Control->ControlStyle << csDisplayDragImage;
TWinControl* pControl = dynamic_cast<TWinControl*>( Control );
if( pControl )
{
for( int x = 0; x < pControl->ControlCount; ++x )
{
SetControlsControlStyle( pControl->Controls[ x ] );
}
}
}
//-----------------------------------------------
~ JD
JD,
thanks for the response and your efforts to develop the code.
I really appreciate it. I was away for a few days, so I didn't
get to respond sooner.
I took pointers from your solution above.
One thing I didn't understand is..
Control->ControlStyle << csDisplayDragImage;
Isn't this supposed to allow the Control that is BEING DRAGGED
to be visible from point A to point B? In my case, when I
start dragging the control, it stays at A (all I see is the
cursor in drag mode). When I drop the Control at point B, only
then I see it at somewhere other than point A. Do you see
my problem? I was expecting to see the control all along
staring at point A and then at some points A1, A2, A3,...
and then at point B.
Is what I was expecting incorrect or what I am expecting should
be happening and something else is wrong.
Please reply.
thanks,
Veebo
 

{smallsort}

Re:How to Drag and Drop a TSpeedButton -- Problems!

"Veebo" < XXXX@XXXXX.COM >wrote:
Quote
[...] One thing I didn't understand is..
Control->ControlStyle << csDisplayDragImage;

Isn't this supposed to allow the Control that is BEING DRAGGED
to be visible from point A to point B?
The code I posted creates a drag image. For that drag image to
be visable while over a specific control, that specific control
must have csDisplayDragImage included in it's ControlStyle. The
reason for the function is that by default, only one or 2 types
of control have that flag set.
Quote
In my case, when I start dragging the control, it stays at A
(all I see is the cursor in drag mode). When I drop the Control
at point B, only then I see it at somewhere other than point A.
To be clear, the control that is being dragged does not move
until after the drag operation has ended and only then *if*
the target has accepted it. What you should be seeing is an
opaque or 'ghost' image of the control being dragged around.
Quote
Do you see my problem?
I'm guessing that the csDisplayDragImage flag hasn't been set
correctly. If you dynamically allocated a control *after* you
call the function that sets csDisplayDragImage, then you need
to manually set that flag for the new control.
The other thing to keep in mind is that if you are expecting
to see the drag image for controls other than the button I
made, you won't. For that to happen, you have to subclass the
control and override it's DoStartDrag method so that you have
a chance to create the drag image. You will also need to
override the control's DoEndDrag as well so that you can
delete the drag image. The code that you need is exactly the
same as that for the button I created with the exception
that 'inherited' would be typedef'ed as some other control.
Overriding the DragOver as well (as I did) is also a good idea
but can be handled by installing events using the Object
Inspector instead.
Quote
I was expecting to see the control all along staring at point A [...]
I hope that I made it clear that you would see the drag image -
not the control - moving.
The code I posted is complete and simple enough to get running.
Spend a few minutes doing that and you'll know exactly what to
expect.
~ JD