Tips   >   Misc   >   Misc (All Contents)

Misc

This section covers miscellaneous topics relating to Omnis Studio.

Boolean Logic in expressions

There is no need to compare the expression value to kTrue or kFalse for any expression in Omnis Studio which returns true or false.

Here are some code examples to clarify this.

; Calculate the number of selected lines.
Do ListVar.$sendall(LineCount.$assign(LineCount+1),$ref.$selected=kTrue)

; Using $ref.$selected is shorter and is the same as using $ref.$selected=kTrue
Do ListVar.$sendall(LineCount.$assign(LineCount+1),$ref.$selected)

; Using not($ref.$selected) to count the unselected lines.
Do ListVar.$sendall($ref.value.$assign(10),not($ref.$selected))

; Check the return flag.
If FlagOK=kTrue
   ; Do something
End If

; Using FlagOK is the same as FlagOK=kTrue
If FlagOK=kTrue
   ; Do something
End If

; Use not(FlagOK) to test for FlagOK=kFalse
If not(FlagOK)
   ; We have a problem.
End If

; Check if the file exists.
Do FileOps.$doesfileexist(Path) Returns bFileExists
If bFileExists
   ; Do something
End If

; You can put the expression in an If calculation since it evaluates to true or false.
If FileOps.$doesfileexist(Path)
   ; Do something
End If

; Check if a certain name is in the list.
Do ListVar.$search($ref.FirstName='Dawid',1,0,0,0) Returns LineNum
If LineNum>0
   ; Do something
End If

; You can put the expression in an If calculation since it evaluates to a numeric value.
If ListVar.$search($ref.FirstName='Dawid',1,0,0,0)
   ; Do something
End If

Calling any class method

You might run into a situation where you want to call a class method without first creating an instance of the class.

The trick for doing this is to use the Do code method command. The Do code method is not limited to Code Classes; it can be pointed to the class method of any type of class.

Do code method [$clib().$name].wWinClassName.MethodName (pParam1,pParam2)

Warning

I'm not sure why you would need to do this... this style of code is something the object-oriented programming police might pull you over for. You should be using a public method of a class instance.

Change IDE Font Sizes

Omnis Studio allows you to change the font sizes in most of the IDE (Interactive Developer Environment) windows. This can be especially helpful if you are doing a presentation.

To change the font size of the code list, methods treelist, Property Manager, Catalog, etc.

  1. Click on the list so it has the focus.
  2. Ctrl/Cmd+] to increase the size. (STRG+* to increase size - German keyboard layout)
  3. Ctrl/Cmd+[ to decrease the size. (STRG+� to increase size - German keyboard layout)

The changed font size will only be temporary. If you close and reopen the IDE window, the font size will revert to the original size.

To save the font size for the IDE window, right-click on the window and select Save Window Setup.

Code Classes

Here's a word of advice about code class... don't use them. (Okay, that was 3 words.)

When I started learning Omnis Studio I tried object classes and said to myself, "This is too hard, Code Classes are much easier to work with and call, I'm sticking with code classes". Six months later at my first Omnis Conference, Geir Fjaerli convinced me to use object classes. The statement he made proved true, "Anything you can do with a code class, you can do better with an object class."

Code classes are a slippery slope that draws you away from object-oriented programming, and back into Omnis Classic style coding. (There is a difference.) Omnis Studio will admit to you they only included code classes to make it easier for converting from Classis to Studio. I was told the original design of Omnis Studio did not include code classes.

There are several problems you will run into with code classes:

  1. Code classes don't extend instances. I discovered this when in a code class method I defined a list variable from a table class using $definefromsqlclass and then called a method in the table class. Everything worked fine until the table class needed the value of a task variable. Oops, it was out of scope!!
  2. Code class can't have private methods. All methods in your code class are public. As you get deeper into object-oriented programming (OOP) you will discover the importance and beauty of having public vs. private methods. (In Omnis Studio public methods start with the $ character, private methods do not start with $ character.)
My advices is that you avoid code classes. Use object classes instead. Anything you can do with a code class, you can do with an object class ... better. If you are coming from Omnis Classic, object classes might seem like a pain at first, but stick with them, it is worth the effort. Object classes will provide more powerful and elegant solutions.

Convert Numbers to Text

If you ever need to print checks, you'll need to convert the dollar value of the check to text.

For example, you may need to convert 550.25 to five hundred and fifty -------- 25/100

You can download a free library called convertNumberToText from the downloads section of the studiotips.net website.

www.studiotips.net/downloads

The library contains an object class which you can copy to your application and then instantiate and send messages to.

Calculate Number as 11578.97
Do oConvertNumberToText.$convertNumberToText(Number) Returns Text

Click the Run Demo button in the StudioTips Browser window to try out the oConvertNumberToText object class.

Delete stubborn ivar

To delete an instance variable from a class, you right-click on the variable in the variables pane and select Delete variable... in the context menu. As long as the variable is not used in any code, Omnis Studio will prompt you with a Delete variable No/Yes dialog. In some rare situations, Omnis Studio won't let you delete an instance variable, even though you have deleted every piece of code that contains the instance variable.

Find and Replace comes up empty and you've closed and reopened the class, but the stubborn ivar can't be deleted. One trick for getting rid of a stubborn instance variable is to drag it from the Instance variables pane to the Class variables pane. For some reason, once you've done that Omnis Studio allows you to delete the class variable.

Description Property - $desc

The $desc property is found is several different location in Omnis Studio:

  1. Class Description
  2. Method Decription
  3. Variable Description

The class desciption can be modified by selecting the class in the F2 Browser > pressing F6 Property Manager > selecting the General tab and clicking on the $desc property.

The method description for public methods (prefixed with $ character) can be viewed/modified through the Interface Manager. In the F2 Browser right-click on a class > select Interface Manager. At the bottom of the Interface Manager click the Description tab to view and modify the description property of the method.

Private and public method descriptions up to 255 characters can be edited in the method editor. Select the method in the method editor, then click on the grey field just below the code list.

The variable descriptions are accessed through the variables pane of the method editor.

Click the Run Demo button to find out the maximum length of each $desc property.

Developer vs. Runtime Versions

There is a difference between the developer, runtime, and web server versions of Omnis Studio. The runtimes contain less resources in order to have a smaller footprint. You can serialize a developer version as a runtime but you would not be able to do the opposite since the runtime version does not contain the debug resources.

Do redirect

This obsure little command redirects a command to a method of another instance with the same method name.

You could have a shell window which has an $editRecord method that redirects the message to the current subwindow.

The message could be forwarded as follows:

Do rSubWin.$editRecord(pPrimaryKey) Returns FlagOK

or using redirect

Do redirect rSubWin Returns FlagOK

An interesting feature of Do redirect is that you do not have to declare any parameters. Do redirect passes the incoming parameters without declaring them. Cool!

Note

You can only redirect to a method of the exact same name.

Install Size of Omnis Studio

The install size of Omnis Studio v4.2 is quite a bit bigger on the Mac OS X version than the Windows version.

A lot of it is the Mac xcomps, which for some reason all seem to link in a large library of common code. The simplest xcomp ends up being over 400K. You can look through them and discard those that your app doesn't use, notably the oXML at 6.4MB, DAMs you aren't using, docView, etc. - for every 2 xcomps you can remove you save a meg - and if you don't use graphs you can lose the Frameworks folder to save another 5MB. The webclient.plugin in the localclient folder can save almost 9MB if you don't use remote forms.

By trimming out things like that, you can get a 4.2 runtime plus library and data files down to a 50MB downloadable dmg file containing an Apple installer. The previous version, non-universal and not nearly as well cleaned-out, was 32MB.

The above info was provided by Kelly Burgess.

Localized Versions of Omnis

Omnis Studio is localized for different languages. The localized versions replaces the Yes/No/OK prompt button text wtih the appropriate local language text. It may also default to different separator characters in $prefs.$separators.

$separators is a 5 character string.The characters in this string are:

Character 1: the decimal point character
Character 2: the decimal number thousands separator
Character 3: the function parameter separator
Character 4: the import/export decimal point character
Character 5: the import/export comma delimiter character

I ran into trouble with the function parameter separator and the German localized version.

In the English version character 3 is a , comma.
In the German version character 3 is a ; semi-colon.

In the English version concatentate looks like this:
Calculate FullName as con(FirstName,' ',LastName)

If you opened the same library in the German version, you code would look like this:
Calculate FullName as con(FirstName;' ';LastName)

Your normal Omnis code will run without a problem in the English or German version. The problem will be write some code that assumes what the separator will be. The problem I hit in StudioTips was with a replaceall() function.

I wanted to determine then text of the tab strip which the user clicked on. The $event code for the tab strip object was something like this.

CODE THAT BROKE WHEN USING THE GERMAN VERSION OF OMNIS STUDIO.

On evClick
  Find out the text string represented by the tab strip.
  Calculate TabStripTabs as irTabStrip.$tabs
  e.g. About StudioTips, Basics Tutorial, Miscellaneous

  Convert the string for use in a pick statement. Replace each comman with quote-comma-quote ','
  Calculate ReplaceValue as "','" ;; quote-comma-quote
  Calculate PickString as replaceall(TabStripTabs,",",ReplaceValue)
  e.g. About STP',' Basics Tutorial',' Miscellaneous
  Add opening and closing quotes.

  Calculate PickString as con("'",PickString,"'")
  Prepare the pick() statement string to and evaluate it.
  Calculate EvalString as con('pick(',pTabStripNum-1,',',PickString,')')
  Calculate TabStripName as eval(EvalString)

The above code failed with the German localized version because I had assumed the comma separator in the replaceall() function, and in the eval(pick()) function.

FIXED CODE THAT WORKS WITH THE ENGLISH AND GERMAN VERSIONS OF OMNIS STUDIO.

To fix above code I modified it as follows:

On evClick
  Find out the text string represented by the tab strip.
  Calculate TabStripTabs as irTabStrip.$tabs
  e.g. About StudioTips, Basics Tutorial, Miscellaneous

  Find out the Omnis Studio localized function separator.
  Calculate FnSep as mid($prefs.$separators,3,1) ;; English comma, German semi-colon

  Convert the string for use in a pick statement. Replace each comman with quote-comma-quote ','
  Calculate ReplaceValue as con("'",FnSep,"'") ;; quote-FnSep-quote
  Calculate PickString as replaceall(TabStripTabs,",",ReplaceValue)
  e.g. About STP',' Basics Tutorial',' Miscellaneous ;; English
  e.g. About STP';' Basics Tutorial';' Miscellaneous ;; German

  Add opening and closing quotes.
  Calculate PickString as con("'",PickString,"'")
  Prepare the pick() statement string to and evaluate it.
  Calculate EvalString as con('pick(',pTabStripNum-1,FnSep,PickString,')')
  Calculate TabStripName as eval(EvalString)

TESTING YOUR OWN APPLICATION

Testing your own application for different localized version of Omnis Studio is quite simple to do.

In the Omnis Studio > local folder, there is a file named omnisloc.df1.

  1. Quit Omnis Studio if you have it open.
  2. Rename omnisloc.df1. eg. English_omnisloc.df1.
  3. Drag another language omnisloc.df1 file into the local folder.
  4. Open Omnis Studio again, and you are running with the new localized version.

You can download a free zip file containing the English, French, German, and Swedish versions of the omnisloc.df1 file from the downloads section of the studiotips.net website.

www.studiotips.net/downloads

Open PDF File

I wanted to be able to open PDF files from Omnis Studio.

MAC PLATFORM

On the Mac platform that's pretty easy to do:

Send Finder event {Open Files (FilePath)}

Note: This Omnis command does not affect the flag. So you can't test flag true or false.

WINDOWS PLATFORM

Life gets a little more complicated on the Windows platform. Tech Support was kind enough to provide me with sample code.
You have to use the Omnis commands Register DLL and Call DLL. These Omnis commands are not available to you in Omnis Studio when you are using a Mac.

Here's what we ended up with for the Windows platform:

Register DLL ("SHELL32.DLL","ShellExecuteA","JJCCCCJ")
Call DLL ("SHELL32.DLL","ShellExecuteA",0,"open",FilePath,"","",0) Returns HINST_EXPLORER

NOTES:

  1. You can only edit this code on a Windows machine.

    If you look at this code on a Mac, it will looks more like this:

    X250825050 ("SHELL32.DLL","ShellExecuteA","JJCCCCJ")
    X250825051 ("SHELL32.DLL","ShellExecuteA",0,"open",FilePath,"","",0) Returns HINST_EXPLORER

  2. HINST_EXPLORER is long integer, representing a handle on the instance of the launched program.
  3. JJCCCCJ is the calling convention for the parameters passed, so that Call DLL knows how to typecast the parameters passed.
  4. The code should work for any registered file type.

CROSS-PLATFORM CODE

Putting it together in a Switch/Case for cross-platform use, we end up with the following:

; Prompt the user to select a PDF file.
Do FileOps.$getfilename(FilePath,'Select a PDF file','*.pdf') Returns FlagOK
If not(FlagOK)
   Quit method kFalse
End If

; Open the file, using code for the specific platform.
Switch sys(6)
   Case 'M','X' ;; Mac or Mac OS X
      Send Finder event {Open Files (FilePath)}
   Case 'W','N' ;; Windows or NT
      Register DLL ("SHELL32.DLL","ShellExecuteA","JJCCCCJ")
      Call DLL ("SHELL32.DLL","ShellExecuteA",0,"open",FilePath,"","",0) Returns Hinst_Explorer
      If not(Hinst_Explorer)
         OK message (Icon) {Opening the file failed.}
         Quit method kFalse
      End If
   Default
      OK message [sys(85)] (Icon) {Open file code not written for sys(6) = [sys(6)]////Please notify the programmer.}
      Quit method kFalse
End Switch
Quit method kTrue

Optimize Method

A word of caution about the Optimize method command... Omnis programmers have been burned by this command.

The documentation says you can add this command to the first line of a method which you call frequently. The optimized version of the method will be stored in memory and used each time the method is called.

However, Optimize method has not proven to be reliable.

It caused my payroll program to completely crash Omnis Studio when I added it to a method which included twelve Switch/Cases. Others have reported various problems.

Personally, I didn't find any speed improvement. I think Optimize method had its purpose when we were running slow clunky computers. But in this age of faster and faster computers, Optimize method, has much less impact.

You decide whether or not you want to use Optimize method. For my peace of mind, I won't be using it in any application I write.

Show System Focus Mac OS X

The Mac OS X Aqua Interface draws a thick bluish border around the entry field which has the focus. This can be a problem if you have entry fields that are tightly spaced, or if you are using a different method for showing which field has the current focus.

The Omnis Studio preferences allow you to turn on the Mac OS X system focus.

You can set this preference in your application's Startup_Task using notation as follows:

Do $prefs.$showsystemfocus.$assign(kFalse)

Testing Item References

How can you tell for sure that a reference is valid? Testing for a valid reference is easy.

If Ref
   ; Valid reference.
Else
   ; Not a valid reference.
End If

The problem is if you are trying to just trap an invalid reference.

If not(Ref) will give you a problem if the reference is null or is an Unset Reference.

An interesting solution is to test the $ident property of the reference.

If not(Ref.$ident)
   ; NULL or Unset Reference.
End If

Warning

The only exception to the $ident test is testing a library refererence. All other references seem to work okay.

A sure fire way to set for invalid references is to always test first for the valid reference and put the invalid test in the else.

; This test will correctly trap a null or unset reference.
If Ref
Else
   ; NULL or Unset Reference.
   OK message {The item reference is unset or null.}
End If

Click the Run Demo button to step through the test code which runs the various item reference tests.

Trapping Null, Blank, or Zero

Trapping null, blank, or zero is a tricky thing to do cleanly in Omnis Studio.

One might expect the following If calculation to catch the 3 possibilities:

If isnull(Var)|Var=''|Var=0

but it won't work if Var=NULL

If Var=NULL, then the 2nd test, Var='', is testing NULL=''. Omnis Studio can't evaluate NULL='', and therefore it can't properly evaluate the If calculation.

Switch/Case doesn't help much either.

The difficulty with many solutions is that they may work if Var=kCharacter but fail if Var=kNumeric
(or visa versa)

Here's a bullet proof, one liner test for null, blank, or zero!

If pick(isnull(Var),Var,'')=''|pick(isnull(Var),Var,'0')='0'
   OK message {IS Null, Empty, or Zero////Var = [Var]}
Else
   OK message {IS NOT Null, Empty, or Zero////Var = [Var]}
End If

To avoid repeating the above If calculation statement all over your code, I recommend you add it as a method in your oFunctions object class. The following $isNullBlankZero method is included with the oFunctions object class in StudioTips. Feel free to copy it to your own library.

$isNullBlankZero method (included in oFunctions)

; Test for null, blank, or zero.
If pick(isnull(pfVariable),pfVariable,'')=''|pick(isnull(pfVariable),pfVariable,'0')='0'
   Quit method kTrue
End If
Quit method kFalse

If oFunctions is instantiated using the Startup_Task variable fn, you could do the following in your code.

If fn.$isNullBlankZero(Var)
   ; Action to take if null.
Else
   ; Action to take if a value.
End If

; Use not() if you just want to do something if there is a value.
If not(fn.$isNullBlankZero(Var))
   ; Action to take if a value.
End If

Click the Run Demo button in the StudioTips Browser window to step through an extensive series of null, blank, or zero tests which demonstrate the problems you can run into with testing for nulls, and the solution provided in this tip.

Updating to New Version Omnis Studio - Keeping Preference

When you upgrade your developer version of Omnis Studio to a newer version you lose all of your IDE preferences. (IDE window size and locations, toolbar settings, treelist preferences, etc.)

Your Omnis Studio preferences are stored in the omnis.cfg file which is located in the studio folder inside the Omnis Studio folder.
Your Omnis Studio SQL Browser session and VCS sessions are stored in the sql.df1 file which is located in the studio folder inside the Omnis Studio folder.

You simply need to copy the omnis.cfg file from your old version of Omnis Studio to the same location in your newer version of Omnis Studio.

If you have any SQL Browser sessions or VCS sessions also copy the sql.df1 file located in the studio folder to the same location in your newer version of Omnis Studio.

Be sure to copy any extra external components (i.e. Third party DAMs) from your old xcomp folder to the newer version xcomp folder. You might need to download newer versions of some of the external components rather than copy them across.

Note

As of Omnis Studio v4.2 on Mac OS X to get to the studio folder you will need to:


  1. Right-click on the Omnis Studio app icon/folder located in the Applications folder and select Show Package Contents.

  2. Right-click on the Contents folder and select Show Package Contents.

  3. The studio folder is located inside the MacOS folder.

The serial number is also imbedded in the omnis.cfg file. If you are moving to a version of Omnis Studio that requires a new serial number you will not be able to open Omnis Studio after you copy the omnis.cfg file. To solve this, you will need to enter the new serial number in the serial.txt file located in the Omnis Studio folder.

The format for the serial.txt file is:

UN=user name (not required)
CN=company name (not required)
SN=serial number

[Plugins]

The serial number in this file will over-ride the one that is held in the omnis.cfg file. Then when you are up and running, you can re-serialise and force the new serial number into the omnis.cfg file.

Use Field Ref or Return Value?

There are two ways a method can return a value to the sender.

  1. Return a value to the sender in the Quit method command.
  2. Pass a value to the sender using a field reference parameter.

This method uses Quit method to return the value to the sender.

$retRemoveSpaces(pVariable)
Quit method replaceall(pVariable,' ','')

This method uses a field reference parameter to pass the value back to the sender.

$removeSpaces(pfVariable)
Calculate pfVariable as replaceall(pfVariable,' ','')
Quit method kTrue

I normally use the return value to return a boolean value to indicate to the sender either: success (kTrue), or failure (kFalse) in the called method.

If you use the return value to return a real value it becomes more complicated to indicate failure to the sender. In most cases you could return null instead of a real value. The user can then use isnull(ReturnValue) to test for failure by the called method. However, if the method could return null as a valid value you have no way of testing for failure. That makes it very difficult for error handling in non-visual class methods.

The advantages of using the return value to return a real value is that you can use the method for inline calculations.

  1. They are simple to read and use in your code.

    Calculate NewValue as fn.$retRemoveSpaces(TextString)
  2. They can be used for inline calculations.

    Calculate MethodName as con('$',fn.$retRemoveSpaces('Button Name With Spaces'))

The advantages of using field reference parameters to pass values back to the sender are:

  1. The return value can always be used for returning a boolean to indicate success or failure.
  2. You can return more than one value to the sender by using multiple field reference parameters.

The disadvantages of using field reference parameters to pass values back to the sender are:

  1. You must first calculate the value to a variable, before sending it to the method. (Rarely a problem.)
  2. You can not use methods which return values through field reference returning for inline calculations.
I use both techniques for returning values to the sender, it all depends on what the method does. See Naming Conventions > Coding Conventions > Quit method Return Something for more information.

eval() Find Error

If you use an eval() function in your code and there is an error in your eval() how do you find the error?

You can use Test for valid calculation with #ERRTEXT. The beginning of the #ERRORTEXT will tell you the first and last character of your string which is causing the error in the eval().

#ERRTEXT might be something like

0:4:Unrecognized variable name, item name or attribute

. The 0:4 specifies that the error is with characters zero (0) to four (4) in the string.

The following sample code gives you a working example:

; Calculate a text string to be evaluated. ('midx' is not a valid function.)
Calculate EvalString as 'midx(#S1,1,3)'

; Test the evalf() with 'Test for valid calculation'.
Test for valid calculation {evalf(EvalString)}
If flag false
   
   ; The #ERRTEXT will look like '0:4: ...'. The 0:4 is the first and last characters in the string causing the error.
   
   ; Parse out the first and last character positions.
   Calculate ErrorTextString as #ERRTEXT
   Calculate FirstChar as strtok('ErrorTextString',':')
   Calculate LastChar as strtok('ErrorTextString',':')
   
   ; If the first character is zero, it will cause problems with the "mid()" function. Use pick() to set zero to one.
   Calculate FirstChar as pick(FirstChar=0,FirstChar,1)
   
   ; Calculate the portion of the eval string causing the error.
   Calculate EvalStringError as mid(EvalString,FirstChar,LastChar-FirstChar+1)
   
   OK message [sys(85)] (Icon) {There is a problem with the eval string: ////'[EvalString]' ////The error is: '[EvalStringError]'}
   
End If

Quit method kTrue

Click the Run Demo button in the StudioTips Browser to run through the sample code.

Component Store

The F3 Components Store is where you get window and report objects when designing windows and reports.

You can modify the existing classes and design objects in the F3 Component Store, or add your own objects and classes. You can even add your own class group/icon to show up in the Component Store's first level toolbar.

To modify the Component Store you need to get inside of it.

  1. Double-click any window or report class in the F2 Browser.
  2. Press F3 to open the Component Store.
  3. Right-click anywhere on the Component Store > select Show Component Store in Browser.
  4. Press F2 to open the Browser.
  5. Select the Component Library in the F2 Browser.

There are a number of different things you can do with the Component Library. Read the other topics in this section for further instructions.

When you finish making your modifications to the Component Library click the Hide Comps.lbs button in the F2 Browser.

Warning

When a new version of Omnis Studio is released, it's a bit of a hassle saving and moving your previous Component Store to the newer version. Be careful on how much time you invest into customizing the Component Store.

Modifying Existing Components

With the Component Library open in your F2 Browser:

  1. Click on the Name column header a couple times until the _Field Components window class sorts to the top section of the list.
  2. Double-click the _Field Components window class to open it up. You will see all the window field components which you can drag out of the Component Store onto your design window.
  3. Click on any window object. Press F6 to look at/change the object's properties.

You can modify the object's properties to suit your preference. You can double-click on the object and modify it's $event method, add you own $construct method, etc.

You can also duplicate a window object to create two flavours of the same object. The objects are listed in the Component Store in field order, so you should renumber the duplicate component to be next in order to the one you duplicated. That way they'll show up next to each other in the Component Store.

The duplicate object will have the same tooltip name in the Component Store, that is the type of object which it is, so you can only tell them apart by their order in the Component Store.

To test what you've done.

  1. Close the _Field Components window class
  2. Click the Hide Comps.lbs button in the F2 Browser.
  3. Open any window in design mode.
  4. Press F3 to open the Component Store.
  5. Drag and drop the object you modified and confirm that the changes you made now show up in the object you just dragged from the Component Store.

Congratulations you just modified the Component Store to your own preferences! You can do the same thing for _Report Components, etc.

Warning

When a new version of Omnis Studio is released, it's a hassle saving and moving your previous Component Store objects to the newer version. Be careful on how much time you invest into modifying existing objects in the Component Library. I recommend that you add your own components to a separate class. That way you can quickly copy and paste them into a newer version of Omnis Studio when it is released. Uniquely name or describe your classes so they'll sort together in the list.

Adding your own Classes

With the Component Library open in the F2 Browser:

  1. Drag and drop any class from your own library onto the Component Library.
  2. Press F6 to open the Property Manager.
  3. Optionally you can set the $iconcomponent property for your class to some unique icon.
  4. Right-click on the class, select Component Store Type..., set to Template.

To test what you've done.

  1. Click the Hide Comps.lbs button in the F2 Browser.
  2. Select a library and then click the Class Wizard button in the F2 Browser.
  3. Click the respective class which you added as a template to the Component Library.
  4. Click the Templates button in the F2 Browser.
  5. Select the template class which you added, enter the class name near the bottom of the F2 Browser and click the Create button. A copy of the template class will be added to the selected library.

Adding your own Design Objects

To add your own Design objects (window or report objects) to the Component Library.

With the Component Library open in the F2 Browser:

  1. Drag and drop any window class from your own library onto the Component Library, or create a new one in the Component Library and add some window objects to it. A quick way to create your own design components is to right-click on _Field Components in the Components Library and > select Duplicate.
  2. Press F6 to open the Property Manager.
  3. Set the $name property. The name you assign will be the tooltip in the Component Store.
  4. Set the $iconcomponent property for the window class to some unique icon. It will be the icon that shows up in the Component Store's window objects toolbar.
  5. Right-click on the class, select Component Store Type..., set to Design objects.
  6. Set the properties on any of the window objects inside the window class.

To test what you've done.

  1. Click the Hide Comps.lbs button in the F2 Browser.
  2. Open any window in design mode.
  3. Press F3 to open the Component Store. Your icon/name class should be in the Component Store's toolbar. When you click on it, your design objects should be displayed in the Component Store.
Tip

I recommend adding your own design objects vs. modifying existing components. When a newer version of Omnis Studio is released, you simply copy and paste your own field components classes from the Component Library in the older version to the Component Library in the newer version. Doing this is a lot easier than hunting through the _Field Components and _Report Components classes and copy/pasting the individual objects from the older version to the newer version.

Adding your own Group

You can add your own Group to the Component Store, so that it will show up with it's own icon in the Component Store's main toolbar.

With the Component Library open in your F2 Browser:

  1. Add/create a new class in the Component Library. (any class will work)
  2. Right-click on the new class > select Template.
  3. F6 to open the Property Manager.
  4. Select $componenticon and select any icon by clicking on the droplist arrow.
  5. Set the $desc property to something like, My Group/1715~. The word(s) preceeding the / (backslash) will be the group name, the number between the / and ~ is the iconid of the icon which will be displayed in the Component Store toolbar.
  6. Add/create additional classes (must be of the same class type) to include in your group. Set their $desc property to My Group/1715~

To test what you've done.

  1. Click the Hide Comps.lbs button in the F2 Browser.
  2. Open any window in design mode.
  3. By Omnis Studio magic the My Group icon 1715 appears at the end of the Component Store toolbar.
  4. Click on the My Group icon in the toolbar. Your classes should show up in the Component Store with their own icons and tooltip names.

Super Components

If you are using multiple libraries and storing your superclasses in the main library, you will have discovered that creating subclasses of your superclasses in the other libraries is a bit of a pain.

You right-click on the superclass and select Create subclass, and then name it. But then have to drag the subclass to the destination library, change the $superclass property to include the source library name, and then go back to the source library and delete the extra copy.

Supercomponents solves the problem and makes it really easy to create subclasses in any library! All you do is set the $issupercomponent property of the superclass to kTrue.

After you have set the $issupercomponent property to kTrue, you can create a subclass of your supercomponent in any library as follows:

  1. Select the target library or folder for the subclass in the F2 Browser.
  2. Click the Class Wizard button.
  3. Click the type of class you wish to create.
  4. Click the Subclasses button. Omnis Studio will display all of the supercompoent classes it finds in the open libraries.
  5. Select a supercomponent, enter the subclass class name, and click the Create button.

    Omnis Studio creates a subclass of the supercomponent you selected in the target library or folder you selected. Easy, eh?!

Data Import/Export

Importing data in Omnis Studio is easy and extremely fast. You can import data from a file into a list variable. You simply define the list columns to match the order of the data you wish to import, then tell Omnis Studio to import the data. Once the data is imported into the list variable you can loop through the list and process the data.

This section covers topics relating to importing and exporting data, and includes sample code and demos.

Export Data to File

The following sample code makes a list of classes in the current library, prompts the user to enter a file name and location, and then exports the data to the file.

; Create a list of classes in the current library.
Do $clib.$classes.$makelist($ref.$name,$ref.$classtype,$ref.$moddate) Returns List
Do List.$cols.1.$name.$assign('name')
Do List.$cols.2.$name.$assign('classtype')
Do List.$cols.3.$name.$assign('moddate')

; Prompt the user for a file name and location.
Calculate Title as "Save Export Records As"
Calculate cPath as "ClassesList.txt"
; FileOps.$putfilename(path[,prompt,filter,initial-directory,appflags])
Do FileOps.$putfilename(cPath,Title) Returns bContinue
If bContinue
   
   ; Proceed with exporting the data to the file.
   Begin reversible block
   
   Set print or export file name {[cPath]}
   Prepare for export to file {Delimited (tabs)}
   
   End reversible block
   
   ; Export the list to the file.
   Export data List
   End export
   
   Close print or export file
   
   OK message (Icon) {Open the exported file using Excel or another spreadsheet program to check the contents.////[cPath]}
End If

Quit method kTrue

Click the Run Demo button in the StudioTips Browser window to try out the code.

Import Data from File

This method prompts the user to select an import file. The records are then imported into a predefined list variable.

; Make sure the exoprt data method has been run.
Yes/No message (Icon) {You must first run the 'Export Data' demo, before running this Import data demo.////Have you run the 'Export Data' demo?}
If flag true
   
   ; Define a list for importing the data.
   Do List.$cols.$add('name',kCharacter,kSimplechar,500)
   Do List.$cols.$add('classtype',kCharacter,kSimplechar,500)
   Do List.$cols.3.$name.$assign('moddate')
   
   ; Prompt the user to select the file.
   ; $getfilename(path[,prompt,filter,initial-directory,appflags])
   Calculate Title as "Select the import file"
   Do FileOps.$getfilename(cPath,Title,'*.txt',cPath) Returns bContinue
   If bContinue
      
      Begin reversible block
      
      Set import file name {[cPath]}
      Prepare for import from file {Delimited (tabs)}
      
      End reversible block
      
      Import data List
      
      Close import file
      
      OK message (Icon,Sound bell) {The import list has [List.$linecount] records.}
   End If
End If
Quit method kTrue

Click the Run Demo button in the StudioTips Browser window to try out the code.

Importing Data from Excel

When importing multiple columns of data, I generally use a spreadsheet program like Excel to review and cleanup the data, and then save the spreadsheet as a tab delimited text file.

You can then import the tab delimited text file into a list variable defined to match the Excel columns.

There are some things you need to watch out with when importing data from text files saved from an Excel spreadsheet:

  1. Excel suppresses leading zeroes in fields that it regards as numbers. Thus Americal postal codes (aka ZIP codes) for the Eastern Coastal states are changed. Example: 00123 becomes 123. This can be fixed with programming after import.

    Calculate Company.PostalCode as jst(ImportedPostalCode,'-6p0')
  2. There is feature in Excel which counters its propensity to strip preceding zeros. It involves formatting the columns with this type of data to Text. When you open a text file in Excel, the three part wizard asks several questions. On the third page of the wizard, choose Text for the relevant column.
  3. Always double check exported and imported dates. They can get messed up by Excel misreading the date columns.

Including Column Names in Export File

You may want to include the column names at the top of your export data file. If all of the columns in your list variable were text columns you could simply add a line at the top of the list variable and calculate each column value to be the column name. However, there will be times when the list has date and number columns, so you need a better solution.

One technique it to create a special column names row variable which has all character type columns and each column value in the row is the column name of the list variable which is being exported.

You then first export the column names row variable, and then the actual export list variable.

The following sample code demonstrates how this would be done.

; Create a list of classes in the current library.
Do $clib.$classes.$makelist($ref.$name,$ref.$classtype,$ref.$moddate) Returns List
Do List.$cols.1.$name.$assign('name')
Do List.$cols.2.$name.$assign('classtype')
Do List.$cols.3.$name.$assign('moddate')

; Prompt the user for a file name and location.
Calculate Title as "Save Export Records As"
Calculate cPath as "ClassesList.txt"
; FileOps.$putfilename(path[,prompt,filter,initial-directory,appflags])
Do FileOps.$putfilename(cPath,Title) Returns bContinue
If bContinue
   
   ; Proceed with exporting the data to the file.
   Begin reversible block
   
   Set print or export file name {[cPath]}
   Prepare for export to file {Delimited (tabs)}
   
   End reversible block
   
   ; Prepare a special row variable for exporting the column names.
   ; Loop the the export list's columns.
   Do List.$cols.$first() Returns rCol
   While rCol
      
      ; Add a column to the row variable.
      Do ColNamesRow.$cols.$add(rCol().$name,kCharacter,kSimplechar,1000)
      
      ; Set the value in the row to be the column name.
      Calculate ColNamesRow.[rCol().$name] as rCol().$name
      
      Do List.$cols.$next(rCol) Returns rCol
   End While
   
   ; Export the column names row.
   Export data ColNamesRow
   
   ; Add an empty line to the top of the export list.
   Do List.$addbefore(1)
   
   ; Export the list to the file.
   Export data List
   End export
   
   Close print or export file
   
   OK message (Icon) {Open the exported file using Excel or another spreadsheet program to check the contents.////[cPath]}
End If

Quit method kTrue

Click the Run Demo button in the StudioTips Browser window to try out the code.

Tip

One of the things you have to watch when including column names in the export file, is to not process them as data if you import the file. A technique I use for indentifying column names is to add XML style tags in the line before and after the column headings line.


<columnnames>
Column1Name Column2Name Column3Name ...
</columnnames>

Export Text to File

To export text to a file you can use the FileOps external object.

The following sample code export a string of text to a text file.

; Prompt the user for a file name and location.
Calculate Title as "Save Text as"
Calculate cPath as "TempText.txt"
; FileOps.$putfilename(path[,prompt,filter,initial-directory,appflags])
Do FileOps.$putfilename(cPath,Title) Returns bContinue
If bContinue
   
   ; oFileOpsExt.$createfile(cFile-path [,cFile-type,cCreator-type,bCreateres])
   Do oFileOpsExt.$createfile(cPath) Returns FlagOK
   If FlagOK
      
      Calculate Text as 'Hello World!'
      Do oFileOpsExt.$writefile(Text) Returns FlagOK
      
   End If
   Do oFileOpsExt.$closefile()
   
   OK message (Icon,Sound bell) {The text '[Text]' has been exported to the file.}
End If
Quit method kTrue

Click the Run Demo button in the StudioTips Browser window to try out the code.

Import Text from File

To import text from a file you can use the FileOps external object.

The following sample code imports the contents of a text file into a character variable.

; Prompt the user to select the file.
; $getfilename(path[,prompt,filter,initial-directory,appflags])
Calculate Title as "Select a text file to import"
Do FileOps.$getfilename(cPath,Title,'*.txt',cPath) Returns bContinue
If bContinue
   
   ; oFileOpsExt.$openfile(cFile-path [,bReadonly])
   Do oFileOpsExt.$openfile(cPath,bReadonly) Returns FlagOK
   If FlagOK
      Do oFileOpsExt.$readfile(Text) Returns FlagOK
   End If
   Do oFileOpsExt.$closefile()
   
   OK message (Icon,Sound bell) {The file has been imported into a character variable.////There variable contains [len(Text)] characters.}
End If
Quit method kTrue

Click the Run Demo button in the StudioTips Browser window to try out the code.

Find And Replace

The Find and Replace tool in Omnis Studio is a great help to developers. If you aren't already familiar with this tool you should get to know it. This section covers various topics related to the Find and Replace tool.

Use the shortcut key combination Ctrl/Cmnd+F to open the Find and Replace window.

The window defaults to a fairly small size. I recommend stretching the window to a fairly large size and resizing the first column in the log list so that it is about 10 cm (4 inches) wide. Also to be safe, check the Replace all in selected log lines only checkbox. After you do this right-click anywhere on the window and select Save Window Setup.

Tab one lets you enter the find and replace criteria. Tab two lets you select the library and classes, or multiple libraries which you wish to search.

After you enter the find and replace criteria, click the Find All button. The occurrences are listed in the log list.

You can double-click on any line in the log and Omnis Studio will open the appropriate IDE window with the item selected.

You can select any line(s) in the log list and with Replace all in selected log lines only checked click the Replace All button. The find value will be replaced with the replace value for the selected lines if the replacement value is valid.

Regular Expresssions

The Find and Replace dialog window has a check box for Regular Expressions.

Regular expression is a term from the Unix world and is often shortened to regexps.

If you are not experienced with using the regexps (like me) the following tips and examples will be helpful.

Most of us are familiar with using the * and ? wildcard characters when listing files at the DOS prompt or Unix command line.

With regular expresssions the meaning of the * and ? are different and the dot . is added to the mix.

The * character means any number of characters including none of the metacharacter that precedes the * character.

t* will find t, tt, ttt
t* will not find ta, tcp, tub

The . (single dot) character means any single character

ta. will find tab, tan, tab
ta. will not find ta

Combining the . and * together matches any number or any character.

t.* will find t, tt, ttt, ta, tab, tan, tap

If we want to find all the lines of code which use a $search within a $search the following regexps would do the trick:

search.*search

The $ character is a special character used by regexps so you need to escape the $ with a \ backslash character if you want to include the actual $ character in your regexps

\$search.*\$search

The | vertical bar character means or

tt|tan will find tt, ttt, tan, tank

This is only the tip of the iceburg of what you can do with regexps.

Advanced regexps

Square brackets [] give you extra flexibility in your searches.

gr[ae]y will find gray, and grey

li[ce]en[sc]e will find license, licence, lisence, licence

555-[0-9][0-9][0-9][0-9] will find phone numbers beginning with 555-

[Tt][Cc][Pp] will find TCP, tcp, TcP

You can use more than one range.

[0-5a-fA-F] will find any number between 0 and 5 or a letter A to F or a to f

The ^ character means begins with.

Note: If you are searching comment lines remember they begin with the ; character followed by 2 spaces.

^;..555- will find all the comment lines that begin with 555 (The dot . is a single character wildcard)

The $ character means ends with.

$-1212 will find all the lines that end with -1212

For more information on regexps check out
http://www.regular-expressions.info

$findandreplace

Omnis Studio has a $findandreplace class method.

$findandreplace(cFind,cReplace[,bIgnoreCase=kTrue,bWholeWord=kFalse,bRegExp=kFalse])

Note the 4th optional parameter, bRegExp. If you send bRegExp as kTrue Omnis will allow you to use the power of regular expressions (regexps) in your find and replace notation! This makes it possible to do some pretty crazy find and replace code and save yourself a ton of work and time if you need to do some extensive changes to existing code. (Always work with a test copy of your library!)

How about adding regexps intelligence to the correspondence module of an application you have written? With a little creative code you can use OMST's regexps engine for this feature. Here's how.

  1. Create a bogus object class.
  2. Copy the text you want to process to the $desc property of the object class
  3. Send a $findandreplace regexps message to the class.
  4. Copy the text back from the object class $desc property.

Click the Run Demo button in the StudioTips Browser to try it out.

Here's the demo code.

; Correct the spelling of the word 'license' in the following string.

Calculate InputString as 'license, licence, lisense, lisence'

; Copy the string to the $desc property of the object class.
Do $clib.$objects.oRegExps.$desc.$assign(InputString)

; Use regexps for the 'find' parameter
Calculate Find as 'li[cs]en[cs]e'

Calculate Replace as 'license'

; Send a $findandreplace message to the object class.
; $findandreplace(cFind,cReplace[,bIgnoreCase=kTrue,bWholeWord=kFalse,bRegExp=kFalse])
Do $clib.$objects.oRegExps.$findandreplace(Find,Replace,kFalse,kFalse,kTrue)

; Copy back the $desc property of the object class.
Calculate OutputString as $clib.$objects.oRegExps.$desc

Multiple Libraries

I recommend that you build your application using multiple libraries. It makes your code a lot easier to view because the code is broken into logical groups (libraries). Instead of looking at a list of all the schemas, or all the file classes, or all the window classes, or all the report classes, in your entire application, using multiple libraries you just view the classes in, for example, the Accounts Payable library.

There are just a few things to get used to/do differently with multiple libraries, but once you get those things set up, it's very easy to work with.

You need a main library which the users double-click on to open your application. In the $construct of the Startup_Task of the main library, you call a method ie. openLibraries, which the automatically opens your sub-libraries.

See the topic Directory Structure for a recommended way of organizing your sub-libaries in subfolders.

In my app, the main library only contains generic code. Superclass tables, superclass windows, superclass objects are all stored in the main library. That way if there is a problem with a superclass, I go straight to the main library and can always find it there. Commonly used menus, toolbars, and objects are also stored in the main library.

There's a couple things you have to watch with multiple libraries.

  1. Set the $defaultname property of every library. If the $defaultname is not set, the library name will change if you or the user changes the file name. Changing the library name will break multi-library dependent code.
  2. If you create a subclass in one library and then drag the class to another library, you must prefix the $superclass property of the class with the name of the library which contains the superclass. A better way to handle superclasses is to set the $issupercomponent property to kTrue and create the subclass through the Class Wizard. See the Super Components topic for more information.
  3. Set the $designtaskname property in all your windows, reports, objects to the main library startup task. In my application, the main library default name is 'CSP', so all my Window, Report, Table, Schema, object classes have the $designtaskname property set to "CSP.Startup_Task".
  4. If you need to access a window, report, or object class in a different library, the $external property of the class must be set to kTrue, otherwise it won't be visible in design mode and won't be accessible in runtime mode. Omnis Studio defaults new classes to $external=kTrue. You can set $external to kFalse if you don't want the class to be visible or accessible from outside of the library.
  5. Be aware of the current task instance ($ctask). Unless you need multiple tasks, just run your application under the main library's Startup_Task task instance... the one that automatically opens when the main library is opened.
  6. When you open sub-libraries from your main library check the Do not close others and Do not open startup task options. This will prevent the sub-libraries from having their own tasks. You can even go so far as to delete the Startup_Task class from your sub-libraries. To make my life easier for the developer I put a modified copy of the main library Startup_Task in each sub-library so that the main library Startup_Task task variables are visible to all the classes in each sub-library in design mode. In the sub-libraries I delete all of the Startup_Task methods and then add a $construct method with a single line of code which immediately closes the task instance. Do $cinst.$close()

Think of the task instance as a container of instances. When you open your main library it automatically opens up a task instance. When you install a menu from the task instance, the menu instance is contained in the task instance. When you use that menu to open a window (in the same library, or another library), that window instance is now also contained in the task instance. Being part of the task instance gives anything within the task access to the task variables. They are said to be in scope of the task.

If you are working in development mode on a window in a sub library and you Ctrl/Cmnd+T to open the window instance, you will find if you step through code that is run by the window, the task variables in the main library's startup task instance are out of scope. You can't access them. That is because the window instance was not opened by something which was contained in to the main library startup task instance. When you did a Ctrl/Cmnd+T to open the window instance the window instance was probably opened in the default task. Runtime users don't get the chance to do this, it's more when you are developing that you run into this.

Multi-libraries, I strongly recommend them! There's lots of advantages, very few disadvantages. Easy to do. See the sample code and demo in this section for Open Sub-Libraries.

Directory Structure

There are many ways to set up your directory structure for multiple libraries.

Here is a sample multi-library file structure which I use for my applications.

(> character is used for indenting)


MyApp (folder)

> open_MyApp.lbs

> libraries (folder)

> > MainLibrary.lbs

> > modules (folder)

> > > SubLibrary1.lbs
> > > SubLibrary2.lbs

The user double-clicks open_MyApp.lbs, its Startup_Task $construct method looks for a libraries folder within the same parent folder and then opens any libraries directly contained in the libraries folder. In this case, there is only one library, MainLibrary.lbs. The Startup_Task of MainLibrary looks for a modules folder within the same parent folder and then opens any libraries directly contained in the modules folder.

You could have other folders within the libraries folder that contain special utilities or demo libraries.

When the main library is closed, the $destruct method of its Startup_Task closes any libraries who's $pathname is within the main library's parent folder.

In the studiotips/demos folder there is a multi_library folder which gives you an idea of how you can set it up. Double-clicking on open_MyApp.lbs opens /libraries/MainLib.lbs which then opens a main window. The main window has an Open Sub-Libraries button which opens the modules and a window in each of the modules.

Click the Run Demo button in the StudioTips Browser window to open the multi-library demo. Feel free to experiment with the multi-library demo.

Open Sub-Libraries

This method is in the main library and is used to open all the sub-libraries. The sub-libraries are located in the modules folder which is located within the same parent folder as the main library.

Click the Run Demo method in the StudioTips Browser window and then look at the oOpenLibraries object class in the MainLib.

AutoUpdater

Tired of manually uploading new versions of your software to the server, telling users to download the updates, and then having users mess up on the relatively simply job of dragging the updated version from the server to their computer?

Here's a strategy that I use with my multi-library applications, that has relieved me of all of the above.

Rather than having the users directly open the main library of your application have them double-click on an AutoUpdater library. Place your application's libraries in a libraries subfolder. See the Directory Structure topic for a suggested folder structure.

When the user opens the AutoUpdater library on their local computer, it first checks a files server for any newer version libraries, downloads them to the local computer, and then proceeds with opening the main library. The main library's Startup_Task then automatically opens all the sub-libraries on the user's local computer.

I developed a utility called AutoUpdater which does all of the above plus the following extra features:

  1. Each time AutoUpdater is opened it compares the subfolder structure and files specified on the files server to its own subfolder structure and files and automatically creates any new subfolders and downloads any new files. Initially all you need on the user's computer is the AutoUpdater.lbs file in an empty folder. The first time AutoUpdater is opened it will automatically download all the subfolders and files from the files server.
  2. Allows the developer to easily upload newer version libraries to the files server as stable or beta release libraries.
  3. Allows the runtime user to switch from stable to beta release versions of the libraries.
  4. Support using FTP or a database as the files server.
  5. Users can be given a Check for Updates... menu option.
For more information visit
http://www.studiotips.net/docs/autoupdater/autoupdater.html

Speeding up Studio

If you are converting an existing Omnis Classic application to Omnis Studio you might be in for a surprise... your application will likely run slower in Omnis Studio than it did in Omnis Classic.

Studio gives you lots of neat new objects and features and it gives you an object-oriented programming development tool, but there is a price to pay.

This section provide information on things you can do in Omnis Studio to help your application run quicker.

If you know of additional things or have additional information please email me.

COMMENTS BY REG PALING (a snip from the List Server)

Occasionally in the past we've had a lister getting into a panic about performance of their new Studio app because they made a few design decisions which didn't work well on the first attempt, and with help from the list they've made a few adjustments and it has all been sweet.

So, with Studio you do have to design carefully to avoid creating structures that get bogged down in too many levels of processing. If you really need to create complexity, then don't be shy about demanding the fastest possible hardware to run it on. It's your choice as a designer: create simple structures and they will run fine on six-year-old hardware, or create complex structures which require a newer-generation machine.

For those who have simple applications, the conversion from O7 to Studio can be fairly easy. For those with complex applications in O7, think about how much time and effort you applied to fine-tuning your application, but then don't expect that same fine-tuning is going to work well in Omnis Studio - maybe the opposite, and you'll have to put in double effort to dismantle all that work and then re-do it.

All in all, it's worth the move, and we just need to unlearn some habits and learn a few new ones. Cheers, Reg.

Complex Grids

There have been complaints about the wave effect redraw of complex grids. You can do almost anything with a complex grid, but as the name states, they are complex. Each line of a complex grid is in effect a separate instance. A 100 line complex grid has 100 line instances. Each instance can have exceptions for each field inside of it.

I use complex grids whenever I want the user to be able to edit data directly in a list. (Purchase Order items, AR and AP Invoice items, etc.) Fortunately, the complex grids in my application are generally under 10-40 lines so I haven't hit the slow redraw wave effect that others complain about.

If you are using 100+ line complex grids with lots of exceptions and $event methods you might get frustrated with complex grids.

Max Cached Classes

In the Omnis Studio Preferences , there is a property $maxcachedclasses. I believe Omnis defaults it to 100. Bump it up to 200 or higher. You can experiment with different settings.

Before Omnis added this property my application would run slower and slower the longer I used it until finally I'd have to quit and restart Omnis Studio. If you find your application running slower the longer you run it ... there's a good chance you need to bump $maxcachedclasses up. To set this for runtime users I set it using notation in the main library's Startup_Task.

There is no clear answer on the best setting for this property. The default is 100. At the time of writing this tip I have set to 200 in the Startup_Task of my main library to make sure runtime users of my application are automatically set to 200.

Do $prefs.$maxcachedclasses.$assign(200)

Setting $maxcachedclasses to some wildly high number is not advisable as it could start to hamper performance.

Advice from Tech Support

Some developers have noticed that issuing $definefromsqlclass causes a sizeable performance hit.

At present we are unable to fully explain exactly what is happening here. Our thought is that this may be connected to class caching. In that the SQL classes are not in the cache and must be read from disk. This may mean that you need to adjust $prefs.$maxcachedclasses to remove this problem. We have not seen the problem and therefore this may not be the case.

I include the details on the sys functions that may be used to analyse the caching performance.

sys(190) returns the number of times Omnis has loaded a class from disk and added it to the memory class cache.

sys(191) returns the number of times Omnis deleted a memory class cache entry, when it added a
class to the cache, meaning that the class deleted from the cache may need to be reloaded.

sys(190) and sys(191) provide information you can use to monitor the impact of changing the $maxcachedclasses preference. Too low a value for this preference may result in a performance hit, due to too many classes being repeatedly loaded from disk.

OmnisSQL

The Omnis data file is a helpful SQL learning tool, it's free, and it works good, but don't expect it to work like Oracle, MySQL, or FrontBase.

OMNISSQL will bog down if you include too many arguments in your SQL select, include an ORDER BY, or try join too many tables.

If you are running into speed issues with the Omnis data file switch to using a real RDBMS. At the time of writing this FrontBase is free so why not download and install it and enjoy all the benefits of a real RDBMS. (primary and foreign key integrity, commit and rollback, etc.) It might take a few days to adapt to a real RDBMS but the benefits are tremendous.

Subwindows

If you load up a window with 100+ fields using tab panes and/or page panes opening the window will seem to take forever. The reason is that the Omnis Studio supports multiple window instances, superclass/subclass structure, a $construct for every field in the window, etc. All that neat object-oriented programming stuff comes at a price.

The solution I found is to use tab panes and page panes. Each layer of the tab/page pane has an empty subwindow field set to kEFposClient and the $classname left blank. Opening a window like this is very fast. :-)

Using notation you $assign the $classname for the tab/page panes only as the user requests to do so. So in reality tab/page pane 1 will have its $classname set when you open the window. When the user clicks on a different tab you intercept the click and set the $classname.

; $event method of tab pane with empty subwindows on each pane.
On evTabClicked
Switch pTabNumber
   Case 2
      Do $cinst.$objs.TabPane.$objs.SubWin2.$classname.$assign('wSubwindowNameB')
   Case 3
      Do $cinst.$objs.TabPane.$objs.SubWin2.$classname.$assign('wSubwindowNameC')
End Switch

This stratgy keeps your window classes light and flat. Using subwindows also allows you to reuse the same window in many places. I have a single headed list subwindow that is instantiated for virtually every headed list in my application ... it's properties are assigned notationally on the fly.