Cats and Dogs! How to domesticate OpenROAD list-handling to obey your every
Transcription
Cats and Dogs! How to domesticate OpenROAD list-handling to obey your every
§ Sean Thrower, Actian Cats and Dogs! How to domesticate OpenROAD § June 2013 list-handling to obey your every Disclaimer This document is for informational purposes only and is subject to change at any time without notice. The information in this document is proprietary to Actian and no part of this document may be reproduced, copied, or transmitted in any form or for any purpose without the express prior written permission of Actian. This document is not intended to be binding upon Actian to any particular course of business, pricing, product strategy, and/or development. Actian assumes no responsibility for errors or omissions in this document. Actian shall have no liability for damages of any kind including without limitation direct, special, indirect, or consequential damages that may result from the use of these materials. Actian does not warrant the accuracy or completeness of the information, text, graphics, links, or other items contained within this material. This document is provided without a warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability, fitness for a particular purpose, or noninfringement. Confidential © 2012 Actian Corporation 2 Abstract § To handle sets of business items, OpenROAD provides you with two representations: customized and type specific Arrays (cats); and dynamic, optimized, generically structured ChoiceLists (dogs). Cats and dogs are complementary: cats provide focused browsing and editing via TableFields; dogs provide at-a-glance selection via five different types of ChoiceField; between them they cover the range of multi-item handling that most applications require. § To get the best results, dogs in particular must be approached in the right way. This presentation begins with a common requirement – how to provide row-specific dropdown lists in TableFields – to illustrate some key techniques; then follows with advanced techniques for building lists from a range of source types (database, files, datastrings, arrays, master-lists). The session will also give you the opportunity to raise and discuss other list- Contents § Cats and Dogs § Row-specific dropdown lists in table fields § Techniques for building lists § Other list-handling issues (Q&A) • And incidentally, plenty of coding techniques! Cats ... § To handle sets of business items, OpenROAD provides you with CATS customized and typespecific arrays ... ... and Dogs § And DOGS – dynamic, optimized, generically structured choicelists ... Cats ... § Cats provide • focused browsing and editing via • TableFields. ... Dogs § Dogs provide • at-a-glance selection via • 5 different types of ChoiceField. You need both ... § Select ... .. And sometimes both at once! § ... and Browse/Update Row-specific dropdown lists § Today is about training your dog. • Can you get it to do this? Row-level dropdown in a table field. Row-specific dropdown lists • And this? Row-level, row-specific dropdown in a table field. Image dropdown lists • And this? Row-level image dropdown in a table field. Implementable in OpenROAD 5.0 + § This is not new capability. It takes a little invention, but you can do this in any supported version of OpenROAD § The example code here will make use of § StringObject Split and Join methods § StringHashTables • as the code is much simpler using these features, added in version 5.1 What do we need? Final appearance, in editor § Mobile dropdown button • SubForm valuesholder, containing § ButtonField arrowbtn with dropdown icon § OptionField valuesopt • The button and option field are aligned TopRight, with the button on top § You may need to wrap the button in a nameless StackField, in earlier OpenROAD releases § Button field has RequireRealField=TRUE What do we need? § Procedures, to manage the dropdown and list behaviour § PopulateDropList § ShowArrowDropPanel § ApplyArrowDropSelection § TargetInfo • None of these procedures live in the frame; instead you put them in a service object ... What do we need? § The service object • Let’s call it § ServiceObject • Containing § ArrowDropDown • A setup method for the mobile dropdown § plus the 4 procedures on the previous page (it will also contain setup methods and procedures for other services you might implement) What do we need? § Events, to trigger the necessary behaviours: § Initialize • setup the mobile dropdown § ChildMouseEnter tbl (or column) • populate and show the appropriate dropdown list § ChildClick valuesholder • select an item from the option field list § Scroll tbl, MouseExit tbl, WindowResized • hide the mobile dropdown § ChildEntry tbl • change the dropdown’s background on a highlighted row 4GL code: Events Setup Events: Initialize Initialize = declare /* ** Declare the service object and the procedure (prochandle) interface */ SO = ServiceObject; PopulateDropList = ProcHandle default null; ShowArrowDropPanel = ProcHandle default null; ApplyArrowDropSelection = ProcHandle default null; TargetInfo = procedure returning Object; enddeclare { /* ** Setup the name and value dropdown */ arrowBox = FIELD(valuesholder); SO.ArrowDropDown(); Populate Events: ChildMouseEnter tbl On childmouseenter tbl (or tbl[*].<columnname>) /* ** Get the target information */ trigger = curframe.TriggerField; rc = TargetInfo.Call(trigger=trigger, indx=Byref(indx)); /* ** Populate and show the dropdown panel */ rc = PopulateDropList.Call( frame=curframe, trigger=trigger, listdefs=listdefs, list=Byref(list) ); Selection Events: ChildClick valuesholder On childclick valuesholder /* ** Get the target cell, record or field the user was working on; ** Set and display the new value */ IF CurFrame.TriggerField.IsA(choicefield) = TRUE THEN rc = ApplyArrowDropSelection.Call( arrowBox=arrowBox, frame=curframe, target=Byref(item)); ENDIF; Hide Events: MouseExit tbl , Scroll tbl, On scroll tbl On mouseexit tbl WindowResized On windowresized /* ** Hide the arrowbox */ arrowrow.AllBias = FB_INVISIBLE; Display Events: ChildEntry tbl On childentry tbl /* ** Match the arrow background to the highlight ** if there is an arrow */ arrowrow = ChoiceField(arrowBox.FieldByName('valuesopt')) .ValueList.ChoiceItems.ClientInteger; if arrowrow > 0 then cell = col.CellAttribute(record=arrowrow); endif; if arrowrow = row then arrowBox.FieldByName('arrowbtn').SetAttribute( BgColor=$HIGHBG; FgColor=$HIGHFG); elseif cell is not null then arrowBox.FieldByName('arrowbtn').SetAttribute( BgColor=cell.BgColor; FgColor=cell.FgColor); endif; 4GL code: Methods ArrowDropDown method: Sets up the service procedures § Contains a text list of the procedure names § name1 name2 name3 ... § Uses each name to get the procedure’s ProcHandle reference § GetProcHandle(name=name1); § Stores each prochandle in the calling frame • in a variable of the same name. Now each procedure can be executed from the calling frame, using: § Name.Call() ArrowDropDown method 4GL code ... Method ArrowDropDown( caller = ProcExec default null; //the frame using the service )= { namestring = 'showarrowdroppanel applyarrowdropselection populatedroplist'; names = ToString(text=namestring).Split(delimiter=' '); for i = 1 to names.LastRow do name = names[i].Value; //next name from the namestring dyn = caller.Scope.CreateDynExpr(string=name); //has the caller declared it? if dyn is null then return FALSE; //caller is not ready endif; dyn.SetValue(value=curmethod.Scope.GetProcHandle(name=name)); //set it endfor; return TRUE; //caller is ready } 4GL code: Procedures TargetInfo procedure Gets essential information about the list target § Gets the tablefield associated with the trigger § WhichTableField() § Gets the tablefield’s: § array § targeted row (object and index) § targeted column § targeted cell § Returns them all, byref TargetInfo procedure 4gl code ... procedure TargetInfo( trigger tbl col arr indx cell = FormField default null; //the mouseentered field = TableField default null; //the target tablefield - byref = ColumnField default null; //the target columnfield - byref = ARRAY of Object default null; //the target array - byref = integer not null; //the target row number - byref = CellAttribute default null; //the target cell’s cellattribute - byref )= Returns the targeted cell, or record, or field directly, but all of these are also returned byref, plus column and table field and row number. TargetInfo procedure continued ... /* ** Get the tablefield */ tbl = trigger.WhichTableField(); /* ** Get the array */ tbl.GetFieldValue(value=Byref(arr)); /* ** Get the row */ indx = tbl.WhichRow(trigger); if indx > 0 then elseif trigger.IsDescendantOf(tbl.TableBody) = FALSE then else /*the "trigger" was the protofield or the column*/ indx = tbl.ActiveRow; endif; TargetInfo procedure continued ... /* ** Get the column ** (for complex columns, the trigger may not have been named) */ col = tbl.TableBody.FieldByName(name=trigger.Name); if col is null and indx > 0 then cols = tbl.TableBody.ChildFields; for i = 1 to cols.LastRow do if trigger.IsDescendantOf(cols[i]) = FALSE then continue; endif; col = cols[i]; endloop; endfor; endif; TargetInfo procedure continued ... /* Get the cell */ if indx = 0 or col is null then elseif col.HasCellAttributes = TRUE then cell = col.CellAttribute(record=indx); endif; /* ** Return the targeted field, cell or record */ if cell is not null then target = cell; elseif indx > 0 then target = arr[indx]; else target = trigger; endif; return target; PopulateDropList procedure Populates the dropdown with the appropriate list § Gets the listholder for the list § A cell or row of the table field § Accesses a cached list or builds a new one § Builds the list based on a listdefinition § Keyword + evaluation string § References the listholder from the list § References the trigger, which may be different More details later ... ShowArrowDropPanel procedure Displays the dropdown arrow § Hides the dropdown arrow § While the list is being switched and the arrow moved § Attaches the correct list § Ensures the arrow’s background matches its new location § Moves and redisplays the dropdown arrow Now the dropdown is visible and ready to be clicked ShowArrowDropPanel procedure 4GL code ... procedure ShowArrowDropPanel( ddArrow = SubForm default null; //the arrow “panel” itself frame = FrameExec default null; //the target frame trigger = FormField DEFAULT NULL; //the field that was clicked list = ChoiceList default null; //must be a caller-scope variable )= Returns ER_OK or ER_FAIL, plus the attached list itself (byref) ShowArrowDropPanel procedure continued ... /* ** Hide the list while setting up */ ddarrow.AllBias = FB_INVISIBLE; /* ** Attach the list to the dropdown */ ddarrow.FieldByName(name='valuesopt').SetAttribute(valuelist=list); /* ** Get the target information */ callproc TargetInfo(trigger=Byref(trigger), tbl=Byref(tbl), cell=Byref(cell)); ShowArrowDropPanel procedure continued ... /* ** Match the arrow background to the cell or field it is on */ if tbl is not null and cell is not null then ddarrow.FieldByName(name='arrowbtn').SetAttribute( BgColor=cell.BgColor, FgColor=cell.FgColor); else ddarrow.FieldByName(name='arrowbtn').SetAttribute( BgColor=trigger.BgColor, FgColor=trigger.FgColor); endif; ShowArrowDropPanel procedure continued ... /* ** Display the dropdown */ ddarrow.SetAttribute( parentField=CurFrame.TopForm); ddarrow.SetAttribute( layersequence=$TOPMOST, AbsXRight=trigger.AbsXRight, AbsYTop=trigger.AbsYTop, AllBias=FB_CHANGEABLE); return ER_OK; ApplyArrowDropSelection procedure Applies the selected value to the target § Hides the dropdown § Gets the target cell, record or field § Applies the selected value to the target § Refreshes the target display § To show the new value ApplyArrowDropSelection procedure 4gl code ... procedure ApplyArrowDropSelection( arrowBox frame target trigger = SubForm default null; = FrameExec default null; = Object default null; = FormField default null; //the dropdown itself //the target frame //the target cell, record or field (byref) //the original trigger (byref) )= Returns ER_OK or ER_FAIL, plus the target and the trigger (byref) ApplyArrowDropSelection procedure 4gl code ... /* ** Hide the list */ arrowBox.AllBias = FB_INVISIBLE; /* ** Get the owner and trigger of the item the user was working on */ list = listFld.ValueList; lookuphash = list.ClientData; owner = lookuphash.Find(key='owner'); trigger = lookuphash.Find(key='trigger'); ApplyArrowDropSelection procedure continued ... /* ** Get the item the user was working on ** (the field, if owner is a field outside a tablebody; the rowobject, ** if owner is a cell or row) */ callproc TargetInfo(trigger=Byref(trigger), tbl=Byref(tbl), arr=Byref(arr), indx=Byref(entry)); if owner.IsA(class=CellAttribute) = TRUE then target = arr[entry]; elseif tbl is not null and owner != trigger then target = owner; else target = trigger; endif; ApplyArrowDropSelection procedure continued ... /* ** Apply the selected value to the target */ choice = list.ChoiceItems[ list.IndexByText(textvalue=listFld.CurEnumText) ]; if tbl is not null then name = tbl.FullName + '[' + varchar(entry) + '].' + trigger.Name; else name = trigger.FullName; endif; dyn = curframe.Scope.CreateDynExpr(string=name); ApplyArrowDropSelection procedure continued ... case Left(trigger.Datatype, Locate(trigger.Datatype,'(')-1) of 'integer', 'smallint', 'integer8': dyn.SetValue(value=choice.EnumValue); 'float': dyn.SetValue(value=Float8(choice.EnumText)); 'money': dyn.SetValue(value=Money(choice.EnumText)); 'date': dyn.SetValue(value=Date(choice.EnumText)); 'decimal': dyn.SetValue(value=Decimal(choice.EnumText,15,8)); 'varchar', 'char': dyn.SetValue(value=choice.EnumText); 'stringobject': dyn.SetValue(value=ToString(text=choice.EnumText)); 'bitmapobject': dyn.SetValue(value=choice.EnumBitmap); default: return ER_FAIL; endcase; ApplyArrowDropSelection procedure continued ... /* ** Show the new value */ if tbl is not null then tbl.UpdField(clearhasdatachanged=0); else trigger.UpdField(clearhasdatachanged=0); endif; return ER_OK; More about list building PopulateDropList procedure Populates the dropdown with the appropriate list § Gets the listholder for the list § A cell or row of the table field § Accesses a cached list or builds a new one § Builds the list based on a listdefinition § Keyword + evaluation string § References the listholder from the list § And the trigger, which may be different PopulateDropList procedure 4GL code ... procedure PopulateDropList( frame = FrameExec default null; //the target (caller) frame trigger = FormField DEFAULT NULL; //the mouse-entered field listdef = varchar(256) not null; //the list definition to populate from list = ChoiceList default null; //must be a caller-scope variable notcell = integer not null default FALSE; //whether the listholder is a cell )= Returns ER_OK or ER_FAIL, plus the populated list (byref) PopulateDropList procedure continued ... /* ** Get details of the listholder we are getting the list for ** Don't use the default listholder (the cell) ..if notcell was specified */ listholder = TargetInfo(trigger=Byref(trigger), tbl=Byref(tbl), arr=Byref(arr), indx=Byref(indx), cell=Byref(cell)); if listholder is null then return ER_FAIL; endif; if listholder = cell and notcell = TRUE then listHolder = arr[indx]; endif; /* ** Determine whether we need to build a list or have one already cached */ //not covered in this presentation PopulateDropList procedure continued ... /* Build a dropdown valuelist for the indicated item */ choices = list.Choiceitems; tokens = ToString(text=listdef).Split(delimiter=‘:‘, exactrows=2); valuekey = tokens[1].Value; valuestring = tokens[2]; case valuekey of ‘SQL’: //eg. SQL: select id, name, nickname from employee { i = 1; EXECUTE IMMEDIATE :valuestring.Value into choices[i].EnumValue, choices[i].EnumText, choices[i].EnumDisplay { i = i + 1; } commit; } PopulateDropList procedure continued ... ‘IN’: { //eg. ‘IN: true, false, unknown’ tokens = ToString(text=valuestring).Split(delimiter=‘,’); FOR i = 1 TO tokens.LastRow DO value = tokens[ct].Value; list.AddItem( EnumValue = list.ChoiceItems.LastRow + 1; TextValue = value; DisplayValue = value; BitmapValue = null); ENDFOR; } ...etc. ‘ARRAY’, ‘FILE’, ‘STRING’, ‘PROCEDURE’ can all be sources of lists too endcase; PopulateDropList procedure continued ... /* ** Add a back link to the list owner and the trigger ** (lookuphash is a StringHashTable) */ lookuphash.InsertObject(key='owner', object=listHolder); lookuphash.InsertObject(key='trigger', object=trigger); list.ClientData = lookuphash; /* ** Forget it - if the list is empty */ if list.ChoiceItems.LastRow = 0 then return ER_FAIL; endif; return ER_OK; } Questions about ... Row-specific dropdown lists? Questions about ... Predefining list definitions? • IN • SQL • ARRAY • FILE • STRING • PROCEDURE Questions about ... Cats & Dogs? Click icon to add picture dynamic, optimized, generically structured choicelists customized and typespecific arrays Session Overview § Cats and Dogs § Row-specific dropdown lists in table fields § Techniques for building lists § Other list-handling issues (Q&A) • And incidentally, plenty of coding techniques! Confidential © 2012 Actian Corporation 56 Thank You www.actian.com facebook.com/actiancorp @actiancorp Confidential © 2012 Actian Corporation 57