Typically, projects created in the My Visual Database development environment contain only one module with scripts – the script.pas file. However, if the program contains a lot of additional functionality, then the size of this file can be huge. Therefore, it is quite logical to divide the scripts into logical parts and store them in separate files.

The modular architecture improves the perception of the source text, allows you to reuse individual modules by copying the necessary files to a new project. Therefore, the next improvement of the Developer’s Guide is related to the display of the modular structure of projects.

Stored data

Since filling in information about modules will be automatic, and modules can be stored in a hierarchical folder system, the data storage structure needs to be changed by adding several fields:

The full name of the module will be stored in the path field, the parentID, nullID and isGroup fields are used to organize a tree representation of information about modules.

Forms

The table view form is dynamic, so it only takes a few lines in the dform.ini file to create it.

[Unit_Tree]
table=unit
fields=unit.name,unit.status,unit.description,unit.editDate
captions=Name,Status,Description,Edit date
sort=ParentID,name
parentField=parentID
masterField=id_project
isDetail=True
parentControl=frmMain.tshUnit
filter=id=-1Code language: SQL (Structured Query Language) (sql)

Traditional editing form:

Since a localization system is implemented in the Developer’s Guide, it is necessary to add translations for the elements of this editing form to the files “English.txt” and “Русский.txt”.

Updating the list of modules

The UpdateProjectList_UpdateUnitList() update procedure can be called both separately (by the button on the main form) and in the UpdateProjectList_UpdateList() execution loop, so displaying progress is optional in it. The second important difference is the automatic creation of parent tree nodes that correspond to physical folders for storing files.

procedure UpdateProjectList_UpdateUnitList( AIDProject:integer; AShowProgress:boolean);
// updating information on project modules
// AIDProject - project ID
// AShowProgress - progress display flag
var
  tmpScriptDir: string;
  tmpFileList: string;
  tmpFiles: array of string;
  i: integer;
  tmpCount: integer;
  tmpMaxCount: integer;
  tmpUnitName: string;
  tmpSQL: string;
  tmpDataSet:TDataSet;
  tmpFileName: string;
  tmpIDParent: string;
  tmpName: string; // short name
  tmpDirs: array of string;

  function FindParent:string;
  var
    j: integer;
    s: string;
    tmpPredResult: string;
  begin
    tmpSQL := 'SELECT id FROM unit WHERE (id_project='+IntToStr(AIDProject)+') and (isGroup = 1) and (path = '+StrToSQL(ExtractFilePath(tmpUnitName))+')';
    Result := SQLExecute(tmpSQL);
    // if the name is compound and the parent is not found, then create the parent!
    if (Result = '') and (tmpUnitName <> tmpName) then
    begin
      tmpPredResult := 'NULL';
      tmpDirs := SplitString(tmpUnitName,'\');
      s:='';
      for j:= 0 to Length(tmpDirs)-2 do
      begin
        if s<>'' then
          s := s+'\';
        s := s + tmpDirs[j];
        // проверяем папки
        tmpSQL := 'SELECT id FROM unit WHERE (isGroup = 1) and (id_project = '+IntToStr(AIDProject)+') and (path = '+StrToSQL(s)+') ';
        Result := SQLExecute(tmpSQL);
        if Result = '' then
        begin
          tmpSQL := 'INSERT INTO unit (id_project,path,name,status,ParentID,isGroup) VALUES ('+IntToStr(AIDProject)+','+StrToSQL(s)+','+StrToSQL(ExtractFileName(s))+','+'"Новая папка",'+tmpPredResult+',1)';
          SQLExecute(tmpSQL);
          Result := IntToStr( Last_Insert_ID() );
        end;
        tmpPredResult := Result;
      end;
    end
    else
    if Result = '' then
      Result := 'NULL';
  end;


begin
  if AShowProgress then
    Progress(0,0,'Module data update');
  try
    tmpScriptDir := SQLExecute('SELECT path FROM project WHERE id='+IntToStr(AIDProject))+'\script';

    if not DirectoryExists(tmpScriptDir) then
      exit;

    // checking existing records
    tmpSQL := 'SELECT count(*) FROM unit WHERE id_project='+IntToStr(AIDProject);
    tmpMaxCount := SQLExecute(tmpSQL);
    tmpCount := 0;
    tmpSQL := 'SELECT * FROM unit WHERE id_project='+IntToStr(AIDProject);
    SQLQuery(tmpSQL,tmpDataSet);
    try
      while not tmpDataSet.EOF do
      begin
        if AShowProgress then
          Progress(tmpCount,tmpMaxCount,'Checking folders and files')
        else
          Application.ProcessMessages;
        tmpFileName := Trim( tmpScriptDir +'\'+ tmpDataSet.FieldByName('path').asString );
        if (tmpDataSet.FieldByName('isGroup').asInteger = 0) AND (not FileExists( tmpFileName )) then
        begin
          tmpSQL := 'UPDATE unit SET status = "Module not found" WHERE id = '+tmpDataSet.FieldByName('id').asString;
          SQLExecute(tmpSQL);
        end
        else
        if (tmpDataSet.FieldByName('isGroup').asInteger = 1) AND (not DirectoryExists( tmpFileName )) then
        begin
          tmpSQL := 'UPDATE unit SET status = "Folder not found" WHERE id = '+tmpDataSet.FieldByName('id').asString;
          SQLExecute(tmpSQL);
        end;
        tmpDataSet.Next;
        inc(tmpCount);
      end;
    finally
      tmpDataSet.Free;
    end;
    // adding new
    tmpFileList := Trim(GetFilesList(tmpScriptDir,'*.pas'));

    tmpFiles := SplitString(tmpFileList,chr(10));
    tmpMaxCount := Length(tmpFiles);
    for i:=0 to Length(tmpFiles)-1 do
    begin
      if AShowProgress then
        Progress(i,tmpMaxCount,'Module Analysis')
      else
        Application.ProcessMessages;

      tmpUnitName := Trim( tmpFiles[i] );
      tmpName := ExtractFileName(tmpUnitName);

      delete(tmpUnitName,1,Length(tmpScriptDir)+1);
      tmpSQL := 'SELECT count(*) FROM unit WHERE (id_project = '+IntToStr(AIDProject)+') AND (path = '+StrToSQL(tmpUnitName)+')';
      if SQLExecute(tmpSQL) = 0 then // module not found
      begin
        //
        tmpIDParent := FindParent;
        tmpSQL := 'INSERT INTO unit (id_project,path,name,status,ParentID,isGroup) VALUES ('+IntToStr(AIDProject)+','+StrToSQL(tmpUnitName)+','+StrToSQL(tmpName)+','+'"Новый модуль",'+tmpIDParent+',0)';
        SQLExecute(tmpSQL);
      end;
    end;
  finally
    if AShowProgress then
      Progress();
  end;
end;Code language: Delphi (delphi)

The procedure works in two phases:

  • Checking Existing Records
  • Adding new

The absence of a folder with scripts is not a mistake, as there may be projects that do their job perfectly without a single line of code.

Formatting source codes

The Formatter_FormatDir() procedure has been added to the formatter.pas module, which can format folders with source files.

const
  FORMATTER_PARAM_DIR = '-silent -config:%0.s -d %1.s -r '; //

procedure Formatter_FormatDir( APath:string );
// formatting Delphi source text by external utility - all files in folder, recursively
var
  tmpConfigFile: string;
  tmpFormatter: string;
  tmpParam: string;
begin
  // the utility and the configuration file are in a special folder inside our project
  tmpFormatter := ExtractFilePath( Application.ExeName ) + FORMATTER_EXE;
  tmpConfigFile := ExtractFilePath( Application.ExeName ) + FORMATTER_CONFIG;
  // if something is not found, then raise an exception
  if not FileExists(tmpFormatter) then
    RaiseException('Format utility not found: '+tmpFormatter)
  else
  if (FORMATTER_CONFIG <> '') and  not FileExists(tmpConfigFile) then
    RaiseException('Configuration file not found: '+tmpConfigFile)
  else
  begin
    // do the conversion
    tmpParam := Format(FORMATTER_PARAM_DIR,[tmpConfigFile,APath]);
    OpenFile(tmpParam,tmpFormatter);
  end;
end;
Code language: Delphi (delphi)

Attention! In the rules configuration file for the formatter.exe utility, you need to change the following formatting parameter:

  <Option Name="DelphiFormatter.CompilerDirectivesCapitalization" Type="Borland.Together.OpenAPI3.Config.CAPITALIZATION" Category="DelphiCapitalization" Value="AsIs" Levels="Formatter" />Code language: HTML, XML (xml)

View file

Selecting a module and then selecting the Code tab at the bottom of the screen will display the module’s source code with syntax highlighting.

Other improvements

The dtfUnit_Tree form created a precedent in which the form is both dependent and displays tree-like data, so the semantics of the description of dynamic forms had to be finalized by separating the concepts

  • parentField – a field for storing a link to the parent node of the tree
  • masterField – a field for storing a link to the main table

Improvements also affected the following procedures and functions: DTF_Create(), frmUpdateProjectList_btnUpdate_OnClick(), UserApp_GetMasterID(), UserApp_UpdateDD().

Links

Leave a Reply

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