Chapter 8: ePortfolio Version 0.1

Bringing Everything Together

You will recall that I have based this guide around a "learning project" which I have called ePortfolio and by the time you have completed all of the chapters in the guide, you will have developed a basic share portfolio management program which could to be used to track share investments made as part of a single investment portfolio.

This chapter will complete the first stage in creating this program which is the "launch" program. In effect, this will be exactly what the user sees on screen when they double click on the short cut to the program in Windows. In later parts of this guide, we will develop the functionality required to actually do something with the program, such as capture and save details of company shares, and explore other capabilities that are available in HMG such as user menus, database files and so on.

As we create the launch program, we will firstly explore how you can simplify your programming projects in future by using more than one source code file and I will explain how you compile these files and "link" them together to create your executable program file. I'm sure you can imagine how long your source code file would be if you only used one for your entire program! Taking a modular approach to program design means separating its functionality into independent single modules which can be modified or replaced more easily without affecting other functionality you have already developed. Using multiple source code files makes this simpler to implement and, by the end of this chapter, the "launch" program will be a module in its own right which contains "links" to other modules that we will develop later on.

The second aspect we'll explore in this chapter is how to perform tasks when the program starts up for the first time and how to perform a task before it closes down. These initialisation and release tasks occur just once at start up and close down for the main window but I will also introduce how to perform similar tasks for "child" windows (as well as introducing what these are) where the initialisation and release tasks are executed each time that child window is opened or closed.

The third area we'll explore is the use of concatenation which is the technical name for "joining" two or more character strings together to create one single character string and I will briefly explain why such concatenation is useful.

The forth subject covers the use of the TOOLTIP property which is a quick and simple way of providing the user with some useful information about each of the controls, such as picture buttons, that they will see displayed on a screen.

Finally, we'll explore two further subjects; how to create a logical condition by comparing two character strings to each other, which is required for the IF statement to work, and how to use the <span class="code">IF</span> statement where there are more than two conditions that need to be evaluated.

Creating the First ePortfolio Modules

To start understanding this aspect better, we need to create two source code files, giving each a unique name, and instruct the HMG compiler to "link" these two together when it builds the program file.

Using your chosen editor, create the following two source code files and save each with the names I provide before each code list.

The first source code file should be saved to the MySourceCode folder and named HMGGuide-Ch8.prg.

  #include "hmg.ch"

  FUNCTION Main()

  LOCAL cMainWinTitle := "ePortfolio Version 0.1"

  // See Chapter 8 of the HMG Guide for a more detailed explanation
  //   of the ON INIT and ON RELEASE properties. These instruct the
  //   program to perform the stated function ONCE when the program
  //   starts (ON INIT) and ONCE when it closes (ON RELEASE)
  DEFINE WINDOW Win_1 ;
    AT 0,0 ;
    WIDTH 704 ;
    HEIGHT 480 ;
    TITLE cMainWinTitle ;
    ON INIT MsgInfo("Welcome to ePortfolio.", "Start Up") ;
    ON RELEASE MsgInfo("Thank you and goodbye.", "Close Down") ;
    WINDOWTYPE MAIN

    // See Chapter 8 of the HMG Guide for more details of the
    //   changes made to the PICTURE property below. The
    //   TOOLTIP property is also explained in Chapter 8.
    DEFINE BUTTON btnMngShTran
     ROW 32
     COL 32
     WIDTH 164
     HEIGHT 164
     PICTURE ProgFilePaths("Icons") + "MngShTran.jpg"
     ACTION MngShTran()
     TOOLTIP "Click here to enter details of share " + ;
        "transactions, (Buy, Sell etc)"
    END BUTTON

    DEFINE BUTTON btnMngShReg
     ROW 32
     COL 256
     WIDTH 164
     HEIGHT 164
     PICTURE ProgFilePaths("Icons") + "MngShReg.jpg"
     ACTION MngShReg()
     TOOLTIP "Click here to enter details of company " + ;
        "shares that you have invested in"
    END BUTTON

    DEFINE BUTTON btnReports
     ROW 32
     COL 480
     WIDTH 164
     HEIGHT 164
     PICTURE ProgFilePaths("Icons") + "MngReports.jpg"
     ACTION MngReports()
     TOOLTIP "Click here produce reports"
    END BUTTON

    DEFINE BUTTON btnMngCash
     ROW 240
     COL 32
     WIDTH 164
     HEIGHT 164
     PICTURE ProgFilePaths("Icons") + "MngCash.jpg"
     ACTION MngCash()
     TOOLTIP "Click here to manage cash transactions"
    END BUTTON

    DEFINE BUTTON btnMngShVals
     ROW 240
     COL 256
     WIDTH 164
     HEIGHT 164
     PICTURE ProgFilePaths("Icons") + "MngShVals.jpg"
     ACTION MngShVals()
     TOOLTIP "Click here to update share valuations"
    END BUTTON

    DEFINE BUTTON btnReminders
     ROW 240
     COL 480
     WIDTH 164
     HEIGHT 164
     PICTURE ProgFilePaths("Icons") + "MngRmnd.jpg"
     ACTION MngReminders()
     TOOLTIP "Click here to manage actions and reminders"
   END BUTTON

  END WINDOW

  CENTER WINDOW Win_1
  ACTIVATE WINDOW Win_1

  Return Nil

  /* -- End of FUNCTION Main() -- */

  FUNCTION ProgFilePaths(cDirectory)

  // See Chapter 8 of the HMG Guide for a more detailed explanation
  // of how the following IF command works.

  IF cDirectory == "Icons"
     RETURN "c:\hmg.3.4.0\MySourceCode\Icons\"
   ELSEIF cDirectory == "Data"
     RETURN "c:\hmg.3.4.0\MySourceCode\Data\"
   ELSE
     RETURN " c:\hmg.3.4.0\MySourceCode\ "
  ENDIF

RETURN Nil

  /* -- End of FUNCTION ProgFilePaths() -- */

  /* -- End of file ePortfolioLaunch.prg -- */

You can download a copy of the source code file from here - HMGGuide-Ch8.prg

I have also created a set of six simple bitmap images to use with your code. Extract these to folder c:\hmg.3.4.0\MySourceCode\Icons from here - LaunchTiles.zip

The second source code file should be saved to the MySourceCode folder and named ePortfolioPlace.prg.

  #include "hmg.ch"

  FUNCTION MngShTran()

  MsgInfo("The ability to enter details of share transactions " + ;
    "(Buy, Sell etc) will be available in a later release.", ;
    "Manage Share Transactions")

  RETURN NIL

  /* -- This is a separator line -- */

  FUNCTION MngShReg()

  MsgInfo("The ability to capture details of company shares that " + ;
    "you invest in will be available in a later release.", ;
    "Manage Share Register")

  RETURN NIL

  /* -- This is a separator line -- */

  FUNCTION MngReports()

  MsgInfo("The ability to produce reports will be available " + ;
    "in a later release.", ;
    "Reports")

  RETURN NIL

  /* -- This is a separator line -- */

  FUNCTION MngCash()

  MsgInfo("The ability to manage the cash account associated with " + ;
    "your portfolio will be available in a later release.", ;
    "Manage Cash")

  RETURN NIL

  /* -- This is a separator line -- */

  FUNCTION MngShVals()

  MsgInfo("The ability to update share valuations will be " + ;
    "available in a later release.", ;
    "Update Share Valuations")

  RETURN NIL

  /* -- This is a separator line -- */

  FUNCTION MngReminders()

  MsgInfo("The ability to create and view actions and reminders " + ;
    "will be available in a later release.", ;
    "Actions and Reminders")

  RETURN NIL

  /* -- End of file ePortfolioPlace.prg -- */

You can download a copy of the source code file from here - ePortfolioPlace.prg

We now need to instruct the HMG compiler that we are using two different source code files for our program and that these should be "linked" together when the program is compiled. Fortunately, this is easy to do with HMG if you use a source code list rather than the name of a single source code file. The source code list contains the names of all the source code files that you want HMG to compile and "link" together and it should be named with an .hbp file extension.

Using your chosen editor, create the following file and save it as ePortfolio.hbp ensuring that you change the file extension to .hbp rather than .prg!

HMGGuide-Ch8.prg
ePortfolioPlace.prg

The next step is to build and "link" your two source code files into one executable program. To do this, use the "..\build ePortfolio.hbp" command line prompt. It is not necessary for you to add the .hbp> extension as HMG already "knows" about these but I have included this here to make things a little clearer.

The new launch program should now run. Take a few moments to familiarise yourself with it and notice what happens if you hover the mouse over a button without actually clicking on it. Finally, click on each of the buttons and see what the pop-up messages say.

You will also have noticed that the two source code files contain new sections of code and new ways of performing certain operations. I will now explain each of these a little further focusing on the highlighted sections of code listed above.

Performing Tasks When the Program Starts

There will be times when you want your program to perform certain tasks when the user starts the program and before they are able to actually use the program. For example, you might want to be able to tell the program where it will be able to find and open database files that it will use later in the program. This is known as an initialisation task and DEFINE WINDOW includes a property that makes this easy to do!

ON INIT MsgInfo("Welcome to ePortfolio.", "Start Up") ;

When the MAIN window is defined at the start of program execution, this property is used to perform a task once and only once! In this example, we have instructed the program to display a pop-up message to welcome the user to the program once it has started up and the main window has been displayed on screen. Once they click "Ok" on that pop-up message, they will not see the welcome message again while the program is running. I have deliberately used a very simple example that actually does display something on screen so that you can see something has actually happened on initialisation. It is possible to write a function that runs once at initialisation that doesn't actually "show" anything to the user.

It is also possible, as you will see later on in this guide, to define multiple windows which are opened from the MAIN window (think of these as being the "children" of the main window with the MAIN window being the parent window). The capability to run a function each time you define and display a new "child" window is also available by including an ON INIT <InitProcedureName> property and argument (remember that ON INIT is the property of the window and <InitProcedureName> is the argument you pass to the property). However, unlike a MAIN window that loads when the program launches, this function will be run every time the child window is defined and displayed. Don't worry too much if this is a little confusing! We will explore this is more detail later in this guide.

Performing Tasks When the Program Ends

You may be wondering it is possible to make the program perform certain tasks once and only once when the user has finished using it and before the program closes and the answer is "yes".

ON RELEASE MsgInfo("Thank you and goodbye.", "Close Down") ;

In this example, we have instructed the program to display a pop-up message when the user decides they are finished using the program. When the user does this, the program needs to "release" WINDOW Main() but before it does that, the message is displayed. Once the user clicks on "Ok" in that pop-up message, the window is released (i.e. it closes down and disappears off the screen) and the program closes.

As above, this release procedure will only be executed once and only once in the case of the MAIN window. Also like above, any release procedure defined in "child" windows will be executed every time you close the child window! We will also explore this capability in greater detail later in this guide.

Introducing Concatenation

Concatenation is used when your program needs to "join together" two or more sets of characters into a single line. Previously, I have simply referred to any "sets" of characters as "text" but in programming, these are actually known as character strings. Quite simply, a character string consists of a series of individual alphanumeric characters (letters, punctuation and numbers expressed as text) that will make sense to either your program or the user. A good example of a character string is the title that appears at the top of the main window. We created a LOCAL variable called cMainWinTitle and gave it a value of "ePortfolio Version 0.1". From this point onwards, whenever I refer to a "character string" or "text", please assume that these mean the same thing unless I explicitly state any differences.

There will be times when you will want to join together two or more different character strings into one character string which is where concatenation is used. This is better understood by looking at the first place in the code where I have concatenated two character strings into one.

PICTURE ProgFilePaths("Icons") + "MngShTran.jpg"

In previous chapters, we have expressed the path to the directory and the file name of the picture to display as a picture button as a single character string by saying, for example, PICTURE "c:\hmg.3.4.0\MySourceCode\Icons\IconCh4.bmp". I now want to tell my program the full name of the directory path in only one place so that I can change the directory path much more easily in future if I decide to do so. In other words, I no longer want to hard code the directory paths throughout my code every single time I use them.

To do this, I have created a small UDF called ProgFilePaths(). This, as you can see, accepts a parameter (see chapter 5 if you need a reminder on what a parameter is) which is called cDirectory in the UDF. We will look at the code in that UDF to explore how to manage multiple conditions with the IF command later on in his chapter, but for now, take it that ProgFilePaths() will return a character string containing the directory path. This is then "joined together", or concatenated, with the name of the picture ("MngShTran.jpg" in the example above) into "c:\hmg.3.4.0\MySourceCode\Icons\MngShTran.jpg" which is then used as the value that the PICTURE property uses as an argument.

Introducing the TOOLTIP

A tooltip can be used to briefly display a short message to the user if they place the mouse cursor over any particular control we have defined and displayed on the screen without them actually clicking on it. This is usually used to provide a short description of what the control is being used for and what would happen if they actually click on it.

TOOLTIP "Click here to enter details of share " + ;
   "transactions, (Buy, Sell etc)"

As you can see above, we have added a TOOLTIP property each time we define a picture button and added a character string as its argument which contains the message to display if the user places the mouse cursor over the picture button.

Creating Logical Conditions

In chapter 6 we briefly explored the IF command and you may recall that I said the "most important thing to remember about the IF command is that it requires a logical condition – one that is either true or false – for it to work". In that chapter, we had only used a value that was logical (.T. or .F.) but what happens if you need to work with values that aren't logical such as character strings?

To do that, we have to ensure that our code will produce a logical condition for the IF command to work using relational operators. These compare the "relationship" between two values to produce a logical condition and using these also enables us to work with multiple conditions rather than just one that can only be true or false. Before we explore multiple conditions, we need to understand how to use a relational operator in order to produce a logical condition.

IF cDirectory == "Icons"

As you know, we created the UDF ProgFilePaths() to return a character string that represents a directory path and this UDF accepts a parameter cDirectory. The first line of code that executes, in this example, compares the value of the parameter to the character string "Icons" using the relational operator == which means "exactly equal to". This results in a logical condition which is true (.T.) if the value of cDirectory is "Icons" or false (.F.) if it is not.

An important point to remember when comparing the relationship between two character strings using this relational operator is that the two must be exact equal to one another to produce a true condition and that includes the use of upper and lower case letters. So, for example, comparing "Hello" to "hello" to see if it they are exactly equal would result in a false condition due to there being an uppercase "H" in the first character string and a lowercase "h" in the second character string.

Managing Multiple Conditions with the IF Command

Whenever IF cDirectory == "Icons" is exactly equal to "Icons", in other words the logical condition is true, the following line of code executes;

RETURN "c:\hmg.3.4.0\MySourceCode\Icons\"

You already know that a RETURN statement demarcates the end of a function, so why have more than one RETURN statement? The simple answer is that there will be times, especially with larger UDFs, when the UDF has done what you need it to towards the beginning of the code and you need to "exit" without executing any more code later in the UDF. Where this is the case, you can use a RETURN statement which, in this example, is used to return a character string for the icons directory path.

So, what happens if the value produced by the first logic condition is false and we need to evaluate other possible conditions?

ELSEIF cDirectory == "Data"

Where you need to evaluate multiple conditions rather than just one, the ELSEIF statement an be used.  In the code example used in this chapter, this statement will be executed next whenever IF cDirectory == "Icons" has produced a false condition; in other words, whenever the value of cDirectory is not exactly equal to "Icons". In this example when this is the case, the value of cDirectory will be compared to "Data" and, if the condition is now true, it will return a character string to represent the directory path for the data files we will use in later chapters.

Finally, how do we handle situations where none of the earlier comparisons in IF... ELSEIF are true? As in chapter 6, the ELSE statement can be used and, in this example, I have simply returned a value of "c:\hmg.3.4.0\MySourceCode\".

You may be thinking that the final line of code in this UDF, RETURN Nil, which appears immediately after ENDIF is not necessary as it will never actually be executed. You are absolutely correct! The IF... ELSEIF... ELSE... ENDIF code  will always ensure that this is the case as every condition produces a result that exits the UDF before the final RETURN Nil statement but I have included this anyway! The program will still compile and run without a problem if you removed that final RETURN Nil statement (feel free to try this yourself) but I always include a final RETURN statement, simply out of habit, and because other UDFs you write might not exit before this and you should always try to return something!

The final comments I will make for now about IF... ELSEIF... ELSE... ENDIF is that there is no real limit to the number of conditions you can compare using ELSEIF so you can compare a large range of potential conditions.

Secondly, using IF is not the only way of comparing conditions. HMG includes other ways of doing this, such as DO CASE... ENDCASE, which is similar to IF... ENDIF. Furthermore, HMG also allows you to use an IF... ENDIF within an IF... ENDIF, which is known as a nested IF, and you can even use IF... ENDIF within DO CASE... ENDCASE and vice versa! As I have said (many) times previously, don't worry if this sounds a little confusing. All of these capabilities will be explained later in this guide.

Final Code for ePortfolio Version 0.1

One of the objectives of this chapter was to complete the first module - the "launch" program - of the "learning project", ePortfolio. The code providing in this chapter has achieved that but I would suggest that after a while the "welcome" and "goodbye" pop-up messages displayed when you start and close the program might become a little annoying!

Therefore, I suggest that you now remove the ON INIT and ON RELEASE properties and their arguments defined for WINDOW Win_1 and save the code file naming it ePortfolioLaunch.prg.

You will also need to change the ePortfolio.hbp file so that the "..\build ePortfolio" command works when you compile the final version of ePortfolio for this chapter.

ePortfolioLaunch.prg
ePortfolioPlace.prg

You can download a copy of the source code file from here - ePortfolioLaunch.prg

What's Coming Next in The HMG Guide?

In the first section of this guide, you have learnt quite a lot about using HMG which, I hope, have given you some confidence that you can develop the skills necessary to undertake your own programming projects. We have done this by slowly building up your knowledge whilst actually developing the first program module and I have slowly introduced a number of capabilities, features and "basics" of using HMG.

Before we develop the next module, and in the next section, we need to focus our attention on developing understanding of certain "basics" of the language which are fundamental to any project you may undertake in future. Think of these "fundamentals" as being the foundation on which you build your programs in future and, I hope, you will also realise that the following section is critically important.

In the section that follows, we will go into greater detail about "Data Types" to build on your knowledge of character strings. We will also expand on your knowledge of LOCAL variables by exploring the other types of variable available and understand which parts of your program can see them; their scope. Finally, we will explore the different types of operator to develop the knowledge you have gained when using == and :=. These include mathematical operators, relational operators, logical operators and assignment operators.

Chapter 7: User Defined Functions <<     >>  Section 2 Overview