The DataCAD Developer Network (DDN) is an online resource for information and support for DCAL® (DataCAD Applications Language) developers as well as anyone interested in creating fonts, toolbars, hatch patterns, or linetypes for use in DataCAD.
#50921 by Mark F. Madura
Sun Nov 07, 2010 9:22 am
Download iniBrowser BETA for DataCAD 15 (Updated 11.03.2012)
Download iniBrowser BETA for DataCAD 14 (Updated 11.03.2012)
Download iniBrowser BETA for DataCAD 13 (Updated 11.20.2010)

'iniBrowser' BETA is a DCAL for Delphi Macro. I developed this macro to learn how to incorporate Delphi forms into a DataCAD macro.

This macro allows you to browse and edit all of the keys in your DCADWIN.INI file located in the DataCAD program folder. The INI sections and keys are organized alphabetically.

Enjoy!

Here is the source code to the main unit 'iniBrowser.dpr':
Code: Select all{
DCAL program iniBrowser.
}

library iniBrowser;

uses
  // Delphi Units
  Forms,

  // iniBrowser Units
  iniBrowserForm in 'iniBrowserForm.pas' {iniForm},

  // DCAL for Delphi Units
  UConstants in '..\..\Header Files\UConstants.pas',
  UInterfaces in '..\..\Header Files\UInterfaces.pas',
  UInterfacesRecords in '..\..\Header Files\UInterfacesRecords.pas',
  URecords in '..\..\Header Files\URecords.pas',
  UVariables in '..\..\Header Files\UVariables.pas';

{$E dmx}  //dmx is the extension for DCAL Dll's
{$R *.res}

//The values stored in the records you define here will be rememberd
//between dispatcher calls because they are pushed onto the command stack
type
  iniBrowserL = record
    state: asint;
    case byte of
      0: (getp: getpointArg);
  end;
  PiniBrowserL = ^iniBrowserL;

//Main Function (Re-entrant State Machine)
function iniBrowser_main(act : action; pl, pargs : Pointer) : wantType;
//The values stored in variables you define here will not be retained
//between dispatcher calls because they are not pushed onto the stack
var
   retval : asint;
   l : PiniBrowserL;
begin
   l := PiniBrowserL(pl);

   {Section 3: Aagain - Return to menus and wait for input}
   if act = aagain then begin  { Re-entrant section }
      case l.state of { Case }
         1 :
         begin
            //If a key was pressed or a menu button was selected...
            if l.getp.Result = res_escape then begin
               case l.getp.key of
                  f1 : l.state := 2;  {Show Form}

                  s0 : l.state := 0;  {Exit}
               end;  { Which key was pressed }
            end;  { Was a key pressed or menu selected? }
         end;  { 1 }
         2 :
         begin
            l.state := 1
         end;  { 2 }
      else
         l.state := 0;
      end; { Case }
   end;  { Re-entrant Section }

    {Section 2: Afirst - Tasks to perform only once}
    if act = Afirst then begin  //Initialize local state and other variables
      wrterr('iniBrowser Macro - BETA Version 1.0.0.2');
      l.state := 1;
    end

    {Section 4: Alast - If your macro has been interrupted by user action}
    else if act = Alast then begin
      //If you're going to get blown off the stack...
      //(i.e.) the user pressed a hotkey,
      //then this is your last chance to clean up temporary data.
    end

    {Section 1: AlSize - Allocate memory on stack for local variables}
    else if act = AlSize then begin
      SetLocalSize(sizeof(l^));
    end;

    if act <> alsize then begin  { Action section - Invoke the Dispatcher }
      case l.state of
        1 :
        begin  { 1: Main Menu }
          wrtlvl('iniBrowser');  //Set Menu Title
          lblsinit;
          lblset(1, 'DCADWIN', 'Browse and edit DCADWIN.INI');
          lblson1;
          getpoint(l.getp, retval);
          {Dispatcher will call as soon as it has control, go to Aagain: 1}
        end;  { 1 }
        2:
        begin
           iniForm := TiniForm.Create(Application);
           iniForm.ShowModal;
           iniForm.Free;
           callNone(retval);{ You have to ask the Dispatcher to do something. }
                           { In this case, ask the dispatcher to do nothing. }
          {Dispatcher will call as soon as it has control, go to Aagain: 2}
        end;  { 2 }
      else
        retval := XDone; //Don't go back to Aagain
      end; { Case }
    end; { Action section }
    Result := retval;
end; { iniBrowser_main }
//End Main Function (Re-entrant State Machine)

function Main(dcalstate : asint; act : action; pl, pargs : Pointer) : wantType; stdcall;
begin
   case dcalstate of
      XDcalStateBegin : Result := iniBrowser_main(act, pl, pargs);
         else
            Result := XDone;{Necessary}
   end;
end;
//End of Main Function (Re-entrant State Machine)

exports
   Main;{This is the entry function that will be called by DataCAD.
         All DCAL Dlls will have this function.}

begin

end.


Here is the source code to the main form unit 'iniBrowserForm.pas':
Code: Select allunit iniBrowserForm;

interface

uses
   // Delphi Units
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, Grids, ValEdit, inifiles, FileCtrl,

   // DCAL for Delphi Units
   UConstants, UInterfaces, UInterfacesRecords, URecords, UVariables;


type
   TiniForm = class(TForm)
      BtnLoadINI: TButton;
      EditINI: TEdit;
      gbSection: TGroupBox;
      gbKeys: TGroupBox;
      lbINISection: TListBox;
      vlINIKeys: TValueListEditor;
      DlgOpenINI: TOpenDialog;
      procedure BtnLoadINIClick(Sender: TObject);
      procedure lbINISectionClick(Sender: TObject);
      procedure vlINIKeysEditButtonClick(Sender: TObject);
      procedure FormClose(Sender: TObject; var Action: TCloseAction);
      procedure vlINIKeysValidate(Sender: TObject; ACol, ARow: Integer; const KeyName, KeyValue: string);
      procedure FormCreate(Sender: TObject);
   private
    { Private declarations }
   public
    { Public declarations }
   end;

var
   iniForm: TiniForm;
   Ini: TIniFile;

implementation

{$R *.dfm}

procedure TiniForm.BtnLoadINIClick(Sender: TObject);
begin
   DlgOpenINI.Execute();
   EditINI.Text := Trim((DlgOpenINI.FileName));
   Ini := TIniFile.Create(EditINI.Text);
   Ini.ReadSections(lbINISection.Items);
   lbINISection.Sorted := True;
  // Ini.free;
end;

function getvalue(tempdir: string): string;
var
   c, d, e: byte;
   pathdir: string;
begin

   if tempdir <> '' then begin
      c := length(tempdir);
      d := length(tempdir);
      while (tempdir[c] <> '=') do begin
         dec(c);
      end;
      inc(c);
      pathdir := '';
      for e := c to d do begin
         pathdir := pathdir + tempdir[e];
      end;
   end;
   result := pathdir
end;

procedure TiniForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   if Assigned(Ini) then
      Ini.Free;
end;

procedure TiniForm.FormCreate(Sender: TObject);
var
   path : ShortString;
begin
   DlgOpenINI.Filter := 'INI files (*.ini)|*.ini';
   getpath(path, Pathinstall);
   EditINI.Text := path + 'DCADWIN.INI';
   Ini := TIniFile.Create(EditINI.Text);
   Ini.ReadSections(lbINISection.Items);
   lbINISection.Sorted := True;
end;

procedure TiniForm.lbINISectionClick(Sender: TObject);
var
   id, count, x: Integer;
   Ini: TIniFile;
   idname: string;
   sl1, sl2: Tstringlist;
   svValue1, svValue2: string;
   slTF: Tstringlist;
begin
   vlINIKeys.Strings.Clear;
   id := lbINISection.ItemIndex;
   idname := lbINISection.Items.Strings[id];

   sl1 := Tstringlist.Create;
   sl2 := Tstringlist.Create;
   slTF := Tstringlist.Create;
   Ini := TIniFile.Create(EditINI.Text);

   slTF.Add('TRUE');
   slTF.Add('FALSE');

   with Ini do begin
      readsection(idname, sl1);
      readsectionvalues(idname, sl2);
   end;

   count := sl1.count;

   for x := 0 to count - 1 do begin
      vlINIKeys.InsertRow(sl1.Strings[x], getvalue(sl2.Strings[x]), True);
      svValue1 := sl1.Strings[x];
      svValue2 := getvalue(sl2.Strings[x]);
      if SysUtils.directoryExists(svValue2) then
         vlINIKeys.ItemProps[x].EditStyle := esEllipsis;
      if (svValue2 = 'TRUE') or (svValue2 = 'FALSE') then begin
         vlINIKeys.ItemProps[x].EditStyle := esPickList;
         vlINIKeys.ItemProps[x].ReadOnly := True;
         vlINIKeys.ItemProps[x].PickList := slTF;
      end;
   end;
  // Ini.Free;
   sl1.Free;
   sl2.Free;
   slTF.Free;
end;

procedure TiniForm.vlINIKeysEditButtonClick(Sender: TObject);
const
   SELDIRHELP = 1000;
var
   dir: string;
   PRow: Integer;
   svPath: string;
   svSection: string;
begin
   PRow := vlINIKeys.Row;
   svPath := vlINIKeys.Keys[PRow];
   dir := vlINIKeys.Values[svPath];
   if SelectDirectory(dir, [
    { sdAllowCreate, sdPerformCreate, sdPrompt } ], SELDIRHELP) then begin
      vlINIKeys.Values[svPath] := dir;
      svSection := lbINISection.Items.Strings[lbINISection.ItemIndex];
      Ini.WriteString(svSection, vlINIKeys.Keys[PRow], dir + '\');
      vlINIKeys.Values[svPath] := dir + '\';
   end;
end;

procedure TiniForm.vlINIKeysValidate(Sender: TObject; ACol, ARow: Integer; const KeyName, KeyValue: string);
var
   svSection: string;
begin
   svSection := lbINISection.Items.Strings[lbINISection.ItemIndex];
   Ini.WriteString(svSection, KeyName, KeyValue);
end;

end.
Last edited by Mark F. Madura on Mon Nov 22, 2010 5:24 pm, edited 3 times in total.
#50925 by Miguel Palaoro
Mon Nov 08, 2010 8:10 am
Buddy! You're serious!!!

I just loved it!!

Thanks, Mark!
Miguel
#51031 by Jsosnowski
Mon Nov 15, 2010 1:28 pm
Mark,

I have been writing in Delphi for V12. Your new macros only work in 13. What has been changed / added to the macro interface to cause the restriction? Is there any documentation that identifies what is changed?
#51032 by Mark F. Madura
Mon Nov 15, 2010 3:57 pm
Jsosnowski wrote:I have been writing in Delphi for V12. Your new macros only work in 13. What has been changed / added to the macro interface to cause the restriction?


If you compare the header files you'll see the differences.

In particular, some procedures and functions in UInterfaces have been updated (i.e., parameters added), and some string and math functions have been removed since Delphi provides these directly.

In URecords a case 3 has been added to the TAttribXData record and a TBrowseType has been added.

In UVariables, there are around 46 new fields in the savar record and around 14 new fields in the svtype record.

I think UConstants and UInterfacesRecords are the same.

UDCLibrary was added specifically for the AEC_Model macro.

MFM
#51132 by Miguel Palaoro
Mon Nov 22, 2010 6:55 am
Mark F. Madura wrote:...This macro allows you to browse and edit all of the keys in your DCADWIN.INI file located in the DataCAD program folder. The INI sections and keys are organized alphabetically.

Hi Mark,

I'd like to help, putting hands on to the enhancement of this macro. If you'd want to create a "description" additional field I can gather all data available for every valid entry on the INI file.
Just send me a note how to format a table which you could use to load on the macro source. Or instruct if I can use an Excel two collums spreadsheet to fill it up.

HTH,
Miguel
#51153 by Jsosnowski
Mon Nov 22, 2010 2:30 pm
Mark,

The Delphi header files in the X3 installation appear to be the same as for 12. Are these files downloadable from this website?
#51159 by Mark F. Madura
Mon Nov 22, 2010 3:23 pm
They have been updated since the initial release of DataCAD X3. Do you have the latest update to DataCAD X3?
#51164 by Mark F. Madura
Mon Nov 22, 2010 5:30 pm
Miguel Palaoro wrote:I'd like to help, putting hands on to the enhancement of this macro.


I'll have to think about how to do this. If you look at the source above, you'll see that I am using a for loop to build the list of ini sections and keys. To do what you describe, each ini section and key would have to be defined explicitly and that's a lot of typing!

MFM
#51177 by rod_walker
Tue Nov 23, 2010 7:53 pm
Mark
Your samples are a great help.

I am using DelphiXE. To create a form I used the IDE to design the form.
Then in the dcal 'action' section of the 'library' file I added
Code: Select all2:
               begin
                 wrtmsg('Set batten properties');
                Application.CreateForm(TfrmSetting, frmSetting);
                end;

In the source file of the form I close the form with:
Code: Select allprocedure TfrmSetting.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Free;
end;

Is it still necasary to use CallNone(retval); as per your example? My code works fine without it.

Regards
Rod Walker
#51186 by Miguel Palaoro
Wed Nov 24, 2010 12:44 pm
Mark F. Madura wrote:
Miguel Palaoro wrote:I'd like to help, putting hands on to the enhancement of this macro.


I'll have to think about how to do this. If you look at the source above, you'll see that I am using a for loop to build the list of ini sections and keys. To do what you describe, each ini section and key would have to be defined explicitly and that's a lot of typing!

MFM

I'd suggest you to use an external file, attached to the macro, which would store the informations above mentioned.

AFAICT this is the FOR loop which you use for searching the INI data:
Code: Select all...
   for x := 0 to count - 1 do begin
      vlINIKeys.InsertRow(sl1.Strings[x], getvalue(sl2.Strings[x]), True);
      svValue1 := sl1.Strings[x];
      svValue2 := getvalue(sl2.Strings[x]);
      if SysUtils.directoryExists(svValue2) then
         vlINIKeys.ItemProps[x].EditStyle := esEllipsis;
      if (svValue2 = 'TRUE') or (svValue2 = 'FALSE') then begin
         vlINIKeys.ItemProps[x].EditStyle := esPickList;
         vlINIKeys.ItemProps[x].ReadOnly := True;
         vlINIKeys.ItemProps[x].PickList := slTF;
      end;
   end;
...

You might be able to split this routine, right after getting the entry string in the actual INI file, and before it's placed in the Inspector. The macro would do a search on that 'external file' and bring the "third" field value, which would be the informational string.

Actually every INI entry can be treated independently from the Section where is it residing.

For building the Inspector table on Key/Value pane the macro creates a table with two collumns. Can you create a third field/collumn to be filled with the information stored on the external file ?
The external file would have only two columns: the Entry string and the informational contens. It could be a simple Tab separated text file, generated from an Excel spreadsheet, and would be updated every time when a new 'Entry' is created, with no intervention in the macro itself.

Hope this is of any help.

Anyway, Mark, your effort is being really great!

Regards,
Miguel
#51843 by Jsosnowski
Mon Jan 17, 2011 10:28 am
Mark F. Madura wrote:UDCLibrary was added specifically for the AEC_Model macro.

MFM


The downloads for D4D X3 do not include UDCLibrary. Will this file be made available?
#51849 by Mark F. Madura
Mon Jan 17, 2011 4:17 pm
Jsosnowski wrote:
Mark F. Madura wrote:UDCLibrary was added specifically for the AEC_Model macro.

MFM


The downloads for D4D X3 do not include UDCLibrary. Will this file be made available?
It is included with the latest DataCAD X3 updater.

Who is online

Users browsing this forum: No registered users and 8 guests

About DataCAD Forum

The DataCAD Forum is a FREE online community we provide to enhance your experience with DataCAD.

We hope you'll visit often to get answers, share ideas, and interact with other DataCAD users around the world.

DataCAD

Software for Architects Since 1984