| **Navigation:**  [[welcome to my product .htm|User's Guide and Tutorials]] > Guided Tour of Working in Clarion >====== 11 - Advanced Topics ====== | [[lc control and extension templates.htm|{{btn_prev_n.gif|Previous page}}]][[welcome to my product .htm|{{btn_home_n.gif|Return to chapter overview}}]][[lc creating reports.htm|{{btn_next_n.gif|Next page}}]] | | || **Set Up the UpdateOrder Form** For the Order Update form, we'll place the columns from the Order table on an update form, perform an automatic lookup to the Customer table, add a //BrowseBox// Control template to show and edit the related detail items, calculate each line item detail, then calculate the order total. **Starting Point:** **The LCLESSON.APP file and Application Editor (Tree) should be open.** **Create the Orders table's data entry Form** 1.Highlight //UpdateOrder// in the //Application Tree// dialog, then press the **Properties** button. 2.Select the **Defaults** tab, choose //FORM(Add/Change/Delete)//, then press the **Select** button. 3.Select the **Window** tab, and then press the **Designer** button to open the Window Designer. 4.Resize the window taller by dragging its bottom middle handle. Move the three controls to the bottom of the window after resizing. __**Place the entry controls for the Orders table**__ Instead of using the //Populate Column// IDE menu option, we'll use the //Data / Tables Pad// dialog to populate multiple controls. 1.In the //Data / Tables Pad// dialog, highlight the //<;ToDo>// item under the //Update Record on Disk//, then press the **Add** toolbar button. 2.Highlight the //Orders// table from the //Select// list, then press the **Select** button. 3.Highlight the //Orders// table, and in the bottom half of the Data / Tables Pad, highlight //OrderDate//, then DRAG near the top left of the window, and DROP. 4.Back in the //Data / Tables Pad//, highlight //OrderNote//, then DRAG just to the right of the entry box placed for the date. 5.Switch to the **Toolbox** Pad, and if not visible, open it by selecting **View** {{blttria.jpg|BLTTRIA.jpg}} **Toolbox** from the IDE Menu. 6.Choose (DRAG) the ENTRY control from the **Toolbox** Pad and drop it under the //OrderDate //entry control. 7.With the Entry control still selected, press the F4 key to switch focus to the **Properties** Pad. 8.Enter //ORD:CustNumber// in the **Use** property. Since the ORD:CustNumber column is setup as a string in the dictionary, we need to add an entry control. 9.Switch back to the **Toolbox** Pad, and choose (DRAG) the PROMPT control from the **Toolbox** Pad and drop it to the left of the //CustNumber// entry control. 10.Note the **Edit Value** Smart Tag shown just after populating. Click on it, and enter //&Cust Number: //in the Text control: {{l10customprompt.jpg|L10CustomPrompt.jpg}} 11.With the PROMPT control still selected, press the F4 key to switch focus to the **Properties** Pad. 12.Change the **Use** property to //?CustomerPrompt//. {{l10customprompt2.jpg|L10CustomPrompt2.jpg}} __**Add a lookup procedure call into the customer list**__ 1.RIGHT-CLICK on the //ORD:CustNumber// entry control and select **Actions** from the popup menu. The standard actions for any entry control allow you to perform data entry validation against a row in another table, either when the control is Selected (just before the user can enter data) or when the control is Accepted (right after the user has entered data). 2.In the //When the Control is Accepted// group box, press the ellipsis button ( ... ) located to the right of the **Lookup Key** entry box. 3.Highlight the //Orders// table in the //Select Key// dialog, then press the **Add** button. 4.Highlight the //Customer// table in the //Select// list, and press the **Select** button. These last two steps add the Customer table to the procedure's Table Schematic as an automatic lookup from the Orders table. This will automatically lookup the related Customer table row for you, and the lookup is based on the table relationship set up in the data dictionary. 5.Highlight //CUS:KeyCustNumber// in the //Select Key// dialog, then press the **Select** button. This makes //CUS:KeyCustNumber// the key that will be used to attempt to get a matching valid row from the Customer table for the value the user enters into this control. 6.Press the ellipsis button ( ... ) for the **Lookup Field** entry box. This makes //CUS:CustNumber// the column that must contain the matching value to the value the user enters into this control. 7.Choose the //BrowseCustomers// procedure from the **Lookup Procedure** drop list. This calls the //BrowseCustomers// procedure when the user enters an invalid customer number, so the end user can select from a list of customers. 8.Check the **Perform Lookup during Non-Stop Select** and **Force Window Refresh when Accepted** boxes to ensure that the data displayed on screen is always valid and current. {{l10entryactions.jpg|L10EntryActions.jpg}} 9.Press the **OK** button to close the Entry Actions. 10.RIGHT-CLICK on the Entry control, and select **Embeds'** from the popup menu. {{l10controlembeds.jpg|L10ControlEmbeds.jpg}} This displays a list of just the embed points associated with this one control. This is the quickest way to get to a specific control's embed points, and it's the second way you've seen so far to get to an embed point. There is a third method that's still to come. 11.Highlight the //Selected// event under //Control Events// then press the **Insert** button. The Selected event occurs just before the control gains input focus. This embed point allows you to do any "setup" specific to this one control. 12.Highlight //Source// then press the **Select** button. This calls the Text Editor to allow you to write any Clarion source code you want. Notice that the floating //Populate Column// toolbox is present. Whenever you DOUBLE-CLICK on a column in this toolbox, it places the name of the column (including any prefix) in your code at the insertion point. This not only helps your productivity (less typing), but also ensures you spell them correctly (eliminating misspelled column name compiler errors). Type the following code: {{l10controlembeds2.jpg|L10ControlEmbeds2.jpg}} 13.Press the **Save and Close** button to return to the //Embedded Source// dialog. It is "standard Windows behavior" that, if the user does not enter data into a control and just presses tab (or CLICKs the mouse) to go on to another control, an Accepted event does not happen (this is very different from the way DOS programs work). This allows users to easily tab through the window's controls without triggering data-entry validation code on each control. However, sometimes you need to override this "Windows standard behavior" to ensure the integrity of your database. The **?ORD:CustNumber{PROP:Touched} = TRUE** statement uses the Clarion language Property Assignment syntax. By setting PROP:Touched to TRUE in the //Selected// event for this control, an //Accepted// event is always generated'whether the user has entered data or not. This forces the lookup code generated for you into the //Accepted// event for this control (from the information you entered on the **Actions** tab on the previous page) to execute. This ensures that the user either enters a valid Customer number, or the Customer list pops up to allow the user to select a Customer for the Order. 14.Press the **Save and Close **button to return to the Window Designer. __**Add a "display-only" control**__ 1.Switch back to the **Toolbox** Pad, and choose (DRAG) the STRING control from the **Toolbox** Pad and DROP it to the right of the customer number entry box you placed before. 2.RIGHT-CLICK the string control you just placed, and select **Properties** from the popup menu. 3.In the Properties Pad, change the **IsPicture** property to TRUE, the **Picture** property to //@s30//, and the **Use** property to //CUS:Company//: {{l10varstring.jpg|L10VarString.jpg}} **Placing the Detail Table's Control Templates** The next key element in this window is a browse list box control, synchronized to the Order Number of this form. This will show all the rows in the Detail table related to the currently displayed //Orders// table row. __**Add a Detail list**__ 1.Open the **Control Templates** Pad, highlight //BrowseBox//, then DRAG the control just below the customer number entry box you placed before, and DROP. 2.Highlight the //<;ToDo>// item below the //File-Browsing List Box// and press the **Add** button. 3.Select the //Detail// table from the //Select a Table// dialog, then press the **Select** button. 4.Highlight the //Detail// table, and press the **Change** button. 5.Highlight //OrderNumberKey// in the //Select// //Key from Detail// dialog, then press the **Select** button. 6.Highlight //ProdNumber// in the //Columns// list, then press the **Select** button. 7.RIGHT-CLICK on the Browse box just populated, and select **List Box format'** from the popup menu. 8.Select //Center// from the Data group's **DataJustification** property drop list. 9.Press the **Add Field** {{addfield.jpg|AddField.jpg}} button. 10.Highlight //Quantity// in the //Columns// list, then press the **Select** button. 11.Select //Center// from the Data group's **DataJustification** property drop list. 12.Press the **Add Field** button. 13.Highlight //ProdAmount// in the //Columns// list, then press the **Select** button. 14.Select //Center// from the Data group's **DataJustification** property drop list. 15.Press the **Add Field** button. 16.Highlight //LOCAL DATA UpdateOrder// in the //Select Column// list, then press the **New** button. This **New** button allows you to add a local variable without going all the way back to the Data / Tables Pad. We will use this new variable to display the total price for each line item (the quantity multiplied by the unit price). 17.Type //ItemTotal// in the **Name** entry. 18.Select //DECIMAL// from the **Type** drop list. 19.Type //7// in the **Characters** field. 20.Type //2// in the **Places** field then press the **OK** button. 21.Press the **Cancel** button to close the Local Data entry process and return to the List Box Formatter. 22.In the //ItemTotal// column, select //Center// from the Data group's **DataJustification** property drop list. 23.Press the **Add Field** button. 24.Highlight the //Detail// table item below the //File-Browsing List Box// and press the **Add** button. 25.Select the //Products// table from the //Select// dialog, then press the **Select** button. This adds the //Products// table to the Control template's table schematic as a lookup table. The related row from the //Products// table is automatically retrieved for you so you can display the product description in the list. 26.Highlight //ProdDesc// in the //Columns// list, then press the **Select** button. 27.Resize the columns and adjust the display formatting as you want (you've done this a couple of times already). Use the diagram below as a guide. 28.Press the **OK** button to close the List Box Formatter. 29.Resize the list box to display all the columns you populated into it. {{l11updateorders.jpg|L11UpdateOrders.jpg}} __**Synchronize the browse list box**__ We want this list to only display the //Details// table rows that are related to the Customer table row currently being edited. Therefore, we need to specify a Range Limit. 1.RIGHT-CLICK the list box you just placed, and select **Actions** from the popup menu. 2.Press the ellipsis ( ... ) button for the **Range Limit Field**. 3.The //DTL:OrderNumber// column is automatically populated. 4.Choose //File Relationship// from the **Range Limit Type** drop list. 5.Type //Orders// in the **Related File** column, or use the ellipsis to select it from a selection dialog. __**Add an order invoice total calculation**__ Now we want to calculate the order total and save it in the Orders table. 1.Select the **Totaling** tab. 2.Press the **Insert** button. 3.Press the ellipsis ( ... ) button for the **Total Target Field**. 4.Highlight the //Orders// table in the Tables list, select //ORD:InvoiceAmount// from the //Columns// list, then press the **Select** button. This is the column that will receive the result of the total calculation. 5.Choose //Sum// from the **Total Type** drop list. 6.Press the expression ( **E** ) button for the **Field to Total**. 7.In the Expression Editor dialog, DOUBLE-CLICK on //ItemTotal// from the //Columns// list, then press the **OK** button. This is the column whose contents will be used in the total calculation. So far we've only declared this column and not done anything to put any value into it, but we'll get to that very soon. 8.Choose //Each Record Read// from the **Total Based On** drop list. 9.Press **OK** to close the //Browse Totaling// dialog. __**Change the object name**__ Now we want to change the name of the browse object to make our code more readable (you'll see why a little later). 1.Select the **Classes** tab. 2.Type //BrowseDTL// in the **Object Name** column. 3.Press the **OK** button to close the Actions dialog and return to the Window Designer. __**Add horizontal and vertical scroll bars**__ 1.With the list box still selected, press F4 to switch focus to the **Properties** Pad. 2.Set the **Horizontal** and **Vertical** properties to //TRUE//. 3.Press the **Save and Close** button to save your changes, and then let's continue and re-enter the Designer by pressing the **Designer** button. __**Add the standard table update buttons**__ 1.In the //Control Templates Pad// dialog, DRAG the //BrowseUpdateButtons// control template, then DROP below the list box. The **Insert**, **Change**, and **Delete** buttons all appear together. 2.RIGHT-CLICK on the **Delete** button only, then choose **Actions** from the popup menu. 3.Check the **Use Edit in place** box. Checking this box for one button in the Control template checks it for all three. We will be using the Edit in Place technique to update the Details table rows instead of an update Form procedure. This will allow us to demonstrate some fairly advanced programming techniques and show just how easy they are to perform within the Application Generator. 4.Press the **OK** button. __**Add a "display-only" control for the invoice total**__ 1.Switch back to the **Toolbox** Pad, and choose (DRAG) the STRING control from the **Toolbox** Pad and DROP it below the bottom right corner of the list box. 2.RIGHT-CLICK the string control you just placed, and select **Properties** from the popup menu. 3.In the Properties Pad, change the **IsPicture** property to TRUE, the **Picture** property to //@n$10.2//, and the **Use** property to //ORD:InvoiceAmount//: This specifies the control will display data from a variable, not just a string constant. __**Change the form window caption and exit the Window Designer**__ 1.CLICK on the caption bar of the sample window. 2.Type //Order Form// in the **Title** field in the //Properties Pad// toolbox, then press the TAB key. 3.Choose **Save and Close** to close the Window Designer. **Making it all Work** There are a couple of things we need to do to make this procedure fully functional'add a Formula, and configure the Edit in Place. **Using the Formula Editor** To make the //ItemTotal// calculate the correct amount for each Detail row in the browse list box, you need to add a Formula to the procedure. This will also allow the browse totaling to correctly place the invoice total in the //ORD:InvoiceAmount// column. 1.Press the **Formulas** tab in the //Procedure Properties// dialog. The //Formula Editor// dialog appears. This dialog lists all the formulas in the procedure. {{l11formulaeditor.jpg|L11FormulaEditor.jpg}} 2.Highlight //Format Browse// in the //Template Classes// list, and press the **Insert** button to add a new formula. The //Formula Editor// design dialog appears. 3.Type //Item Total Formula// in the **Name** field. 4.Select //Format Browse// from the Drop List selection in the **Class** Field. The Class field simply specifies the logical position within the generated source code at which the formula is calculated. The //Format Browse// class tells the //BrowseBox// Control template to perform the calculation each time it formats a row for display in the list box. 5.Press the ellipsis ( ... ) button to the right of the **Result** Field. 6.Highlight //LOCAL DATA UpdateOrder// in the //Tables// list, select //ItemTotal// from the //Select// //Columns// list, then press the **Select** button. This names the column that will receive the result of the calculation. This is the column we defined earlier through the List Box Formatter. 7.Press the **Data** button in the **Operands** group. 8.Highlight the //Detail// table in the //Tables// list, select //Quantity// from the //Columns// list, then press the **Select** button. This places //DTL:Quantity// into the Statement column for you. The Statement column contains the expression being built. You can type directly into the Statement field to build the expression, if you wish. 9.Press the ***** button in the **Operators** group. This is the multiplication operator. 10.Press the **Data** button in the **Operands** group. 11.Highlight the //Detail// table in the //Tables// list, select //ProdAmount// from the //Columns// list, then press the **Select** button. 12.Press the **Check** button to validate the expression's syntax. A green checkmark appears left of the statement, indicating the syntax is correct. If a red X appears, the expression's syntax is incorrect and the highlighted portion of the statement is what you must change. {{itemtotalformula.jpg|ItemTotalFormula.jpg}} 13.Press the **Save and Close** button to close the Formula Editor. The //Formula Editor// dialog with a formula list re-appears. **Configuring Edit in Place** Now we need to configure the Edit in Place characteristics. We previously used Edit in Place for the Phones table and simply took all the default behaviors, because that was a fairly simple table. However, now we're editing a line item //Detail// row for an order entry system, which means we need to do some data entry validation beyond simply ensuring the user types in a number that fits the display picture. To do this, we'll need to extend the simple Edit in Place functionality provided by the Application Builder Class (ABC) Library. __**Set Column Specific Classes**__ 1.Press the **Extensions** tab in the //Procedure Properties// dialog. 2.Highlight //Update a Record from Browse Box on Detail //then press the **Properties** button. 3.Press the **Configure Edit in place** button. The //Configure Edit in place// dialog appears. The options on this dialog allow you to specify what behavior occurs while the user is editing data and presses enter or an arrow key, along with several save options. We'll accept all the defaults. 4.Press the **Column Specific** tab control. 5.Press the **Insert** button. 6.Press the ellipsis ( ... ) button for the **Field** field. 7.Highlight the //Detail// table, and then //ProdNumber//, and then press the **Select** button. The ABC Library contains an object class called EditEntryClass that is the default Edit in Place class. We're going to override some of the methods of the default class for this column so we can modify the default behavior. Adding this column to the **Column Specific** list of columns is what allows us to override the default Edit in Place methods for this one column. We will do this so that we can perform data validation on this column when the user enters data'we want to make sure that they can only enter a number that refers to a valid //Products// table row. 8.Press the **OK** button. 9.Press the **Insert** button. 10.Press the ellipsis ( ... ) button for the **Field** field. 11.Highlight //DTL:Quantity//, then press the **Select** button. The ABC Library's EditEntryClass defaults to using an ENTRY control, and for this column we want to use a SPIN control so the user can just spin to the quantity they want to order. Therefore, we need to override some methods for this column too, to have a SPIN instead of an ENTRY control. 12.Clear the **Use Default ABC** check box. This allows you to specify the exact class from which to derive. 13.Choose **EditSpinClass** from the Base Class drop list. The EditEntryClass does too much that's unnecessary for this, since we're going to override the methods anyway. Therefore, we're going to derive and override from the Base Class all the Edit in place classes were derived from: EditClass. 14.Press the **OK** button. 15.Press the **Insert** button. 16.Press the ellipsis ( ... ) button for the **Field** field. 17.Highlight //DTL:ProdAmount//, then press the **Select** button. 18.Clear the **Allow Edit in Place** check box. This turns off Edit in Place for this one column of the list box. Only columns in the Primary table for the //BrowseBox// can be edited in place, and the default is that all Primary table columns are editable. In this case, this means all the Detail table columns can be edited and the Products table columns cannot be edited. However, for this procedure we do NOT want the user to be able to edit the //DTL:ProdAmount// column because we're going to get its value directly from the Products table and we don't want the user to be able to change it. That's why we turned off the **Allow Edit in Place** box. 19.Press the **OK** button twice to return back to the Extensions dialog, then press **Save and Close** to return to the Application Tree. Save your work by pressing the **Save** button {{savebutton.jpg|savebutton.jpg}} on the IDE toolbar. **Using the Embeditor** 1.RIGHT-CLICK on //UpdateOrder// and select **Source** from the popup menu. {{sourceeditor.jpg|SourceEditor.jpg}} The **Source** popup menu selection opens the "Embeditor"'the third method of accessing embed points in a procedure. The Embeditor is the same Text Editor you've already used, but opened in a special mode which allows you to not only to edit all the embed points in your procedure, but to edit them within the context of template-generated code. Notice that most of the code is on a gray background and the points where you can write your code have a white background. There are also identifying comments for each embed point. You can turn these comments on and off as you choose through the **Setup** {{blttria.jpg|BLTTRIA.jpg}} **Application** **Options** dialog. Once you become familiar with them, you'll probably want to turn them off so you can see more of the actual code. You'll notice that a message briefly appeared that said "Generating LCLesson." The Embeditor displays all possible embed points for the procedure within the context of all the possible code that may be generated for the procedure. Notice the distinction here'Embeditor does not show you the code that will be generated, but all the code which could be generated for you, if you chose every possible option and placed code into every available embed point. You are not likely to ever do that. Therefore, a lot more generated code shows up in the Embeditor than will actually be in the generated code when you compile your application. After we finish here, we'll go look at the generated code to see the difference. At the right end of the toolbar are four buttons which are very important to know when working in the Embeditor. These are (from left to right) the Previous Embed, Next Embed, Previous Filled Embed, and Next Filled Embed buttons (hover your mouse over them and the tooltips appear naming the buttons). They allow you to quickly get from one embed point to another'particularly after you've written code into some of them. __**Detecting Changed Orders**__ One of the things we want this procedure to do is to detect changes to existing orders and make sure the changes do not result in a data mis-match between the //Orders// and //Detail// tables. This system is storing the total dollar amount of an order in the ORD:InvoiceAmount column, so when the user changes a Detail item in an existing Order, we want to make sure the Orders table row is updated, too. There's fairly simple way to do that which will allow us to demonstrate the ABC Library's flexible error handling. 1.CLICK on the **Next embed** button {{nextembedbut.jpg|NextEmbedBut.jpg}} (about 5 times) until you get to the embed point immediately preceding the line of code reading //ThisWindow CLASS(WindowManager)//. {{sourceeditor2.jpg|SourceEditor2.jpg}} Each embed point potentially has 10,000 priority levels within it. This Embed code Priority level system is designed to allow you to embed your code before or after any generated code'whether that code is generated for you by Clarion's ABC Templates or any third-party templates you choose to use. This makes the embed system completely flexible, allowing you to add your own code at any logical point needed'before or after most any "chunk" of generated code. 2.Type (or copy and paste) the following code: **LocalErrGroup**** GROUP** **                USHORT(1)** **                USHORT(99)** **                BYTE(Level:Notify)** **                PSTRING('Save the Order!')** **                PSTRING('Some Item changed -- Press the OK button.')** **              END** **SaveTotal****     LIKE(ORD:InvoiceAmount)** The red text indicates that the text begins in Column 1, and identifies a data label. Clarion's ABC (Application Builder Class) Templates generate Object Oriented code for you using the ABC Library. The ABC Library contains an error handling class called ErrorClass. This bit of code declares a LocalErrGroup GROUP (in exactly the form that the ErrorClass requires'see the ABC Library Reference - Vol I) containing a "custom" error number and message that we are defining for use by the ErrorClass object in our application. The SaveTotal declaration is a local variable which is defined LIKE (always has the same data type) the ORD:InvoiceAmount column. We'll use this variable to hold the starting order total when the user is updating an existing order. 3.Choose **Search** {{blttria.jpg|BLTTRIA.jpg}} **Find** to bring up the //Find// dialog. 4.Type //ThisWindow.Init// into the **Find what** entry, then press the **Find** button. This takes you directly to the **ThisWindow.Init** method. Press **Close (the Red X)** to close the //Search and Replace// dialog. 5.In the embed point immediately following the line of code reading **SELF.Errors &= GlobalErrors** (this should be at Priority 5300), type the following code: **   SELF.Errors.AddErrors(LocalErrGroup)   !Add custom error** **   IF SELF.Request = ChangeRecord           !If Changing a row** **     SaveTotal = ORD:InvoiceAmount         !Save the original order total** **   END** {{sourceeditor3.jpg|SourceEditor3.jpg}} This code calls the //AddErrors// method of the //GlobalErrors// object to add the //LocalErrGroup// to the list of available errors that the object handles. The //GlobalErrors// object is an instance of the ErrorClass which the ABC Templates declare globally to handle all error conditions in the application. Adding our //LocalErrGroup// enables the //GlobalErrors// object to handle our "custom" error condition. This demonstrates the flexibility of Clarion's ABC Library. The **IF** statement detects when the user is editing an existing order and saves the original order total. This embed point is in the //ThisWindow.Init PROCEDURE// which performs some necessary initialization tasks. This is a virtual method of the //ThisWindow// object. //ThisWindow// is the object which handles all the window and control handling code. You may not have noticed, but the ABC Templates generate exactly one line of executable source code within the //UpdateOrder PROCEDURE// itself (**GlobalResponse = ThisWindow.Run**) so all of the functionality of the //UpdateOrder PROCEDURE// actually occurs in object methods'either virtual methods specific to the //UpdateOrder PROCEDURE// itself or standard ABC Library methods. This is true of every ABC Template generated procedure. Generating fully Object Oriented code makes the code generated for you very tight and efficient'only the code that actually needs to be different for an individual procedure is handled differently. Everything else is standard code that exists in only one place and has been tested and debugged to ensure consistent performance. Object Oriented Programming (OOP) in Clarion starts with the CLASS structure. See //CLASS// in the Language Reference Help for a discussion of OOP syntax. The Advanced Programming Resources PDF contains several articles which discuss OOP in depth, and the ABC Library Reference fully documents Clarion's Application Builder Class (ABC) Library. 6.Press **Search **{{blttria.jpg|BLTTRIA.jpg}} **Find** (or press **CTRL + F**) to open the search dialog again. Type //ThisWindow.Kill// into the **Find what** entry, then press the **Find **button. 7.In the embed point immediately following the line of code reading CODE (this should be [Priority 2300]), type the following code: **     SELF.Errors.RemoveErrors(LocalErrGroup)     !Remove custom error** {{sourceeditor4.jpg|SourceEditor4.jpg}} This calls the ABC Library method to remove our "custom" error. The ThisWindow.Kill method is a "cleanup" procedure (performs necessary exit tasks) which executes when the user is finished working in the UpdateOrder procedure, so the error is no longer needed at that point. 8.Type //EVENT:CloseWindow// into the **Find what** field, then press the **Find next** button. You will need to press the **Find **button twice to find the embed point referenced in Step 9. Close the //Search// dialog once you have found the desired embed point. 9.In the embed point immediately following the line of code reading //OF EVENT:CloseWindow// (this should be Priority 2500), type the following code: ** IF SELF.Request = ChangeRecord     AND |  ! If Changing a row** **   SELF.Response <;> RequestCompleted AND|  ! and OK button not pressed** **   SaveTotal <;> ORD:InvoiceAmount          ! and detail recs changed** **  GlobalErrors.Throw(99)                   ! Display custom error** **  SELECT(?OK)                              ! then select the OK button** **  CYCLE** ** END** {{sourceeditor5.jpg|SourceEditor5.jpg}} This is the code that will detect any attempt by the user to exit the UpdateOrder procedure without saving the //Orders// table row after they've changed an existing order. Note the vertical bar characters (|) at the end of the first two lines of code. These are absolutely necessary. Vertical bar (|) is the Clarion language line continuation character. This means that the first three lines of this code are a single logical statement which evaluates three separate conditions and will only execute the **GlobalErrors.Throw(99)** statement if all three conditions are true. __**Overriding the Edit in Place Classes**__ OK, now you've seen an example of how you can //use// the ABC Library in your own embedded source code. Now we'll show you how to //override// a class to provide custom functionality that the ABC Library does not provide. The CLASS declarations for the objects that we named through the //Configure Edit in Place// dialogs are generated for you by the ABC Templates. These CLASSes are both derived from the //EditClass// ABC Library class. 1.Press **Save and Close **{{saveclose.jpg|SaveClose.jpg}} to close the Embed Editor. We will use the Embed tree to find the remaining embeds. 2.RIGHT-CLICK on //UpdateOrder// and select **Embeds** from the popup menu. 3.To make the embed tree easier to read, press the **Contract All** button. {{l11embedcontract.jpg|L11EmbedContract.jpg}} 4.By pressing the **+** button on the tree, expand the //Local Objects// {{blttria.jpg|BLTTRIA.jpg}} //ABC// //Objects// {{blttria.jpg|BLTTRIA.jpg}} //EIP Field Manager for Browse Using ?List for column DTL:Quantity (EditSpinClass)// {{blttria.jpg|BLTTRIA.jpg}} //Init// {{blttria.jpg|BLTTRIA.jpg}} //Code// {{blttria.jpg|BLTTRIA.jpg}} //Parent Call//. 5.Press the **Source** button. You should now be in the embed point immediately following the line of code reading **PARENT.Init(FieldNumber,Listbox,UseVar)** (this should be [Priority 5300]), type the following code: ** SELF.Feq{PROP:Text} = ListBox{PROPLIST:Picture,FieldNumber}** **                                  !Set entry picture token** ** SELF.Feq{PROP:RangeLow} = 1      !Set RANGE values for the SPIN** ** SELF.Feq{PROP:RangeHigh} = 9999** This code sets the data entry picture token and range of valid data for the SPIN control. 6.Scroll to the **EditInPlace::DTL:Quantity.SetAlerts** method (this should immediately follow the EditInPlace::DTL:Quantity.ResetControl method). In the CODE section just prior to the Parent Call, enter the following code: ** SELF.Feq{PROP:Alrt,5} = ''** ** SELF.Feq{PROP:Alrt,6} = ''** {{notebox.jpg|NoteBox.jpg}} If you wish to see the base class code that we've overridden, open the //\CLARION7\LIBSRC\ABEIP.CLW// file and search for **EditSpinClass**. 7.Press **Search **{{blttria.jpg|BLTTRIA.jpg}} **Find** to open the search dialog again. Type //EditInPlace::DTL:ProdNumber.TakeEvent// into the **Find what** field, then press the **Find** button. 8.In the embed point immediately following the line of code reading **ReturnValue = PARENT.TakeEvent(Event)** (this should be [Priority 5300]), type (or copy and paste) the following code: ** UPDATE(SELF.Feq)                              !Update Q field** ** IF ReturnValue AND ReturnValue <;> EditAction:Cancel OR |** **      EVENT() = EVENT:Accepted                 !Check for completion** **  PRD:ProdNumber = BrowseDTL.Q.DTL:ProdNumber  !Set for lookup** **  IF Access:Products.Fetch(PRD:ProdNumberKey)  !Lookup Products row** **    GlobalRequest = SelectRecord               !If no row, set for select** **    BrowseProducts                             ! then call Lookup proc** **    IF GlobalResponse <;> RequestCompleted      !Row selected?** **      CLEAR(PRD:Record)                        ! if not, clear the buffer** **      ReturnValue = EditAction:None            ! and set the action to** **    END                                        ! stay on same entry field** **  END** **  BrowseDTL.Q.DTL:ProdNumber = PRD:ProdNumber  !Assign Products table** **  BrowseDTL.Q.DTL:ProdAmount = PRD:ProdAmount  ! values to Browse QUEUE** **  BrowseDTL.Q.PRD:ProdDesc   = PRD:ProdDesc    ! fields** **  DISPLAY                                      ! and display them** ** END** This is the really interesting code. Notice that the first executable statement (generated for you) is a call to the **PARENT.TakeEvent** method. This calls the **EditSpinClass.TakeEvent** method we're overriding so it can do what it usually does. All the rest of the code is there to give this derived class extra functionality not present in its parent class. This is the real power of OOP'if you want everything the parent does, plus a little bit more you don't have to duplicate all the code the parent executes, you just call it. In this case, all the extra code is to perform some standard data entry validation tasks. This code will verify whether the user typed in a valid Product Number and if they didn't, it will call the ViewProducts procedure to allow them to choose from the list of products. __**Overriding Methods in the Embeditor**__ There is a very important point to understand about working in the Embeditor or the Embed Tree. When you are overriding methods: //as soon as you type anything into an embed point in an overridable method, you have overridden it//. Even a simple ! comment line makes this happen, because the Application Generator notes that you have written some of your own code, and so generates the proper method prototype into the local CLASS declaration for you. To prevent you from accidentally adding a comment that causes the method override which consequently "breaks" the functionality, the ABC Templates automatically generate PARENT method calls and RETURN statements for you, as appropriate. You'll notice that all of our overridden methods contained generated calls to their PARENT method, and the TakeEvent method also had a generated RETURN statement. Sometimes you want these statements to execute, and sometimes you don't (usually, you do). For those cases where you do not want them to execute, simply write your code in the embed point which comes before the PARENT method call and write your own RETURN statement at the end of your code. This means that the generated PARENT method call will never execute. Clarion's optimizing compiler is smart enough to recognize that these statements can never execute and optimizes them out of compiled object code. 1.Choose **Save and Close** to close the Embeditor, then press **Save and** **Close** to close the embed tree. Make sure to save your work when prompted. __**Update the Procedure Call Tree**__ The **EditInPlace::DTL:ProdNumber.TakeEvent** method calls the BrowseProducts procedure from within its code. Since this is just embedded source code, the Application Generator doesn't know you've called this procedure, and needs to be told (if you don't, you'll get compiler errors), so it can generate the correct MAP structure for the module containing this procedure. 1.RIGHT-CLICK on //UpdateOrder// and select **Procedures** from the popup menu. 2.Highlight //BrowseProducts// then press the **Save and Close** button. {{l11proccalls.jpg|L11ProcCalls.jpg}} 3.Choose **File** {{blttria.jpg|BLTTRIA.jpg}} **Save**, or press the **Save** button {{savebutton.jpg|savebutton.jpg}} to save your work. __**Generate code**__ 1.Press the **Generate All **button {{generateallbut.jpg|GenerateAllBut.jpg}} in the IDE toolbar to generate the source code for your application. 2.RIGHT-CLICK on //UpdateOrder// and select **Module** from the popup menu. The Text Editor appears containing the generated source code for your UpdateOrder procedure. Notice that there is a lot less code here than there was in the Embeditor. All that generated code in the Embeditor was there to provide you with context, and to provide you with embed points with which to override methods, should you need to. However, Clarion's Application Generator and ABC Templates are smart enough to only generate the code you actually need, when you actually need it. 3.From the IDE Menu, choose **File **{{blttria.jpg|BLTTRIA.jpg}}** Close **{{blttria.jpg|BLTTRIA.jpg}}** File** to close the Text Editor. Now might be a good time to try out your application. You've got all the data entry portions completed and the only things lefts to do now are the reports, which we'll get to in the next lesson. **OK, What Did I Just Do?** Here's a quick recap of what you just accomplished: {{blueck.jpg|blueck.jpg}}  You created a new Form procedure. {{blueck.jpg|blueck.jpg}}  You created a "Scrolling-Form" metaphor Edit-in-place browsebox to update the Detail table. {{blueck.jpg|blueck.jpg}}  You created a total column to total up the order. {{blueck.jpg|blueck.jpg}} You created a Formula to total each line item in the order. {{blueck.jpg|blueck.jpg}}  You used the Embeditor to write your embedded source code within the context of template-generated code. {{blueck.jpg|blueck.jpg}}  You used the power of OOP to extend the standard error handling functionality of the ABC Library. {{blueck.jpg|blueck.jpg}}  You used the power of OOP to derive and override the Edit-in-place classes to extend the standard functionality of the ABC Library. {{blueck.jpg|blueck.jpg}}  You generated source code to compare the difference between the code shown in the Embeditor to that which is actually generated. We're almost finished with this application. In the next lesson we'll create the application's reports. [[lc creating reports.htm|Click here to jump to the next lesson]]