Delphi: Getting Copy/Paste to work - delphi

So I want to add a Copy/Paste function to my Delphi application that draws different shapes and does stuff with them.
Here's the on even handler for the Copy menu item:
procedure TForm1.Copy1Click(Sender: TObject);
begin
Clipboard.Open;
if SelectShape <> nil then
clipboard.SetComponent(SelectShape);
Clipboard.Close;
end;
And I get the error:
Incompatible types: 'TComponent' and 'TBaseShape'
TBaseShape is the ancestor class for all shapes in my application
I have no idea why it doesn't work...

Another approach would be to use a private data format, and serialize your object to XML or another easy-to-debug text-based structure, and put that onto the clipboard. It would also be useful/polite to render your object (assuming it's a graphic of some sort) onto a Bitmap, and place that on the clipboard as well (clipboard can hold multiple/many formats simultaneously) so that the user can paste into paint, word, etc., and get "something".
Here's a question that does something along these lines, using GPX data
How to paste a custom format clipboard data into a TMemo?

Related

Delphi use memory stream to read lines

I have made a C++ program that uses ofstream to create 2 files in my disk: one is called details.obf and the other is records.txt. The file details has only 1 line inside (1 integer) and the file records.txt has a non fixed number of lines (they are all strings).
With the code below I can get the value inside the file details. It's pretty simple and I am using a MemoryStream.
m := TMemoryStream.Create;
try
try
m.LoadFromFile(TPath.Combine(TPath.GetHomePath, 'details.obf'));
m.Read(i, sizeOf(i));
//other stuff...
except
//...
end;
finally
m.Free;
end;
With the code below instead I am reading the content of the records file:
a := TStreamReader.Create('C:\Users\betom\Desktop\records.txt');
try
while not(a.EndOfStream) do
begin
Memo1.Lines.Add(a.ReadLine);
end;
finally
a.Free;
end;
In the second block of code I have used a different class (TStreamReader) and I have written that code looking at embarcadero's documentation. I had to use the while not(a.EndOfStream) do because the lenght of records.txt is unknown.
I have seen that MemoryStream (and other classes) are all subclasses of TStream. Why I cannot call something like while not(m.EndOfStream) do with m a TMemoryStream?
I cannot understand the difference between a MemoryStream and a StreamReader. From what I have understood the latter can read automatically all the values in a given range while the first cannot.
Note: I have read on the docs that I can have a TStreamReader and a TStreamWriter and both are fine when I need to create a file that contains some data. I just cannot understand what are memorystream used for if I have the same behavior with a StreamReader.
TStreamReader is a general purpose class for reading text/character data from any stream. It does not support any other form of data in a stream and is not intended for use with any other form of data.
A stream itself might be a file on disk or data on the network or data in memory. Different stream classes exist to provide stream-access to data from those different sources.
TMemoryStream exists specifically to provide access to data in memory as a sequence of bytes, which may be binary data or text/character data or a mixture of both.
To answer your actual question:
I have seen that MemoryStream (and other classes) are all subclasses
of TStream. Why I cannot call something like while not(m.EndOfStream) do
with m a TMemoryStream?
First a correction. It is correct that TMemoryStream and some other stream manipulating classes (e.g. TFileStream) inherit from TStream. That is however not the case with TStreamReader (and TStringReader). These inherit from TTextReader, which together with TTextWriter and its descendents TStreamWriter and TStringWriter mainly exist to provide familiar classes for .Net users.
Here's the hierarchy of some of the discussed classes:
TObject
TStream
TCustomMemoryStream
TMemoryStream
TBytesStream
TStringStream
THandleStream
TFileStream
TWinSocketStream
TOleStream
TTextReader
TStreamReader
TStringReader
TBinaryReader
The answer is that the property EndOfStream is declared in TStreamReader, iow in a different branch than TMemoryStream.
In TStream descendents you can use e.g. the Position and Size properties to determine if you are at the end of the stream.

How to add Exif functionality to TBitmap?

I want to create a general purpose function that loads 'any' type of image (gif, jpg, bmp, png, etc) from disk and returns a bitmap.
function LoadGraph(FileName: string): TBitmap; {pseudocode}
begin
if FileIsJpeg then
jpeg.LoadFromFile;
Bitmap.Exif.Assign(Jpeg.Exif);
end;
The thing is that I need to have access to the Exif data when the input type is Jpeg.
So, I wanted to create a class helper like this:
TYPE
TBitmapHelper = class helper for TBitmap
public
FExifData: TExif;
end;
However, it seems that the Delphi compiler does not have this capability (yet?) as I get this compile error:
E2599 Field definition not allowed in helper type
How to achieve this?
There is quite complex hierarchy of graphic objects in Delphi intended to work with various image formats: TGraphic is abstract class for some image you can load from file, save to file and draw on canvas. TPicture is container for TGraphic which allows you to write just one line of code:
Picture.LoadFromFile(Filename);
and it will look up correct graphic class, create it and load your image.
So one of solutions for you would be to use TPicture instead of TBitmap. For TBitmap and its descendants TPicture will contain Bitmap property, while for the others you can draw on Canvas or assign Picture.Graphic to your TBitmap (works for TJPEGImage, TPNGImage, but still fails with TIcon).
If it's too bulky, for example, you need not just show image on screen but modify it in some way and not think each time how it's actually represented, I'd recommend to create descendant from TBitmap, not just helper, it's quite possible, you can pass such an object everywhere in program where TBitmap is expected, but now you can extend it with new fields. I'm going to do similar thing to add ICC profiles capability (supported in BitmapHeaderV5).

How to get the word under the cursor in a Windows Word editor or in a Pdf editor with using Delphi?

I'm trying to prepare a Delphi Project. In this project I need get the word which was selected by user in Windows Word editor or in Pdf editor. So far I saw some examples about getting the word under the cursor in Memo or in Notbook, but I need to get the word under the cursor in other editor like MsWord or AdobePdf editor.
This works for me using D7 and Word 2007:
procedure TForm1.GetSelectedText;
var
MSWord,
Selection,
SelectedText : OleVariant;
begin
try
MsWord := GetActiveOleObject('Word.Application');
except
try
MsWord := CreateOleObject('Word.Application');
MsWord.Visible := True;
except
Exception.Create('Error');
end;
end;
Selection := MSWord.Selection;
if not VarIsEmpty(Selection) then begin
SelectedText := Selection.Text;
Caption := SelectedText;
end;
end;
Obviously, it would be trivial to re-write the above as a stand-along function and pass in a reference to an existing MSWord object, if you already have one elsewhere in your code.
Like I said in my comment, doing the equivalent in Acrobat/Pdf is a different technical question, requiring its own q. However, having investigated it, I don't think there's a straightforward method of doing it.
If you import the Acrobat type library into Delphi, giving you a unit Acrobat_Tlb.Pas, you'll see that it contains a COM object interface, CAcroPDDoc.
You can instantiate that object for the document currently active in Acrobat. However, one* of the weird things about Acrobat's COM object model is that it doesn't provide any useful access to the text contents, if any, in the document.
However, from the CAcroPDDoc interface, you can get access to Acrobat's internal JavaScript object for the document by using a call to CAcroPDDoc.GetJSObject.
Once you've got a reference to the JS object, you can do a very limited amount of text processing, in particular there's a method GetPageNthWord(Page, WordNum, StripWhiteSpace) that "does what it says on the tin". That's as far as I've got, implementation-wise (if you want to see the code, post a new question).
From there, if you have the time, know-how and inclination - and I currently don't - you can apparently retrieve another JS object representing the on-screen selection rectangle and by somehow comparing that with the coordinates of the various Nth words from GetPageNthWord, you can identify which of them is/are selected. Not QED, ISTM.
All of which is a sufficient performance that if it were me, I'd just tell the user to copy the selection to the clipboard, where it would be easy to pick up from Delphi code.
* - Sometimes working with the Acrobat automation I get the feeling that Adobe have gone out of their way to not make it easy to get at the text in a .PDF file. Whether that's anything to do with the possibly apocryphal story that originally PDF was conceived as a write-only medium, so that you could guarantee that you could put one in the field and be sure that it couldn't be modified, I don't know.

How can i work RegisterClass() to units

Base from the anwser How to eliminate variables... i get and accepted it works great when i have all this components and make the actions like a button1.click from the main form...
But i use to make the actions from units... so
When i click a button i great a procedure DoMaths(Sender: TObject);
procedure Tform1.DoMaths(Sender: TObject);
begin
if TButton1(Sender).hint := 'Make the standard Package' then
do_Maths_standard_package;
end;
the do_Maths_standard_package is in unit ComplexMaths.
is the procedure do_Maths_standard_package form unit ComplexMaths it calls some components form Form1... like Form1.label1 etc...
So when i call the RegisterClass(TLabel) and erase the Tlabel from the type it gives an error that it cant find the Label1...
Please can someone help me so not to do the hole program from the start...
Thank you..
You might be able to reference your components like this:
TLabel(Form1.FindComponent('Label1')).Caption := '...';
TCheckBox(Form1.FindComponent('CheckBox12')).Checked := False;
But it's really a pain...
I think you have two options.
1) you can assign each component a unique numeric ID.
And save it into .Tag property.
Just like u use to generate and bind IDs in .HelpContext properties.
Then to get the control by number you would enumerate Form.Controls and get the one with proper Tag value.
The problem would be to have two separate ID lists, in PAS files and DFM files, in sync. Mistyping would be hard to notice. Especially since you have no constants in DFM but only "magic numbers".
2) Set .Name property and use iMan Biglari's recipe - FindComponent by name.
The question is if you can have .Name but not variable. Since no one answers - just try and see.
To my experience - with Delphi 5, hopefully D7 is mostly the same - you can just delete the variable.
If you made wrong declaration of variable, then Delphi editor would notice and ask to correct it.
If you have variable without DFM object, Delphi would notice it and ask to remove it.
But if there is DFM object without corresponding variable, then Delphi editor is oblivious. Maybe it thinks that the object is inherited or whatever.
But if you did not declare it at all it does not mind.
However, since you deleted Names, it looks like it is not possible to you for some reason.
In both cases you would have to cache value if some procedure makes a lot of accesses to some control. And maybe even across the procedures. In effect yu would manually restore those variables at least for most used controls.

Delphi ignores CS_OWNDC flag when creating TFrame component, wglMakeCurrent subsequently fails

I'm trying to put together some components for using OpenGL in RAD Studio 2009. I want to have multiple Rendering Contexts.
My idea is to have something like a "main OpenGL component", called GLMaster. It's a TFrame descendant so it should provide me with a DC. Also there's a component for a GLMonitor, acting as camera.
I create an OpenGL RC in the overriden GLMaster.Loaded inside an "if not (csDesigning in ComponentState) then"-clause. The DC for this I do not keep, it's stated in the RAD Studio help that you shouldn't:
"TWinControl.Handle
Do not refer to the Handle property during component creation or streaming. The underlying window does not exist until the first time the Handle property is referenced. When this occurs, the HandleNeeded method is called automatically."
I handle this by function pointers in components using GLMaster pointing to GLMaster.GetCurrentDC (returns a HDC).
During destruction GLMonitor wants to clean up some render-to textures and other OpenGL resources. When retrieving the DC for a call to wglMakeActive the function pointer is followed and execution jumps to GLMaster.GetCurrentDC. Pointer(Self) tells me that it's the same GLMaster that we created the "master RC" in during the streaming of the components. The property [GLMaster.]Handle is now invalid! If I however free the GLMonitor in the app form's OnClose (using simply GLMonitor_1.Free;) the Handle inside GLMaster.GetCurrentDC is valid end everything works.
I managed to see that the handle used in GLMaster.Loaded is not the same (!?!?) as after the initialization is done and the app form is showed to the user. When I found this I googled my problem and added an overriden CreateParams to add CS_OWNDC to the GLMaster component. A breakpoint there tells me that it's being executed.
Why does the VCL/Delphi silently provides the newly created components with other Handles, and thereby indirectly other DCs, even if I provide the OWNDC flag? Is there someway to get an "ALL components are now loaded and their properties are read from the .dfm file and"-message so I can run GLMaster.InitEverything inside the component?
Right now I'm thinking the fastest way onward is to put this InitEverything in the main form's OnShow handler. And then a call to GLMatser.CleanAll in the main form's OnClose. But I don't want any code in the form, that's the reason I created the components in the first place!
P.S.
Not that I think it matters for this particular problem but I use OpenGL 3.2 Core Profile.
I'm answering the "Why does the VCL/Delphi silently provides the newly created components with other Handles".
Why an control's Window Handle might change at run-time
Your TFrame (an TWinControl descendant) is placed on an other TWinControl, let's say a TForm. The parent container provides property wrappers around many settings, allowing us to easily make changes; Some of those changes can't be implemented without re-creating the windhow handle. You can get a long list of properties that cause this to happen by doing a search for RecreateWnd in Forms.pas, Controls.pas etc.
Examples of properties that call RecreateWnd to implement a change at run-time:
TScollBox.BorderStyle
TForm.BorderIcons (for MDI childs)
When your TFrame's parent needs to change Window Handle, your TFrame is going to have to change Window Handle as well. Your Delphi Control, no matter the descendense, needs to be handle this, and other controls have it worst: Controls that are implemented as wrappers around Windows controls need to save state and reload state (TEdit, TComboBox, ...)
What I think is going on
While streaming (loading your form) some control does something that needs a Window Handle before loading finishes. It's very likely your own code! After everything finishes loading the Form might need to re-create it's Handle, and this in turn causes your Frame's handle to be changed.
What you might want to override
Given the way the VCL works, you need to be prepared for your Window Handle to change. You should do a search in Controls.pas for the words Handle, HDC. If your control is so intimately linked to it's Window Handle and HDC, it's in your best interest to read up on this stuff.
Take a look at those routintes. Maybe you can sopot a better place to hook:
CreateHandle
CreateWnd
DestroyHandle
DestroyWnd
GetDeviceContext
If the title is the question, then no, Delphi does not ignore CS_OWNDC when creating a frame. Just tested with the below procedure with a frame on a form. When the flag is not specified, the line is drawn on itself again and again, resulting with a line from (0,0) to (10,10). When the DC is owned, the line extends from (0,0) to (50,50), no need to tell but the retrieved DC is same at all times.
procedure TFrame2.WmPaint(var Msg: TWMPaint);
var
i: Integer;
DC: HDC;
Pt: TPoint;
begin
inherited;
for i := 1 to 5 do begin
DC := GetDC(Handle);
GetCurrentPositionEx(DC, #Pt);
LineTo(DC, Pt.X + 10, Pt.Y + 10);
ReleaseDC(Handle, DC);
end;
end;
if your question is this:
ALL components are now loaded and their properties are read from the .dfm file and
The answer is:
Yes, just override the Loaded method of your component. It is called when all the references are valid (after dfm reading is finished).

Resources