This tutorial will give a simple introduction to the usage of the Eiffel WEL library. We will build a simple calculator in 3 easy steps. The method employed here will make exclusive use of  pure WEL Eiffel code only i.e  no external dependency on third-party programs such as resource editors.  The calculator interface will consist of a main window, a menu, an about dialog box and a dialog box for choosing an operator for the calculation of the entered operands. A simple multi-line text field will allow the user to edit comments and save them to a text file.  Optionally, the multi-line text field can also be filled with the contents of a text file.  The main window will appear as follows:

In each step, I'll first provide the complete source code for the class in question and then discuss line by line the logic of what is going on. The ace file for the calculator application is here

Note: The ace file makes use of the multi-threaded precompiled WEL classes.

 

STEP 1

In this step, we will build the main window of the calculator.

Every program in Eiffel as you know has a main root class. This class is responsible for initializing the whole system. Similarly, every windows application build using WEL has a root class. However, the restriction is that this root class MUST descend from the deferred class WEL_APPLICATION. We will call our root class WEL_CALCULATOR. The source for WEL_CALCULATOR is as follows:

class

    WEL_CALCULATOR

inherit

    WEL_APPLICATION

creation

    make

feature

    main_window: WEL_FRAME_WINDOW is

    -- Create the application's main window.

    once

        !! Result.make_top ("WEL Calculator")

    end

end -- class WEL_CALCULATOR

The creation feature 'make' for our root class is simply inherited from class WEL_APPLICATION. The feature 'main_window' above is declared as a deferred feature of class WEL_APPLICATION and is of class type WEL_COMPOSITE_WINDOW . This feature contains a reference to the application's main window and so MUST be made effective in all descendent classes of class WEL_APPLICATION. Class WEL_FRAME_WINDOW, a descendent of deferred class WEL_COMPOSITE_WINDOW encapsulates a generic window with a system menu (the one you get when you click the upper left hand corner icon of a window), a close, maximize/minimize and dock buttons (all on the upper right hand corner of a window). Class WEL_FRAME_WINDOW also contains a creation feature called 'make_top'. We make the deferred feature 'main_window' effective above simply by creating an instance of WEL_FRAME_WINDOW by calling the creation feature 'make_top'. The parameter passed is the title of the window (in this case 'WEL Calculator'). Note that the effective 'main_window' feature of class WEL_CALCULATOR above changes its class type from WEL_COMPOSITE_WINDOW as declared in ancestor class WEL_APPLICATION to class type WEL_FRAME_WINDOW. This is valid as per Eiffel's type conformance rules.  Also note that feature 'main_window' is a once feature. Obviously, the main window should only be created once upon startup. The above listing produces a generic window like this:

 

This generic window is not awfully useful. To create a customized main window, what we have to do is descend from the generic class WEL_FRAME_WINDOW and override it's default behaviour.  We will create a descendant of WEL_FRAME_WINDOW called CALCULATOR_MAIN_WINDOW.  The features we will override are the ones responsible for the dimensions of the window i.e the width and the height of the window and the window background color.  The resultant code for class CALCULATOR_MAIN_WINDOW is:

class

    CALCULATOR_MAIN_WINDOW

inherit

    WEL_FRAME_WINDOW

        redefine

            class_background,

            default_width,

            default_height

        end   

creation

    make

feature -- Initialization

    make is

    -- Make the main window

    do

        make_top ("WEL Calculator")

    end

feature {NONE} -- Implementation

    class_background: WEL_LIGHT_GRAY_BRUSH is

    -- Light gray background

    once

        !! Result.make

    end

    default_width: INTEGER is

    do

        Result := 400

    end

    default_height: INTEGER is

    do

        Result := 210

    end

end -- class CALCULATOR_MAIN_WINDOW

This class simply redefines the features 'default_width',  'default_height'   and 'class_background' from the ancestor class WEL_FRAME_WINDOW.   The 'default_width' and 'default_height' features are pretty much self-explanatory.  Feature  'class_background' is declared as class type WEL_BRUSH in ancestor class WEL_FRAME_WINDOW.   Because class WEL_LIGHT_GRAY_BRUSH is a descendent of WEL_BRUSH, our redefinition of feature 'class_background' as class type WEL_LIGHT_GRAY_BRUSH is type conformant to the original declaration. Feature 'class_background' simply returns an instance of class WEL_LIGHT_GRAY_BRUSH which encapsulates a gray shade brush. Creation feature 'make' above simply calls creation feature 'make_top' of ancestor class WEL_FRAME_WINDOW to initialize the main window with the title 'WEL Calculator'.   Because we redefined class WEL_FRAME_WINDOW and created a custom class CALCULATOR_MAIN_WINDOW,   our root class WEL_CALCULATOR redefines feature 'main_window' of class type WEL_COMPOSITE_WINDOW as declared in ancestor class WEL_APPLICATION to class type CALCULATOR_MAIN_WINDOW.   The redefinition of feature 'main_window' from class type WEL_COMPOSITE_WINDOW to class type CALCULATOR_MAIN_WINDOW is valid as per Eiffel type conformance rules.  The resultant root class is:

class

    WEL_CALCULATOR

inherit

    WEL_APPLICATION

creation

    make

feature

    main_window: CALCULATOR_MAIN_WINDOW is

    -- Create the application's main window.

    once

        !! Result.make

    end

end -- class APPLICATION

Note the redefinition of 'main_window' and how the implementation of this feature differs from the previous listing of this root class. The above code changes results in a window that looks like this:

 

step1a.bmp (20118 bytes)

 

Whew, we are almost done with creating the main window :-)  What is left now is to add two single line edit controls for the user to be able to enter the two operands, another single line edit control to display the resultant value of the calculation, a multi-line edit control to enter comments, and a button to compute the result.  This part is really much simpler than it sounds.  The modified class CALCULATOR_MAIN_WINDOW is:

class

    CALCULATOR_MAIN_WINDOW

inherit

    WEL_FRAME_WINDOW

    redefine

        class_background,

        default_width,

        default_height,

        on_control_id_command

   end

creation

    make

feature -- Initialization

    make is

    -- Make the main window

    do

        make_top ("WEL Calculator")

        !!edit_cntrl.make(Current,"",10, 5, 150, 25, -1)

        !!edit_cntrl2.make(Current,"",10, 35, 150, 25, -1)

        !!compute_btn.make(Current,"Compute -->", 10, 65, 150, 40, Id_compute_btn)

        !!result_cntrl.make(Current, "",10,110,150,25,-1)

        !!comment_edit.make(Current,"", 175, 5, 200, 145, -1)

    end

feature {NONE} -- Implementation

    Id_compute_btn: INTEGER is unique

    edit_cntrl, edit_cntrl2, result_cntrl: WEL_SINGLE_LINE_EDIT

    comment_edit: WEL_MULTIPLE_LINE_EDIT

    compute_btn: WEL_PUSH_BUTTON

 

    class_background: WEL_LIGHT_GRAY_BRUSH is

    -- Gray background

    once

        !! Result.make

    end

    default_width: INTEGER is

    do

        Result := 400

    end

    default_height: INTEGER is

    do

        Result := 210

    end

    on_control_id_command(control_id: INTEGER) is

    do

    end

end -- class CALCULATOR_MAIN_WINDOW

Note: A control in the following discussion is a synonym for a window (albeit a small one).   Examples include buttons, edit boxes, list boxes etc.

In this modified class, we introduce features 'edit_cntrl', 'edit_entrl2', and 'result_cntrl' of class type WEL_SINGLE_LINE_EDIT, feature 'comment_edit' of class type WEL_MULTI_LINE_EDIT and feature 'compute_btn' of class type WEL_PUSH_BUTTON and a constant 'Id_compute_btn'. A little exposition on the new class types: class WEL_SINGLE_LINE_EDIT encapsulates a standard single line edit control; class WEL_MULTI_LINE_EDIT encapsulates a standard multi-line edit control; class WEL_PUSH_BUTTON as you might have guessed, abstracts a standard push button. The modified creation feature 'make' creates the three single line edit controls, one multi-line edit control and a button after the main window is created. The reason will be clear shortly.  If you notice, each of the controls has a similar creation style.  The 'make' feature on all these controls has exactly seven parameters. We'll pick the following arbitrary line to explain the parameters:

!!compute_btn.make(Current,"Compute -->", 22, 75, 150, 40, Id_compute_btn)

The first parameter passed is Current. The Current object in this case is of class type CALCULATOR_MAIN_WINDOW.  The reason for passing this is based on  the Win95/NT windowing model (virtually all windowing systems also adopt a similar approach).  Every window you see on your desktop (yes, even buttons, edit boxes, combo boxes, list boxes and radio buttons are considered to be small windows) has a parent-child relationship to some other window. For example, the OK button you see in a dialog box has the dialog box as its parent window.  Another example: Every application's main window has the typical Explorer desktop as its parent window because the Explorer desktop window is the first and top-most window you see upon startup. One reason among others for maintaining this hierarchy is that when an event is generated in a window, say........ a click of a button in a dialog box, then the dialog box (i.e the parent window of the button) should be notified that the button was pressed.  Why should the dialog box be notified ???   Well, one possible reason could be to close the dialog box in response to an OK button click. Another reason for maintaining this hierarchy is that when a parent window is destroyed, then all child windows are also automatically destroyed. Because of this parent-child dependency, you can now understand why the controls are created after the main window (parent window) is initialized in feature 'make' as mentioned before.

The second parameter is the title of the window.  For a push button, the title is simply the caption of the button.  For an edit control, the title is the simply the text that is displayed in the edit control.  If you notice, the title for the three edit controls in the above code listing simply passes an empy string " ". As you might have guessed, we want an empty edit control. The third and  fourth parameters denote the control's top left (x,y) coordinates relative to the parent window.  The fifth and sixth parameters are the width and height of the control.  The last parameter is the control's integer id.   What purpose does this id serve ???  Remember the parent-child hierarchy example mentioned above.  Suppose a dialog box has many buttons; say an OK and a CANCEL button.  When a click occurs in one of these buttons, the dialog box as we discussed before is notified of this event.  But how does the dialog box know which button generated the event i.e. which button was clicked?   The id of the button is how the dialog box distinguishes one button from the other.  Similarly, whenever any event occurs in any child window, the parent window is notified of what event occured and which child window generated the event; via the child window's id.   Notice that for all the three edit controls, we simply pass an id of -1.  This contradicts what I mentioned of having each child control a unique id.   The answer is quite simple. The reason for giving a unique id to each child control is to be able to distinguish which child control generated an event  We choose an arbitrary non unique number (-1) because we are NOT interested in processing any events that these controls generate. We could have passed any other number; it was only a matter of personal choice choosing -1.  Nothing prevents us from giving the controls different ids, but why bother ?? We are not going to be needing them anyway!  Because we are only interested in the button control click event, we therefore give it a unique id.

One last addition to this class is the redefinition of feature 'on_control_id_command'.   Remember the parent-child hierarchy ?    When a notification message arrives in a parent window, the event intercepting feature in the ancestor class WEL_COMPOSITE_WINDOW (an ancestor of WEL_FRAME_WINDOW) calls an empty (non-implemented)  feature 'on_control_id_command' passing the id of the child window (control) that generated the event as a parameter. We simply redefine the empty feature 'on_control_id_command' to process the events.  Right now, we don't process any events, so clicking the button now doesn't do anything as yet ! Step 3 will implement the feature 'on_control_id_command'.

You might ask whether there exists any other method of catching control events.   The control ids mentioned above is actually not necessary.   You can get rid of those unique ids all together (just pass a -1 or any other number you like) and still be able to process events for the controls.   We defer this discussion of catching events without relying on unique ids using feature 'on_control_command' in Step 2

Our customized main window now looks like this:

step1b.bmp (20118 bytes)

 

This brings us to the end of step 1. As you have seen, most of what we have done so far is simply extend and specialize (by redefinition) the WEL_FRAME_WINDOW class to suite our calculator application.

 

STEP 2

In this step we will add a menu and an about dialog box.

Our main menu has two pop-up menus: Options and Help.

Options has five menu-items with the following captions:

  1. 'Load Comment...'

  2. 'Save Comment...'

  3. 'Change operator...'

  4. A seperator menu-item.

  5. 'Exit...'.

Help has one menu-item with caption 'About...'.

The two pop-up menus look like this:

menu1.bmp (10118 bytes)                                menu2.bmp (10118 bytes)

Now we incorporate this menu into our application.  Our modified class CALCULATOR_MAIN_WINDOW is:

class

	CALCULATOR_MAIN_WINDOW



inherit

	WEL_FRAME_WINDOW

		redefine			

			class_background,

			default_width, 

			default_height,

			on_control_id_command,

			on_menu_command

		end



creation

	make



feature -- Initialization



	make is

			-- Make the main window

		

		do

			make_top ("WEL Calculator")

			set_menu(main_menu)

			!!edit_cntrl.make(Current,"",10, 5, 150, 25, -1)

			!!edit_cntrl2.make(Current,"",10, 35, 150, 25, -1)

			!!compute_btn.make(Current,"Compute -->", 10, 65, 150, 40, Id_compute_btn)

			!!result_cntrl.make(Current, "",10,110,150,25,-1)

			!!comment_edit.make(Current,"", 175, 5, 200, 145, -1)

		end



feature {NONE} -- Implementation



	Id_compute_btn: INTEGER is unique



	Cmd_about: INTEGER is 40001

	Cmd_change_operator: INTEGER is 40002

	Cmd_save_comment: INTEGER is 40003

	Cmd_load_comment: INTEGER is 40004

	Cmd_exit: INTEGER is 40005



	edit_cntrl, edit_cntrl2, result_cntrl: WEL_SINGLE_LINE_EDIT

	comment_edit: WEL_MULTIPLE_LINE_EDIT 



	compute_btn: WEL_PUSH_BUTTON



	class_background: WEL_LIGHT_GRAY_BRUSH is

			-- Gray background

		once

			!! Result.make

		end



	default_width: INTEGER is 

		do

			Result := 400

		end



	default_height: INTEGER is

		do

			Result := 210

		end



	main_menu: WEL_MENU is

		local

			options, help: WEL_MENU

		once

			!!options.make

			options.append_string("&Load Comment...", Cmd_load_comment )

			options.append_string("&Save Comment...", Cmd_save_comment)

			options.append_string("&Change Operator...",Cmd_change_operator)

			options.append_separator

			options.append_string("E&xit", Cmd_exit)

			

			!!help.make

			help.append_string("&About...", Cmd_about)



			!!Result.make

			Result.append_popup(options, "&Options")

			Result.append_popup(help, "&Help")

		end



	on_control_id_command(control_id: INTEGER) is

		do

		end



	on_menu_command(menu_id: INTEGER) is

		do

			inspect menu_id

				when Cmd_about then

				when Cmd_change_operator then

				when Cmd_load_comment then

				when Cmd_save_comment then

				when Cmd_exit then

					destroy

			end

		end



end -- class CALCULATOR_MAIN_WINDOW

We add a once feature called 'main_menu' of class type WEL_MENU.   Class WEL_MENU as its name suggests encapsulates a menu.   Feature 'main_menu' declares two local references 'options' and 'help' of class type WEL_MENU.  These local references encapsulsate the two pop-up menus mentioned above. You might be confused by the pop-up menu and the main menu both being class type WEL_MENU.  Actually, each pop-up menu is a sub-menu.   In other words, an application's main menu consists of potentially many sub-menus (popup-menus) which are similar to the main menu in functionality, hence the same class type. This nesting of menus can be extended to any arbitrary depth. i.e a menu can have a sub-menu which in turn can have a sub-menu and so on. To create the 'options' popup-menu, we call feature 'append-string' of class WEL_MENU.  Feature 'append_string' creates a menu-item and has two input parameters.  The first one is simply the string caption of the menu item. Don't be alarmed by the ampersand '&' in the caption string.  No, this is NOT the address operator you're accustomed to seeing in C :-).   All the ampersand does is instruct  feature 'append_string' which character of the menu-item string caption to underline.   This enables the user to simply press the underlined character key of the menu-item string caption to select that menu-item when the containing pop-up menu is highlighted. The second parameter is the menu-item's unique integer id.  The reason for passing this id will be clear shortly. Pop-up menu 'help' is created in a similar fashion. 

Note: Our choice of the integer constants for the menu-item ids was arbitrary.  You may choose any integer constant you like.

After the two pop-up menus are created, they are merged together in the application's main menu, which is referenced by feature 'main_menu'.  This merging is done by feature 'append_popup'.  The feature's first parameter is the pop-up menu object reference.  The second parameter is the string caption of the pop-up menu.   The only addition to creation feature 'make' of our root class CALCULATOR_MAIN_WINDOW is to display the main menu.  This is done by a simple call to feature 'set_menu'  inherited from class WEL_FRAME_WINDOW, passing it a reference to an object of class type WEL_MENU, which in our case is simply feature 'main_menu'.  Simple...huhh  :-). 

One more change to our root class is the redefinition of feature 'on_menu_command'.   When the user clicks on a menu-item, a notification is sent to the menu containing parent window  (Remember the parent-child hierarchy for the push buttons and other controls?  Think of a menu as a child window like the controls discussed previously).    When that notification message arrives, the event intercepting feature in the ancestor class WEL_COMPOSITE_WINDOW (an ancestor of WEL_FRAME_WINDOW) calls an empty (non-implemented)  feature 'on_menu_command' passing the id of the menu-item that generated the event as a parameter. We simply redefine the empty feature 'on_menu_command' to process the events for the menu-items. Just to brush up your memory, this is very much analogous to feature 'on_control_id_command' from step 1.  The implementation of feature 'on_menu_command' inspects the menu_id parameter to check which menu-item generated the event by using an inspect clause and then taking the appropriate action.  Right now, only the event for the Exit menu-item is processed.  When the Exit menu-item is clicked, the application is closed by calling the feature 'destroy' inherited from ancestor class WEL_COMPOSITE_WINDOW (an ancestor of WIN_FRAME_WINDOW).

We now add an about dialog box to spice up our calculator. The class corresponding to the about dialog box is:

class 

	ABOUT_DIALOG

inherit

	WEL_FRAME_WINDOW

		redefine

			on_control_command,

			default_style

		end;
creation

	make
feature -- Initialization
	make(dlg_parent: WEL_COMPOSITE_WINDOW) is	

		do

			make_child(dlg_parent,"About")

			move_and_resize(0, 0, 243, 112, true)

			!! ok_btn.make(Current,"OK",150,25,74,24, -1)

			!! static_text.make(Current,"WEL Calculator",10,20,100,18,-1)

			!! static_text2.make(Current,"By Dr. Ostroff",10,40,140,18, -1)

		end
feature

	activate is

		do

			show

		end
	on_control_command(control: WEL_CONTROL) is

		do

			if control = ok_btn then

				hide		

			end

		end
	default_style: INTEGER is

		do

			Result := Ws_caption + Ws_ex_dlgmodalframe

		end
feature {NONE}
	ok_btn: WEL_PUSH_BUTTON

	static_text: WEL_STATIC

	static_text2: WEL_STATIC
end

Because a dialog box is similar to other windows (like our main window), class ABOUT_DIALOG as you might have guessed descends from WEL_FRAME_WINDOW.  In fact, this is the class to descend from to create any dialog boxes. To be really honest, I lied when I mentioned any. Actually, one dialog box I know of doesn't descend from WEL_FRAME_WINDOW, however, that is not much of a concern to us right now :-)   A little digression here: If you were persistent enough to have read the previous version of this tutorial that relied on resource editors, you might have noticed that class WEL_MODAL_DIALOG  was used as an ancestor class there instead of WEL_FRAME_WINDOW.  As a matter of fact, class WEL_MODAL_DIALOG also descends from WIN_FRAME_WINDOW , however, it's intended to be used only with dialog boxes already contained in an application's resource i.e. Any dialog box in an application's resource can be encapsulated by class WEL_MODAL_DIALOG (at-least as I understand it). What we are now doing is creating the dialog box dynamically using pure WEL Eiffel code withoug relying on resources, so using WEL_MODAL_DIALOG is out of the question.

Notice above that the ABOUT_DIALOG class creation feature 'make' DOES NOT call inherited creation feature 'make_top' from class WEL_FRAME_WINDOW, as was the case for the main window; instead we call inherited creation feature 'make_child'.   Feature 'make_child' as its name suggests, creates a child window. The about dialog box is a child window of the applications's main window.  The first parameter in feature 'make_child' is the parent window object reference.  Remember, this is important to maintain the parent-child hierarchy as discussed previously.  The second parameter is simply the title of the about box. Feature 'move_and_resize' does exactly what it says.   It specifies the top-left coordinate along with the width and height of the about box.  The true parameter at the end simply instructs feature 'move_and_resize' to repaint the about box after the move and resize operation. Could we have overriden features 'default_width' and 'default_height' as we did for the main window to achieve the same end?? Yes, we could have, but why bother with overriding two features when the same thing can be achieved with a simple feature call !!!

The dialog box has a push button captioned 'OK' of class type WEL_PUSH_BUTTON (already discussed before).  In addition, we introduce a new WEL library class here namely WEL_STATIC.  A static control is simply a label or a text displaying control that can't be edited, hence the name STATIC.   The two static text controls are captioned "WEL Calculator" and "By Dr. Ostroff".   The controls are instantiated in the about box's 'make' feature.  The seven parameters passed to each controls 'make' feature is exactly as mentioned in step 1.  Note how we maintain the parent-child hierarchy between the about box and the three child controls by passing 'Current' in the three controls 'make' feature. An important subtlety to remember here is that after the about box's 'make' feature is called, the dialog box exists but is NOT yet displayed.  To display the dialog box,  feature 'show' inherited from WEL_FRAME_WINDOW needs to be called.  This is exactly what feature 'activate' does. Feature 'default_style' is overriden to give the window a dialog box outlook. Don't worry too much about the Ws_..... constants for now.

You might be wondering why we DON'T pass a unique id for any of the three controls (we simply pass a -1 id) in the about box's creation feature 'make'.  Based on previous discussion, this means we are not interested in processing events for these controls, right ???................... WRONG !!!  Remember, I mentioned in step 1 that there was a way of catching control events without relying on ids.  That's exactly what we'll be doing here.  I'm not mentioning this method here to cram your head with further redundancy.  I only intend to show you of the viable options and flexibility the WEL class library offers.   Feature 'on_control_command' enables us to process control events without using ids.  If you glance at this feature's signature on_control_command(control: WEL_CONTROL), it's very much similar to feature 'on_control_id_command' with signature on_control_id_command(control_id: INTEGER).  The latter is used to trap events using a control's id as mentioned in step 1.  How is the former one different ?? Well, the only difference is that rather than relying on a control's unique id, the control's object reference is used instead.  For example, the OK button is encapsulated by the object reference 'ok_btn' declared above of class type WEL_PUSH_BUTTON.   When the user clicks the OK button, feature 'on_control_command' is called with the parameter 'control' passed being the object reference of the OK button (similar to ok_btn).  It's only a matter of checking the parameter 'control' of feature 'on_control_command' to check which control generated an event. In our case, we simply perform a reference equivalence check to see if the OK button generated an event and if so, simply hide the about box by calling inherited feature 'hide'.

The about dialog box looks like this:

about.bmp (14130 bytes)

 

We woud like to display this dialog box in response to the 'About...' menu-item click by the user.  Our modified class CALCULATOR_MAIN_WINDOW is:

class

	CALCULATOR_MAIN_WINDOW
inherit

	WEL_FRAME_WINDOW

		redefine			

			class_background,

			default_width, 

			default_height,

			on_control_id_command,

			on_menu_command

		end
creation

	make
feature -- Initialization
	make is

			-- Make the main window

		
		do

			make_top ("WEL Calculator")

			set_menu(main_menu)

			!!edit_cntrl.make(Current,"",10, 5, 150, 25, -1)

			!!edit_cntrl2.make(Current,"",10, 35, 150, 25, -1)

			!!compute_btn.make(Current,"Compute -->", 10, 65, 150, 40, Id_compute_btn)

			!!result_cntrl.make(Current, "",10,110,150,25,-1)

			!!comment_edit.make(Current,"", 175, 5, 200, 145, -1)

		end
feature {NONE} -- Implementation
	Id_compute_btn: INTEGER is unique
	Cmd_about: INTEGER is 40001

	Cmd_change_operator: INTEGER is 40002

	Cmd_save_comment: INTEGER is 40003

	Cmd_load_comment: INTEGER is 40004

	Cmd_exit: INTEGER is 40005
	edit_cntrl, edit_cntrl2, result_cntrl: WEL_SINGLE_LINE_EDIT

	comment_edit: WEL_MULTIPLE_LINE_EDIT 
	compute_btn: WEL_PUSH_BUTTON
	class_background: WEL_LIGHT_GRAY_BRUSH is

			-- Gray background

		once

			!! Result.make

		end
	default_width: INTEGER is 

		do

			Result := 400

		end
	default_height: INTEGER is

		do

			Result := 210

		end
	main_menu: WEL_MENU is

		local

			options, help: WEL_MENU

		once

			!!options.make

			options.append_string("&Load Comment...", Cmd_load_comment )

			options.append_string("&Save Comment...", Cmd_save_comment)

			options.append_string("&Change Operator...",Cmd_change_operator)

			options.append_separator

			options.append_string("E&xit", Cmd_exit)

			
			!!help.make

			help.append_string("&About...", Cmd_about)
			!!Result.make

			Result.append_popup(options, "&Options")

			Result.append_popup(help, "&Help")

		end
	on_control_id_command(control_id: INTEGER) is

		do

		end
	on_menu_command(menu_id: INTEGER) is

		do

			inspect menu_id

				when Cmd_about then

					about_dialog.activate

				when Cmd_change_operator then

				when Cmd_load_comment then

				when Cmd_save_comment then

				when Cmd_exit then

					destroy

			end

		end

	about_dialog: ABOUT_DIALOG is

		once

			!!Result.make(Current)

		end
end -- class CALCULATOR_MAIN_WINDOW

Processing the menu-item event is darn simple.  The only feature we add is 'about_dialog' of custom class type ABOUT_DIALOG.  This class as discussed above encapsulates our about dialog box and is analogous to WIN_FRAME_WINDOW, which abstracts the main window. Feature 'about_dialog' simply calls creation feature 'make' and passes 'Current' as the first parameter to maintain the parent-child hierarchy between the main window and the about box.  When the user clicks on the 'About...' menu-item, feature 'on_menu_command' as discussed before is called with the id corresponding to the 'About...' menu-item (Cmd_about).  When that happens, we simply call feature 'activate' of class ABOUT_DIALOG to display the dialog box.  Pretty simple, isn't it ?? 

This brings us to the end of step 2.  One more step to go!

 

STEP 3

So far we really have only been working on the user-interface of the calculator.  Now, we'll begin work on the calculator's actual functionality.  In this step, we'll allow the user to choose an operator (you guessed it, via a dialog box ! ), enter two operands (real numbers) and let the calculator compute the result. We also enable the user to enter and edit comments and optionally save them in a text file.  In addition, we also allow the user to load the contents of the comment edit control from a text file.  Before we code the functionality, we have to take care of the operator dialog box. It's very much similar to the about box from step 2.

Let's start off with the class corresponding to the operator dialog box:

class 

	CHANGE_OPERATOR_DIALOG

inherit 

	WEL_FRAME_WINDOW

		redefine

			on_control_command,

			default_style

		end;
creation

	make
feature -- Initialization
	make(dlg_parent: WEL_COMPOSITE_WINDOW) is

		do

			make_child(dlg_parent, "Choose Operator")

			move_and_resize(0, 0, 285, 140, true)

			!!grp_box.make(Current,"Operator", 12, 7, 140, 98, -1)

			!!ok_btn.make(Current,"OK",190,15,80,24, -1)

			!!cancel_btn.make(Current,"Cancel",190,45,80,24, -1)

			!!plus_radio.make(Current,"Plus",20,28,80,15,-1)

			!!minus_radio.make(Current,"Minus",20,46,80,15,-1)

			!!multiply_radio.make(Current,"Multiply",20,64,80,15, -1)

			!!divide_radio.make(Current,"Divide",20,82,80,15,-1)

		end

		
feature {NONE}
	ok_btn: WEL_PUSH_BUTTON

	cancel_btn: WEL_PUSH_BUTTON
	plus_radio: WEL_RADIO_BUTTON

	minus_radio: WEL_RADIO_BUTTON

	multiply_radio: WEL_RADIO_BUTTON

	divide_radio: WEL_RADIO_BUTTON
	grp_box: WEL_GROUP_BOX
feature {CALCULATOR_MAIN_WINDOW}
	activate is

		local

			p: CALCULATOR_MAIN_WINDOW

		do

			p ?= parent

			inspect p.operator 

					when '+' then

						plus_radio.set_checked

					when '-' then

						minus_radio.set_checked

					when '*' then

						multiply_radio.set_checked

					when '/' then

						divide_radio.set_checked

			end

			show

		end
feature {NONE}
	on_control_command(control: WEL_CONTROL) is

		local

			p: CALCULATOR_MAIN_WINDOW

		do

			if control = ok_btn  then

				p ?= parent

				if plus_radio.checked then

					p.set_operator('+')

				elseif minus_radio.checked then

					p.set_operator('-')

				elseif multiply_radio.checked then

					p.set_operator('*')

				else

					p.set_operator('/')

				end

				hide

			elseif control = cancel_btn then

				plus_radio.set_unchecked

				minus_radio.set_unchecked

				multiply_radio.set_unchecked

				divide_radio.set_unchecked

				hide

			end

		end
	default_style: INTEGER is

		do

			Result := Ws_caption + Ws_ex_dlgmodalframe

		end
end -- CHANGE_OPERATOR_DIALOG

This class is very much similar to the ABOUT_DIALOG class.  All the features in this class have already been discussed previously.  The only addition is the introduction of a new WEL library class WEL_RADIO_BUTTON. Notice how we make use of the parent-child hierarchy in feature 'activate'.  Before displaying the dialog box (i.e. calling feature 'show'),  we query feature 'operator' contained in the parent window object (i.e. calculator's main window having class type CALCULATOR_MAIN_WINDOW) and correspondingly set the correct radio control in the check state. Just so you are not confused, feature 'operator' is only introduced in the updated main window class later below.  Check the updated main window class (CALCULATOR_MAIN_WINDOW) further below for the changes.

Whent the user clicks either the OK or Cancel button, feature 'on_control_command' as discussed before is called.  If the OK button is clicked, we update feature 'operator' contained in the parent window object with the operator the user checked.  If the Cancel button was clicked, we simply uncheck all the radio controls so that when the dialog box is invoked at a later time, the appropriate radio control will be checked by feature 'activate' as explained before.  In both cases, we hide the dialog box at the end by calling feature 'hide'.

The operator dialog box looks like this:

operator.bmp (20278 bytes)

 

We woud like to display this dialog box in response to the 'Change Operator...' menu-item click by the user.  Our modified CALCULATOR_MAIN_WINDOW class is:

class

	CALCULATOR_MAIN_WINDOW
inherit

	WEL_FRAME_WINDOW

		redefine			

			class_background,

			default_width, 

			default_height,

			on_control_id_command,

			on_menu_command

		end
creation

	make
feature -- Initialization
	make is

			-- Make the main window

		
		do

			make_top ("WEL Calculator")

			set_menu(main_menu)

			!!edit_cntrl.make(Current,"",10, 5, 150, 25, -1)

			!!edit_cntrl2.make(Current,"",10, 35, 150, 25, -1)

			!!compute_btn.make(Current,"Compute -->", 10, 65, 150, 40, Id_compute_btn)

			!!result_cntrl.make(Current, "",10,110,150,25,-1)

			!!comment_edit.make(Current,"", 175, 5, 200, 145, -1)

			set_operator('+')

		end
feature {CHANGE_OPERATOR_DIALOG}
	operator: CHARACTER
	set_operator(new_op: CHARACTER) is

		do

			operator := new_op

		end
feature {NONE} -- Implementation
	Id_compute_btn: INTEGER is unique
	Cmd_about: INTEGER is 40001

	Cmd_change_operator: INTEGER is 40002

	Cmd_save_comment: INTEGER is 40003

	Cmd_load_comment: INTEGER is 40004

	Cmd_exit: INTEGER is 40005
	edit_cntrl, edit_cntrl2, result_cntrl: WEL_SINGLE_LINE_EDIT

	comment_edit: WEL_MULTIPLE_LINE_EDIT 
	compute_btn: WEL_PUSH_BUTTON
	class_background: WEL_LIGHT_GRAY_BRUSH is

			-- Gray background

		once

			!! Result.make

		end
	default_width: INTEGER is 

		do

			Result := 400

		end
	default_height: INTEGER is

		do

			Result := 210

		end
	main_menu: WEL_MENU is

		local

			options, help: WEL_MENU

		once

			!!options.make

			options.append_string("&Load Comment...", Cmd_load_comment )

			options.append_string("&Save Comment...", Cmd_save_comment)

			options.append_string("&Change Operator...",Cmd_change_operator)

			options.append_separator

			options.append_string("E&xit", Cmd_exit)

			
			!!help.make

			help.append_string("&About...", Cmd_about)
			!!Result.make

			Result.append_popup(options, "&Options")

			Result.append_popup(help, "&Help")

		end
	on_control_id_command(control_id: INTEGER) is

		local 

			value: REAL

		do

			if control_id = Id_compute_btn then

				if (edit_cntrl.text.is_real) and (edit_cntrl2.text.is_real) then

					result_cntrl.clear

					inspect operator

						when '+' then

							value := edit_cntrl.text.to_real + edit_cntrl2.text.to_real

						when '-' then 

							value := edit_cntrl.text.to_real - edit_cntrl2.text.to_real

						when '*' then 

							value := edit_cntrl.text.to_real * edit_cntrl2.text.to_real

						when '/' then

							 value := edit_cntrl.text.to_real / edit_cntrl2.text.to_real

					end

					result_cntrl.set_text(value.out)

				end

			end

		end
	on_menu_command(menu_id: INTEGER) is

		do

			inspect menu_id

				when Cmd_about then

					about_dialog.activate

				when Cmd_change_operator then

					change_operator_dialog.activate

				when Cmd_load_comment then

				when Cmd_save_comment then

				when Cmd_exit then

					destroy

			end

		end
	about_dialog: ABOUT_DIALOG is

		once

			!!Result.make(Current)

		end
	change_operator_dialog: CHANGE_OPERATOR_DIALOG is

		once

			!!Result.make(Current)

		end
end -- class CALCULATOR_MAIN_WINDOW

The changes made to the main window class (CALCULATOR_MAIN_WINDOW) are as follows:

Congratulations, we have a simple working calculator!  Believe it or not, we are almost done with the tutorial.  I can already sense you relentlessly grinding your teeth and begging when will this finally be over :-(  Calm down.......... just a little bit of patience and we'll have a full-fledged (at-least for this tutorial) calculator application working :-)

Lastly, we add the abiltiy for the user to load and save comments to a text file.   As of now, you can edit the text in the comment edit control, but once the application quits, that text is lost.  Our updated (I promise the last one!) main window class CALCULATOR_MAIN_WINDOW  is:

class

	CALCULATOR_MAIN_WINDOW
inherit

	WEL_FRAME_WINDOW

		redefine			

			class_background,

			default_width, 

			default_height,

			on_control_id_command,

			on_menu_command

		end
creation

	make
feature -- Initialization
	make is

			-- Make the main window

		
		do

			make_top ("WEL Calculator")

			set_menu(main_menu)

			!!edit_cntrl.make(Current,"",10, 5, 150, 25, -1)

			!!edit_cntrl2.make(Current,"",10, 35, 150, 25, -1)

			!!compute_btn.make(Current,"Compute -->", 10, 65, 150, 40, Id_compute_btn)

			!!result_cntrl.make(Current, "",10,110,150,25,-1)

			!!comment_edit.make(Current,"", 175, 5, 200, 145, -1)

			set_operator('+')

			set_window_title(Void)

		end
feature {CHANGE_OPERATOR_DIALOG}
	operator: CHARACTER
	set_operator(new_op: CHARACTER) is

		do

			operator := new_op

		end
feature {NONE} -- Implementation
	Id_compute_btn: INTEGER is unique
	Cmd_about: INTEGER is 40001

	Cmd_change_operator: INTEGER is 40002

	Cmd_save_comment: INTEGER is 40003

	Cmd_load_comment: INTEGER is 40004

	Cmd_exit: INTEGER is 40005
	edit_cntrl, edit_cntrl2, result_cntrl: WEL_SINGLE_LINE_EDIT

	comment_edit: WEL_MULTIPLE_LINE_EDIT 
	compute_btn: WEL_PUSH_BUTTON
	class_background: WEL_LIGHT_GRAY_BRUSH is

			-- Gray background

		once

			!! Result.make

		end
	default_width: INTEGER is 

		do

			Result := 400

		end
	default_height: INTEGER is

		do

			Result := 210

		end
	main_menu: WEL_MENU is

		local

			options, help: WEL_MENU

		once

			!!options.make

			options.append_string("&Load Comment...", Cmd_load_comment )

			options.append_string("&Save Comment...", Cmd_save_comment)

			options.append_string("&Change Operator...",Cmd_change_operator)

			options.append_separator

			options.append_string("E&xit", Cmd_exit)

			
			!!help.make

			help.append_string("&About...", Cmd_about)
			!!Result.make

			Result.append_popup(options, "&Options")

			Result.append_popup(help, "&Help")

		end
	on_control_id_command(control_id: INTEGER) is

		local 

			value: REAL

		do

			if control_id = Id_compute_btn then

				if (edit_cntrl.text.is_real) and (edit_cntrl2.text.is_real) then

					result_cntrl.clear

					inspect operator

						when '+' then

							value := edit_cntrl.text.to_real + edit_cntrl2.text.to_real

						when '-' then 

							value := edit_cntrl.text.to_real - edit_cntrl2.text.to_real

						when '*' then 

							value := edit_cntrl.text.to_real * edit_cntrl2.text.to_real

						when '/' then

							 value := edit_cntrl.text.to_real / edit_cntrl2.text.to_real

					end

					result_cntrl.set_text(value.out)

				end

			end

		end
	on_menu_command(menu_id: INTEGER) is

		do

			inspect menu_id

				when Cmd_about then

					about_dialog.activate

				when Cmd_change_operator then

					change_operator_dialog.activate

				when Cmd_load_comment then

					open_dialog.activate(Current)

					if open_dialog.selected then

						load_from_text_file(open_dialog.file_name)

					end

				when Cmd_save_comment then

					save_dialog.activate(Current)

					if save_dialog.selected then

						save_to_text_file(save_dialog.file_name)

						load_from_text_file(save_dialog.file_name)

					end

				when Cmd_exit then

					destroy

			end

		end

	about_dialog: ABOUT_DIALOG is

		once

			!!Result.make(Current)

		end
	change_operator_dialog: CHANGE_OPERATOR_DIALOG is

		once

			!!Result.make(Current)

		end
	open_dialog: WEL_OPEN_FILE_DIALOG is

		once

			!!Result.make

			Result.set_filter(<<"Comment file">>, <<"*.cmt">>)

		end
	save_dialog: WEL_SAVE_FILE_DIALOG is

		once

			!!Result.make

			Result.set_filter(<<"Comment file">>, <<"*.cmt">>)

		end

	load_from_text_file(file_name: STRING) is

		local

			text_file: PLAIN_TEXT_FILE

			buffer: STRING

		do

			!!text_file.make_open_read(file_name)
			from

				!!buffer.make(text_file.count)

			until

				text_file.end_of_file

			loop

				text_file.read_line

				buffer.append(text_file.last_string)

				buffer.append("%R%N")

			end
			comment_edit.set_text(buffer) 

			text_file.close

			set_window_title(file_name)

		end

	
	save_to_text_file(file_name: STRING) is

		local

			text_file: PLAIN_TEXT_FILE

			buffer: STRING

			line: INTEGER

		do

			!!text_file.make_open_write(file_name)

			
			from

				line := 0

			until

				line = comment_edit.line_count

			loop

				!!buffer.make(comment_edit.line_length(line))

				buffer := comment_edit.line(line)

				text_file.put_string(buffer)

				text_file.new_line

				line := line + 1

			end
			text_file.close

		end
	set_window_title (file_name: STRING) is

			-- Set the window's title with `file_name'.

		local

			s: STRING

		do

			s := clone (Title)

			s.append (" - ")

			if file_name /= Void then

				s.append (file_name)

			else

				s.append ("Untitled")

			end

			set_text (s)

		end	
	Title : STRING is "WEL Calculator"
end -- class CALCULATOR_MAIN_WINDOW

Let's wrap this quickly................

The following are the changes added:

There you are, the tutorial is finally over! Granted, it's not easy to learn the WEL library in the beginning, but, the effort pays off since most of the process of building WEL applications is repetitious. Whatever you have done in this tutorial, you'll find yourself doing pretty much the same thing again in many of your projects (at-least as far as the user interface is concerned).  The WEL library is large and rich.  Take your time to learn the class internals and yes.....don't get lost navigating those myriad classes !!   Happy learning  :-)