Solved

How to access and read dragged text?

Posted on 1998-08-20
12
526 Views
Last Modified: 2010-04-04
If I select text from some other application and then drag
it to drop it into my RichEdit-control, how I can access and read this text before it will display in my control?
OnDragDrop doesn't effect.
0
Comment
Question by:raunol
  • 6
  • 3
  • 2
  • +1
12 Comments
 
LVL 10

Expert Comment

by:viktornet
ID: 1337336
Oh, kinda difficult task!

Regards,
Viktor
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337337
Yo,
you will have to implement OLE drag and drop services. Use the RegisterDragDrop function to register your rich edit as a drop target and provide an implementation of IDropTarget as an interface. In its DragEnter implementation, you ca nquery the clipboard format of the IDataObject and read its content with the GetData method.

Slash/d003303
0
 

Author Comment

by:raunol
ID: 1337338
d003303,

Thank's for the hint. I grade it,if You can solve what's wrong here:


First I made TDropTarget type:

Uses ole2

Type

TDropTarget=Class(iDropTarget)
Public
   function Drop(DataObj:IDataObject; rfKeyState:Longint;
                 const pt: TPoint; var dwEffect:Longint):Hresult;
                 override;

   function DragOver(grfKeyState: Longint; const pt: TPoint;
                    var dwEffect: Longint): HResult;override;
End;

TMyRich=Class(TCustomRichEdit)
public
      DropTarget:TDropTarget;

End

Then I put this into the MyRich's WM_CREATE handler: RegisterDragDrop(handle,DropTarget);

When I now test it and drag text from an other edit-application to my control, nothing will happen - no effects will disapear
under functions TdropTarget.Drop or TDropTarget.DragOver ??
 
- Rauno -
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1337339
just an idea.. why not when selecting, copying it to the clipboard? (Or isn't this also already done, just for dragging and dropping to another application?) Zif?. The clipboard can be easely entered from delphi.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337340
OK,
you don't have any effects because the RichEdit control is already registered as a Drag/Drop target when you call RegisterDragDrop, take a look at the return value. You can re-register it with RevokeDragDrop and then call RegisterDragDrop again. But, all RichEdit internal Drag/Drop events do not work anymore :-(

I'm currently looking for a way to get the previous IDropTarget of a registered drop target window, so you can proxy/stub the whole thing in your implementation.

Slash/d003303
0
 

Author Comment

by:raunol
ID: 1337341
d003303,

"no effects will appear" I should had written ;-)
(My english is not good)

Yes, I notised the function returned E_INVALIDARG.
I changed it a little and then it returned
DRAGDROP_E_ALREADYREGISTERED.

I also tried this RevokeDragDrop, but it caused
always error.


ZifNaf,

If I drag text from an other application to my
control, for example from WordPad, the text isn't
there in the clipboard.

There is the SelectionChange procedure in the RichEdit.
I dropped dragged text different position when the
SelectionChange activated. Under the procedure I
opened the ClipBoard and red the contents, but dragged
text wasn't there. There was some other text copied
before.

Obviously, due to OLE operation, I think the
text is stored somewhere in iDropTarged. But how to
get out it from there, before it will display in my control?
I want to do this, because I want to change the style of the
text (size and so on) before it will display.

- Rauno


0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 4

Accepted Solution

by:
d003303 earned 200 total points
ID: 1337342
Yep, got it.
Here's the code. Create a form, drop a richedit onto is and apply this code:

// BOC

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ActiveX;

type

  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    procedure OnTextDropped(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const CF_RichTextFormat = 49226;

type

  TDropTargetProxy = class(TInterfacedObject, IDropTarget)
  private
    FOriginalTarget : IDropTarget;
    FOnTextDropped  : TNotifyEvent;
  public
    constructor Create(TargetToProxy: IDropTarget);
    destructor Destroy; override;
    function DragEnter(const dataObj: IDataObject; grfKeyState: Longint;
      pt: TPoint; var dwEffect: Longint): HResult; stdcall;
    function DragOver(grfKeyState: Longint; pt: TPoint;
      var dwEffect: Longint): HResult; stdcall;
    function DragLeave: HResult; stdcall;
    function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint;
      var dwEffect: Longint): HResult; stdcall;
    // manipulation interface
    property OnTextDropped: TNotifyEvent read FOnTextDropped write FOnTextDropped;
  end;

  TDataObjectProxy = class(TInterfacedObject, IDataObject)
  private
    FOriginalDataObject : IDataObject;
    FDroppedText,
    FReplacedText       : PChar;
    FReplacedTextSize   : LongInt;
    FOnTextDropped      : TNotifyEvent;
  public
    constructor Create(DataObjectToProxy: IDataObject);
    destructor Destroy; override;
    function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium):
      HResult; stdcall;
    function GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium):
      HResult; stdcall;
    function QueryGetData(const formatetc: TFormatEtc): HResult;
      stdcall;
    function GetCanonicalFormatEtc(const formatetc: TFormatEtc;
      out formatetcOut: TFormatEtc): HResult; stdcall;
    function SetData(const formatetc: TFormatEtc; var medium: TStgMedium;
      fRelease: BOOL): HResult; stdcall;
    function EnumFormatEtc(dwDirection: Longint; out enumFormatEtc:
      IEnumFormatEtc): HResult; stdcall;
    function DAdvise(const formatetc: TFormatEtc; advf: Longint;
      const advSink: IAdviseSink; out dwConnection: Longint): HResult; stdcall;
    function DUnadvise(dwConnection: Longint): HResult; stdcall;
    function EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
      stdcall;
    // manipulation interface
    procedure ReplaceDroppedText(NewText: PChar; NewTextSize: LongInt);
    property DroppedText: PChar read FDroppedText;
  end;

constructor TDropTargetProxy.Create(TargetToProxy: IDropTarget);
begin
  inherited Create;
  FOriginalTarget := TargetToProxy;
  FOriginalTarget._AddRef;
end;

destructor TDropTargetProxy.Destroy;
begin
  FOriginalTarget._Release;
  inherited Destroy;
end;

function TDropTargetProxy.DragEnter(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult;
begin
  Result := FOriginalTarget.DragEnter(dataObj, grfKeyState, pt, dwEffect);
end;

function TDropTargetProxy.DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult;
begin
  Result := FOriginalTarget.DragOver(grfKeyState, pt, dwEffect);
end;

function TDropTargetProxy.DragLeave: HResult;
begin
  Result := FOriginalTarget.DragLeave;
end;

function TDropTargetProxy.Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult;
var DataObjectProxy : TDataObjectProxy;
begin
  // create proxy data object
  DataObjectProxy := TDataObjectProxy.Create(dataObj);
  DataObjectProxy.FOnTextDropped := FOnTextDropped;
  DataObjectProxy._AddRef;
  // pass proxy data object to caller
  Result := FOriginalTarget.Drop(DataObjectProxy, grfKeyState, pt, dwEffect);
  DataObjectProxy._Release;
end;

constructor TDataObjectProxy.Create(DataObjectToProxy: IDataObject);
begin
  inherited Create;
  FOriginalDataObject := DataObjectToProxy;
  FOriginalDataObject._AddRef;
  FDroppedText := nil;
  FReplacedText := nil;
end;

destructor TDataObjectProxy.Destroy;
begin
  FOriginalDataObject._Release;
  inherited Destroy;
end;

function TDataObjectProxy.GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult;
var MemPointer    : Pointer;
    RichText      : PChar;
    TextLen       : LongInt;
begin
  Result := FOriginalDataObject.GetData(formatetcIn, medium);

  // check for RichEdit format
  if (Succeeded(Result))
    and (formatetcIn.cfFormat = CF_RichTextFormat)
    and (formatetcIn.tymed = TYMED_HGLOBAL) then
   begin
     // here's the point to manipulate the data !

     TextLen := GlobalSize(Medium.hGlobal) + 1;
     MemPointer := GlobalLock(Medium.hGlobal);
     GetMem(RichText, TextLen);
     FillChar(RichText[0], TextLen, 0);
     try
       StrLCopy(RichText, MemPointer, TextLen - 1);
       FDroppedText := RichText;
       if Assigned(FOnTextDropped)
        then FOnTextDropped(Self);
     finally
       FreeMem(RichText, TextLen);
       GlobalUnlock(Medium.hGlobal);
     end;

     FDroppedText := nil;
     if FReplacedText = nil
      then Exit;

     GlobalReAlloc(Medium.hGlobal, FReplacedTextSize, GMEM_MOVEABLE);
     MemPointer := GlobalLock(Medium.hGlobal);
     try
       StrCopy(MemPointer, FReplacedText);
     finally
       FreeMem(FReplacedText, FReplacedTextSize);
       FReplacedText := nil;
       GlobalUnlock(Medium.hGlobal);
     end;
   end;
end;

function TDataObjectProxy.GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium):HResult;
begin
  Result := FOriginalDataObject.GetDataHere(formatetc, medium);
end;

function TDataObjectProxy.QueryGetData(const formatetc: TFormatEtc): HResult;
begin
  Result := FOriginalDataObject.QueryGetData(formatetc);
end;

function TDataObjectProxy.GetCanonicalFormatEtc(const formatetc: TFormatEtc; out formatetcOut: TFormatEtc): HResult;
begin
  Result := FOriginalDataObject.GetCanonicalFormatEtc(formatetc, formatetcOut);
end;

function TDataObjectProxy.SetData(const formatetc: TFormatEtc; var medium: TStgMedium; fRelease: BOOL): HResult;
begin
  Result := FOriginalDataObject.SetData(formatetc, medium, fRelease);
end;

function TDataObjectProxy.EnumFormatEtc(dwDirection: Longint; out enumFormatEtc: IEnumFormatEtc): HResult;
begin
  Result := FOriginalDataObject.EnumFormatEtc(dwDirection, enumFormatEtc);
end;

function TDataObjectProxy.DAdvise(const formatetc: TFormatEtc; advf: Longint; const advSink: IAdviseSink; out dwConnection: Longint): HResult;
begin
  Result := FOriginalDataObject.DAdvise(formatetc, advf, advSink, dwConnection);
end;

function TDataObjectProxy.DUnadvise(dwConnection: Longint): HResult;
begin
  Result := FOriginalDataObject.DUnadvise(dwConnection);
end;

function TDataObjectProxy.EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
begin
  Result := FOriginalDataObject.EnumDAdvise(enumAdvise);
end;

procedure TDataObjectProxy.ReplaceDroppedText(NewText: PChar; NewTextSize: LongInt);
begin
  if FDroppedText = nil
   then Exit;
  FReplacedTextSize := NewTextSize;
  GetMem(FReplacedText, FReplacedTextSize);
  StrLCopy(FReplacedText, NewText, FReplacedTextSize);
end;

////////////////////////////////////////////////////////////////////////////////

procedure TForm1.FormCreate(Sender: TObject);
var PropHandle         : THandle;
    DropTarget         : TDropTargetProxy;
    RichEditDropTarget : IDropTarget;
    RegisterResult : LongInt;
    ErrorStr : string;
begin
  // get original drop target
  PropHandle := GetProp(RichEdit1.Handle, 'OleDropTargetInterface');

  // look if existant
  if PropHandle = 0
   then Exit;

  // create proxy drop target instance
  RichEditDropTarget := IDropTarget(Pointer(PropHandle));
  DropTarget := TDropTargetProxy.Create(RichEditDropTarget);
  DropTarget.OnTextDropped := OnTextDropped;

  // unregister original drop target
  RevokeDragDrop(RichEdit1.Handle);

  // ... and register our proxy one
  RegisterResult := RegisterDragDrop(RichEdit1.Handle, DropTarget as IDropTarget);

  // error check
  if (RegisterResult <> S_OK) and (RegisterResult <> DRAGDROP_E_ALREADYREGISTERED) then
   begin
     case RegisterResult of
       DRAGDROP_E_INVALIDHWND : ErrorStr := 'Invalid window handle';
       E_OUTOFMEMORY : ErrorStr := 'Out of memory';
     end;
     DropTarget.Free;
     raise Exception.Create(ErrorStr);
   end;
end;

procedure TForm1.OnTextDropped(Sender : TObject);
//(RichText: PChar; var NewText: PChar; var NewTextLen: LongInt);
// insert string contains trailing '}', overriden by a #0 in code
const InsertString: PChar = ' >through Slash''s proxy<}';
var DataObject: TDataObjectProxy;
    NewTextLen: LongInt;
    NewText,
    StrPointer: PChar;
begin
  if Sender is TDataObjectProxy then
   begin
     DataObject := (Sender as TDataObjectProxy);
     // length + terminator + insertion string
     NewTextLen := StrLen(DataObject.DroppedText) + 1 + StrLen(InsertString);
     GetMem(NewText, NewTextLen);
     FillChar(NewText[0], NewTextLen, 0);

     // find closing bracket
     StrPointer := StrRScan(DataObject.DroppedText, '}');
     // cut off original string
     StrPointer[0] := #0;
     StrCopy(NewText, DataObject.DroppedText);
     StrCat(NewText, InsertString);
     DataObject.ReplaceDroppedText(NewText, NewTextLen);
     FreeMem(NewText, NewTextLen);
   end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  RevokeDragDrop(RichEdit1.Handle);
end;

end.

// EOC

This can easily be implemented into any richedit derivant. What is does is to obtain the previous IDropTarget interfave of the richedit and replaces it with its own. When the data is written over (see source), you can manipulate is however you like.

Have fun,
Slash/d003303
0
 

Author Comment

by:raunol
ID: 1337343
Thanks for Your kindness, the code looks fine,
but I have still broblems. I think, the code is for
Delphi3 or 4, and I have Delphi 2 :-(

I don't find ActiveX unit for me, but obviously OLE2 unit
works as well, becouse there is all what are used in the
code - except TInterfacedObject. But Class-clause respects
only one descent so I must remove TInterfacedObject anyway.

Also I must make some other changes. There is some "out" type
in the code or it is bug (e.g. out medium) I changed it "Var".
Also I must change "Const" types to "Var", because
functions (e.g. GetData) don't respect Class constants
as variable. Then I change _Addref And _release without "_" Addref And Release.

That's all. After those changes Delphi2 compiled the code.
But If I try to run the program, it breaks off in the RegisterDragDrop-function which is under the
FormCreate -event, and causes run-time error 210
that means: "Object not initialized". What to do?

- Rauno  -
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337344
I still got a Delphi 2 installed somewhere, I'll port you the code. Hang on.

Slash/d003303
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337345
hum, hum, hum. I re-designed the objects for D2 and it will not run properly. Bad, bad. I keep on diggin...
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1337346
d003303, If you keep digging, you'll end up in China :-).
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337347
...or in New Zealand...
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now