Tips   >   Misc   >   Misc (All Contents)
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
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 ; it can be pointed to the class method of any type of class.
Do code method [$clib().$name].wWinClassName.MethodName (pParam1,pParam2)
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.
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.
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 .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:
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.
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
To delete an instance variable from a class, you right-click on the variable in the variables pane and select
in the context menu. As long as the variable is not used in any code, Omnis Studio will prompt you with a 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. 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 variables pane to the variables pane. For some reason, once you've done that Omnis Studio allows you to delete the class variable.The $desc property is found is several different location in Omnis Studio:
The class desciption can be modified by selecting the class in the $desc property.
> pressing F6 > selecting the tab and clicking on theThe method description for public methods (prefixed with $ character) can be viewed/modified through the . In the right-click on a class > select . At the bottom of the click the 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 button to find out the maximum length of each $desc property.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!
You can only redirect to a method of the exact same name.
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.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.
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/downloadsI 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:
X250825050 ("SHELL32.DLL","ShellExecuteA","JJCCCCJ")
X250825051 ("SHELL32.DLL","ShellExecuteA",0,"open",FilePath,"","",0) Returns HINST_EXPLORER
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
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.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)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
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
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
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 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.df1 file located in the studio folder to the same location in your newer version of Omnis Studio.
sessions or sessions also copy theBe 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.
As of Omnis Studio v4.2 on Mac OS X to get to the studio folder you will need to:
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]
There are two ways a method can return a value to the sender.
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.
The advantages of using field reference parameters to pass values back to the sender are:
The disadvantages of using field reference parameters to pass values back to the sender are:
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
The
is where you get window and report objects when designing windows and reports.You can modify the existing classes and design objects in the
, or add your own objects and classes. You can even add your own class group/icon to show up in the 's first level toolbar.To modify the
you need to get inside of it.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 button in the .
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.
With the Component Library open in your :
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
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 .The duplicate object will have the same tooltip name in the
, that is the type of object which it is, so you can only tell them apart by their order in the .To test what you've done.
Congratulations you just modified the _Report Components, etc.
to your own preferences! You can do the same thing forWhen a new version of Omnis Studio is released, it's a hassle saving and moving your previous 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.
objects to the newer version. Be careful on how much time you invest into modifying existing objects in theWith the Component Library open in the :
To test what you've done.
To add your own Design objects (window or report objects) to the
.With the Component Library open in the :
To test what you've done.
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.
You can add your own Group to the
, so that it will show up with it's own icon in the 's main toolbar.With the Component Library open in your :
To test what you've done.
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 $superclass property to include the source library name, and then go back to the source library and delete the extra copy.
, and then name it. But then have to drag the subclass to the destination library, change theSupercomponents 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:
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.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
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
When importing multiple columns of data, I generally use a spreadsheet program like
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
columns.There are some things you need to watch out with when importing data from text files saved from an Excel spreadsheet:
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
button in the window to try out the code.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>
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
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
The
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 tool.Use the shortcut key combination Ctrl/Cmnd+F to open the
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
checkbox. After you do this right-click anywhere on the window and select .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 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.The
dialog window has a check box for .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.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.infoOmnis 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.
Click the
button in the 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
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
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.
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 .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 button which opens the modules and a window in each of the modules.
Click the button in the window to open the multi-library demo. Feel free to experiment with the multi-library demo.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 method in the window and then look at the oOpenLibraries object class in the MainLib.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 libraries subfolder. See the topic for a suggested folder structure.
library. Place your application's libraries in aWhen the user opens the Startup_Task then automatically opens all the sub-libraries on the user's local computer.
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'sI developed a utility called
which does all of the above plus the following extra features: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.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.In the Omnis Studio $maxcachedclasses. I believe Omnis defaults it to 100. Bump it up to 200 or higher. You can experiment with different settings.
, there is a propertyBefore 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 Startup_Task.
up. To set this for runtime users I set it using notation in the main library'sThere 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.
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.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