I have written a class for reading and writing PPM files (don't ask, I didn't chose this format). I would like it to be a part of the TBitmap loading/saving system.
Does any one know how I can add this support? Do I really have to write/install a full codec?
SOLUTION:
With Remy Lebeau post I managed to write and register a codec. However all the needed functions aren't documented so It needed some trial/error to get it to work.
To register the new codec you need to use the static member RegisterBitmapCodecClass of TBitmapCodecManager like this.
TBitmapCodecManager::RegisterBitmapCodecClass(".ppm","portable pixmap",true,__classid(TMyCodec));
The codec need these functions to be defined:
class TMyCodec : public TCustomBitmapCodec {
public:
bool __fastcall LoadFromStream(System::Classes::TStream* const AStream, Fmx::Surfaces::TBitmapSurface* const Bitmap);
bool __fastcall LoadFromFile(const System::UnicodeString AFileName, Fmx::Surfaces::TBitmapSurface* const Bitmap);
bool __fastcall SaveToFile(const System::UnicodeString AFileName, Fmx::Surfaces::TBitmapSurface* const Bitmap, const PBitmapCodecSaveParams SaveParams = (PBitmapCodecSaveParams)(0x0));
bool __fastcall SaveToStream(System::Classes::TStream* const AStream, Fmx::Surfaces::TBitmapSurface* const Bitmap, const System::UnicodeString Extension, const PBitmapCodecSaveParams SaveParams = (PBitmapCodecSaveParams)(0x0));
__classmethod System::Types::TPointF __fastcall GetImageSize(const System::UnicodeString AFileName);
__classmethod bool __fastcall IsValid(System::Classes::TStream* const AStream);
bool __fastcall LoadThumbnailFromFile(const System::UnicodeString AFileName, const float AFitWidth, const float AFitHeight, const bool UseEmbedded, Fmx::Surfaces::TBitmapSurface* const Bitmap);
};
The class Fmx::Surfaces::TBitmapSurface has no trace of documentation, but the IDE provided me with available functions. I figured the Pixels[x][y] array is used to read/write the pixels.
After the class has been registered, you can read the new image type as usual with TBitmap->LoadFromFile("");
Enjoy!
PS. Those who voted to close this, please put in a comment to why? How can we improve if we don't know what mistakes we do?
You need to derive a new class from TCustomBitmapCodec and implement the virtual LoadFrom...() and SaveTo...() methods, and then register the class at app startup using TBitmapCodecManager::RegisterBitmapCodecClass().
Related
I am trying to use the abbrevia package ( https://github.com/TurboPack/Abbrevia ) with c++Builder in a console application.
First i tried to manually install the package. But then i found it on GetIt. Issue persists no matter how it setup the package. I think the path settings i did manually are the same as the package installer did.
It results always in unresolved externel for Abdfbase::TAbDeflateHelper::
This is the original error message:
[ilink32 Fehler] Error: Nicht auflösbares externes 'Abdfbase::TAbDeflateHelper::' referenziert von c:\pathtotheobjectfile\main.obj
Just setting up the path and compiling the projects for the c++builder ( therefore getting the objects ) is all the setup aks for. And with GetIt i dont even have to do that.
This is my most simple start which i cant even get to link.
#pragma hdrstop
#pragma argsused
#ifdef _WIN32
#include <tchar.h>
#else
typedef char _TCHAR;
#define _tmain main
#endif
#include <stdio.h>
#include <AbDfBase.hpp>
using namespace Abdfbase;
int _tmain(int argc, _TCHAR* argv[])
{
TAbDeflateHelper* helper = new TAbDeflateHelper();
return 0;
}
This is my first try using c++builder. What system setting or special code am I missing?
For reference the start of the .hpp imported which show the class i try to instantiate.
/ CodeGear C++Builder
// Copyright (c) 1995, 2021 by Embarcadero Technologies, Inc.
// All rights reserved
// (DO NOT EDIT: machine generated header) 'AbDfBase.pas' rev: 35.00 (Windows)
#ifndef AbdfbaseHPP
#define AbdfbaseHPP
#pragma delphiheader begin
#pragma option push
#pragma option -w- // All warnings off
#pragma option -Vx // Zero-length empty class member
#pragma pack(push,8)
#include <System.hpp>
#include <SysInit.hpp>
#include <System.SysUtils.hpp>
#include <System.Classes.hpp>
//-- user supplied -----------------------------------------------------------
namespace Abdfbase
{
//-- forward type declarations -----------------------------------------------
class DELPHICLASS TAbDeflateHelper;
class DELPHICLASS TAbLogger;
class DELPHICLASS TAbNodeManager;
class DELPHICLASS EAbAbortProgress;
class DELPHICLASS EAbPartSizedInflate;
class DELPHICLASS EAbInflatePasswordError;
class DELPHICLASS EAbInternalInflateError;
class DELPHICLASS EAbInflateError;
class DELPHICLASS EAbInternalDeflateError;
class DELPHICLASS EAbDeflateError;
//-- type declarations -------------------------------------------------------
typedef System::StaticArray<int, 536870911> TAbDfIntegerList;
typedef TAbDfIntegerList *PAbDfIntegerList;
typedef void __fastcall (__closure *TAbProgressStep)(int aPercentDone);
class PASCALIMPLEMENTATION TAbDeflateHelper : public System::TObject
{
typedef System::TObject inherited;
private:
int FAmpleLength;
int FChainLength;
System::UnicodeString FLogFile;
int FMaxLazy;
TAbProgressStep FOnProgressStep;
int FOptions;
__int64 FPartSize;
__int64 FSizeCompressed;
__int64 FSizeNormal;
__int64 FStreamSize;
int FWindowSize;
System::WideChar FZipOption;
protected:
void __fastcall dhSetAmpleLength(int aValue);
void __fastcall dhSetChainLength(int aValue);
void __fastcall dhSetLogFile(const System::UnicodeString aValue);
void __fastcall dhSetMaxLazy(int aValue);
void __fastcall dhSetOnProgressStep(TAbProgressStep aValue);
void __fastcall dhSetOptions(int aValue);
void __fastcall dhSetWindowSize(int aValue);
void __fastcall dhSetZipOption(System::WideChar aValue);
public:
__fastcall TAbDeflateHelper();
void __fastcall Assign(TAbDeflateHelper* aHelper);
__property int AmpleLength = {read=FAmpleLength, write=dhSetAmpleLength, nodefault};
__property int ChainLength = {read=FChainLength, write=dhSetChainLength, nodefault};
__property System::UnicodeString LogFile = {read=FLogFile, write=dhSetLogFile};
__property int MaxLazyLength = {read=FMaxLazy, write=dhSetMaxLazy, nodefault};
__property int Options = {read=FOptions, write=dhSetOptions, nodefault};
__property __int64 PartialSize = {read=FPartSize, write=FPartSize};
__property System::WideChar PKZipOption = {read=FZipOption, write=dhSetZipOption, nodefault};
__property __int64 StreamSize = {read=FStreamSize, write=FStreamSize};
__property int WindowSize = {read=FWindowSize, write=dhSetWindowSize, nodefault};
__property __int64 CompressedSize = {read=FSizeCompressed, write=FSizeCompressed};
__property __int64 NormalSize = {read=FSizeNormal, write=FSizeNormal};
__property TAbProgressStep OnProgressStep = {read=FOnProgressStep, write=dhSetOnProgressStep};
public:
/* TObject.Destroy */ inline __fastcall virtual ~TAbDeflateHelper() { }
};
Setting up search paths doesn't tell the compiler/linker which specific libraries you are actually using. Your project is not linking to the Abbrevia package library, hence the error.
Had you created a GUI app instead and visually dropped the Abbrevia component(s) onto a TForm, TFrame, or TDataModule at design-time, the necessary package library reference(s) would have been added for you.
But, in a console app, without at least a TDataModule, you will have to manually add reference(s) to Abbrevia's .bpi package library(s) to the project.
There is a library for parsing.
I added ALXmlDoc.pas to the project, C++Builder created ALXmlDoc.hpp
In ALXmlDoc.pas lines 177,178:
property Nodes [const Name: AnsiString]: TALXMLNode read GetNodeByName; default;
property Nodes [const Index: integer]: TALXMLNode read GetNodeByIndex; default;
In ALXmlDoc.hpp:
__property TALXMLNode * Nodes [const System :: AnsiString Name] = {read = GetNodeByName / *, default */};
__property TALXMLNode * Nodes [const int Index] = {read = GetNodeByIndex};
I get an error about duplication - how to fix it?
In .pas line 705
property OnParseText: TAlXMLParseTextEvent read FonParseText Write FonParseText; // [added from TXMLDocument]
In ALXmlDoc.hpp:
__property _di_TAlXMLParseTextEvent OnParseText = {read = FonParseText, write = FonParseText};
__interface TAlXMLParseTextEvent: public System :: IInterface
{
virtual void __fastcall Invoke (System :: TObject * Sender, const System :: AnsiString Path, const System :: AnsiString Str) = 0;
};
private:
_di_TAlXMLParseTextEvent FonParseText;
protected:
void __fastcall DoParseText (const System :: AnsiString Path, const System :: AnsiString Str);
In my Unit1.h:
void __fastcall OnParseText (System :: TObject * Sender, const System :: AnsiString Path, const System :: AnsiString Str);
In my Unit1.cpp:
void __fastcall TForm1 :: OnParseText (System :: TObject * Sender, const System :: AnsiString Path, const System :: AnsiString Str)
{
ShowMessage(Str);
}
// ------------------------------------------------ ---------------------------
void __fastcall TForm1 :: Button1Click (TObject * Sender)
{
TALXMLDocument * aXMLDocument = new TALXMLDocument ("root");
aXMLDocument-> OnParseText = OnParseText;
}
I get an error:
[bcc32 Error] Unit1.cpp (30): E2235 Member function must be called or its address taken
How to declare an event?
In C++, array properties can't be overloaded solely on their index type. So you will have to rename one of the offending properties, there is no other option. And then I would suggest you file a report with the library author asking to make the library be more friendly to C++ users.
In the Delphi code, TAlXMLParseTextEvent is a reference to an anonymous method:
TAlXMLParseTextEvent = reference to procedure (Sender: TObject; const Path, Str: AnsiString);
Which is why it gets translated to an __interface on the C++ side (because anonymous methods really are implemented behind the scenes using interfaces). Delphi-style anonymous methods require extra handling in C++, as is documented on Embarcadero's DocWiki:
How to Handle Delphi Anonymous Methods in C++
Under the cover, Delphi implements anonymous methods types (also known as method references) via an interface that implements an Invoke(...) method.
So a method that takes a method reference parameter in Delphi is exposed to C++ as a method that takes an interface.
As such, your C++ code would need to do something more like this instead:
struct TParseTextMethod
{
void operator()(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
ShowMessage(Str);
}
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
// the TMethodRef functor is defined in the documentation,
// you can copy/paste it as-is into your code...
typedef TMethodRef<TAlXMLParseTextEvent,
TParseTextMethod,
void,
TObject *Sender,
const AnsiString,
const AnsiString> MyMethRef;
aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new MyMethRef(TParseTextMethod()));
}
Or, you can streamline the functor usage a bit more by not using the TMethodRef wrapper (see Inheritance and Interfaces and Implementing Interfaces):
class TParseTextMethod : public TCppInterfacedObject<TAlXMLParseTextEvent>
{
public:
TParseTextMethod() {}
INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
void __fastcall Invoke(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
ShowMessage(Str);
}
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new TParseTextMethod());
}
Or, if you want to keep using your OnParseText() method as-is, then you will have to wrap it inside a functor, eg:
void __fastcall TForm1::OnParseText(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
ShowMessage(Str);
}
//------------------------------------------------ ---------------------------
typedef void __fastcall (__closure *TAlXMLParseTextMethod)(TObject *Sender, const AnsiString Path, const AnsiString Str);
struct TParseTextMethod
{
TAlXMLParseTextMethod Method;
TParseTextMethod(TAlXMLParseTextMethod aMethod) : Method(aMethod) {}
void operator()(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
Method(Sender, Path, Str);
}
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
typedef TMethodRef<TAlXMLParseTextEvent,
TParseTextMethod,
void,
TObject *Sender,
const AnsiString,
const AnsiString> MyMethRef;
aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new MyMethRef(TParseTextMethod(&OnParseText)));
}
Or:
void __fastcall TForm1::OnParseText(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
ShowMessage(Str);
}
//------------------------------------------------ ---------------------------
typedef void __fastcall (__closure *TAlXMLParseTextMethod)(TObject *Sender, const AnsiString Path, const AnsiString Str);
class TParseTextMethod : public TCppInterfacedObject<TAlXMLParseTextEvent>
{
public:
TAlXMLParseTextMethod Method;
TParseTextMethod(TAlXMLParseTextMethod aMethod) : Method(aMethod) {}
INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
void __fastcall Invoke(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
Method(Sender, Path, Str);
}
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new TParseTextMethod(&OnParseText));
}
Alternatively, if you are using one of the Clang-based compilers then you can use a C++ style lambda instead of a functor:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
aXMLDocument->OnParseText = [](TObject*, const AnsiString, const AnsiString Str) {
ShowMessage(Str);
};
}
Or:
void __fastcall TForm1::OnParseText(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
ShowMessage(Str);
}
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
aXMLDocument->OnParseText = [this](TObject *Sender, const AnsiString Path, const AnsiString Str) {
OnParseText(Sender, Path, Str);
};
}
I have read this article:
Article
It clearly shows hyperlinks are supported in the footer. I can’t work out how to do it. I don’t want a literal URL in the text but other text that hyperlinks to a help article in the program.
This works:
#include "stdafx.h"
#include "CMyTaskDialog.h"
IMPLEMENT_DYNAMIC(CMyTaskDialog, CTaskDialog)
CMyTaskDialog::CMyTaskDialog(_In_ const CString& strContent,
_In_ const CString& strMainInstruction,
_In_ const CString& strTitle,
_In_ int nCommonButtons,
_In_ int nTaskDialogOptions, _In_ const CString& strFooter)
: CTaskDialog(strContent, strMainInstruction, strTitle, nCommonButtons, nTaskDialogOptions, strFooter)
{
}
CMyTaskDialog::~CMyTaskDialog()
{
}
HRESULT CMyTaskDialog::OnHyperlinkClick(const CString& strHref)
{
HWND hwnd =
HtmlHelp(
GetDesktopWindow(),
_T("d:\\MeetSchedAssist.chm::/") + strHref,
HH_DISPLAY_TOPIC,
NULL);
return S_OK;
}
However, there are two issues still:
CTaskDialog does not have a GetSafeHWnd API call so I don't know how to set it as the parent.
The OnHyperlinkClick is generic so if you have multiple links on the task dialog you might have to test the phrase to decide how you want to handle it.
I want to implement a collection or list using TOwnedCollection / TCollectionItem. I need a persistent list (to load and create from a FileStream) of classes with polymorphism.
Here is (part of) my code so far, but I didn't succeed to create the derived class TGenerator instead of its parent TPowerComponent and add it to the Collection.
//-------------------------------------------------------------------------------------
class TPCCollection : public TOwnedCollection
{
typedef TOwnedCollection inherited;
private:
TPowerComponent* __fastcall GetPowerComponent(int Index);
void __fastcall SetPowerComponent(int Index, TPowerComponent *Value);
public:
__fastcall TPCCollection(TPersistent *Owner);
HIDESBASE TPowerComponent* __fastcall Add(void);
HIDESBASE TPowerComponent* __fastcall Insert(int Index);
__property TPowerComponent* PCCollection[int Index] = {read=GetPowerComponent, write=SetPowerComponent};
};
//-------------------------------------------------------------------------------------
class TPowerComponent : public TCollectionItem
{
typedef TCollectionItem inherited;
public :
int X, Y, Rotation;
PowSymbType HisType;
__fastcall TPowerComponent(TCollection *Collection, PowSymbType AType );
void __fastcall Assign(TPersistent *Source);
virtual void __fastcall Paint(TCanvas * Canvas);
};
//-------------------------------------------------------------------------------------
class TGenerator : public TPowerComponent
{
typedef TPowerComponent inherited;
public :
double PG, Qgmin, Qgmax, Vsch;
__fastcall TGenerator(TCollection *Collection, PowSymbType AType );
void __fastcall Assign(TPersistent *Source);
virtual void __fastcall Paint(TCanvas * Canvas);
};
//-------------------------------------------------------------------------------------
// implementation
//-------------------------------------------------------------------------------------
// TPCCOllection
//-------------------------------------------------------------------------------------
__fastcall TPCCollection::TPCCollection(TPersistent *Owner)
: TOwnedCollection(Owner, __classid(TPowerComponent))
{
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::Add()
{
return static_cast<TPowerComponent>(inherited::Add());
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::Insert(int Index)
{
return static_cast<TPowerComponent>(inherited::Insert(Index));
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::GetPowerComponent(int Index)
{
return static_cast<TPowerComponent>(inherited::GetItem(Index));
}
//-------------------------------------------------------------------------------------
void __fastcall TPCCollection::SetPowerComponent(int Index, TPowerComponent *Value)
{
inherited::SetItem(Index, Value);
}
//-------------------------------------------------------------------------------------
// TPowerComponent
//-------------------------------------------------------------------------------------
__fastcall TPowerComponent::TPowerComponent(TCollection *Collection, PowSymbType AType )
: TCollectionItem(Collection)
{
HisType=AType;
Rotation=0;
}
//-------------------------------------------------------------------------------------
void __fastcall TPowerComponent::Assign(TPersistent *Source)
{
TPowerComponent *Src = dynamic_cast<TPowerComponent>(Source);
if( Src )
{
// copy members from Src...
}
else inherited::Assign(Source);
}
//-------------------------------------------------------------------------------------
// se dessine
void __fastcall TPowerComponent::Paint(TCanvas * Canvas)
{
...
}
//-------------------------------------------------------------------------------------
// TGenerator
//-------------------------------------------------------------------------------------
__fastcall TGenerator::TGenerator(TCollection *Collection, PowSymbType AType )
:TPowerComponent( Collection, AType )
{
PG=0; Qgmin=0; Qgmax=0; Vsch=1.0; Con=-1;
}
//-------------------------------------------------------------------------------------
void __fastcall TGenerator::Assign(TPersistent *Source)
{
TGenerator *Src = dynamic_cast<TGenerator>(Source);
if( Src )
{
// copy members from Src...
}
else inherited::Assign(Source);
}
//-------------------------------------------------------------------------------------
// Usage
TPCCollection * NetWork = new TPCCollection(this);
// Usage to Access all the collection
for( int i=0; i< NetWork->Count; i++)
{
((TPowerComponent*)(NetWork->Items[i]))->Paint(Canvas);
}
To add a TGenerator and not a TPowerComponent, I use:
TGenerator * Gen=new TGenerator( NetWork, Generator);
The creation of the TCollectionItem child automatically add itself to the TCollection
The problem here is that we can't separate the process of item creation from adding it to the collection.
When I need another list that can have some of the items of the first collection list, for example, SelectedComponents can have one or some of the Items of the NetWork Collection, without recreating them.
This can be done with
std::list<TPowerComponent*> SelectedComponents;
but I can't write/read them using FileStream / persistent list. I need to put them in a TCollection but without recreating them.
How?
The RTL's native DFM streaming for TCollection objects only partially supports polymorphic TCollectionItem classes.
You can add polymorphic TCollectionItem objects to a TCollection in code (at runtime, as well as at design-time with the aid of a custom editor), as long as they all derive from a common base class that is passed to the TCollection constructor. And such a collection can even be saved as-is to a DFM.
However, when loading back a DFM, native streaming will force all collection items read from the DFM to use whatever TCollectionItem class type you pass to the TCollection constructor. So, polymorphic classes can't be loaded natively.
The only way to override that behavior is to disable native streaming for the collection (make your TCollection property be non-published, or at least mark it as stored=false), and then stream the collection items manually.
Have your main component (or whatever TPersistent class owns the collection) override the virtual DefineProperties() method to call TFiler.DefineProperty() to register custom reading/writing methods for streaming the collection items. To support polymorphic classes, you will have to write each item's ClassName to the DFM before writing its property values, then read the name back so you know which class to instantiate before then reading property values.
Delphi syntax:
procedure Move(const Source; var Dest; Count: Integer);
C++ syntax:
extern PACKAGE void __fastcall Move(const void *Source, void *Dest, int Count);
I have used Function Move in Delphi long time ago,
recently I want to call it in C++ Builder 6,
I wrote it as the same as I did in Delphi,
the error appearance --> "Expression Syntax".
Dose anyone know how to call it?
or there is other function works similar to it?
BYTE src[] = "Source Data";
BYTE dst[11];
Move(src, dst, sizeof(dst));
It is better to use memmove in C++:
memmove(dst, src, sizeof(dst));