Author Topic: 14. Control Configuration  (Read 1069 times)

0 Members and 1 Guest are viewing this topic.

Offline LarryMc

  • Administrator
  • Hero Member
  • *****
  • Posts: 5965
  • 'All I like is finishing'
14. Control Configuration
« on: August 24, 2011, 09:58:16 PM »
In the previous section, we got our control's class registered with the OS and we made a way to create an instance of our control.

Now we need to configure our control.  This is what makes one instance of our control distinct from all other instances of our control.

In the User's Control Spec section we had the following list:

Next we need to identify the things we need to be able to configure/control:
set the style/type of gage
set the overall size of the gage
set the max min values for the raw data sent to a gage
set the max min values for the displayed range on a gage
set the text identifier for a gage/pointers
set the range multiplier/units text for a gage/pointers, if appropriate
set the offset of clocks to show time zones other than local time.
set the color of rectangle containing the gage
set the white/grey color of the dial face
set the current position of pointer(s), where appropriate


Note: In the above version of the list we have added numbers to facilitate the following discussion.

Item #2 was taken care of in the CreateGageRLM with our left, top, and diameter parameters that were passed to it.

What we want now is a command that can address as many of the remaining list items as we can and that are also practical to set at this point.

We'll start by giving our configuration  function a name. As is the case with all our library functions that can be called by the user the name needs to be really unique.
 
Code: [Select]
SUB ConfigGageRLM( )

RETURN

The following lists each parameter we will pass, along with the item number it addresses in the above list, if appropriate.
win as WINDOW
   Required to identify the parent window of this instance of the control.
style as INT
   list item #1
title as STRING
   list item #5
u as STRING
   list item #6
rawmin as INT
   list item #3
rawmax as INT
   list item #3
dialmin as INT
   list item #4
dialmax as INT
   list item #4 (For clocks this is used for list item #7)
sf as INT
   list item #6
id as UINT
   Required to identify this instance of the control.


Inserting the parameters gives us:
Code: [Select]
SUB ConfigGageRLM(win as WINDOW,_
    style as INT,_
    title as STRING,_
    u as STRING,_
    rawmin as INT,_
    rawmax as INT,_
    dialmin as INT,_
    dialmax as INT,_
    sf as INT,_
    id as UINT )

   RETURN
ENDSUB

Two things should be very clear at this point of the tutorial. One, that our control's message handler IS our control and two, that the only way we can communicate with the control is via messages.

There are several ways to get the above parameter values to our control.  And, at least one way can be done without sending any messages.  However, I believe that the sending of messages is the simplest to understand considering all our discussions to this point.

They can be addressed in any order but we will implement them in the order they are listed. We will use the standard IWBasic SENDMESSAGE command (that you're growing tired of) to send our values. The resulting code is added to our configuration function:
Code: [Select]
SUB ConfigGageRLM(win as WINDOW,_
    style as INT,_
    title as STRING,_
    u as STRING,_
    rawmin as INT,_
    rawmax as INT,_
    dialmin as INT,_
    dialmax as INT,_
    sf as INT,_
    id as UINT )

   SENDMESSAGE(win, GAGE_STYLE, 0,style, id)
   SENDMESSAGE(win, GAGE_TITLE, 0,title, id)
   SENDMESSAGE(win, GAGE_UNITS, 0,u, id)
   SENDMESSAGE(win, GAGE_RAWRNG, rawmin,rawmax, id)
   SENDMESSAGE(win, GAGE_DIALRNG, dialmin,dialmax, id)
   SENDMESSAGE(win, GAGE_MULTI, 0,sf, id)
   RETURN
ENDSUB

The logical question is where did the GAGE_ message ID constants come from.  Right now out of thin air.  We need to define them. We do that with the following code:

Code: [Select]
ENUM GMsg
GAGE_STYLE = 0x401
GAGE_TITLE
GAGE_UNITS
GAGE_RAWRNG
GAGE_DIALRNG
GAGE_MULTI
GAGE_SETPANEL_COLOR
GAGE_DIAL_DARK
GAGE_CLKOFFSET
GAGE_SETPOS1
GAGE_SETPOS2
ENDENUM
For those who haven't used ENUM before it is merely an easy way to assign a series of variable constants.
Each ENUM block has to have a name; in this case we picked 'GMsg'.  We assign a value to the first item in the list and ENUM assigns the rest is sequential order after that.  The fact that we chose to start our message ID constants at 0x401 is not a random act. 

When we assign message ID values we have to be absolutely sure that we are not giving our messages the same values as ones the OS is using like WM_CREATE or WM_CLOSE.  The OS has set aside the numbers at 0x400 and up for user defined messages.

The above ENUM block is added to SECTION 2 of the CCT_lib.iwb file.  Since the user will be unaware that these even exist there is no need to add them to the CCT_test.iwb file.

We went ahead and defined the bottom five constants in the ENUM block since we will address them before this section is complete.

While we're at this point addressing the message ID constants we might as well take care of one other thing.
When we create messages specific for our control we have every expectation that we will use them.  In order to use them they have to be added to our message handle routine.  The following shows all of our messages added to the handler skeleton in SECTION 3 of the CCT_lib.iwb file:

Code: [Select]
SUB MyProc_CC(hWnd:UINT, uMsg:UINT, wParam:UINT, lParam:ANYTYPE),UINT

SELECT uMsg
CASE WM_CREATE

RETURN 0
      CASE WM_DESTROY

RETURN 0
      CASE WM_PAINT

RETURN 0
      CASE WM_ERASEBKGND

RETURN 0
      CASE WM_SETFOCUS

RETURN 0
      CASE WM_KILLFOCUS

RETURN 0
      CASE GAGE_STYLE

RETURN 0
      CASE GAGE_TITLE

RETURN 0
      CASE GAGE_UNITS

RETURN 0
      CASE GAGE_RAWRNG

RETURN 0
      CASE GAGE_DIALRNG

RETURN 0
      CASE GAGE_MULTI

RETURN 0
      CASE GAGE_SETPANEL_COLOR

RETURN 0
      CASE GAGE_DIAL_DARK

RETURN 0
      CASE GAGE_CLKOFFSET

RETURN 0
      CASE GAGE_SETPOS1

RETURN 0
      CASE GAGE_SETPOS2

RETURN 0
ENDSELECT
    RETURN(DefWindowProc(hWnd, uMsg, wParam, lParam))
ENDSUB
Okay, back to the ConfigGageRLM command.  Since it will be called by the user we need to add the GLOBAL statement to Section 1 of CCT_lib.iwb which results in the section looking like this:

Code: [Select]
GLOBAL CreateGageRLM
GLOBAL ConfigGageRLM

We also need to add the declaration statement for it in Section 1 of CCT_test.iwb which results in that section looking like this:

Code: [Select]
'$USE "myControl.lib /* uncomment when converted to include file */
DECLARE EXTERN CreateGageRLM(win as WINDOW,l as INT,t as INT,d as INT,id as UINT )
DECLARE EXTERN ConfigGageRLM(win as WINDOW,style as int,title as string,u as string,rawmin as int,rawmax as int,dialmin as int,dialmax as int,sf as int,id as UINT )

It appears we are through with the ConfigGageRLM command.  Not even close. 
This is where the quality of our User's Control Spec comes into play.  First we have to account for our different styles of gages.  We need to define style constants the user can pass to the ConfigGageRLM command to tell our message handler which type of gage to draw. These style constants are no different than the style_flags we use when we create controls with the CONTROL command.

From the list we made in the User's Control Spec section we arrive at the following:

Code: [Select]
CONST ROUND270B = 1
CONST ROUND270T = 2
CONST ROUND270L = 3
CONST ROUND270R = 4
CONST ROUND360SGL = 5
CONST ROUND360DBL = 6
CONST ROUNDCLOCK12 = 7
CONST ROUNDCLOCK24 = 8
CONST ROUND2PENV = 9
CONST ROUND2PENH = 10

Notice that instead of using the ENUM block, as before, we chose to use the CONST declaration. We could have used ENUM.  Remember that we have to give the block a name.  Before when we used it, the declaration was only in the CCT_lib.iwb file.  So there was no possibly of a naming conflict.  But these constants will be used directly by the user.  So, to prevent any possibility of name conflicts, we didn't use ENUM.  It's really nothing significant either way.

We'll add the above code to Section 2 of the CCT_lib.iwb file

Code: [Select]
ENUM GMsg
GAGE_STYLE = 0x401
GAGE_TITLE
GAGE_UNITS
GAGE_RAWRNG
GAGE_DIALRNG
GAGE_MULTI
GAGE_SETPANEL_COLOR
GAGE_DIAL_DARK
GAGE_CLKOFFSET
GAGE_SETPOS1
GAGE_SETPOS2
ENDENUM
const ROUND270B = 1
const ROUND270T = 2
const ROUND270L = 3
const ROUND270R = 4
const ROUND360SGL = 5
const ROUND360DBL = 6
const ROUNDCLOCK12 = 7
const ROUNDCLOCK24 = 8
const ROUND2PENV = 9
const ROUND2PENH = 10

and Section 1 of the CCT_test.iwb file.

Code: [Select]
'$USE "myControl.lib /* uncomment when converted to include file */
DECLARE EXTERN CreateGageRLM(win as WINDOW,l as INT,t as INT,d as INT,id as UINT )
DECLARE EXTERN ConfigGageRLM(win as WINDOW,style as int,title as string,u as string,rawmin as int,rawmax as int,dialmin as int,dialmax as int,sf as int,id as UINT )

const ROUND270B = 1
const ROUND270T = 2
const ROUND270L = 3
const ROUND270R = 4
const ROUND360SGL = 5
const ROUND360DBL = 6
const ROUNDCLOCK12 = 7
const ROUNDCLOCK24 = 8
const ROUND2PENV = 9
const ROUND2PENH = 10
   

Now, we will take care of some things in our ConfigGageRLM function that normally would be discovered during the course of developing the code in our message handler routine.  But, since we already know what they are (since we are using an existing custom control) we'll cover them all in this section.

The way our code exists right now it is assuming that all our different types of gages use the same parameters.  Also, there are no restrictions of any kind on what the user can pass as parameter values other than the type of value.

We'll go about this discussion by addressing each of the SENDMESSAGE commands we have already coded in the order we coded them(shown below):

Code: [Select]
   SENDMESSAGE(win, GAGE_STYLE, 0,style, id)
   SENDMESSAGE(win, GAGE_TITLE, 0,title, id)
   SENDMESSAGE(win, GAGE_UNITS, 0,u, id)
   SENDMESSAGE(win, GAGE_RAWRNG, rawmin,rawmax, id)
   SENDMESSAGE(win, GAGE_DIALRNG, dialmin,dialmax, id)
   SENDMESSAGE(win, GAGE_MULTI, 0,sf, id)

GAGE_STYLE

There is nothing we need to do to it although we could add code to test the value of style to see if it indeed one of the values we have defined.

GAGE_TITLE

Although there is nothing specifically needed to be done to the title we are going to use it to inform us of another problem.

The four 270 degree gages have a restriction placed on them that minimum dial range can be one unit (0 to 1; -15 to -14; 43 to 44; etc)  and that the maximum dial range can be fifteen units (0 to 15; -15 to 0; -3 to 12; etc.).  If the user passes values that do not meet these criteria we're going to change the title of the gage to read "Dial Min/Max Error!" and we'll set the value to the default(0 to 1).

The following is what the resulting code will look like:
Code: [Select]
   SENDMESSAGE(win, GAGE_STYLE, 0,style, id)
   STRING ttemp=title
   select style
      case ROUND270B
      case& ROUND270T
      case& ROUND270R
      case& ROUND270L
         if (dialmax-dialmin < 1) | (dialmax-dialmin > 15)
            dialmin=0 : dialmax=1
            ttemp="Dial Min/Max Error!"
         endif
   endselect
   SENDMESSAGE(win, GAGE_TITLE, 0,ttemp, id)
   SENDMESSAGE(win, GAGE_UNITS, 0,u, id)
   SENDMESSAGE(win, GAGE_RAWRNG, rawmin,rawmax, id)
   SENDMESSAGE(win, GAGE_DIALRNG, dialmin,dialmax, id)
   SENDMESSAGE(win, GAGE_MULTI, 0,sf, id)

GAGE_UNITS

No changes  or conditions required.

GAGE_RAWRNG

All of our gage types need a set of values for the range of the raw data except for the two clock types. Also, clocks do not need a set of dial range values because their dials are preset. What our clocks do need is an offset value so that we can display different time zones.  And, instead of creating a new parameter just for the offset we'll use an existing one that won't be used otherwise for clocks.  We'll let the user pass the offset in as the dialmin parameter.  And we will send that value with one of our 5 remaining predefined message IDs; GAGE_CLKOFFSET(which takes care of item #7 in our list at the start of this section).
We can handle all of this in a single SELECT block. The resulting code now looks like this:

Code: [Select]
   SENDMESSAGE(win, GAGE_STYLE, 0,style, id)
   STRING ttemp=title
   select style
      case ROUND270B
      case& ROUND270T
      case& ROUND270R
      case& ROUND270L
         if (dialmax-dialmin < 1) | (dialmax-dialmin > 15)
            dialmin=0 : dialmax=1
            ttemp="Dial Min/Max Error!"
         endif
   endselect
   SENDMESSAGE(win, GAGE_TITLE, 0,ttemp, id)
   SENDMESSAGE(win, GAGE_UNITS, 0,u, id)
   SELECT style
      CASE ROUNDCLOCK12
      CASE& ROUNDCLOCK24
         SENDMESSAGE(win, GAGE_CLKOFFSET, dialmin,0, id)
      DEFAULT
         SENDMESSAGE(win, GAGE_RAWRNG, rawmin,rawmax, id)
   ENDSELECT
   SENDMESSAGE(win, GAGE_DIALRNG, dialmin,dialmax, id)
   SENDMESSAGE(win, GAGE_MULTI, 0,sf, id)

GAGE_DIALRNG

We've already discussed above that the four 270 degree gages need a set of  min/max dial range values from the user.  Also, that the two clocks were fixed.  All the rest will be predefined values which the user can't change.  This was done to eliminate a lot of display spacing and text size issues.  After adding the code to support this we have:

Code: [Select]
   SENDMESSAGE(win, GAGE_STYLE, 0,style, id)
   STRING ttemp=title
   select style
      case ROUND270B
      case& ROUND270T
      case& ROUND270R
      case& ROUND270L
         if (dialmax-dialmin < 1) | (dialmax-dialmin > 15)
            dialmin=0 : dialmax=1
            ttemp="Dial Min/Max Error!"
         endif
   endselect
   SENDMESSAGE(win, GAGE_TITLE, 0,ttemp, id)
   SENDMESSAGE(win, GAGE_UNITS, 0,u, id)
   SELECT style
      CASE ROUNDCLOCK12
      CASE& ROUNDCLOCK24
         SENDMESSAGE(win, GAGE_CLKOFFSET, dialmin,0, id)
      DEFAULT
         SENDMESSAGE(win, GAGE_RAWRNG, rawmin,rawmax, id)
   ENDSELECT
   SELECT style
      CASE ROUND360SGL
      CASE& ROUND360DBL
      CASE& ROUND2PENV
      CASE& ROUND2PENH
         dialmin = 0 : dialmax = 10
   ENDSELECT
   SENDMESSAGE(win, GAGE_DIALRNG, dialmin,dialmax, id)
   SENDMESSAGE(win, GAGE_MULTI, 0,sf, id)


GAGE_MULTI

No changes  or conditions required.

Before leaving the ConfigGageRLM command we need to add one more line of code. This line calls a function we will address later:

Code: [Select]
GageInvalidateBackground(win, id)
so our final ConfigGageRLM looks likes this:

Code: [Select]
SUB ConfigGageRLM(win as WINDOW,_
    style as INT,_
    title as STRING,_
    u as STRING,_
    rawmin as INT,_
    rawmax as INT,_
    dialmin as INT,_
    dialmax as INT,_
    sf as INT,_
    id as UINT )
   SENDMESSAGE(win, GAGE_STYLE, 0,style, id)
   STRING ttemp=title
   select style
      case ROUND270B
      case& ROUND270T
      case& ROUND270R
      case& ROUND270L
         if (dialmax-dialmin < 1) | (dialmax-dialmin > 15)
            dialmin=0 : dialmax=1
            ttemp="Dial Min/Max Error!"
         endif
   endselect
   SENDMESSAGE(win, GAGE_TITLE, 0,ttemp, id)
   SENDMESSAGE(win, GAGE_UNITS, 0,u, id)
   SELECT style
      CASE ROUNDCLOCK12
      CASE& ROUNDCLOCK24
         SENDMESSAGE(win, GAGE_CLKOFFSET, dialmin,0, id)
      DEFAULT
         SENDMESSAGE(win, GAGE_RAWRNG, rawmin,rawmax, id)
   ENDSELECT
   SELECT style
      CASE ROUND360SGL
      CASE& ROUND360DBL
      CASE& ROUND2PENV
      CASE& ROUND2PENH
         dialmin = 0 : dialmax = 10
   ENDSELECT
   SENDMESSAGE(win, GAGE_DIALRNG, dialmin,dialmax, id)
   SENDMESSAGE(win, GAGE_MULTI, 0,sf, id)
   GageInvalidateBackground(win, id)
   RETURN
ENDSUB

We'll place our completed function in Section 6 of the CCT_lib.iwb file.
While we at it we'll create our shell for the GageInvalidateBackground routine:

Code: [Select]
SUB GageInvalidateBackground(win as WINDOW, id as UINT)

ENDSUB

and place it in Section 8 of the CCT_lib.iwb file.  We'll cover it in detail later while we're fleshing out the code.  For now we'll just say that it is used to insure that our control gets updated properly when the configuration is changed.

We'll finish up this section with our four remaining messages.  Each one of them is wrapped in a subroutine callable by the user.

Code: [Select]
SUB SetGagePanelColorRLM(win as WINDOW,id as UINT,pnlcolor as UINT)
SENDMESSAGE(win, GAGE_SETPANEL_COLOR, pnlcolor,0, id)
RETURN
ENDSUB
When we create a control it is in a rectangle.  With the SetGagePanelColorRLM function the user will be able to set the color of that rectangle to any desired color. It may match the window background or it may be a contrasting color.

Code: [Select]
SUB SetGageDialDarkRLM(win as WINDOW,id as UINT)
SENDMESSAGE(win, GAGE_DIAL_DARK, 0,0, id)
RETURN
ENDSUB
With the SetGageDialDarkRLM function the user will be able to select a grey gage dial face as opposed to the default white dial face.

Code: [Select]
SUB SetGagePos1RLM(win as WINDOW,id as UINT,pos as INT)
SENDMESSAGE(win, GAGE_SETPOS1, pos,0, id)
RETURN
ENDSUB
The SetGagePos1RLM function is used to set the primary pen position of all gages except clocks.
It is always the longest pen when there are two pens using the same pivot point. For ROUND2PENV type gages it is the pen on the left.  For ROUND2PENH type gages it is the pen on the top.

Code: [Select]
SUB SetGagePos2RLM(win as WINDOW,id as UINT,pos as INT)
SENDMESSAGE(win, GAGE_SETPOS2, pos,0, id)
RETURN
ENDSUB
The SetGagePos2RLM function is used to set the secondary pen position of the ROUND2PEN type gages. For ROUND2PENV type gages it is the pen on the right.  For ROUND2PENH type gages it is the pen on the bottom.

The preceding four functions take care of items #8, 9 and 10 in our list at the beginning of this section. We can now add these to Section 6 of CCT_lib.iwb.  And since the user will be calling these routines they have to be declared as GLOBAL in Section 1 of CCT_lib.iwb. They also have to be declared in Section 1 of CCT_test.iwb.
The current listing of the files after making all the changes are attached.

_____________________________

Coming Next - Saving Passed Parameters
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library