Improvements in Message functions

With HMG 3.1.3 release, (2013/06/16), all Msgxxx funstions (MsgBox, MsgExclamation, MsgInfo, MsgOkCancel, MsgRetryCancel, MsgStop, MsgYesNo) accept all type of data ( even in title ( except array ); not only characters, even others, including arrays 😮

( MsgMulti() has been replaced 😦 )

Now, we have a new ( and higly useful ) msg function MsgDebug() with an extra feature: this function accept “elastic” count of paramaters 😮

And another very handy debug function : ListCalledFunctions()

( WhereIsIt() has been replaced 😦 )

This little sample will try demonstrate these important ( at least for me 😉 ) enhancement in HMG.

Please don’t take wrong; absolutely I’m not sad by replaced my functions; on the contrary I’m very happy. Because of first, all advance in HMG make me happy; and second these new functions are definetely better than mines. And third, system functions are always better (both in speed and safety) than UDFs.


Viva HMG, viva Dr. Soto 😀

HMG Message functions

As noticed in Hello Word – 3 sample, HMG has a lot of message functions with name prefixed by “Msg”:

MsgBox() :

Shows a Message Box

Syntax: MsgBox(<cMessage>,[<cTitle>]) –> Nil

Example : MsgBox( “This a simple message”, “MsgBox Example” )

MsgExclamation() :

Shows a message box with an exclamation icon and a special sound.

Syntax: MsgExclamation(<cMessage>,[<cTitle>]) –> Nil

Example : MsgExclamation( “Wrong selection !”, “MsgExclamation Example” )

MsgInfo() :

Shows a message box with an information icon and a special sound.

Syntax: MsgInfo (<cMessage>,[<cTitle>]) –> Nil

Example : MsgInfo( “For your information”, “MsgInfo Example” )

MsgStop() :

Shows a message box with an stop icon and a special sound.

Syntax: MsgStop(<cMessage>,[<cTitle>]) –> Nil

Example : MsgStop( “End of file reached”, “MsgStop Example” )

MsgOkCancel() :

Shows a message box with [Ok] and [Cancel] buttons

Syntax: MsgOkCancel(<cMessage>,[<cTitle>]) –> lResponse

Example : MsgOkCancel( “Are your to exit ?”, “MsgOkCancel Example” )

MsgRetryCancel() :

Shows a message box with [Retry] and [Cancel] buttons

Syntax: MsgRetryCancel(<cMessage>,[<cTitle>]) –> lResponse

Example: MsgRetryCancel( “File open error !”, “MsgRetryCancel Example” )

MsgYesNo() :

Shows a message box with [Yes] and [No] buttons

Syntax: MsgYesNo( <cMessage> , <cTitle> ) –> lResponse

Example: MsgYesNo( “Are you sure to delete file ?”, “Confirm Delete” )

Like MsgBox(), all message functions accept parameters as a string constant representing the message itself. All message functions also have a second parameter, again a string constant representing the title of message box.

As noticed in syntax, the first four message functions returns NIL, and the last three returns a logical value.

<lResponse> is .T. when selected first option; Ok, Retry and Yes.

Try and observe results.

Hints :

– As a rule, every HMG project must have one and only one “Main” window. This message functions are exception of this rule. If you have only message function(s) you can build a program without main window.

– For multiple lines message body you may use CRLF. CRLF is a predefined constant in HMG. If required you may define it:

CRLF := HB_OsNewLine()

Example :

MsgBox( "This is a" + CRLF + "multiple line" + CRLF + ;
        "message", CRLF + "Multi-Line" )

– Since message function accept only “C” ( Character ) type arguments as <cMessage> and <cTitle>; for messaging other that ‘C’ type arguments, you may use appropriate function to convert  to character type. See below for an example.

Example :

 HMG Message functions
#include <>

dToday := DATE()
lMaried := .T.
nSalary := 123.456


  dToday := DATE()
  lMaried := .T.
  nSalary := 123.456
  DEFINE WINDOW frmMsgFuncs ;
     AT 0,0 ;
     WIDTH 400 ;
     HEIGHT 200 ;
     TITLE 'Message Functions' ;

        DEFINE POPUP '&Test'

          ITEM 'Msg&Box' ACTION ;
                MsgBox( "This a simple message", "MsgBox Example" ) 
          ITEM 'Msg&Exclamation' ACTION ;
                MsgExclamation( "Wrong selection !", "MsgExclamation Example" ) 
          ITEM 'Msg&Info' ACTION ;
                MsgInfo( "For your information", "MsgInfo Example" )
          ITEM 'Msg&Stop' ACTION ;
                MsgStop( "End of file reached", "MsgStop Example" ) 
          ITEM 'Msg&OkCancel' ACTION ;
                MsgOkCancel( "Are your to exit ?", "MsgOkCancel Example" ) 
          ITEM 'Msg&RetryCancel' ACTION ;
                MsgRetryCancel( "File open error !", "MsgRetryCancel Example" )
          ITEM 'Msg&YesNo' ACTION ;
                MsgYesNo( "Are you sure to delete file ?", "Confirm Delete" )
 ITEM 'Multiple &Line' ACTION ; 
       MsgBox( "This is a" + CRLF + "multiple line" + CRLF + ;
               "message", CRLF + "Multi-Line" ) 
 ITEM 'Non-Character' ACTION ; 
       MsgBox( "Today : "   + DTOC( dToday ) + CRLF +;
               "Married : " + IF( lMaried, "Yes", "No" ) + CRLF +;
               "Salary : "   + LTRIM( STR( nSalary ) ) ,;
               "Non-Char" )

 ITEM "E&xit" ACTION ThisWindow.Release


END MENU // Main()




RETURN // Main()

Hello World – 3

Don’t worry ! This is the last; and important.

Extract DEFINE LABEL – END LABEL structure or @ …,… LABEL command ( which one is currently exist) and then instead of them place this line :

MsgBox( "Hello World !" )

In HMG, there are a lot of message functions, with name prefixed by “Msg“; for details please look at HMG Message Functions page.

After building and running, try a more important feature: reduce your program to these minimum size:

#include <>
   MsgBox( "Hello World !" )

RETURN // Main()

In fact, as a rule, every HMG project must have one and only one “Main” window. This message functions are exception of this rule. If you have only message function(s) you can build a program without main window.

Basic Controls – 2

( Text Box  )

We are continuing with Viva_HMG.hbp, Main.prg and Main.fmg. We have assign real actions other than MsgBox() to our two buttons now : Open File and Edit Record. Open File not required GUI controls ( at least for now ), so we can begin with it: For Open File we need a file ( a table ) first: it’s here; a table with four field: Clients.dbf :

No:  Field Name Type Width Dec
---  ---------  ---- ----- ---
  1  CLI_ID       N      5   0
  2  CLI_SNAM     C     12   0
  3  CLI_NAME     C     12   0
  4  CLI_TLF      C     11   0

And then add a little routine to Main.prg for open (USE) it:

      MsgStop( "Clients.dbf file not found !")
RETURN // OpenTable()

And assign this procedure to ACTION of  Open File  button.

Now, we can begin Edit Record task. For this task we need a separate form, a sub form.  Then let’s begin. “New form” from tool-bar and assign a name : EditReco. Assign a title : “Edit Record”, a type : MODAL. Our table has four fields, so we need four LABEL first:

Names :  lblCLI_ID,  lblCLI_SNAM,  lblCLI_NAME, lblCLI_TLF;

Values ( Captions ) : ID,  Surname, Name, Tlf

Rows : 60, 100, 140, 180 Col : 60

Cols :  60, 60, 60, 60

Widths : 70, 70,  70, 70


We can see our job at work:

Now we need a place  for display the current data and accept user input. The control for this purpose is text box. So we need to define four text boxes for each field in the table.

The button of text box in the IDE tool bar is :

Names :  txbCLI_ID,  txbCLI_SNAM,  txbCLI_NAME, txbCLI_TLF;

Rows : 55, 95, 135, 175

Col : 140

DataTypes : First : NUMERIC, others : CHARACTER

We can see our job at work:

Well …

But where are table data ?

To see table data we need assign field values to text boxes as values.

Again, a little procedure:

   EditReco.txbCLI_ID.Value   := CLIENTS->CLI_ID
   EditReco.txbCLI_SNAM.Value := CLIENTS->CLI_SNAM
   EditReco.txbCLI_NAME.Value := CLIENTS->CLI_NAME
   EditReco.txbCLI_TLF.Value  := CLIENTS->CLI_TLF
RETURN // ReadData()

and a call command for this procedure to ON INIT event of  EditReco form.

The result :

Everything is OK ?

No !

This is only first record of table; how we will see others ?

Yes, we need now yet another feature: navigation; that is travelling between records of table.

But before navigation, we have a problem: Open Table must be processed before Edit Record.

Otherwise a run time error will occurs: Alias does not exist. 

What we can do?

–       Discard Open Table button, open the table automatically; at beginning of program or at beginning of editing.

–       Before editing, check the table, if doesn’t open,

–          a)  open automatically or

–          b)  warn user and don’t load Edit Table form.

Probably most convenient is : disable Edit Record button until table is open.

First a mini procedure :

PROCEDURE Initialize()
   Main.btnEditRec.Enabled := .F.
RETURN // Initialize()

And then add this procedure ON INIT event of form main:

Last point: enable it after USE table:

      Main.btnEditRec.Enabled := .T.
      MsgStop( "Clients.dbf file not found !")
RETURN // OpenTable()

Run and see:

Before Open File :

After Open File:

Now we can pass to navigation:

We need seven buttons: Go Top, Go Next, Go Previous, Go Last, Save, Discard, Exit.

Name: btnGoTop, Caption : Top,  Col : 50, Row: 220, Height: 28, Width: 60

Name: btnGoNext, Caption : Next,  Col : 130, Row: 220, Height: 28, Width: 60

Name: btnPrevious, Caption : Previous,  Col : 200, Row: 220, Height: 28, Width: 60

Name: btnGoLast, Caption : Last,  Col : 270, Row: 220, Height: 28, Width: 60

Name: btnSave Caption : Save,  Col : 380, Row: 60, Height: 28, Width: 100

Name: btnDiscard, Caption : Discard,  Col : 380, Row: 140, Height: 28, Width: 100

Name: btnExit, Caption : Exit,  Col : 380, Row: 220, Height: 28, Width: 100

Common: Font Name: Tahoma, Font Size: 9

Actions :

btnGoTop: ( DBGOTOP(), ReadData() )
btnGoNext: ( DBSKIP(), ReadData() )
btnPrevious: ( DBSKIP( -1 ), ReadData() )
btnGoLast: ( DBGOBOTTOM(), ReadData() )
btnSave: SaveData()
btnDiscard: ReadData()

btnExit: ThisWindow.Release

Note that actions of four first buttons include two actions, separated by comma and enclosed by parenthesis.  With this notation we can define more than one action together.

SaveData() is the inverse of  ReadData(): copy values of text boxes to table fields.

PROCEDURE SaveData()         // Save data from text boxes to table
   CLIENTS->CLI_ID   := EditReco.txbCLI_ID.Value
   CLIENTS->CLI_SNAM := EditReco.txbCLI_SNAM.Value
   CLIENTS->CLI_NAME := EditReco.txbCLI_NAME.Value
   CLIENTS->CLI_TLF  := EditReco.txbCLI_TLF.Value
RETURN // SaveData()

Discard is simply re-reading data from table.

The result:

To be continued …

Download source files