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 functionsCode 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.

Это изображение имеет пустой атрибут alt; его имя файла - izobrazhenie_2022-07-25_122619968-1024x806.png

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.

Since some themes are similar to each other, there are fewer unique sets of images than those that suggest optimization opportunities for this system. In the meantime, I slightly modified the image system code by linking it to the style system:
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

Some of the information is static, and some may change during operation, so I connected my UserApp_UpdateStatistic() handler to the form display event, in which I update the statistics.
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

Leave a Reply

Your email address will not be published. Required fields are marked *