I need to find basic WYSIWYG HTML editor component for C++Builder 5 to let users to create some simple text that I will paste into existing HTML page template.
Just a simple support to create links, add images, use headers/bold/italic.
You can drop a TWebBrowser on a form and enable designmode on it, like this:
// Delphi code..
(WebBrowser1.Document as IHTMLDocument2).designMode := 'on';
After executing the above line, the page will be editable. You can type extra text, delete, etc. If you want to make selections bold or insert images, you're going to have to add some buttons to program that. The cool thing is that you can do that either from Delphi (or C++ builder in your case) or you can add javascript on the page to edit itself.
The contents of the page can be retrieved from
(WebBrowser.Document as IHTMLDocument2).body.innerHTML;
Remember that (WebBrowser.Document as IHTMLDocument2) could be nil.
Anyway, I can imagine that there are components around that do all the work for you, which is probably a better route to take than reinventing the wheel.
I would recommend TRichView due to its world class support, and deep feature set. While it is not a true "HTML" editor, it does support the ability to export to HTML, even generating the appropriate CSS styles if necessary. I use it for handling the email portion of our main product and it works very well. Internally the storage is either RTF (extended to support images better), or as a proprietary format. There are plenty of examples of a simple editors which would easily suit your needs.
in C++ Builder, it would be something like this:
(wb is a TCppWebBrowser)
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "mshtml.h"
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SHDocVw_OCX"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnNavigateAndEditClick(TObject *Sender)
{
wb->Navigate((WideString)"www.google.com");
while (wb->Busy)
Application->ProcessMessages();
if (wb->Document)
{
IHTMLDocument2 *html;
wb->Document->QueryInterface<IHTMLDocument2>(&html);
html->put_designMode(L"On");
html->Release();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnInsertImageClick(TObject *Sender)
{
if (wb->Document)
{
IHTMLDocument2 *html;
wb->Document->QueryInterface<IHTMLDocument2>(&html);
VARIANT var;
VARIANT_BOOL receive;
html->execCommand(L"InsertImage",true,var, &receive);
html->Release();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnGetHtmlClick(TObject *Sender)
{
if (wb->Document)
{
IHTMLDocument2 *html;
wb->Document->QueryInterface<IHTMLDocument2>(&html);
IHTMLElement *pElement;
html->get_body(&pElement);
pElement->get_parentElement(&pElement);
wchar_t *tmp;
pElement->get_outerHTML(&tmp);
Memo1->Lines->Text = tmp;
pElement->Release();
html->Release();
}
}
//---------------------------------------------------------------------------
http://www.bsalsa.com/
supplies a free set of EmbeddedWebBrowser components with an Edit Designer component
that you link to the EmbeddedBrowser window to control design mode and have edit control
save to file, insert links, images, etc...
seems to work well!
Related
Im trying to use embarcadero sample on useing android camera and geting an error:
"type TMessage is not a defined class with virtual function" on lines:
void __fastcall TForm1::DoMessageListener(const TObject *Sender, TMessage const *M) {
TMessageDidFinishTakingImageFromLibrary const *v = dynamic_cast<TMessageDidFinishTakingImageFromLibrary const *>(M);
if (v) {
Image1->Bitmap->Assign(v->Value);
}
}
In Delphi, TMessage works fine, but in C++Builder you must use TMessageBase instead:
void __fastcall TForm1::DoMessageListener(const TObject *Sender, TMessageBase const *M)
This is clearly stated in the documentation:
Sending and Receiving Messages Using the RTL:
The RTL only defines one type of message, TMessage. It is actually a template that you can use to create messages for specific types of values; for example: TMessage<int> or TMessage<UnicodeString>. You can also subclass TMessage to define your own message types or, if you are using FireMonkey, you can reuse the message types defined by the framework.
Note: For C++ projects use TMessageBase instead of TMessage.
System.Messaging.TMessage
TMessage represents the base class used for message purposes. It can be inherited in order to send custom messages.
Warning: For C++ projects, use TMessageBase instead.
System.Messaging.TMessageBase
Alias to System.Messaging.TMessage.
Use System.Messaging.TMessageBase for C++ projects instead of System.Messaging.TMessage.
This use of TMessageBase is also demonstrated in the documentation's System.Messaging (C++) example.
This is related to Assigning events to a VCL control created dynamically at runtime.
I used the above listed/reference post to solve a problem that I had with some code. Thank you for providing that example. I found it very useful, and implemented the "alternative" way that it provided as I was unable to make the first way work.
I am using C++Builder 10.x from Embarcadero. I just updated to C++Builder 10.3. This new update is now throwing a warning:
[bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
The line it is throwing on is:
Method.Code = &LogLevelComboBoxOnChange;
I am not sure how to "fix" this.
The code is in support of a logging function to a memo field, where the page for the logging memo has a TComboBox to select the logging level/verbosity.
The TComboBox is external to the logging function, as it is on the user's form. I want the TComboBox::OnChange event to call my LogLevelComboBoxOnChange function, which adjusts the logging level based on the TComboBox item/entry selected.
Supporting code around this includes.
Function declaration - TComboBox::OnChange Event Function
void __fastcall LogLevelComboBoxOnChange(void *pThis, TObject *Sender);
Function Declaration - Logging start up where the TMemo field to log to & the TComboBox are provided
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles);
This is the function that assigns the OnChange function to the TComboBox object on the user's logging form.
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles)
{
static TMethod Method;
//
// Set-Up CombBox and OnChange Event
// - Save ComboBox pointer
// - Assign List of Log Levels
// - Assign/Set-Up OnChange Function
//
LogLevelComboBox = AppLogLevelComboBox;
AppLogLevelComboBox->Items->Text =
"Off\n"
"All Messages\n"
"Verbose\n"
"Trace\n"
"Informational\n"
"Warning\n"
"Error\n"
"Severe\n"
"Fatal";
AppLogLevelComboBox->ItemIndex = iThreshold + 1;
//
// Set-Up - On Change Function for "external" Log Level Combo-Box
//
Method.Data = NULL; // passed to the pThis parameter, can be whatever you want
//
// The Following line generates the warning
// [bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
//
Method.Code = &LogLevelComboBoxOnChange;
LogLevelComboBox->OnChange = reinterpret_cast<TNotifyEvent&>(Method);
return 0;
}
You are using C++Builder's CLang-based C++ compiler. The "implicit conversion" warning you are seeing is a CLang issue that has cropped up during the past few years in multiple toolchains that use CLang, not just in C++Builder. This issue does not affect C++Builder's "classic" (non-CLang) compiler.
&LogLevelComboBoxOnChange creates a pointer-to-function, which CLang does not like to implicitly convert to void* (which is what the TMethod::Code field is declared as) unless Clang is put into a Microsoft compatibility mode.
Possible solutions are to either:
type-cast the pointer explicitly:
(UPDATE: apparently CLang doesn't like static_casting a pointer-to-function to void*, either!)
Method.Code = static_cast<void*>(&LogLevelComboBoxOnChange);
Method.Code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&LogLevelComboBoxOnChange));
enable the -Wmicrosoft-cast flag in the CLang compiler settings.
disable the warning in your code by using #pragma statements around the assignment to Method.Code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmicrosoft-cast"
Method.Code = &LogLevelComboBoxOnChange;
#pragma clang diagnostic pop
use a union (though, I'm sure some people will argue that this probably violates Strict Aliasing rules, but if it works...):
union {
void (__fastcall *func)(void*, TObject*);
void *ptr;
} u;
u.func = &LogLevelComboBoxOnChange;
Method.Code = u.ptr;
My Environment:
C++ Builder XE4
using VCL component
Indy 10.6.0.4975
I was studying to use MD5, SHA-1, and SHA-2s.
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <IdHashSHA.hpp> // SHA-1, SHA-2
#include <IdHashMessageDigest.hpp> // for MD5
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Memo1->Lines->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String msg;
msg = L"Hello, world";
String hash;
// 1. MD5
TIdHashMessageDigest5 *md5;
md5 = new TIdHashMessageDigest5();
//
hash = md5->HashStringAsHex(msg, IndyTextEncoding(TEncoding::ASCII)).LowerCase();
Memo1->Lines->Add(L"MD5: " + hash);
delete md5;
// 2. SHA-1
TIdHashSHA1 *sha1;
sha1 = new TIdHashSHA1();
//
hash = sha1->HashStringAsHex(msg, IndyTextEncoding(TEncoding::ASCII)).LowerCase();
Memo1->Lines->Add(L"SHA-1:" + hash);
delete sha1;
// 3. SHA-2 (SHA-512)
TIdHashSHA512 *sha512;
sha512 = new TIdHashSHA512();
//
hash = sha512->HashStringAsHex(msg, IndyTextEncoding(TEncoding::ASCII)).LowerCase();
Memo1->Lines->Add(L"SHA-512:" + hash);
delete sha512;
}
//---------------------------------------------------------------------------
The result is as follows.
Then, I found the following:
TidHashSHA512.isavailable is false on Windows 10
According to the suggestion, I add two files to where the .exe file exists:
ssleay32.dll
libeay32.dll
Still, the SHA-512 returns NULL.
What I am missing?
10.6.0.4975 is a VERY old version of Indy 10. The current version is 10.6.2.5485. You need to upgrade.
In any case, Indy 10 has native implementations of MD5 and SHA-1, those do not rely on any external hashing library at all. But SHA-512 does. Yet, you are not telling Indy which hashing library to use, such as OpenSSL. You are not instructing Indy to load the OpenSSL DLLs so it can initialize itself to use OpenSSL's SHA-512 functionality. As such, sha512->IsAvailable returns false, and sha512->HashStringAsHex() returns a blank string 1.
This is clearly stated in the accepted answer to the question you linked to:
Indy provides an implementation that uses hashing functions from OpenSSL. To use it, you can either:
add the IdSSLOpenSSLHeaders unit to your uses clause, and then call its Load() function at runtime.
add the IdSSLOpenSSL unit to your uses clause, and then call its LoadOpenSSLLibrary() function at runtime.
In this case, since you are using C++ instead of Pascal, you need to add the corresponding #include statement to your code instead, either #include <IdSSLOpenSSLHeaders.hpp> or #include <IdSSLOpenSSL.hpp>, and then you can call the relevant Load function, such as in your Form's constructor.
1: BTW, you should be using IndyTextEncoding_ASCII() instead of IndyTextEncoding(TEncoding::ASCII).
I've checked out many threads of a similar title but they haven't helped.
The following compiles and installs to the component palette, but when I try to add the component to a panel, I get the error message mentioned in the thread title.
Could anyone please explain why?
__fastcall TEditBox::TEditBox(TComponent* Owner) : TGroupBox(Owner)
{
ToolBar=new TToolBar(this);
ToolBar->Parent=this;
TToolButton *Btn=new TToolButton(ToolBar);
Btn->Parent=ToolBar;
}
If I omit the Btn->Parent=ToolBar line, everything's OK, so presumably that's the problem line?
Assigning a ToolButton's Parent requires the ToolBar to have an allocated HWND, which requires it to have a Parent with an allocated HWND, and so on. But your EditBox does not have a Parent (or a Name) assigned yet when its constructor is called, so the ToolBar cannot allocate an HWND yet, hence the error.
If you want your Toolbar to have a default button at runtime, you need to move the creation of the button to the EditBox's virtual Loaded() method (or even the SetParent() method), eg:
__fastcall TEditBox::TEditBox(TComponent* Owner)
: TGroupBox(Owner)
{
ToolBar=new TToolBar(this);
ToolBar->Parent=this;
}
void __fastcall TEditBox::Loaded()
{
TGroupBox::Loaded();
TToolButton *Btn=new TToolButton(ToolBar);
Btn->Parent=ToolBar;
}
I know how to create frame in the design time and place it in the panel in the runtime in Delphi. As for C++ Builder, It looked difficult as I am not familiar with C++ scripts. Please advise how to do the right way?
Thanks in advance
The solution is the exact same as in Delphi, you just need to use C++ syntax instead.
Something like this should work:
/*
Assuming your frame is located in a unit called Frame1, and it's
called TMyFrameType, this is what you should add your Form unit
cpp file.
*/
#include "Frame1.h"
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// This assumes you have a panel in this form called "ThePanelWhereIWantIt".
// You could move the MyFrameInstance to the class definition, if you need to
// access it somewhere after in your form code, but this is trivial.
TMyFrameType *MyFrameInstance;
MyFrameInstance = new TMyFrameType(ThePanelWhereIWantIt);
MyFrameInstance->Parent = ThePanelWhereIWantIt;
MyFrameInstance->Align = alClient;
}
//---------------------------------------------------------------------------