C G D   P R O G R A M M I N G
        Version 2006 (c) Jacques Basaldúa
Installing / uninstalling CGD
A simple modal dialog
Different control types
Modifying the default options
A dialog without a .CGD file
Callback functions
Previewing with a callback function
Previewing with an application-owned bitmap
Modeless dialogs
Using CGD with C
Using CGD with VisualBasic


Installing / uninstalling CGD
 
    CGD does not use the Windows registry at all, and does not require any special installation. These guidelines are rules to share and update CGD between different applications. If you do not want to follow this, just copy the DLL (cgdrun20.dll) to your application's .exe path, and never override any instance of the DLL in the path defined by GetSystemDirectory().

    If this is your first reading, or you do not feel comfortable with Windows API subjects such as runtime DLL linking, etc., skip this. This is a rather advanced subject to create commercial level applications.
 
    Recommended installation

    Important subject: CGD applications should share one single instance of cgdrun20.dll located in the Windows system directory. Its is always safe to override a DLL by an other DLL of the same name as long as its cgdInternalRelease() method returns a higher value. Never do that if not. If any incompatibility could happen, the DLL would be renamed as cgdrun21.dll or cgdrun30.dll (not used yet). The return value of cgdRuntimeVersion() is used to identify the expected compiler version and API interface. The only function required for installation is cgdInternalRelease().

   
LinkCgdLib is a small dynamically linked function to install CGD:
 
NOT USES cgdTypes; // This contains all necessary, using cgdTypes would produce an error when the DLL is not present
TYPE
    typCGDInternalRelease   = Function                              : longint; StdCall;
    typCGDSetOptions        = Function (const Opts   : cgdOptions;
                                            dlh      : THandle)     : longint; StdCall;
    typCGDGetDefaultOptions = Procedure(var Opts     : cgdOptions)             StdCall;
    typCGDSetCGDFileName    = Procedure(    fnam     : pChar);                 StdCall;
    typCGDDialog            = Function (    aParent  : hWnd;
                                            Caption,
                                            stNam    : pChar;
                                            stSize   : longint;
                                            stAddr   : pointer)     : longint; StdCall;
CONST
    cgdInternalRelease    : typCGDInternalRelease   = nil;
    cgdSetOptions         : typCGDSetOptions        = nil;
    cgdGetDefaultOptions  : typCGDGetDefaultOptions = nil;
    cgdSetCGDFileName     : typCGDSetCGDFileName    = nil;
    cgdDialog             : typCGDDialog            = nil;

Function LinkCgdLib       : boolean;
var opts     : cgdOptions;
    dllpath  : filename;
    Obsolete : boolean;
begin
    repeat
        Obsolete   := false;
        LinkCgdLib := AdvanceCgdRuntime;        if not result then exit;
        LinkCgdLib := AdvanceSetupCGD(cgdnam);  if not result then exit;
        LinkCgdLib := false;
        byte(dllpath[0]) := GetSystemDirectory(@dllpath[1], pred(SizeOf(dllpath)));
        ValidPath(dllpath);
        dllpath := dllpath + 'CgdRun20.DLL';
        ZeroStr(dllpath);
        SetErrorMode(sem_NoOpenFileErrorBox);
        ll := LoadLibrary(@dllpath[1]);         if ll=0 then exit;
        cgdInternalRelease := GetProcAddress(ll, 'cgdInternalRelease');
        if (longint(pointer(@cgdInternalRelease)) = 0)
        or (cgdInternalRelease < 8) then Obsolete := true;
        if Obsolete then begin
            if (not FreeLibrary(ll))
            or not WinErase(dllpath) then begin
                if MessageBox(hWnd_Desktop,
                              'The library CGDRUN20.dll has to be replaced. ' +
                              'It is being used by another program.' + #13+#10 + #13+#10 +
                              'Close all applications and select <Retry>',
                              'W A R N I N G',
                              mb_RetryCancel + mb_IconQuestion) <> id_Retry then exit
            end
        end
    until not Obsolete;
    cgdSetOptions        := GetProcAddress(ll, 'cgdSetOptions');
    cgdGetDefaultOptions := GetProcAddress(ll, 'cgdGetDefaultOptions');
    cgdSetCGDFileName    := GetProcAddress(ll, 'cgdSetCGDFileName');
    cgdDialog            := GetProcAddress(ll, 'cgdDialog');
    if (longint(pointer(@cgdSetOptions))        = 0)
    or (longint(pointer(@cgdGetDefaultOptions)) = 0)
    or (longint(pointer(@cgdSetCGDFileName))    = 0)
    or (longint(pointer(@cgdDialog))            = 0) then exit
    ZeroStr(cgdnam);
    cgdSetCGDFileName(@cgdnam[1]);
    cgdGetDefaultOptions(opts);
    with opts do begin
        OpeningLevel   :=   1;
        tbButtons      :=   1;
        txColor        := $60a0e0;
        bkColor        := $400000;
        StaticIt.name  := 'Times New Roman';
        StaticIt.size  := 16;
        StaticIt.Italic:= false;
        EdPathX        := 256;
        boolMinX       := 256;
        PreV.doPreview := pvPreviewOnTop;
        PreV.pvOnTimer := false;
        PreV.pvX       := 508;
        PreV.pvY       :=  20;
        autoAdjLength  := true
    end;
    cgdSetOptions(opts, dlhModalDialog);
    LinkCgdLib := true
end;
    If the function LinkCgdLib() returns true, CGD has successfully been installed/updated and you can safely call cgdDialog() to install your application.

    NOTES:

      1.
GetSystemDirectory, SetErrorMode, LoadLibrary, GetProcAddress, FreeLibrary and MessageBox are the Windows API functions of the same name.
      2.
filename is a pascal string (first byte indicates the length).
      3.
AdvanceCgdRuntime is a procedure for copying or unzipping cgdrun20.dll from your application's CD or .exe file, ONLY IF IT DOES NOT PREVIOUSLY EXIST, to the Window system directory.
      4.
AdvanceSetupCGD is a procedure for copying or unzipping a setup.cgd from your application's CD or .exe file to some temporary destination. cgdnam is the name of this temporary file returned by the function.
      5.
ValidPath adds a '\' character at the end of a string only if it is missing.
      6.
ZeroStr adds a #0 to a string without modifying its length. DD3 does not contain any pChar<->string[] conversion functions.
      7.
WinErase is a procedure to erase a file via the Windows API: SetFileAttributes() + DeleteFile()
.
    Recommended uninstallation

    Just one rule: Never do it!

    a.
No matter if your application copied the file, an other application may rely on the DLL you copied because it was present at its installation.
    b. Leaving a needless 57 Kbyte library will not hurt, specially in a system in which hundreds of megabytes can be freed with no drawback.

A simple modal dialog
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Hello\Hello.pas

PROGRAM HELLO;
USES cgdTypes;{<STARTDLG CGD20>}
TYPE
{<DIALOG HelloDialog>}
    HelloDialog   = packed record
        {<BLANKLINE>}
        {>  My first CGD dialog.} {<LBOLD>}
        {<BLANKLINE>}
        Speed : single;
        more  : single  {>An other field}
    end;
{<ENDDIALOGS>}
VAR aDia : HelloDialog;
BEGIN
    cgdSetCGDFileName('.\hello');
    aDia.speed := 100;
    aDia.more  := 0;
    cgdDialog(0, 'My first dialog !', 'HelloDialog', SizeOf(HelloDialog), @aDia)
END.

    1. Compile the dialog using cgd.exe. This creates the file Hello.cgd which should be identical to the example file given. Only the part between {<STARTDLG CGD20>} and {<ENDDIALOGS>} is the dialog.

    2. Compile the program using dcc32.exe. This creates the file Hello.exe, depending on your Delphi version, it may not be identical to the example file given. (The 3.5 Kbyte file was compiled with custom system.pas and sysinit.pas libraries.)
    (Only) three things are requested:

        a. Define the path & file name to locate the dialog file hello.cgd, (without the extension).
        b. Initialize a record of the type you pretend to edit. (HelloDialog in this case)
        c. Run the dialog using cgdDialog()
Function cgdDialog (aParent : hWnd;
                    Caption,
                    stNam   : pChar;
                    stSize  : longint;
                    stAddr  : pointer)  : longint; StdCall;
    aParent  a valid Window ID. As in many Windows API functions (e.g. MessageBox): "Identifies the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window."
    Caption similarly: "Points to a null terminated string used for the dialog box title."
    stNam name of the dialog as defined in
{<DIALOG stNam>}
    stSize size of the structure in bytes (for verification, no default value.)
    stAddr pointer to the variable of the dialog type.

    The function returns (if no error) cgdRunOk (or cgdRunCancel, if the user pressed <Esc>).

    The possible error list is:

        cgdFileNotFound   (cgdSetCGDFileName not called, or CGD file does not exist.)
        cgdDescrNotFound (stNam is not the name of a valid dialog in the cgd file.)
        cgdNotEnoughRAM   (The DLL cannot allocate RAM. Memory requirements are very low, you will not see this error easily.)
        cgdWrongSize   (stSize is different than expected. Check: enumeration size, boolean size, integer/longint, etc.)
        cgdDecryptFailed (Internal. CGD file may be corrupted.)

Different control types
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Controls\Controls.pas

PROGRAM CONTROLS;
USES cgdTypes;
{<STARTDLG CGD20>}
TYPE
{<DIALOG ControlsDemo>}
    Locate the following lines:
        ud      : cgdListBox; {>User defined}
        color   : cgdColor;
        fil     : cgdFileName;
        pat     : cgdPath;
        Today   : Date;
        Now     : Time;
        Here    : Point2;
        TheMoon : Point3
    This example is very similar to the previous example, except by the types used. It uses: enumerations, conditionals and the following structured types:
    cgdListBox   = packed record
        opt       : byte;           // The (0-based) option selected.
        TextList  : string [254]    // The (#0-separated) descriptions, ended by a double #0.
    end;

    cgdColor     = packed record
        rgb       : longint         // A Windows ColorRef (as in CreatePen(), ...).
    end;

    cgdFileName = packed record
        name,                       // File name.
        DefDir,                     // Default directory.
        ExtDescr  : string [];      // (#0-separated) extension descriptions, ended by a double #0.
        fOpen,                      // True = open dialog, else save dialog. (GetOpenFileName() or GetSaveFileName())
        cOver     : boolean;        // True = Prompt on overwrite (= Windows flag ofn_OverwritePrompt).
        uSelFmt   : integer         // On return: (0-based) File format selected by the user from the ExtDescr list
    end;

    cgdPath      = packed record
        name,                       // Directory name.
        Caption   : string []       // Message displayed when the Windows dialog shows up.
    end;
    Four more types are used to "concentrate" many variables in a single line:
    Date & time (Note: Dates show up in "european-style" day/month/year. Integer is a 16 bit signed integer.)
    Date   = packed record
        yy,mm,dd : integer
    end;
    Time    = packed record
        hh,mm,ss : integer
    end;
    2D and 3D positions and vectors: (Note: Points and vectors many use unit conversion following isMetric)
    Point2   = packed record
        x,y   : single
    end;
    Point3    = packed record
        x,y,z  : single
    end;

Modifying the default options
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Options\Options.pas

    cgdGetDefaultOptions(opt);
    with opt do begin
        OpeningLevel := 1;
        tbButtons    := tbOEUDeplRetr;
        txColor      := $800000;
        bkColor      := $c0c0c0;
        hasDlgFrame  := false;
        ColorLighter := $3f3f;
        EditBkColor  := $20a0c0;
        EditTxColor  := $80;
        EditFnt.size := 16;
        EditFnt.Bold := true;
        RadioFnt.size:= 18;
        RadioFnt.Bold:= true;
        ComboFnt.name:= 'Courier New';
        ComboFnt.size:= 22;
        BoolMinX     := 180;
        RadioMinX    := 180;
        ComboMinX    := 180;
        EdStringX    := 180;
        EdNumberX    := 180;
        ColorSampX   := 94;
        autoAdjLength:= true
    end;
    cgdSetOptions(opt, dlhModalDialog);
    CGDTranslateBuiltInMsgs(cgdMsgsInSpanish);
    cgdDialog(0, 'Just change some options and . . .', 'ControlsDemo', SizeOf(ControlsDemo), @aDia)
    Many CGD settings: fonts, colors, etc. can be changed. This is valid for both modal and modeless dialogs. Therefore, changing options requires a dialog handle. For modal dialogs, use the constant dlhModalDialog as a handle. Two possible ways of changing the options are:

        1. (The easiest.) Call cgdGetDefaultOptions() to initialize a record of the type cgdOptions with valid values. Change whatever you want. Apply it to the dialog using cgdSetOptions.

        2. Create a CONST of the type cgdOptions from scratch, and apply it to the dialog using cgdSetOptions. (Or copy/paste one from the CGD command: Copy options as source.)

    The options are:

    1. General dialog appearance (and details)

OpeningLevel How deep are tabbed folders deployed when the dialog is showed for the first time. Must be at lest 1. One means no folders deployed (just the main level is visible), two means 2 levels deployed, etc.
ForceScrollbar Controls the visibility of the vertical scrollbar. May be fsNeverShow, fsShowAlways, fsUseDefault (meaning show only if necessary)
tbButtons Number of buttons in the toolbar (In range tbNoToolbar to tbOEUDRCPPreview)
hasDlgFrame Enables/disables a 3D-looking frame around the dialog. (Default is true)
hasThickFolders Makes the folder frames 2 pixels wide instead of 1. (Depending on the colors defined, one pixel is barely visible or 2 pixels is too much.)
HelpTimeout Controls the duration for displaying warning messages (in 1/4 second units)
ParentClick (MAI Internal) Sends a wm_LButtonDown message to the parent of the dialog, each time one is received. This is intended to handle focus control of MAI clients in MAI (Multiple Area Interface) applications.

    2. Editing controls

isMetric See metric.
fullReadOnly Makes the whole dialog read-only.

    3. Colors

txColor,bkColor,ComboTxColor,ComboBkColor,EditTxColor,EditBkColor All colors are in Windows TColorRef 32 bit integers formatted $00BBGGRR (Blue, Green, Red), respectively: text color, background color, text in COMBOBOX, background in COMBOBOX, text in EDIT, background in EDIT (and front of COMBOBOX).
ColorLighter This is a difference color between layers. E.g. 0 = all tabbed layers have the same color. $00101010 means, layer 1 has the color defined by bkColor layer 2 has bkColor +16(B) +16(G) +16(R), Layer 3 = Layer 2 +16(B) +16(G) +16(R), and so on. This makes degraded colors automatically. $00200000 means each layer gets +32 in blue more than the previous one.

    4. Font definitions

StaticN,StaticBd,StaticIt,StaticBI,RadioFnt,ComboFnt,EditFnt Font definitions are: TTF name, size, bold (fw_Bold) or italic as used by the API function CreateFont(). Definitions apply to: text defined as normal, as bold, as italic, as bold+italic (You may use different types, sizes and/or bold/italic settings as you wish, without necessarily corresponding to the original bold/italic settings.) fonts used for RADIOBUTTON, COMBOBOX and EDIT controls.

    5. Previewing

PreV See below.

    6. Sizes of the controls

    In CGD, all sizes are calculated automatically according to selected fonts, content of combo boxes, etc. The resulting sizes may be increased (but not decreased). This is why some values are called xxxMinX meaning minimal size along the X axis.

autoAdjLength To avoid visually unpleasant misalignments, when two control types have similar lengths, the longest of them is used for both, if this is true. Default value is false.
BoolMinX,BoolMinY,RadioMinX,RadioMinY,ComboMinX,EdMinY Minimum sizes for: Boolean (CHECKBOX), Enumerations as RADIOBUTTON, Enumerations as COMBOBOX (Y defined by its contents and font settings) and EDIT controls (X defined according to the type)
EdStringX,EdNumberX,EdFnamX,EdPathX,EdPntCellX,EdColCellX,EdDTBoxX,ColorSampX X sizes for different EDIT controls depending on the type: strings, number (any integer or any real), file name (cgdFilename), path (cgdPath), coordinates (Point2 or Point3), color coordinates (cgdColor), date or time editing controls (date or time) and the length of the color samples (shown as a rectangle to the right of the color coordinates of a cgdColor).

    NOTE: cgdOptions restore their default values when cgdDialog returns, or when a modeless dialog is finished by cgdModelessDlgClose.
 
    A related subject: Translating cgdrun20.DLL's built-in messages

    The DLL uses bitmap buttons with no text on them, plus twelve built-in messages (in english). These messages are stored in ANSI (8 bit) format. The messages are:

cgdLexical   = packed record
    invNum : string [47]; // 'Warning : Invalid number format or range'
    noSuch : string [47]; // 'Failed : No such record in the clipboard'
    AnyFil : string [47]; // 'Any file'
    RidNly : string [47]; // 'Record is read only!';
    OkButt : string [47]; // 'Accept current values = <Enter>'
    EscBtn : string [47]; // 'Escape without modifying = <Esc>'
    UndoBt : string [47]; // 'Undo all changes'
    DeplBt : string [47]; // 'Deploy all'
    RetrBt : string [47]; // 'Retract all'
    CopyBt : string [47]; // 'Copy full record'
    PasteB : string [47]; // 'Paste full record'
    PreviB : string [47]  // 'Preview with current values'
end;

    These messages can be modified (or translated) by calling cgdTranslateBuiltInMsgs() providing a pointer to a cgdLexical record. This is usually done just once when the DLL is loaded.

    When the DLL is reloaded, you get the original messages. An other possibility is to make that translation permanent. This creates a cgdrun20.lex file in the Windows system directory which is loaded automatically when the DLL is loaded. To do so, call MakeTranslationPermanent. It is not recommended to use this function without the consent of the user, since other applications may assume default messages. The best choice: Don't use it at all. Translate the messages to the application's language and set them via cgdTranslateBuiltInMsgs each time you load the DLL.

A dialog without a .CGD file
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Direct\Direct.pas

PROGRAM DIRECT;
USES cgdTypes;
{<STARTDLG CGD20>}
TYPE
{<DIALOG HelloDialog>}
    HelloDialog    = packed record
        {<BLANKLINE>}
        {>  Showing the direct use of CGD via}
        {>  cgdSetDialogDataDirectly()  } {<LBOLD>}
        {<BLANKLINE>}
        Speed : single;
        more  : single  {>An other field}
        {<BLANKLINE>}
    end;
{<ENDDIALOGS>}
CONST DirDialog   : array [0..241] of char
 = 'Codeless Generic Dialog - V2.0'+#26+#0+#11+'HELLODIALOG'+#0+#0
 + #0+#0+#0+#0+#0+#0+#7+#0+#0+#0+#0+#0+#3+#0+#1+'›'+#1+'('+#0+#1
 + '›'+#1+'‡#  Showing the direct use of CGD via&'+#0+#1+'‘€'+#255
 + '‰'+#1+'‡'+#30+'  cgdSetDialogDataDirectly()  '+#3+#0+#1+'›'+#1
 + '%'+#0+#1+'?'+#4+'?'+#6+'‡'+#5+'Speed'+#255+'£€ŠIl;€‚u€° €Ô€ŠIl;€‚'
 + 'u€°T.'+#0+#1+'?'+#4+'?'+#6+'‡'+#14+'An other field'+#255+'š€ŠIl;'
 + '€‚u€°€Ô€ŠIl;€‚u€°T'+#3+#0+#1+'›'+#1;

VAR aDia : HelloDialog;
BEGIN
    aDia.speed := 100;
    aDia.more  := 0;
    cgdSetDialogDataDirectly(@DirDialog, SizeOf(DirDialog), 'HelloDialog');
    cgdDialog(0, 'No CGD file used to display this !', 'HelloDialog', SizeOf(HelloDialog), @aDia)
END.
    If, for some reason (as displaying a dialog in a setup program, displaying a dialog from inside a .dll, etc.), you think that having an external .cgd file is annoying, you can transfer its binary image directly to the dll.

    By the way, there is an other method to call CGD from inside a DLL:

        a. Use cgdGetCGDFileName() to retrieve the current .cgd file name defined by the application and save it.
        b. Use cgdSetCGDFileName() to set a new name.
        c. Call the dialog.
        d. Restore the saved name using cgdSetCGDFileName() to avoid interference with the application.

    1. Direct setting can only be used dialog by dialog. Create a CGD source containing only one dialog, and compile it.

    2. Define the contents of the CGD file (complete) as a constant in your application. If you don't have a better option, you can use the MS/DOS program bin2pas.exe located at
<CGDdirectory>\Pascal\Examples\Direct\ Rename your cgd file first to avoid the coincidence of names. Avoid any alignment. Define it with its real size.

    3. Call cgdSetDialogDataDirectly (pCgd   : pointer;      // pointer to the binary image of the .cgd file.
                                                                cgSiz   : longint;       // length of the .cgd file
                                                                stNam  : pChar)      // name of the dialog

    4. call cgdDialog() or cgdInitModelessDlg()

    Direct CGD data definitions are cleared when cgdDialog returns or when a modeless dialog is finished by cgdModelessDlgClose.

Callback functions
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Evaluate\Evaluate.pas

PROGRAM EVALUATE;

USES WinTyp32, Win32, cgdTypes;

{<STARTDLG CGD20>}

TYPE
    pGuessDialog  = ^GuessDialog;
    {<DIALOG GuessDialog>}
    GuessDialog   = packed record
        {<BLANKLINE>}
        {>  Guess a number from 1 to 100 !}  {<LBOLD>}
        {>  (And Press <Enter>)}             {<LITALIC>}
        {<BLANKLINE>}
        secret : integer;                    {<PRIVATE>}
        guess : integer  {>Your guess}       {<MIN 1>}   {<MAX 100>}
        {<BLANKLINE>}
    end;

{<ENDDIALOGS>}

Function MyEvaluateCallback(    myStruc    : pGuessDialog;
                            var Help       : string;
                                pWinObject : pointer)     : boolean; StdCall;

begin
    MyEvaluateCallback := false;
    if myStruc^.guess > myStruc^.secret then Help := 'Guess is too high !'
    else begin
        if myStruc^.guess < myStruc^.secret then Help := 'Guess is too low !'
        else MyEvaluateCallback := true
    end
end;

VAR aDia : GuessDialog;

BEGIN
    cgdSetCGDFileName('.\evaluate');
    Randomize;
    aDia.secret := succ(Random(100));
    aDia.guess  := 50;
    cgdSetEvaluationCallback (@MyEvaluateCallback, dlhModalDialog);
    if cgdDialog(hWnd_Desktop, '...', 'GuessDialog', SizeOf(GuessDialog), @aDia) = cgdRunOk
    then MessageBox(hWnd_Desktop, 'Your guess was right!', 'Ok.', mb_Ok)
END.

    Callback functions are functions called by the DLL following some triggering events for specific purposes such as viewing and evaluating data. Two special functions: PreviewCallback used for preview ( see below) and MlOkEscCallback used by modeless dialogs ( see below), are explained separately.

    The three remaining functions: EvaluateCallback, EditChangeCallback and TimerCallback are basically for the same purpose, i.e., evaluating data in the record, warning the user with messages and (optionally) changing the contents of the dialog. The difference between the three methods is the event triggering the evaluation.

    EvaluateCallback is triggered when the user presses <Enter> or clicks the <Ok> button, if the function has been defined. If the function returns true, the dialog is closed, else the dialog is not. Usually, in this case, a warning message such as: "The field e-mail cannot be left blank." is returned by the callback.

    EditChangeCallback is triggered every time the user changes data, and the function has been defined.

    TimerCallback is triggered 4 times per second (a 250 ms delay) when defined.

    1. All callback functions have similar interfaces:

           pStruc  : pointer;             // A pointer to the data being edited
     var Help   : string;                   // If not void, the string will be displayed as a warning message
           pWinObject : pointer      // See modeless dialogs
 
       The only exception is EditChangeCallback, which also provides a pItem  : pointer. This points to the record field which has (or may have) been modified. If the whole record may have been modified (by functions as paste or undo) this pointer is nil.

    2. To define a callback function use the corresponding defining function. Provide a valid dialog handle or use the constant dlhModalDialog for modal dialogs. Defining functions are:

        cgdSetViewerCallback to define the PreviewCallback.
        cgdSetEvaluationCallback to define the EvaluateCallback.
        cgdSetEditChangeCallback to define the EditChangeCallback.
        cgdSetTimerCallback to define the TimerCallback.
        cgdModelessOkEscCallback to define the MlOkEscCallback.

    As cgdOptions, callback function definitions restore their default values (no definition at all) when cgdDialog returns or when a modeless dialog is finished by cgdModelessDlgClose.

Previewing with a callback function
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Viewdemo\Viewdemo.pas

PROGRAM VIEWDEMO;

USES WinTyp32, Win32, cgdTypes;

{<STARTDLG CGD20>}
// ... see viewdemo.pas
{<DIALOG Preview>}
// ... see viewdemo.pas
{<ENDDIALOGS>}

Procedure PreviewProc  (    pStruc     : pPreview;
                            pReason    : longint;
                        const drs      : TDrawItemStruct;
                        var Help       : string;
                            pWinObject : pointer);   StdCall;
begin
    // ... see viewdemo.pas
end;

VAR aDia : Preview;
    opt  : cgdOptions;

BEGIN
    cgdSetCGDFileName('.\viewdemo');
    cgdGetDefaultOptions(opt);
    with opt do begin
        PreV.doPreview := pvPreviewOnTop;
        PreV.pvOnEdChng:= true;
        PreV.pvX       := 512;
        PreV.pvY       := 320
    end;
    cgdSetOptions(opt, dlhModalDialog);
    cgdSetViewerCallback(@PreviewProc, dlhModalDialog);
    with aDia.town do begin
// ... see viewdemo.pas
    end;
    cgdDialog(hWnd_Desktop, 'This dialog has a preview method', 'Preview', SizeOf(Preview), @aDia)
END.
    This is a particular case of callback function which justifies an example by itself.
 
    1. The DLL calls a function of the type

Procedure PreviewProc  (    pStruc     : pPreview;
                            pReason    : longint;
                        const drs      : TDrawItemStruct;
                        var Help       : string;
                            pWinObject : pointer);   StdCall;
    pStruc Pointer to the record data.

    pReason May be: prPaintedByWindows (if the OS sends wm_Paint messages), prPaintedByBtnClick (when the user deploys/retracts tabs, etc.), prPaintedByEdChange (when the user edits data and the option pvOnEdChng is true), prPaintedByTimer (every timer tick, when the option pvOnTimer is true).

    drs  : TDrawItemStruct A TDrawItemStruct (called DrawItemStruct in C) is a Windows type defined as "The DRAWITEMSTRUCT structure provides information the owner window must have to determine how to paint an owner-drawn control or menu item." For the operating system, the previewing area is an owner-drawn button. You don't need to care about that, this is just to simplify the dialog object. Probably, you will not use any part of this structure other than the drs.hDC. This is a valid DC handle provided by the system used to paint on the viewing area.

    Help As in all callbacks: if not void, the string will be displayed to the user as a warning message.

    pWinObject Is used in modeless dialogs see below.

    2. To provide a valid callback function, use
cgdSetViewerCallback(@PreviewProc, dlh);

    3. Default options do not use previewing, you will always have to change the options. At least, change:

    PreV.doPreview Must be either pvPreviewOnTop or pvPreviewToRight to place the viewing area on top of the dialog or to the right of it. Since dialogs usually have fixed width (for the same options) but variable height (depending on conditionals, deploying, etc.) the first option is easier to implement.

    PreV.pvOnTimer,pvOnEdChng See pReason just above. This defines what events force calling the callback.

    PreV.pvX,pvY The size of the dialog's viewing area. Since this area should match (either in X (on top) or in Y (to right)) with the dialog's editing area to show up "nicely", you can use cgdModelessGetClientX before setting this option to adjust it. You can also leave it slightly bigger (the dialog size will not be adjusted according to this setting) and paint your scene away from the borders. Anyway, since dialog options (such as character size) significantly change dialog size, don't let this area be fixed, if dialog size is variable due to user defined options.

Previewing with an application-owned bitmap
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Bitmaps\Bitmaps.pas

{<STARTDLG CGD20>}
TYPE
{<DIALOG BitmapDemo>}
    BitmapDemo  = packed record
        {<BLANKLINE>}
        {>    This bitmap is NOT conditional}  {<LBOLD>}
        {<CBITMAP 1001 >}       {<BITMAPSHIFTX -4>}
        {<BLANKLINE>}
        {>    This bitmap is conditional}   {<LBOLD>}
    {<ALIAS Selector>} {\}
        sel1  : (First, Second); {>Select the} {>>case}
    {<CASE Selector = 0>} {\}
        {<CBITMAP 1002 >}       {<BITMAPSHIFTX -4>}
    {<CASE Selector = 1>} {\}
        {<CBITMAP 1003 >}       {<BITMAPSHIFTX -4>}
        {<BLANKLINE>}
        {>    This bitmap is application-owned    } {<LBOLD>}
        {<LBITMAP APPOWNED 1004>}     {<BITMAPSHIFTX 6>}
        {<BLANKLINE>}
    end;
{<ENDDIALOGS>}

    This example merges the three possible bitmaps: conditional (1002/1003), not conditional (1001) and application-owned (1004).

    Functions related with bitmaps are cgdSetBitmapsHInstance and cgdSetAppOwnedBmpHandle. The use of bitmaps is explained in bitmap details.

Modeless dialogs
 
 
    Sources at <CGDdirectory>\Pascal\Examples\Modeless\Modeless.pas

PROGRAM MODELESS;
// ... see modeless.pas
Procedure mlAppWindow.SetupWindow;
begin
    // 1. Initialize the record
    // ... see modeless.pas
    // 2. Init the dialog
    GetClientRect(hWindow, dr);
    dx := dr.right - dr.left;
    dy := dr.bottom - dr.top;
    if cgdInitModelessDlg('ControlsDemo', SizeOf(ControlsDemo), dlgHandle ) = cgdInitOk then begin
        // 2.a Set the callback to let the <Ok> <Esc> buttons close the dialog (The application in this case)
        cgdModelessOkEscCallback(@OkEscCallB, dlgHandle);
        // 2.b A good place to change the options ( !! For the dlgHandle, not the dlhModalDialog)
        cgdGetDefaultOptions(opts);
        // ... see modeless.pas
        cgdSetOptions(opts, dlgHandle);
        // 2.c Now, show it.
        if cgdModelessDlgShow(hWindow , dr.left, dr.top, dx,dy, @aDia, SizeOf(ControlsDemo), dlgHandle, hChildWin, @self)
        = cgdInitOk then begin
            // ... see modeless.pas
        end
    end
end;

Destructor mlAppWindow.Done;
begin
    if dlgHandle > 0 then begin
        // This is the only required part to destroy the dialog and free dlgHandle
        cgdModelessDlgClose (dlgHandle, true);
        // ... see modeless.pas
    end;
    TAppWindow.Done
end;
    1. What is a modeless CGD dialog?

    A modeless CGD dialog is a dialog that can be "left open" while the user (and the application) processes other commands, paint, etc. The dialog is usually showed somewhere in the application's client area. When the user validates the data he is editing, (by pressing <Enter> usually via the toolbar), a Callback function does something with the data. (In our example, just closing the application to show the resulting data in a read-only CGD dialog.) This area (a Window) is usually of variable size. The application is responsible of informing the CGD library when the window is resized.

    An example: Open any of the example files, compile it (F4) and select a dialog from the Dialog menu. The CGD environment displays the dialog in a resizeable area near the left bottom corner. This is a modeless CGD dialog.

    2. How are modeless dialogs created?

    This subject is only directed to programmers who can build their own Window and Application objects. Since this is a rather seldom situation, although it is much easier than you may think, I will not explain Windows message and object details. You don't get source files of your Window and Application objects in Delphi, therefore, you could have difficulties with details such as arrow key processing. (Read the bottom part of cgdTypes.pas, the explanation of the function cgdGossipPeekMsgLoop)

    Nevertheless, the main guidelines for modeless dialog programming are:

    0. Optionally, if your application wants to know the width of the dialog (with the current options) to initialize the size of the containing window, use cgdModelessGetClientX(). The height is always as much as you can provide, scrolled automatically.

    1. Instead of using the constant dlhModalDialog used until now every time a handle was required, now, you get a valid dlgHandle with a successful call to:
cgdInitModelessDlg('ControlsDemo', SizeOf(ControlsDemo), dlgHandle ) This dlgHandle will be used to identify the open dialog in all following CGD calls, until it is destroyed by a cgdModelessDlgClose (dlgHandle, true) .

    2. The cgdInitModelessDlg call just creates a valid handle (which can be used to change options, define callbacks, etc. before starting the dialog) and locates the dialog in the current CGD file set by cgdSetCGDFileName (or via cgdSetDialogDataDirectly). The dialog is not started until you call
cgdModelessDlgShow(hWindow, dr.left, dr.top, dx,dy, @aDia, SizeOf(ControlsDemo), dlgHandle, hChildWin, @self) . When you do so:
        a. hWindow is the window where the dialog is confined. Don't paint in it! (If it has a background brush, set this brush to GetStockObject(null_Brush)).
        b. ox,oy,dx,dy is the initial size of that window. stAddr, stSize define the record containing the initial (and final) content of the dialog. dlh is the handle returned by cgdInitModelessDlg.
        c. ChildWin is the window handle of the dialog (not the same as hWindow). This handle is given to you for low level message processing such as sending wm_KeyDown messages to the dialog. The dialog is a window, send other window messages to it at your own risk.
        d. pWinObject is optional. Don't use it if you don't want. Every time the dialog calls any callback function, it will return this value to your function to help you identifying the caller. The most frequent use of this stuff in Windowing environments (as in the example) is giving a pointer to the instance of the calling object. In the example: cgdModelessDlgShow(..., @self) gives a pointer of the type ^TWindow (descendant) (mlAppWindow is a descendant of TAppWindow, which is a descendant of TWindow) so the callback function, expecting a ^TWindow uses pWinObject^.hWindow to find the window handle of a TWindow object. (If you plan to write your own windowing lib and did not understand this, read it again.)

    3. While the dialog is running:
        a. Your dialog does not scroll or change the control properly when you use the keyboard (Arrow keys, Tab, <Shift>-Tab, etc.) unless you gossip keyboard messages to it. You are responsible of providing some mechanism as the one explained in cgdGossipPeekMsgLoop. In the example, this works because of the line hChildCGDialog := hChildWin. The TApplication uses this handle (when not zero) in its method TApplication.Run as explained in cgdGossipPeekMsgLoop.
        b. At any time, you can read (or modify) the dialog content via cgdModelessDlgGetData (or cgdModelessDlgSetData). Changing data while the user may be typing it, is a practice considered annoying by most users (as typing URLs in Internet Explorer): use cgdModelessDlgSetData prudentially, or not at all.
        c. When the window where the dialog is confined is resized (identified by hWindow), you must tell the dialog calling cgdModelessDlgResize.

    4. To get rid of the dialog: call cgdModelessDlgClose. If you set retOk to true, the record containing the initial data (The one you passed to cgdModelessDlgShow) will be updated with the user's edition, else it will be discarded, unless read via cgdModelessDlgGetData.

Using CGD with C
 
 
    Sources at <CGDdirectory>\C\Examples\Hello\Hello.dsp

    General syntax:

    CGD can be compiled directly from  .c and .cpp source files. This generates an intermediate file with a (c) appended to its file name.
    hello.cpp -> hello(c).pas -> hello(c).cgd
    CGD commands are the same commands as in Pascal, but written in a remark line preceding the line to which they are applied.
//{<CASE Sel1 = 1>}{> An 8 byte real}
    double    y;
    is the same as:
    y    : double;    {<CASE Sel1 = 1>}{> An 8 byte real}
    Other differences with the Pascal spec:

    1. Enumerations are understood in C format as:
enum eSel1
{
  AnInteger = 0,
  AReal     = 1,
  ATextLine = 2
};
    2. Pascal strings can be defined as arrays of unsigned char (index zero corresponding to the length).
 
    Recommended use:

    Open your C source file using Open C. Once you have converted your file, open that xxx(c).pas file. This will prevent the CGD environment from overwriting this file. If the xxx.C file is the source file, CGD will overwrite the xxx(c).pas file every time you re-open it, ignoring any "manual" modifications made to the xxx(c).pas file. If the xxx(c).pas file is the source file, it will not be modified in either way.

    You may require some "manual" adjustment, specially if your compiler aligns record fields differently. Check the appropriate sizes of the records in both systems. CGD writes the size of each record when you compile it. As in the example, you are expected to add {<PRIVATE>] alignment fields to match your C compiler's alignment method:

    These adjustments can be done in your C source files directly.

    About the example:

    1. This example is basically the same as the example controls in Pascal.

    2. The file hello(c).cgd was compiled using the CGD menu option Open C and opening the file hello.cpp.

    3. If Visual C is installed in your computer, double-click the file Hello.dsp, compile and run.

    4. If you cannot use C, forget this example. This is not the recommended way to use CGD.

    5. The example contains a small subset of the content of cgdTypes.pas only. It is intended to explain how CGD "could" be implemented in C. The actual implementation is up to you.

    6. If you use CGD with C and want to generously share your experience with other users, send me some source files
cgd@dybot.com and I will be very pleased to include them as examples or publish them in the CGD website. Of course, you will be mentioned as the author.

Using CGD with VisualBasic
 
 
    Sources at <CGDdirectory>\Basic\Examples\Hello\Hello.vbp

    General syntax:

    Similarly to the C implementation, CGD can be compiled directly from the .bas source file. This generates an intermediate file with a (b) appended to its file name.
    hello.bas -> hello(b).pas -> hello(b).cgd
    CGD commands are the same commands as in Pascal, but written in a remark line preceding the line to which they are applied.
'{<CASE Sel1 = 1>}{> An 8 byte real}
    y       As Double
    is the same as:
    y    : double;    {<CASE Sel1 = 1>}{> An 8 byte real}
    Other differences with the Pascal spec:

    1. Enumerations are understood in VB format as:
'{>Sky}     NOTE THAT: Private, Public or none are ignored.
Private Enum enSky
    Clouded
    WithRainbow
    ByNight
    ByDay
End Enum
    2. Pascal strings can be defined as arrays of byte (index zero corresponding to the length). You could implement conversion functions such as:
Sub BasToPas(ps() As Byte, s As String)
Dim i As Integer
On Error GoTo NoRedim
    ReDim ps(0 To Len(s))
NoRedim:
    ps(0) = Len(s)
    For i = 1 To ps(0)
        ps(i) = Asc(Mid(s, i, 1))
    Next i
End Sub
    (If you use VB, you will surely write something much better than this, it is just to understand what I mean.)
 
    Recommended use:

    Open your VisualBasic source file using Open Basic. Once you have converted your file, open that xxx(b).pas file. This will prevent the CGD environment from overwriting this file. If the xxx.bas file is the source file, CGD will overwrite the xxx(b).pas file every time you re-open it, ignoring any "manual" modifications made to the xxx(b).pas file. If the xxx(b).pas file is the source file, it will not be modified in either way.

    You may require some "manual" adjustment, specially if your compiler aligns record fields differently. Check the appropriate sizes of the records in both systems. CGD writes the size of each record when you compile it. As in the example, you are expected to add {<PRIVATE>] alignment fields to match your VisualBasic compiler's alignment method:
        Booleans are 16 bit wide
        If the size of a field = 0 (MOD 4) align to 4 x i
        Else, If the size of a field = 2 (MOD 4) align to 2 x i
              Else, don't align
    These adjustments can be done in your Basic source files directly.

    About the example:

    1. This example is basically the same as the example controls in Pascal.

    2. The file hello(b).cgd was compiled using the CGD menu option Open Basic and opening the file hello.bas

    3. If VisualBasic is installed in your computer, double-click the file:
Hello.vbp and press <F5> (Run)

    4. If you cannot use VisualBasic, forget this example. This is not the recommended way to use CGD.

    5. The example contains a small subset of the content of cgdTypes.pas only. It is intended to explain how CGD "could" be implemented in VB. The actual implementation is up to you.

    6. If you use CGD with VisualBasic and want to generously share your experience with other users, send me some source files
cgd@dybot.com and I will be very pleased to include them as examples or publish them in the CGD website. Of course, you will be mentioned as the author.