The Butterfly Effect — A small impact on the system can have large and unpredictable consequences, including in a completely different place.
Having completed work on the first version of the “ClassExplorer” program, I decided to improve it a little: to separate the program code into separate modules, and also to add the style management mechanism that was described in the “Chameleon” article. But these seemingly small changes led to the emergence of an improved technology for loading images for buttons and created a lot of hassle in setting up links between modules.
Modules
As you probably already know, My Visual Database allows you to place script sources in several files, which is very convenient if the number of lines of code exceeds a couple of thousand. In order to connect a module (a file with the .pas extension) to the main code, you must specify it in the uses:
uses
// sort by degree of dependency: first those that do not refer to anyone, then dependent
'ConstVar.pas', // global constants and application variables
'System\Utils.pas', // system procedures
'Tools\Tools.pas', // tools
'VClass\VClass.pas', // virtual classes and extensions
'Forms\Forms.pas', // forms
'UserApp.pas'; // general procedures and application functions
Code language: Delphi (delphi)
A prerequisite is that the module file is located inside the Script folder, nested storage of module files in subfolders is also allowed.

Unfortunately, the built-in MVDB editor does not support editing modules, but the solution was easily found in the person of the universal editor Notepad ++, which is used by many programmers. As you can see from the project file tree, there are quite a lot of them, and only six are present in the uses command. The remaining modules are connected in the modules described above, they also contain the uses command for files. For example, the Forms.pas file is used solely to register modules that store handlers.
// list of connected forms
uses
'Forms\ClassType.pas',
'Forms\Event.pas',
'Forms\Example.pas',
'Forms\FuncProc.pas',
'Forms\FuncProcParam.pas',
'Forms\FunctionParam.pas',
'Forms\Main.pas',
'Forms\Method.pas',
'Forms\MethodParamList.pas',
'Forms\Property.pas',
'Forms\SearchResult.pas',
'Forms\Task.pas',
'Forms\TypeConst.pas';
begin
end.
Code language: JavaScript (javascript)
Unfortunately, it is not known exactly how modules are linked, since there are no restrictions on cross-references (as in Delphi). Sometimes it is enough that the used module is present in at least one uses command, and sometimes you have to add dependency references: if a procedure from module B is called in module A, then in module A you need to write: uses “B.pas”;
Another important point is that each module has a main program block:
begin
end.
Code language: Delphi (delphi)
The use of styles has revealed the need to change the standard images on the buttons, as they do not always match the color scheme offered by the selected application design style.
The “Family Album” article takes a detailed look at the button image loading mechanism, which is activated when the application starts and does not require additional code if you choose to add more buttons. It is enough to place the necessary PNG files in the folder, and the mechanism will replace the images using the button naming convention.
But downloading alone does not solve the problem, as the color of the pictures on the buttons must be combined with the background of the buttons themselves. And this is a problem that can’t be solved in an elegant way: MVDB doesn’t have tools for editing PNG images, and BMP images look clumsy due to the lack of midtones on the edge of the image, which gives transparency control. Therefore, the problem had to be solved not by quality, but by quantity: if the button images are not suitable, then a separate folder with images is created for such a theme.

procedure Images_Init;
// data initialization
var
i: integer;
begin
// initialization of data arrays
SetLength(ImagesDir,IMAGES_FORMAT_COUNT);
SetLength(ImagesSize,IMAGES_FORMAT_COUNT);
SetLength(ImagesList,IMAGES_FORMAT_COUNT);
SetLength(ImagesNames,IMAGES_FORMAT_COUNT);
ImagesDir[IMAGES_FORMAT_BUTTON] := IMAGES_DIR + 'Buttons\';
// style magic
Images_AddStyle(ImagesDir[IMAGES_FORMAT_BUTTON]);
// the size
ImagesSize[IMAGES_FORMAT_BUTTON] := 24;
// load data
for i := 0 to IMAGES_FORMAT_COUNT-1 do
begin
ImagesNames[i] := TStringList.Create;
ImagesList[i] := TImageList.Create(frmMain);
Images_Load(i); // load images
end;
Images_ButtonsAssign( IMAGES_FORMAT_BUTTON );
end;
procedure Images_AddStyle(var ADir:string;);
// adding "style" to images
var
tmpDir:string;
begin
tmpDir := ADir + Style_GetStyle() + '\';
if DirectoryExists(tmpDir) then
ADir := tmpDir;
end;
Code language: PHP (php)
It works like this: if there is a subfolder with the same name for the selected style, then the images will be loaded from it, if not, then from the main folder.
Styles
The style system has been improved in such a way that it can be easily used in various applications: the procedure Style_SetStyle( AStyleName: string ) has been added; – switching style.
procedure Style_SetStyle( AStyleName: string );
// style switch
var
tmpCurFile : string;
tmpNewFile : string;
begin
currentStyleName := AStyleName;
IniFile_Write( STYLE_INI_SECTION, STYLE_INI_CUR_STYLE, currentStyleName );
tmpCurFile := ExtractFilePath(Application.ExeName)+STYLE_FILE_NAME;
tmpNewFile := ExtractFilePath(Application.ExeName)+STYLE_DIR+currentStyleName+STYLE_FILE_EXT;
// remove the current style
DeleteFile(tmpCurFile);
// copy file with style
if FileExists(tmpNewFile) then
CopyFile(tmpNewFile, tmpCurFile);
// warning
if MessageBox('A program restart is required to apply changes. Do you want to run now?','Change style ',MB_OKCANCEL ) = mrOK then
begin
frmMain.Close; // close the current application. In fact, this command only creates a message, in fact, the closing will occur after the completion of the current procedure.
OpenFile( Application.ExeName ); // start application
end;
end;
Code language: JavaScript (javascript)
And the initialization procedure is done in such a way as to automatically generate a list of styles available for the application by the presence of .VSF files
procedure Style_Init;
// initialization
var
i: integer;
s:string
tmpFileList: array of string;
begin
s := GetFilesList ( ExtractFilePath(Application.ExeName)+STYLE_DIR );
if s <> '' then
begin
tmpFileList := SplitString(s, CR);
// initialization of the list of styles
SetLength(arStyles, 1);
arStyles[0] := 'Default';
for i:=0 to Length(tmpFileList)-1 do
begin
s := Trim(ExtractFileName( tmpFileList[i] ));
if s<>'' then
begin
delete(s,length(s)-3,4);
SetLength( arStyles, i+2 );
arStyles[i+1] := s; // write the file name without extension
end;
end;
// current style - read from setting
currentStyleName := IniFile_Read( STYLE_INI_SECTION, STYLE_INI_CUR_STYLE, arStyles[0] );
end;
end;
Code language: JavaScript (javascript)
The result is an application that supports all styles, lovers of dark themes in the design of the program interface remain a mystery to me. Probably due to the fact that I stopped working at night 🙂



About
And finally, a little magic that allows you to use the built-in “About” form for your own personal purposes: to place information about the version of the program, copyright and

procedure UserApp_InitAboutForm;
// setting up the "About" form
var
tmpLabel:TLabel;
tmpLabel2:TLabel;
i: integer;
begin
frmdbCoreAbout.Caption := 'About';
tmpLabel:=TLabel( frmdbCoreAbout.FindComponent('LinkLabel1') );
tmpLabel.Caption := frmMain.Caption; // 'My Visual Database Class Explorer';
tmpLabel.Font.Size := 14;
tmpLabel.Font.Style := fsBold;
tmpLabel2 := TLabel.Create( frmdbCoreAbout );
tmpLabel2.Name := 'labVersion';
tmpLabel2.Parent := tmpLabel.Parent;
tmpLabel2.Left := tmpLabel.Left;
tmpLabel2.Top := tmpLabel.Top + 30;
tmpLabel2.Caption := 'Version 1.1';
tmpLabel2.Font.Size := 11;
tmpLabel := TLabel( frmdbCoreAbout.FindComponent('Label2') );
tmpLabel.Caption := 'Copyright '+chr($A9)+' 2022 Konstantin Pankov';
tmpLabel.Top := tmpLabel.Top + 50;
tmpLabel.Font.Size := 11;
tmpLabel2 := TLabel.Create( frmdbCoreAbout );
tmpLabel2.Name := 'labLink';
tmpLabel2.Parent := tmpLabel.Parent;
tmpLabel2.Left := tmpLabel.Left;
tmpLabel2.Top := tmpLabel.Top + 24;
tmpLabel2.Font := tmpLabel.Font;
Label_LinkCreate( tmpLabel2, 'https://k245.ru');
tmpLabel2 := TLabel.Create( frmdbCoreAbout );
tmpLabel2.Name := 'labDatabaseDate';
tmpLabel2.Parent := tmpLabel.Parent;
tmpLabel2.Left := tmpLabel.Left;
tmpLabel2.Top := tmpLabel.Top + 224;
tmpLabel2.Font := tmpLabel.Font;
tmpLabel2.Hint := App_GetDBFileName(True);
tmpLabel2.ShowHint := True;
for i:=1 to 4 do
begin
tmpLabel2 := TLabel.Create( frmdbCoreAbout );
tmpLabel2.Name := 'labStat_'+IntToStr(i);
tmpLabel2.Parent := tmpLabel.Parent;
tmpLabel2.Left := tmpLabel.Left;
tmpLabel2.Top := tmpLabel.Top + 60+ i*20;
tmpLabel2.Font := tmpLabel.Font;
end;
TAForm(frmdbCoreAbout).OnShow := 'UserApp_UpdateStatistic';
end;
procedure UserApp_UpdateStatistic(Sender: TObject; Action: string);
var
tmpLabel:TLabel;
begin
// information about the number of records
FindC(TAForm(frmdbCoreAbout),'labStat_1',tmpLabel );
tmpLabel.Caption := 'Tasks: '+IntToStr(SQLExecute('SELECT count(*) FROM task'));
FindC(TAForm(frmdbCoreAbout),'labStat_2',tmpLabel );
tmpLabel.Caption := 'Classes/types: '+IntToStr(SQLExecute('SELECT count(*) FROM classType'));
FindC(TAForm(frmdbCoreAbout),'labStat_3',tmpLabel );
tmpLabel.Caption := 'Functions: '+IntToStr(SQLExecute('SELECT count(*) FROM funcProc'));
FindC(TAForm(frmdbCoreAbout),'labStat_4',tmpLabel );
tmpLabel.Caption := 'Examples: '+IntToStr(SQLExecute('SELECT count(*) FROM example'));
// information about the database file
FindC(TAForm(frmdbCoreAbout),'labDatabaseDate',tmpLabel );
tmpLabel.Caption := 'Database update date: '+DateToStr(GetFileLastWriteTime(tmpLabel.Hint));
end;
Code language: PHP (php)
Links
- My Visual Database v.1.1 encyclopedia – added tasks that I found solutions on the forum or thought up myself
- Project Files – available to library readers” Visual programming”
- “Chameleon” – article about styles
- “Family Album” – an article with a description m upload images for buttons