Coding responses to user actions in the Code Editor (IDE Tutorial)
Go Up to Starting your first RAD Studio application Index (IDE Tutorial)
By following the instructions in this section, you will make your application interactive
and provide it with the functionality you want. You will code the event handlers, that is, the responses to clicking the various items in the main menu and other types of user interaction.
Contents
Beginning the code
Begin writing code by defining a String
variable that you use
throughout the execution of your application to retain the name of the currently opened
text file.
In Delphi, select the Code tab in the status bar to open the Code Editor. Use the Code Editor to define a String
variable called CurrentFile
in the private section of the TTextEditorForm class in the interface
part:
In C++Builder, select the TextEditorUnit.h tab in the status bar to open the unit header file in the Code Editor. Use the Code Editor to declare a String
variable called CurrentFile
in the private
section of TTextEditorForm:
To toggle between the Form Designer and the Code Editor, you can press F12
.
Updating the status bar in response to actions on the text editor
When users move the caret (text cursor) or modify the content of your TMemo component, you must update the status bar. Since you must update the status bar in response to many different events, you can define a single procedure in your code and later call that procedure from any event handler that needs to update the status bar.
In Delphi, to define this new procedure in your application, add the following procedure signature to the private section of the TTextEditorForm class in the interface
part, right below the CurrentFile
variable that you previously defined:
procedure UpdateStatusBar;
Right-click UpdateStatusBar
in your code, and select Complete Class at Cursor so that RAD Studio adds a skeleton definition for your procedure to the implementation
part of your code:
procedure TTextEditorForm.UpdateStatusBar;
begin
end;
Define the logic of the UpdateStatusBar
procedure between these begin
and end
keywords as follows:
LineNumber.Text := 'L: ' + (Editor.CaretPosition.Line+1).ToString;
ColumnNumber.Text := 'C: ' + (Editor.CaretPosition.Pos+1).ToString;
LineCount.Text := 'Lines: ' + Editor.Lines.Count.ToString;
In C++, to define this new function in your application, add the following function signature to the private
section of the TTextEditorForm class in the TextEditorUnit.h file, right below the CurrentFile
variable that you previously defined:
void UpdateStatusBar();
Select the implementation file, TextEditorUnit.cpp, and implement this function as follows:
void TTextEditorForm::UpdateStatusBar()
{
LineNumber->Text = L"L: " + String(Editor->CaretPosition.Line+1);
ColumnNumber->Text = L"C: " + String(Editor->CaretPosition.Pos+1);
LineCount->Text = L"Lines: " + String(Editor->Lines->Count);
}
You can now call UpdateStatusBar
from any event handler to update your status bar.
The first time that your application should update the information displayed on the status bar is when your application starts. You can use the OnCreate event of your form for this:
- Select the Design tab to get back to the Form Designer.
- On the Structure view select your form component, which you named
TextEditorForm
. - On the Object Inspector open the Events tab.
- On the Events tab, double-click the value field of the OnCreate event. RAD Studio switches to the Code Editor and adds a skeleton definition for your new event handler. Use that event handler to call your
UpdateStatusBar
procedure:
Delphi:
procedure TTextEditorForm.FormCreate(Sender: TObject);
begin
Editor.Lines.Add('');
UpdateStatusBar;
end;
C++:
void __fastcall TTextEditorForm::FormCreate(TObject *Sender)
{
TextEditorForm->Editor->Lines->Add(L"");
UpdateStatusBar();
}
Notice how before calling UpdateStatusBar
, this code adds an empty line to your TMemo component. This initializes the line count of the memo, so that the line count label in the status bar shows "1" line from the beginning instead of "0" lines.
Repeat the steps above with the OnKeyUp and OnMouseUp events of your TMemo component, which you named Editor
:
Delphi:
procedure TTextEditorForm.EditorKeyUp(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
begin
UpdateStatusBar;
end;
procedure TTextEditorForm.EditorMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
UpdateStatusBar;
end;
C++:
void __fastcall TTextEditorForm::EditorKeyUp(TObject *Sender, WORD &Key, System::WideChar &KeyChar,
TShiftState Shift)
{
UpdateStatusBar();
}
void __fastcall TTextEditorForm::EditorMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, float X, float Y)
{
UpdateStatusBar();
}
You are now ready to define the responses to clicking menu items. With the Form Designer open, select the NewAction
in the Structure view:
Then select the Events tab in the Object Inspector, and double-click the edit box corresponding to the OnExecute
event. The Code Editor opens and displays a skeleton for your new event handler. Write in this event handler the code that executes when users select File > New.
Delphi:
implementation
{$R *.fmx}
uses
FMX.DialogService.Sync;
procedure TTextEditorForm.NewActionExecute(Sender: TObject);
var
UserResponse: Integer;
begin
// Ask for confirmation if the memo is not empty.
if not Editor.Text.IsEmpty then
begin
UserResponse := TDialogServiceSync.MessageDialog(
'This will clear the current document. Do you want to continue?',
TMsgDlgType.mtInformation, mbYesNo, TMsgDlgBtn.mbYes, 0);
if UserResponse = mrYes then
begin
Editor.Text := '';
Editor.Lines.Add(''); // Initialize the memo line count to "1".
UpdateStatusBar;
CurrentFile := ''; // New files have no file name until saved.
end;
end;
end;
C++:
//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include "TextEditorForm.h"
#include <FMX.DialogService.Sync.hpp>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
void __fastcall TTextEditorForm::NewActionExecute(TObject *Sender)
{
if (!TextEditorForm->Editor->Text.IsEmpty()) {
int UserResponse = TDialogServiceSync::MessageDialog(
L"This will clear the current document. Do you want to continue?",
TMsgDlgType::mtInformation, mbYesNo, TMsgDlgBtn::mbYes, 0);
if (UserResponse == mrYes) {
TextEditorForm->Editor->Text = L"";
TextEditorForm->Editor->Lines->Add(L""); // Initialize the memo line count to "1".
UpdateStatusBar();
CurrentFile = L""; // New files have no file name until saved.
}
}
}
Click the Design tab to get back to the Form Designer, and repeat the process for the remaining actions used by the File menu entries: OpenAction
, SaveAction
, SaveAsAction
, ExitAction
. These are the implementations of the event handlers of the OnExecute event of each of these actions:
Delphi:
// File > Open
procedure TTextEditorForm.OpenActionExecute(Sender: TObject);
var
FileName: String;
begin
if OpenFileDialog.Execute then
begin
FileName := OpenFileDialog.FileName;
if FileExists(FileName) then
begin
Editor.Lines.LoadFromFile(FileName);
CurrentFile := FileName;
Caption := 'Text Editor - ' + ExtractFileName(FileName);
end;
end;
end;
// File > Save
procedure TTextEditorForm.SaveActionExecute(Sender: TObject);
begin
if CurrentFile = '' then
SaveAsAction.Execute()
else
Editor.Lines.SaveToFile(CurrentFile);
end;
// File > Save As
procedure TTextEditorForm.SaveAsActionExecute(Sender: TObject);
var
FileName: String;
UserResponse: TModalResult;
begin
if SaveFileDialog.Execute then
begin
FileName := SaveFileDialog.FileName;
if FileExists(FileName) then
begin
UserResponse := TDialogServiceSync.MessageDialog(
'File already exists. Do you want to overwrite?',
TMsgDlgType.mtInformation, mbYesNo, TMsgDlgBtn.mbYes, 0);
if UserResponse = mrNo then
Exit;
end;
Editor.Lines.SaveToFile(FileName);
CurrentFile := FileName;
Caption := 'Text Editor - ' + ExtractFileName(FileName);
end;
end;
// File > Exit
procedure TTextEditorForm.ExitActionExecute(Sender: TObject);
begin
Application.Terminate;
end;
C++:
// File > Open
void __fastcall TTextEditorForm::OpenActionExecute(TObject *Sender)
{
if (TextEditorForm->OpenFileDialog->Execute()) {
String FileName = TextEditorForm->OpenFileDialog->FileName;
if (FileExists(FileName)) {
TextEditorForm->Editor->Lines->LoadFromFile(FileName);
CurrentFile = FileName;
Caption = L"Text Editor - " + ExtractFileName(FileName);
}
}
}
// File > Save
void __fastcall TTextEditorForm::SaveActionExecute(TObject *Sender)
{
if (CurrentFile == L"") {
TextEditorForm->SaveAsAction->Execute();
} else {
TextEditorForm->Editor->Lines->SaveToFile(CurrentFile);
}
}
// File > Save As
void __fastcall TTextEditorForm::SaveAsActionExecute(TObject *Sender)
{
if (TextEditorForm->SaveFileDialog->Execute()) {
String FileName = TextEditorForm->SaveFileDialog->FileName;
if (FileExists(FileName)) {
TModalResult UserResponse = TDialogServiceSync::MessageDialog(
L"File already exists. Do you want to overwrite?",
TMsgDlgType::mtInformation, mbYesNo, TMsgDlgBtn::mbYes, 0);
if (UserResponse == mrNo) {
return;
}
}
TextEditorForm->Editor->Lines->SaveToFile(FileName);
CurrentFile = FileName;
Caption = L"Text Editor - " + ExtractFileName(FileName);
}
}
// File > Exit
void __fastcall TTextEditorForm::ExitActionExecute(TObject *Sender)
{
Application->Terminate();
}
To implement the actions for the menu entries in the Edit and Format menus, you must follow the same procedure that you followed for the File menu actions. These event handlers are very simple: they simply forward the action to your memo, as all the required functionality is already implemented in the TMemo class, and they call UpdateStatusBar
if the action may have any effect in the caret position or the line count.
Delphi:
// Edit > Cut
procedure TTextEditorForm.CutActionExecute(Sender: TObject);
begin
Editor.CutToClipboard;
UpdateStatusBar;
end;
// Edit > Copy
procedure TTextEditorForm.CopyActionExecute(Sender: TObject);
begin
Editor.CopyToClipboard;
end;
// Edit > Paste
procedure TTextEditorForm.PasteActionExecute(Sender: TObject);
begin
Editor.PasteFromClipboard;
UpdateStatusBar;
end;
// Edit > Select All
procedure TTextEditorForm.SelectAllActionExecute(Sender: TObject);
begin
Editor.SelectAll;
UpdateStatusBar;
end;
// Edit > Undo
procedure TTextEditorForm.UndoActionExecute(Sender: TObject);
begin
Editor.UnDo;
UpdateStatusBar;
end;
implementation
{$R *.fmx}
uses
FMX.DialogService.Sync, FMX.Memo.Types;
// Edit > Delete
procedure TTextEditorForm.DeleteActionExecute(Sender: TObject);
begin
if Editor.SelLength > 0 then
Editor.DeleteSelection
else
Editor.DeleteFrom(Editor.CaretPosition, 1, [TDeleteOption.MoveCaret]);
UpdateStatusBar;
end;
// Format > Word Wrap
procedure TTextEditorForm.WordWrapActionExecute(Sender: TObject);
begin
Editor.WordWrap := not Editor.WordWrap;
WordWrapAction.Checked := Editor.WordWrap;
UpdateStatusBar;
end;
C++:
// Edit > Cut
void __fastcall TTextEditorForm::CutActionExecute(TObject *Sender)
{
TextEditorForm->Editor->CutToClipboard();
UpdateStatusBar();
}
// Edit > Copy
void __fastcall TTextEditorForm::CopyActionExecute(TObject *Sender)
{
TextEditorForm->Editor->CopyToClipboard();
}
// Edit > Paste
void __fastcall TTextEditorForm::PasteActionExecute(TObject *Sender)
{
TextEditorForm->Editor->PasteFromClipboard();
UpdateStatusBar();
}
// Edit > Select All
void __fastcall TTextEditorForm::SelectAllActionExecute(TObject *Sender)
{
TextEditorForm->Editor->SelectAll();
UpdateStatusBar();
}
// Edit > Undo
void __fastcall TTextEditorForm::UndoActionExecute(TObject *Sender)
{
TextEditorForm->Editor->UnDo();
UpdateStatusBar();
}
//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include "TextEditorForm.h"
#include <FMX.DialogService.Sync.hpp>
#include <FMX.Memo.Types.hpp>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
// Edit > Delete
void __fastcall TTextEditorForm::DeleteActionExecute(TObject *Sender)
{
if (TextEditorForm->Editor->SelLength > 0) {
TextEditorForm->Editor->DeleteSelection();
} else {
TextEditorForm->Editor->DeleteFrom(TextEditorForm->Editor->CaretPosition, 1, TDeleteOptions() << TDeleteOption::MoveCaret);
}
UpdateStatusBar();
}
// Format > Word Wrap
void __fastcall TTextEditorForm::WordWrapActionExecute(TObject *Sender)
{
TextEditorForm->Editor->WordWrap = !TextEditorForm->Editor->WordWrap;
TextEditorForm->WordWrapAction->Checked = TextEditorForm->Editor->WordWrap;
UpdateStatusBar();
}