Tips_todo   >   Misc   >   Misc (All Contents)
Hey, everything needs a "Miscellaneous" category! This section is the odds and ends that didn't fit in the other categories.
Some good stuff in here - none the less.Any expression in Omnis will evaluate to true or false (or NULL) when used in boolean logic, so there is no need to compare the expression to kTrue or kFalse.
I'm still trying to catch on to using Boolean logic in my code. Reg helped me out with the above tip, and examples as follows.
Instead of '$ref.$selected=kTrue', use '$ref.$selected'
Instead of '$ref.$selected=kFalse, use 'not($ref.$selected)'
If you want to reverse selection:
Instead of 'Calculate $ref.$selected as pick($ref.$selected=kTrue,kTrue,kFalse)'
use 'Calculate $ref.$selected as not($ref.$selected)'
You can break into your code at any OK message.
Wintel: Press the "Pause" key.
Mac: Press Option+Command+Delete
This is hugely helpful for debugging. Slap in some helpful OK messages where you are trying to solve a problem in your code. Run the code as many times as you need. Any time you want to break into your code to start stepping through it, press the correct key combination and bingo, you are into the method editor at the location of the OK message!
I must have been sleeping when they taught this trick in Omnis 101! What an amazing revelation for me to find out about this. Try it out yourself by clicking the "Run Demo" button.
Many thanks to Reg Paling for this tip!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" but can be pointed to any class method of any type of class.
So for example, in StudioTips I have a method called "setClassVariable" in the window class "wPreassignVariables". I can run the class method with the following line of code:
Do code method [$clib().$name].wPreassignVariables.SetClassVariable ('cName',Value)
Now when the window opens the the class variable value for "cName" will be [Value].
NOTE: The object-oriented programming police might give you a speeding ticket for doing this. :-)
Thanks to Reg Paling for this tip!Omnis allows you to change the font sizes in most of the IDE (Interactive Developer Environment) windows. This can be especially help if you are doing a presentation.
To change the font size of the code editor, methods treelist, or properties list, etc.
Click on the field so it has the current focus, then:
Ctrl/Cmd+] to increase the size.
Ctrl/Cmd+[ to decrease the size.
Ctrl/Cmd+] to increase the size.
Ctrl/Cmd+[ to decrease the size.
STRG+* to increase size (German keyboard layout)
STRG+† to increase size (German keyboard layout)
To save the font size for the IDE window, Ctrl/Cmd+S.
Thanks to Michael Lange for the German-Keyboard-Layout tip.
If you know of other language keyboard font size changes, please email them to me.Here's a unique tip submitted by Laurent Chaudoreille.
Calculate #F as sys(4019)
This opens a dialog with an object browser that lets you examine properties, functions, events... of each object in Studio.
Press the "Run Demo" button to try it out.If you ever need to print checks, you'll need to convert the dollar value of the check to text.
Included in StudioTips is an object class called "oConvertNumberToText".
You can send this object any 2dp number from 0.01 to 999 million and it returns an english text string of the number ready to print on the check.
Feel free to copy "oConvertNumberToText" to your own application.
With modifications you could get it to print in other languages as well.Here's a handy trick I noticed Jim Pistrang use in his "Studio does SQL" presentation at Euromnis 2000. If you want an "optional breakpoint" in your code use the following:
If #SHIFT
Breakpoint
End If
If you aren't holding the shift key, the code executes without stopping. Hold the shift key and you hit the breakpoint.
Great for debugging and for code demo presentations!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, Studio will prompt you with a "Delete variable No/Yes" dialog. In some rare situations, 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. (Some sort of minor Studio bug) One trick for getting rid of a stubborn instance variable is to drag it from the instance variable list in the variables pane to the class variables tab in the variables pane. For some reason, once you've do that 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 F2 Browser > pressing F6 Property Manager > selecting the General tab and clicking on the $desc property. If you have your F2 Browser set to "Detail View" you can also view and edit the description in the F2 Browser headed list.
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.
The method description for private methods exists but is not accessible through the Omnis Studio IDE. You can access the $desc property through Code Documentor, or using notation.
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.I asked Tech Support if there was any difference between the developer, runtime, and web server version so Omnis Studio. The following is the response I received from Tech Support.
They are not the same. The runtimes contain less resources in order to have a smaller footprint.
A developer installation is 52 megabytes in size versus a runtime which is 13 megabytes and the web server is in between.
Technically you can serialize a developer as a runtime but you would not be able to do the opposite since the runtime does not contain debug resources.This obsure little command has an interesting feature which I discovered while working on the context menu for the treelist in StudioTips. In the StudioTips treelist the context menu calls are directed back to the treelist field's methods. For the $openBookmark method I wanted to pass the call to the class method of the same name in the wTips_Treelist window. I used the following line of code:
Do redirect $cinst
If $openBookmark is called from the Tips menu, the Tips menu calls $openBookmark in the StudioTips Browser window. $openBookmark in the browser window has the following line of code:
Do redirect irSubWinTreelist ;; an item reference to the Treelist subwindow field
The interesting feature I discovered was that I did not have to declare any parameters. Do redirect passes the incoming parameters without declaring them. COOL! (At least I thought it was.)
Note: You can only redirect to a method of the exact same name.When you install a newer version of Omnis Studio you can avoid having to reenter the serial number.
Copy your old Omnis.cfg file to the new Omnis Studio > Studio folder. Omnis Studio should start up like a champ.
Thanks to Scotte Meredith for this tip!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 evali(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.
To keep file confusion to a minimum, after your are done testing:
1. Rename the omnisloc.df1. e.g. German_omnisloc.df1.
2. Copy the original omnisloc.df1 and rename the copy to omnisloc.df1.
You now have 3 files. Each time you need to test a language, toss the current omnisloc.df1, copy the omnisloc.df1 of the language you want to test and name it omnisloc.df1.
The English, French, and German versions of the omnisloc.df1 files are in the studiotips_stuff >$prefs.$maxcachedclasses
Way back in 1999 with Studio version 1 engineering added this property to save my application from disaster. The problem I was running into was as more and more windows opened the application would get slower and slower.
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.
The following is advice from Tech Support on this issue:
At present I am unable to fully explain exactly what is happening here. Our thought is that this may be connected to class caching. In that the sqlclasses are not in the cache and must be read from disk. This may mean that you need to adjust $root.$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 aI 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:A word of caution about the 'Optimize method' command. I, among other 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 Studio when I added it to a method which included 12 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. I'll focus on writing more efficient code.To rename the Omnis application to include its version number, so you can distinguish it lists and menus from other running versions of Omnis is easy in Windows and Mac OS 9. You simply rename the Omnis Studio application file.
If you try that in Mac OS X when you launch Omnis Studio you'll see a zoom rectangle, but then nothing happens.
With Mac OS X there are a few more steps you need to do.
1. Quit the Omnis Studio application if you have it open.
2. Rename the Omnis application file name. (e.g. Omnis > Omnis 3.2 dev)
3. Right-click on the Omnis application file and select > Show Package Contents.
4. In the window that opens, open the Contents folder.
5. Open the Info-macos.plist file. (Use TextEdit or BBEdit.)
6. Near the bottom of the file you will find:
7. Change Omnis to the renamed file name, save changes and close the file.
8. Open the MacOS folder in the Contents folder. Rename the Omnis file to the renamed file name.
9. Close the package windows.
10. Double-click the renamed Omnis application file. Your new name will show up in the dock and in the top menu bar.
Thanks to Kelly Burgess for this tip!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 notationally as follows:
Do $prefs.$showsystemfocus.$assign(kFalse)If you use a lot of calls and subclass to objects somewhere along the line you may hit the dreaded "Too many methods ...." error message. There a couple things you can do.
1. Increase the $stacklimit using the following line of code in your Startup_Task.
Do $root.$modes.$stacklimit.$assign(14)
The above line increase the stacked methods limit from default 10 to 14. Don't raise it too high. My application won't run on the default of 10.
2. Use "Do method" rather than "Do $cinst.$MethodName" for calling methods within the same class. "Do method" does not add to the stacklimit, "Do $cinst.$MethodName" adds to the methods stack.
Note: "Do method $cinst.$MethodName" also adds to the method stack.
Ever wish you could use arguments in a Switch/Case? You can!
Mark Phillips came up with an interesting twist on the use of Switch/Case.
Calculate Value as 10
Switch kTrue
Case Value<1
do something
Case Value<50
do something else
Default
do something for 50 or more
End Switch
________
David Swain added a few more comments to the thread.
Switch kTrue
Case Value<10,Value>50
OK message {Either less than 10 or greater than 50}
Default
OK message {Default case}
End Switch
The case now simply excludes values between 10 and 50 inclusive. The Case command allows us to
include multiple cases combined by OR logic, but only one Case block will be executed in a pass through the Switch master block.
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.
The problem is the old testing for NULL confusion. (See "Trapping NULL")
If not(Ref) ;; Will give you a problem if the reference is NULL.
If not(Ref) ;; Won't trap an "Unset Reference".
An interesting solution, thanks to Jason Bye, is to test the $ident property of the reference.
Click the "Run Demo" button to step through the TEST CODE below.
If not(Ref.$ident)
NULL or Unset Reference.
End If
Trapping NULL, Blank, or Zero is a nasty thing to do cleanly in Omnis.
One might expect the following If statement to catch the 3 possibilities:
If isnull(VAR)|VAR=''|VAR=0 ;; but it doesn'twork.
If VAR=NULL, then the 2nd test, VAR='', is testing NULL=''.
Omnis can't evaluate NULL='', and therefore it can't properly evaluate the If statement.
Switch/Case doesn't help much either.
The difficulty with many solutions is that they may work if VAR=Character but fail if VAR=Numeric
(or visa versa)
Here's a bullet proof, one liner test for NULL, blank, or zero!
If pick(isnull(VAR),VAR,'')=''
OK message TRUE {NULL or Empty (or Zero for numbers)}
Else
OK message FALSE {[VAR]}
End If
Thanks to Bob Preston for the, If pick(isnull(VAR),VAR,'')='' , solution! You have restored my sanity :-)
To avoid repeating the { If pick(isnull(VAR),VAR,'')='' } If statement all over your code, you may want to 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)
If pick(isnull(pfVariable),pfVariable,'')=''
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(Variable)
; action if null blank zero
End If
or test the reverse
If not(fn.$isNullBlankZero(Variable)
; action if not null blank zero
End If
Try to reserve the return value for kTrue and kFalse, indicating whether or not the method executed correctly.
In order reserve the return value for kTrue or kFalse, you need to use 'Field Reference' parameters to 'return' the value.
The downside on using Field Reference parameters are:
1. You must first calculate the value to a Field, before sending it to the method. (Rarely a problem)
2. You can not use Field reference returning methods as 'in-line' methods.
The following code demonstrates the 2 styles.
The code in the 2 methods being called is as follows:If you use an eval() function in your code and there is an error in you 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().
For example:
Calculate string as 'midx(#S1,1,3)'
Test for valid calculation{ evalf(string) }
#ERRCODE will be 835 (not sure what constant this maps to), and #ERRTEXT will be something like
"0:4:Unrecognized variable name, item name or attribute"
Set $firstsel/$lastsel to those values at the front of the string to highlight the erroneous part of the calculation.
Thanks to Kelly Burgess for this tip!In a word ... DON'T USE THEM (Okay, that was 3 words) Exception: (There's alway one) See "Error Handler" topic in this section.
When I started learning 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. He was right. "Anything you can do with a code class, you can do better with an object class."
Code classes are a slippery slope that draw you away from object-oriented programming, and back into Omnis Classic style coding. (There is a difference.) Omnis software 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.
PROBLEMS WITH CODE CLASSES
Code classes don't extend instances. I discovered this when in a code class method I defined a List 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!!
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.
Public methods start with $
Private methods do not start with $
SUMMARY
Don't use 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 will seem like a pain at first. But stick with them, it is worth the effort. Object classes will provide more powerful and elegant solutions.I added Craig's Error Handler code to StudioTips so that you can use StudioTips to check for notation errors in any of your libraries.
A menu item has been added to the "Tips" menu to Load/Unload STP' error handler (all libraries.)
If you always have StudioTips open when you are developing, you can use STP' Error Handler to trap errors for you. Feel free to modify the "cErrorHandler" code class to suit your needs. Let me know if you want any enhancements to the StudioTips Error Handler.
I had some problems getting the StudioTips error handler to trap bad notation in other libraries.
Tech Support looked at the problem and recommended adding a "$" prefix to the code class method name. This appears to have fixed the problem.
Normally when you receive a new version of StudioTips, the Tips menu error handler item will read "Load Error Handler", meaning that it is currently "Unloaded". If you select the menu item the error handler will be loaded and the menu item will change to "Unload Error Handler". StudioTips remembers the last "state" of the error handler when you close and reopen StudioTips.
I ran into quite a number of "bug reports" from developers who were using the error handler. Some of these "bugs" were related to notation errors in OMST's code. Tech Support confirmed that there were some notation errors. The solution was to add some exceptions to the error handler in StudioTips.
The Error Handler certainly helps you write "clean" code. If catches you if you try call a method that does not exist.
STUDIOTIPS ERROR HANDLER DEMO
To test the error handler try the following:
1. Tips menu> select Load Error Handler. (If it isn't already loaded.)
2. Tips menu > select Programmer Test Method. This takes you to the "Breakpoint"
3. In the method editor IDE, Stack menu > select "Clear Method Stack".
4. Add code after the breakpoint, so the Programmer Test Method > $event method code reads as follows:
------- START CODE ---------
Consider anything below this line to be your "code scratch pad".
Breakpoint
Do $cinst.$CallMethodThatDoesNotExist()
Quit method ;; This line is for safety to prevent accidental execution of the Programmer Test Method.
------- END CODE ---------
The line "Do $cinst.$CallMethodThatDoesNotExist()" is obviously "Bad Notation"
5. Tips menu > select Programmer Test Method, take you to the "Breakpoint" again.
6. Click the running man icon in the top left corner of the method editor.
7. The Error Handler should trap the "Bad Notation". If it doesn't trap the error close the TIPS library and reopen TIPS. Repeat step 5 & 6. I haven't quite figured out what causes the error handler to miss errors. Sometimes it will catch an error on the first pass, then later when you rerun the code with the same error it occassionaly skips reporting the error a second time.
8. When you hit the error message, read it, then click OK. You will be at a "Breakpoint" in the error handler. Use the "Stack" menu to get back to the "Bad Notation". Select "Tips.mTips/Programmer Test Method/$event" in the Stack menu.
9. This takes you to the offending code. The grey arrow is where you would reenter the method from the Error Handler. The line above the grey arrow is the offending line. In a normal situation you would fix the code, then double-click on the line above the grey arrow, and retest the line.The F3 Components Store is where you get window and report objects when designing windows and reports. The Component Store also has a host of wizards and classes which you can drag and drop into your library to speed up the design process.
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. Press F3 to open the Component Store.
2. Right-click anywhere on the Component Store > select "Show Component Store in Browser".
3. Press F2 to open the Libraries browser.
4. Double click on "Component Library" in the Libraries Browser get inside.
Now that you are inside the Component Library, there are a number of different things you can do. Read the other topics in this section for further instructions.
When you finish making your modifications to the Component Library:
1. Press F3 to open the Component Store.
2. Right-click anywhere on the Component Store > uncheck "Show Component Store in Browser".
TERMINOLOGY: "Component Store" is what you see when you press F3. "Component Library" is what you see in the F2 Browser after you've checked "Show Component Store in Browser".
GOTCHA: 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.
SIDE NOTE: According to Pages 347-349 in the "Using OMST" manual (v2) you are able to add your own classes and class components to the Component Store. However, there is one important tip missing from the manual. After you add your own class, you must right-click on the class, select "Component Store Type..." and then set the class to "Template" or "Design Objects".
Thanks to Andreas Pffiefer for the tip! (The manual had me stumped for quite a while.)With the Component Store open in your IDE Browser: (see "Component Store" topic)
1. Drag and drop any class from your own library onto the Component Library.
2. Press F6 to open the Property Manager.
3. 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. Press F3 to open the Component Store.To add your own Design Objects (Window or Report objects) to the Component Store. (For this example we'll add our own set of window class objects.)
With the Component Store open in your IDE Browser: (see "Component Store" topic)
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. Press F3 to open the Component Store.
2. Right-click anywhere on the Component Store > uncheck "Show Component Store in Browser".
3. Open any window in design mode.
4. 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.
I recommend "Adding your own Design Objects" vs. "Modifying Existing Components". When Omnis Studio releases a newer version of Studio, you simply copy and paste your own field components class(es) from the Component Store in the older version to the Component Store in the newer version. Doing this is a lot easier than hunting through the "_Field Components" and "_Report Components" class and copy/pasting the individual objects from the older version to the newer version.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 Store open in your IDE Browser: (see "Component Store" topic)
1. Add/Create a new class in the Component Store. (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 "/" 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. Press F3 to open the Component Store.
2. Right-click anywhere on the Component Store > uncheck "Show Component Store in Browser".
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 class(es) should show up in the Component Store with their own icons and tooltip names.
Thanks for this tip go to: P-J Nefkens , Daniel SananŽs, and Geir Fj¾rli.With the Component Store open in your IDE Browser (see "Component Store" topic)
1. Click on the Headed list title 'Name' a couple times until the "_Field Components" window class sorts to the top section of the list.
2. Double click on 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 property 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 eachother in the Component Store. NOTE: 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. Press F3 to open the Component Store.
3. Right-click anywhere on the Component Store > uncheck "Show Component Store in Browser".
4. Open any window class in design mode, press F3, drag and drop the object you modified and confirm that the change(s) 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., etc.
GOTCHA: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 'Create subclass'. Name it. But then have to drag and copy the subclass to the destination library. Then go back to the main library and delete the extra copy.
Supercomponents solves the problem and makes it REALLY easy!
All you do is set the "$issupercomponent" property to "kTrue" for the superclass. You don't even have
to open the Component Library and copy your class into the libary!
Then press F3 to open the Component Store, click on the respective class in the Component Store
toolbar and there is your supercomponent! You can then drag and drop this supercomponent into any
library and 'Shazam!' just like magic, Omnis gives you a subclass of the superclass ready for you to name. Wow! Way to go Omnis!
To demonstrate this right now:
1. Press F3, click on the windows icon in the Component Store toolbar.
2. Notice the StudioTips icon? Hold your cursor over it, see the tooltip. "wBaseDemo".
3. Press F2, Double-click the TIPS library. Press Ctrl/Cmnd-A to to show all components.
4. Click on the wBaseDemo window. Press F6 to show the window properties.
You can see the "$issupercomponent" property has been set to "kTrue". You can also see that the "$componenticon" has been set to "9090", the icon ID for the StudioTips icon. "wBaseDemo" is the superclass window for the demo windows in StudioTips.Addition of this tip was recommended by Chris Banford after a list server discussion on importing text from a file.
Importing data in Studio is real easy and extremely fast. You can import any data into a list variable. You simply define the list columns to match the order of the data you wish to import, then tell Studio to import the data.
If you are importing a text file, you can set up a list variable with a single column, and import the file to list. Each line in the text file becomes a line in the list variable. I'm pretty sure the blank lines are skipped.
You can then loop through the list and process the lines extracting the data you need.
When importing multiple columns of data, I generally use a spreadsheet program like Excel to review and cleanup the data. Then 'Save as...' text format file type, which is Tab delimited.
You can then import the text file as tab delimited into your list defined to match the Excel columns and then process the list to add the data to your data file.
Note1: Earlier versions of Excel (4.0) only handles 255 characters per column and doesn't keep carriage returns. I haven't tested this on later versions of Excel.
Note2: (by Craig Lewis)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. Of course, this can be fixed with programming after import--and should be. I usually use: Calculate Company.PostalCode as jst(ImportedPostalCode,'-6p0')
Note2b: (by Richard Ure) There is a valuable feature in Excel which counters its propensity to strip preceding zeros. It involves formatting the column(s) 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.
Alternatively, if you are in the habit of copying from an Omnis screen report and pasting into an open Excel worksheet (kTabs, conveniently
provided by Omnis), make sure the relevant column is formatted to text before doing the paste.
Note3: Always double check exported and imported dates. They can get messed up by Excel misreading the date columns.
OBJECT CLASS 'oDataImportExport'
In my own application, I created an object class called 'oDataImportExport' which handles all my importing and exporting of data. It has public methods called $exportData and $importData. You send the object class method the right parameters and it does the work for you.
See the methods $exportData and $importData for further details.
If you are importing strings of data which need to be parsed, take a look at the strtok() function.
I used strtok() in the 'oFunctions' object class. The methods $retListFromCSVString and $retListFromCSVMultilineString both use the strtok() to parse Comma Separated Values and add them to a list. You can take a look at those methods in this TIPS library.
The sample code below shows the $CSVStringReturnList method.
There are a number of string functions which might help you with parsing data.
Press F9 to view the Catalog, click the Functions tab, select 'String' and to view the available string functions. Looks at Functions > String functions for explanations and demos.$findandreplace(cFind,cReplace[,bIgnoreCase=kTrue,bWholeWord=kFalse,bRegExp=kFalse])
You can use the $findandreplace method on a class.
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.
Square brackets [] give you extra flexibility in your searches.
gr[ae]y will find gray, and greyli[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 -1212For more information on regexps check out http://www.regular-expressions.info
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, tankThis is only the tip of the iceburg of what you can do with regexps.
In Omnis Classic cross platform fonts were accomplished using the #WIRFONTS, #MARFONTS, and #WIRFONTS, #MAWFONTS tables. In Studio, Omnis introduced the #STYLES tables. #STYLES is a much better way system for handling cross platform fonts.
Using #STYLES allows you to effortlessly change an entire group of window or report fields throughout your library.
In this section I give advice on what I've learned about fonts and #STYLES while developing my own application.The Omnis Studio help states that #STYLES supercedes #WIxFONTS, #MAxFONTS.
#STYLES is better because it not only allows you to specify different fonts for different platforms, but it also give you full control over font size, font style, etc.
Once a style is declared and used in your library, any text which is assigned $fieldstyle is mapped
to the row # in the #STYLES table, not the style name! If you change a style name in the table,
the style name of ALL the text in your library which is mapped to that row will immediately change its $fieldstyle name to match the #STYLES style name. (Try it, it's harmless to do, just be sure to change it back.)
If a text related object with a $fieldstyle is copied into your library, Omnis tries to match its
name to the existing styles in the #STYLES table, if it finds a match, the $fieldstyle is then mapped to that row. If it doesn't find a matching style, Omnis then automatically adds the new style to the next available line in the #STYLES table.
GOTCHA: If you don't have any styles in your library, your styles table will be built in the order that you drag components in. Example: You have empty #STYLES tables in Library A and B. In Libary A and you first drag in a button object, #STYLES row 1 will be OMNISbutton. In Library B you drag in an entry field first, row 1 will be OMNISfield. As you can see, that can get messy if you decide later decide you want the same #STYLES table in all your libraries and you drag a brand new styles table into your library.
If you drag #STYLES from Library A to Library B, the fieldstyles which were mapped to #STYLE row 1, will still be mapped to #STYLE row 1. The $fieldstyles in your library are mapped to the Row#, not the name.
COMPONENT STORE #STYLES
The Component store has a number of fieldstyles already set by Omnis. They are:
1. OMNISbutton - Standard Pushbutton - MS SansSerif 9 (Win) Geneva 10 (Mac)
2. OMNISlist - Standard List - MS SansSerif 9 (Win) Geneva 10 (Mac)
3. OMNISfield - Standard Field - MS SansSerif 9 (Win) Geneva 9 (Mac)
4. SansSerif9 - MS Sans Serif 9 (Win) Geneva 10 (Mac)
5. OMNISweb - Web Component - Arial 9 (Win) Geneva 9 (Mac)
I'm not sure why OMNISfield uses Geneva 9 for OMNISfield and Geneva 10 for the others. In my own styles table I've changed it to Geneva 10.
CREATING YOUR OWN #STYLES
I would strongly recommend that your own #STYLES table includes these 5 styles as the first 5 styles in the exact same order as in the Component Store. If you don't include them, they will be added automatically (randomly) to your #STYLES table as you drag new components from the Component Store into your library. (If you are starting your own #STYLES table you can drag the #STYLES table from the Component Store into your own library.)
You should think through and set up your #STYLES table very early in developing your application. Otherwise you'll be going back doing a lot of extra clean up work later on.
Don't use font names for your style names. Set up separate styles for windows and for reports. Fonts that work well for windows, don't necessarily work well for reports, so keep them separate.
You might want a specific style for titles in windows, another style for Company Name, another style for text in windows. For reports you might set up styles for Company Name, Title Subtitle, report text, Micro text, correspondence text... you get idea. To make style easy to recognize Window styles (other than the 3 Omnis styles) could be prefixed with lower case w, Report styles could be prefixed with lower case r. Don't get carried away and declare more styles than you need. Don't create too many styles that enforce colour or justification. (You'll have too many $fontstyles)
Suggested styles are:
Component Store #STYLES:
1. OMNISbutton - Standard Pushbutton (Mac:Geneva 10, Win:MS SansSerif 9)
2. OMNISlist - Standard List (Mac:Geneva 10, Win:MS SansSerif 9)
3. OMNISfield - Standard Field (Mac:Geneva 10, Win:MS SansSerif 9)
4. SansSerif9 - MS Sans Serif 9 (Win) Geneva 10 (Mac)
5. OMNISweb - Web Component (Mac:Geneva 9, Win:Arial 9)
Additional Styles:
6. OMNISlabel - Standard Labels (Mac:Geneva 10, Win:MS SansSerif 9)
7. wCompanyName - window text in the style that best suits the company
8. wTitle - normal window title text (Mac:Charcoal/Chigaco 12, Win:Arial 10)
9. wBIGTitle - very large titles text (Mac:Helvetica 20, Win:Arial 14)
Report Styles:
10. rText - regular report text (Mac/Win:Helvetica 10)
11. rMediumText - slightly smaller report text (Mac/Win:Helvetica 9)
12. rCorrespText - report text for correspondence (Mac/Win:Times New Roman 12)
13. rSmallText - report text for tiny header or footer text (Mac/Win:Helvetica 6)
14. rTitle - report text for report titles (Mac/Win:Helvetica 12 Bold)
15. rBIGTitle - report text for BIG titles (Mac/Win:Helvetica 18 Bold)
16. rSubtitle - report text for for the subtitles (Mac/Win:Helvetica 10)
17. rCompanyName - report text in the style that best suits the company (Mac/Win:Helvetica 12 Bold?)
18. rSpacer - RED report spacer (Red color makes spacer fields stand out on the report)
19. rMicroSpacer - RED report spacer (Mac/Win:Helvetica 3)
Note: I try to make each style name unique within the first few characters. That way when I open
the text $fieldstyle drop down menu in the property manager, I just have to type 'rsm' to go to 'rSmallText'.
By using styles, you can very quickly change the look of reports and windows in your entire application to suit different clients and applications.
To set up styles, press F2, Command/Ctrl+Shift+Y to show the system classes in your library, then double-click on the #STYLES class. The interface is intuitive and the Omnis documentation is clear, so I won't get into more detail.
See 'Cross Platform Fonts' for recommendations on what fonts to match between Mac and Wintel.If you haven't guessed already, these stand for Windows REPORT Fonts and Mac REPORT Fonts. These table are the way cross platform fonts were handled before Studio introduced #STYLES.
You can leave these tables empty if you are exclusively using #STYLES in your application. Removing all the fonts from these tables, will force you to use styles. Maybe a good way to discipline yourself to use styles. (If wish I did, it would have saved me a lot of rework)
IF YOU DON'T USE STYLES THEN READ ON.
If you don't use #STYLES, then you need to add fonts to these tables in order for them to show up in the 'font' property of report objects.
If you open a brand new library, Omnis shows the following report fonts in these tables:
1 - Mac:Monaco, Win:Arial
2 - Mac:Courier, Win:Courier New
3 - Mac:Helvetica, Win:Times New Roman
4 - Mac:Geneva, Win: MS Sans Serif
5 - Mac:Chicago, Win:Times
I can't say I'd consider all of these as good crossplatform matches.
Monaco and Arial?, Helvetica and Times? I would NOT recommend Geneva OR MS Sans Serif as report fonts.
Mac OS9 has matches for most of the standard wintel fonts, so the REPORT fonts table I might use:
1 - Mac:Helvetica, Win:Arial
2 - Mac:Courier New, Win:Courier New
3 - Mac:Times New Roman, Win:Times New Roman
4 - Mac:Palatino, Win:Palatino
I didn't have a broad range of printers to test this on, so let me know if you run into problems with the report fonts I've suggested.
If you don't assign styles to your report text, then this table is used for picking the 'other' font when your application is used on the 'other' platform. The weakness is that there is not fontsize control with these table, so the same fonts might print bigger from Wintel, than from Mac. #STYLES give you the added control of fontsize, plus color, etc.
Once you're happy with the font matching in this table, don't mess with it. The font properties in your library objects (except #STYLES) are mapped to the Row # in this table, so if you change Helvetica to Times, every text senstive object that doesn't have a style and used Helvetica will now be Times.
So take your time, experiment, and get the report fonts table set up the way you want it. Then don't mess with it. If you need to add fonts, add them to the bottom of the table.
BUT, I recommend you ignore these tables, and just go with #STYLES.If you haven't guessed already, these stand for Windows WINDOW Fonts and Mac WINDOW Fonts. These table are the way cross platform fonts were handled before Studio introduced #STYLES.
Removing all the fonts from these tables, will force you to use styles. Maybe a good way to discipline yourself to use styles. (If wish I did, it would have saved me a lot of rework)
IF YOU DON'T USE STYLES THEN READ ON.
If you open a brand new library, Omnis shows the following window fonts in these tables:
1 - Mac:Monaco, Win:Arial
2 - Mac:Courier, Win:Courier New
3 - Mac:Helvetica, Win:Times New Roman
4 - Mac:Geneva, Win: MS Sans Serif
5 - Mac:Chicago, Win:System
I can't say I'd consider all of these as good crossplatform matches.
Monaco and Arial?, Helvetica and Times?
For my application pretty much the only screen font I use in my windows is Geneva.
So for the window fonts table you might keep it simple as follows:
1 - Mac:Geneva, Win: MS Sans Serif (for most screen text)
2 - Mac:Helvetica, Win:Arial (for titles)
3 - Mac:Charcoal, Win:System (for titles to look like the OS)
4 - Mac:Monaco, Win:Courier (for monospace text)
In Classic I used monospace text in lists, but in Studio I use Headed lists and set alignment, so Geneva/MS Sans Serif work great.
I 'm not a font expert, so let me know if have other suggestions.
If you don't assign styles to your window text, then this table is used for picking the 'other' font when your application is used on the 'other' platform. The weakness is that there is not fontsize control with these tables, so the same fonts might displayed differently.
Once you''re happy with the font matching in this table, don't mess with it. The fonts in your library are mapped to the Row # in this table, so if you change Geneva to Times, every text senstive object that doesn't have a style, and every style definition will be changed.
So take your time, experiment, and get the window fonts table set up the way you want it. Then don't mess with it. If you need to add fonts, add them to the bottom of the table.
BUT, I recommend you ignore these tables, and just go with #STYLES.You should read the #STYLES notes before reading this section. I make some recommendations on what fonts to use for cross-platform compatibility. This is not based on a lot of experience or testing, so use your own judgement, and test your results.
As mentioned in #STYLES, you should have separate styles for reports and windows, don't mix them.
WINDOW(SCREEN) STYLES
Omnis has some default styles for windows which work well.
OMNISbutton - Mac:Geneva 10, Win:MS Sans Serif 9 (bold)
OMNISfield - Mac:Geneva 9, Win:MS Sans Serif 9
OMNISlist - Mac:Geneva 10, Win:MS Sans Serif 9
I'm not sure why Mac:field is 9 and the others 10, you can change them anytime you like. In my application, I set them all to Geneva 10/MS Sans Serif 9. I also changed OMNISbutton to plain (non-bold) text.
You might consider the following styles for your windows
wBIGTitle - Mac:Helvetica 20, Win:Arial 14
wTitle - Mac:Charcoal 12, Win:Arial 10
wMonospace - Mac:Monaco 9, Win:Courier New 9 wCompanyName - whatever looks best for the particular company, will look the same everywhere.
You can set colors in your styles, but I found it made my styles list too long with too many choices, so I went back to keeping it simple, leaving colours to be changed in the window if needed.
REPORT STYLES
Omnis does not have default styles for report text objects in the Component Store. (v2.4)
Do NOT use Geneva or MS Sans Serif for report styles, in my testing they didn't work well.
Between my Mac G3 OS 9 Powerbook and Wintel Windows98 box I found the following decent report fonts to work on both platforms. Courier New, Helvetica, Palatino, Times New Roman. I was printing to Apple and HP laserprinters.
Arial seems to be the Wintel standard sans serif report font, Helvetica for the Mac. You can't go too far wrong with those fonts.
I had an Apple Laserwriter 16/600 and and HP DeskJet 4MV laser printer (postscript) for testing and was printing to them from a Mac OS9 and a Wintel Win98 box. In my situation I found that Helvetica was on both platforms and was on the printers, so I got best results using the same size Helvetica on both platform. Your might experience different. I would appreciate hearing your results on what works and doesn't work at your installations
The only problem I have is that the Wintel reports use less vertical space per line on the page than the Mac reports. (except in kExtendingJst fields) I also found that picture fields print shorter vertically on the Wintel than the Mac.
See #STYLES for the report styles font recommendations.
By using a separate style for Company Name, you can change the font/size for rCompanyName to suit individual companies, and every report will have the Company name in the font/size preferred by that client. Same goes for the other styles. If you stick with them, you can change every report in the click of a mouse.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. OpenSublibraries, which the automatically opens your 'sub' libraries.
See 'Directory Structure' for a recommended way of storing your sub libaries.
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.
Multiple libraries do not require multiple data files. You can still store all your data in a single data file. The Current Session or Current Data file is where all your libraries get/insert/update the data. Unless you change the current session or current data file, your libraries will all point to the same data file.
There's a couple things you have to watch with multiple libraries.
1. Set the Default name of the library in the Property Manager. If you press F2, you will see a library called 'TIPS' in your library browser. Even through the file name is StudioTIPSvXXX.LBS, in the Library Browser it's called TIPS. If you select TIPS and press F6, then click the Prefs tab, the top property is $defaultname. Give every library a default name. I like to use short upper case names. It makes the libary name easy to see in your code, saves typing, and keeps your code strings shorter. (AP, AR, GL, PO, INV, PAY, EMP, ...)
2. Prefix references to classes with the library name, even when referring to classes inside the same library. That is especially true for the "main" library. That way the code will still work, even if you drag it to another library. When you refer to something in another library, Omnis automatically prefixes the library name.
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, object class from a different library, you must set the $external property of the class to kTrue. Otherwise it won't be visible in design mode. To keep the number of "objects" that are visible to a minimum, only set $external to "kTrue" for the classes that you need to access from other libraries, otherwise leave it at kFalse. (Thanks to Greg Olson-Hyde for this tip.)
5. Be aware of the current task instance and unless you need multiple tasks, just run your application under the main library's task instance... the one that automatically starts when the $construct of your main library's Startup_Task kicks in, as it is opened. When you open sublibraries 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. Craig Lewis recommends that you delete the Startup_Task from each sublibrary.
Think of the task instance as a "container of instances". When you open your main library it automatically sets up a task instance. When you install a menu from the task instance, the menu 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", NULL values. 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 created in its own task. Run time users don't get the chance to do this, it's more when you are developing that you run into this.
Multilibraries, I strongly recommend them!! There's lots of advantages, very few disadvantages. Easy to do. See the sample code in this section for "OpenSublibraries" and "OpenUtilitiesLibraries"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 application, that has relieved me of all of the above. (Credit for this idea goes to Herb Payerl who inspired me with somthing similar which he does at First Avenue Information Systems.)
Rather than having the users directly open the main library of your application have them double-click on an "AutoUpdater" library. Hide all your libraries in a subfolder. See "Directory Structure" for a suggested folder structure.
On the server, create a directory called "auto_updates". In the "auto_updates" folder on the server, make an exact copy of the folder structure and files which the users should have on their computers.
When the user opens the "Auto Updater" library on their local computer, it checks the server for any new versions, downloads them using FileOps (or FTP with the internet?), and then proceeds with opening the main library. The main library's Startup_Task then automatically opens all sublibraries 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 in the AutoUpdater folder 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 "auto_updater.lbs" in an empty folder. The first time "AutoUpdater" is opened it will automatically download all the subfolders and files from AutoUpdater folder on the server.
2. Allows the developer to select from a list of the open libraries which to upload to the server. AutoUpdater then closes the selected libraries and uploads them to the server along with a new version number. AutoUpdater then reopens the libraries so the developer can continue working.
3. Uses an auto_archives folder. Each time the developer uploads new libraries to the server, "AuotUpdater" first creates a subfolder in the auto_archives folder and copies the old libraries into the subfolder before uploading the new libraries. The auto_archives subfolders are named using the current date and time including seconds.
e.g. auto_archive_20001224_160455
4. Adds an "Updates" menu to the run time user's menus with a "Download Updates" menu item. If the user selects this option, "AutoUpdater" checks the server for any new updates and then closes those library(s), downloads them using FileOps, and then reopens the library(s) so the user can continue working. "AutoUpdater" does NOT allow development versions of Studio to use the "Download Updates" option. "AutoUpdater" does NOT even show the "Upload New Versions" menu option to runtime versions of Studio.
"AutoUpdater" has been tested in a Wintel/Mac environment. I developed "AutoUpdater" as an Open Source library to run faceless with anyone's application. "Faceless" meaning runtime users can't tell that someone else wrote the code. You can rename the "auto_updater.lbs" to whatever name you like. e.g. MyGreatApplication.LBS. "AutoUpdater" will be made available to StudioTips subscribers. (If you wish to beta test "AutoUpdater", send me an email.)
I'm hoping other developers will adopt "AutoUpdater", enhance it, and then send me their enhancements. (After testing them for a few weeks!) That way we'll all benefit from each other's efforts.There are many ways to set up your directory structure for multiple libraries. At the time of writing this, I have 15 libraries and am accessing the Omnis data file using OmnisSQL. The data file is on a Mac G4 server running Appleshare IP.
My application is called CSP (An acronym for Contacts SuperPro)
Here's what I have currently settled on for a file structure:
(Not all libraries are listed, just enough to give you a flavour for the structure.)
(">" character is used for indenting)
csp (folder)
> csp_startup.lbs (This is my "AutoUpdater" library - See Auto Updater in this section)
> > programs (folder) On the user's computers this folder and its subfolders are normally closed.
> > > modules (folder)
> > > > csp.lbs (The "Main" library which opens all the others and has superclass)
> > > > csp_ap.lbs (Accounts Payable)
> > > > csp_ar.lbs (Accounts Receiveable)
> > > > csp_gl.lbs (General Ledger)
> > > > csp_inv.lbs (Inventory)
> > > > csp_pay.lbs (Payroll)
> > > > csp_po.lbs (Purchasing)
> > > Open Source (folder)
> > > > print_labels.lbs
> > > > prompts.lbs
> > > > report_writer.lbs
> > > preferences (folder)
> > > > csp_localprefs.lbs (Stores the path(s) to the data file(s), and the local printer page setup)
> > > resources (folder)
> > > > sounds (folder)
> > > > > click.wav
> > > > > start.wav
> > > > images (folder)
> > > > > logo.gif
By default the folders are closed, so all the user sees is:
csp (open folder)
> csp_startup.LBS
> > programs (closed folder)
The user double-clicks on csp_startup.lbs, it checks the server for newer version libraries (see Auto Updater) and downloads them. Once the downloads are complete, csp_startup.lbs opens csp.lbs, the main library. csp.lbs handles things from there. It knows the subfolder names, and using its own $pathname can figure out the paths to the other libraries.
csp.lbs first opens csp_localprefs.lbs to get the default path to the data file. It then opens the data file and prompts the user to "Sign In". The user must successfully enter their ID and password (which is stored in the data file) before they can go any further in the application. Once they sign in, the main menu window opens listing all the modules and windows which they have access to.
Each user's password, access privileges, window sizes, digital signature, etc. are all stored in the data file under the user's profile.
When the csp.lbs (main library) is closed, its $destruct is responsible for closing all the libraries which csp.lbs opened.
Sample code for the $OpenAllLibraries is included in this section.
In the studiotips_stuff\demos folder there is a multi_library folder which gives you an idea of how you can set it up. Double-clicking on auto_updater.lbs opens Programs\main_library.lbs which then opens the other libraries. Main library uses its own "oConstants" object class for the paths. When you close main.lbs, it closes the other libraries. Feel free to experiment with the MultiLibrary demo. To open it up from StudioTips, click the "Run Demo" button.This method builds a list of all the files (libraries) in the 'utilities' folder and then opens the libraries. Open source libraries run in their own task instance, so the "Do not open startup task" check box is left unchecked. The path to the Open Source folder is obtained from the "oConstant" object class. (See the "Constants" section)
This method builds a list of all the files (libraries) in the 'Open Source' folder and then open the libraries. Open Source libraries run in their own task instance, so the "Do not open startup task" check box is left unchecked. The path to the Open Source folder is obtained from the "oConstant" object class. (See the "Constants" section)
"Open Source" libraries are libraries which Omnis developers share with eachother. Open Source libraries have very specific purposes and run "standalone". I've been getting pretty excited about the "Open Source" concept. At the time of writing this, I've got a couple "Open Source" libraries built and being tested live.
eg. "print_labels.lbs" - send it any list of records, it prompts the user for label size, then prints the labels.
eg. "prompts.lbs" - replaces the built in 'OK,Yes/No,No/Yes,Prompt for Input' with a much more flexible set of prompts.
By working together on "Open Source" libraries we can develop better and better utilities and all benefit.Programmers are always looking for faster, quicker, ways to do their work.
This sections gives ideas on things you can do to increase efficiency.
Autopairs is a freeware extension available for the Mac platform which automatically types a closing bracket, when you type an opening bracket.
Type ( and AutoPairs automatically types the closing bracket ) leaving the insertion point between the brackets.
Autopairs works for the following types of brackets () {} []
After years of manually type the closing brackets, Autopairs will take a bit of getting used to.
If you install it you should try if for at least a week, then decide whether or not it save you time.
Available at http://www.jwwalker.com
Thanks to Greg Olson-Hyde for this tip.In a complex window, one with paged panes within paged panes, for example, the Field List window can be invaluable when you need to get to an object that's deeply buried.
Just right-click anywhere on the window to get to its context menu and select "Field List". Then click on the
checkbox by the field you need to get to a voila, you're there!
Thanks to Craig Lewis for this tip.
I find the Field List crucial for windows that have objects set to kEFposClient. Another time it's invaluable is if you inadvertently "lose" a field in a complex grid ... you know the field is there but it has become hopelessly hidden. Go to the field list, click on the checkbox by the missing field in the Field List, then F6 to the Property Manager and set the left and top values to zero, voila the missing field reappears!When you are viewing the library browser items you can use the following shortcut key
combinations in order to show just the type of classes you want to look through
Shift-Ctrl/Cmnd-W = W_indow Classes
Shift-Ctrl/Cmnd-O = O_bject Classes
Shift-Ctrl/Cmnd-R = R_eport Classes
Shift-Ctrl/Cmnd-M = M_enu Classes
Shift-Ctrl/Cmnd-T = T_oolbar Classes
Shift-Ctrl/Cmnd-B = ta_B_le Classes
Shift-Ctrl/Cmnd-F = F_ile Classes
Shift-Ctrl/Cmnd-S = S_chema Classes
Shift-Ctrl/Cmnd-Q = Q_uery Classes ;; This doesn't work in Mac OS X.
Shift-Ctrl/Cmnd-Y = s_Y_stem Classes
Shift-Ctrl/Cmnd-A = A_ll Classes
Shift-Ctrl/Cmnd-J= Task Classes ;; I'm not sure how J relates to task classes.
To move objects from one tab to another, selected the objects in one tab either by shift+clicking on the objects, or using Ctrl/Cmnd and dragging a selection rectangle over the objects you want to move.
Once the objects are selected, drag them over the tab which you want to move them to, then press the Alt/Cmnd key to switch to that tab. Drop the objects where you want them.If you are in a subclass and want to go to a the superclass method which is not overridden (blue text). Click on the method in the methods list, then double-click on the method details in the right pane. Bingo, Omnis will take you to the superclass method!
The first consecutive lines(s) of a superclass method which are commented show up in the subclass. I tend to copy, paste, and comment out the superclass code in the superclass method so that at the subclass method, I can see exactly what the code is in the superclass, without having to pop up to the superclass level.
Example - in the table class superclass method
; set modified for the record to today
; Calculate $cinst.Modified as #D
Calculate $cinst.Modified as #D
(The first 2 lines in the above superclass method will show up in the subclass method.)
When you override a superclass method, the commented lines will show up in the subclass. You can use this to your advantage if there is a superclass method you always want overridden and the same standard template of code used in the subclass. After overriding the superclass method, you can then uncomment the code and modify it as required.
Example - in the table class superclass method
; set Id for the record
; Calculate $cinst.Id as 'ENTER SUBCLASS VALUE'
; OVERRIDE THIS METHOD IN THE SUBCLASS AND ENTER THE VALUE.
OK message {[sys(85)]////Override this method in the subclass table and enter the value.}
If you have a variable you can use %%VariableName and the variable will be automatically added when you override and then uncomment the code.
After you have overridden a superclass method, if you want to look at the superclass method, you can right-click on the subclass method and select Superclass method... from the context menu.
Another way of accomplishing this (a habit I've fallen into) is to place a 'Do inherited' after your ending 'Quit method kTrue'. If you click on the 'Do inherited' and then press F8, Omnis will pop you up to the superclass method.If you are converting an existing Omnis Classic application to Omnis Studio, both you and your users are in for a surprise. Your application will likely run slower in Studio than it did in Classic.
Studio gives you lots of neat new objects and features and it gives you an object-oriented development tool, but there is a price to pay. Don't try running Studio on old hardware it's too slow.
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 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 many 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 will likely get frustrated with complex grids. If you hit the slow complex grid redraw problem with your application, send an email to Tech Support to let them know you are not happy with the situation. (Hopefully engineering will find a solution if enough developers keep complaining.)Some users have reported faster startup and speed improvements by loading less of the IDE tools.
You can do this through the Tools menu > Preferences > General tab > idetools property.
Clicking on this property gives you a checkbox list where you can uncheck tools you don't need. I uncheck vcs.lbs (version control system).
I'm not certain what a number of the other tools are for. You can experiment and see if it helps.Under the Tools menu > Preferences , there is a property $maxcachedclasses. I believe Omnis defaults it to 100. Bump it up higher. Some developers set it as high as 1000.
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 Studio. If you find your application running slower the more windows you open and then 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.
The following is advice from Tech Support on this issue:
At present I am unable to fully explain exactly what is happening here. Our thought is that this may be connected to class caching. In that the sqlclasses are not in the cache and must be read from disk. This may mean that you need to adjust $root.$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.
If you are using OMNISSQL note that it is not a "commercial backend" SQL server. It's a great SQL learning tool, it's free, and it works amazingly good (I'm still using it for 10 users and 300MB data file), but don't expect it to work like Oracle or DB2. OMNISSQL will bog down if you include too many arguments in you SQL select, include an ORDER BY, or try join too many tables (more than 4 or 5)
INDEXES
Be sure to use CASE SENSITIVE indexes for all non-numeric data. If you don't Omnis goes through every record making the index useless.
Consider date fields to be non-numeric. Use indexed fields for your selects and joins on tables with large numbers of records.
JOINING TABLES
Be sure to put the "main table column" to the right side of your table joins.
If you were running a report on the books written by Shakespeare in a library joining the "book" table and the "author" table, we could write the join statement two ways:
1. WHERE book.author_key = author.author_key AND author.Name = 'Shakespeare'
2. WHERE author.author_key = book.author_key AND author.Name = 'Shakespeare'
The first way has the main table on the left causing OmnisSQL to go through every record in the database, the second way has the main table on the right and runs lightning quick.
COMPLEX SELECTS
I find it quicker to grab more data than I need, then use $search followed by $remove(kListKeepSelected) or $remove(kListDeleteSelected). Do your sorting after the fetch using $sort.
You will need to experiment with you select statements. I tell my users if any report is taking longer than a minute or two, let me know. For most reports I can usually figure out a solution and get it down to a minute or less.
CONSIDER MOVING TO A REAL SQL BACK END.
If your client needs more speed, they can probably afford to invest in an industrial backend. At the time of writing the beta release of FrontBase is out. I understand it should run around US$500 for unlimited users and give equal support to Mac and Wintel clients. That's a pretty inexpensive solution that will make your OMNISSQL select and fetches look like they are standing still.
That's the beauty of SQL vs. DML ... easy hookup to 3rd party backend SQL.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 Studio window supports multiple instances, superclass/subclass structure, $construct for every field in the window, etc. etc. All that neat OO stuff comes at a price.
The solution I use it 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. Open a window like this is very fast. :-)
Using notation $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. (See sample code below)
Keep your windows light and 'flat'. Using subwindows also allows you to reuse the same window in many places. I have a single superclass window with a 5 layer page pane and 1 of the layers has a 5 layer tab pane. Each layer has an empty subwindow set to kEFposClient. This same superclass window is instantiated for virtually every window in my application. I have a single subwindow with a headed list. This single headed list subwindow is instantiated for virtually every headed list in my application ... it's properties are assigned notationally on the fly.String tables can be used to support multiple languages. String tables are a good discipline to follow, even if you are supporting only 1 language.
Books on GUI recommend that developers use some sort of a common "phrase dictionary" for all the labels, titles, prompts, and messages throughout the application. By using a centralized "phrase dictionary" you are better assured that the same terminology is used for the same things throughout your application. This is especially critical for applications being developed by multiple programmers. The user becomes confused when in one window you refer to the a certain menu as the "Application Menu" and in another window you refer to it as "Main Menu".
String tables could also be used for operating system sensitive terminlogy.
e.g. Mac vs. Win vs. Unix. Ctrl-Click vs. Right-Click
At the time of writing this section in StudioTips, finding the information on String Tables took some effort. Eventually I found a Tech Note on the Omnis web site at: www.omnis.net/develop/resources/notes/technotes.html
You can also download 2 String Table demo libraries from Omnis. "strtest.lbs" and "Table.lbs"
This section of StudioTips goes through the information provided in the Tech Note giving demos and examples of how to implement String Tables. Each String Table function is listed and explained.
Thanks to Jock Philip for helping me get started with String Tables. Thanks goes to Omnis for allowing me to use bits and pieces of their Tech Notes for this section.Do StringTable.$getcolumnname(TableName)
Gets the column name for the current column.
Note that TableName is not required if only one String Table is loaded.Do StringTable.$getcolumnnumber(TableName)
Gets the column number for the current column.
Note that TableName is not required if only one String Table is loaded.Do StringTable.$gettext('[TableName.]ID') Returns Text
[StringTable.$gettext('TableName.ID')] ;; If using square brackets
Gets the text in the current column of the specified String Table for the row matching ID.Do StringTable.$loadcolumn(ColumnName,TableName,Path)
Creates a String Table using a single column of an existing String Table.Do StringTable.$loadlistfromtable(TableName) Returns List
Loads a String Table already in memory to an Omnis list.Do StringTable.$loadstringtable(TableName,Path)
Loads an existing String Table into memory from an existing String Table file.
Note: If the String Table has already been loaded, you must first "$unloadstringtable". Trying to load the same String Table will result in an error.Do StringTable.$loadtablefromlist(TableName,Path,List)
Creates a String Table from an Omnis List. It should be noted that the table is created in memory at this point. To save the table, use $savestringtable.
Although this command requires a path, it doesn't actually create the file until you issue the $savestringtable.
Note: If the String Table has already been loaded, you must first "$unloadstringtable". Trying to load the same String Table will result in an error.Do StringTable.$redraw(WindowInstanceRef.$hwnd)
Redraws the String Table labels on an entire window.
NOTE: Not including the ".$hwnd" will cause Studio to crash. (I know from experience!)Redraw all windows after selecting a new String Table.
This is NOT a String Table function. This is a method you can create/use for redrawing all the String Table labels in all the open windows.Do StringTable.$removestringtable(Path)
Deletes a String Table stored on disk.
DANGER! This function really does delete the String Table file!Do StringTable.$rowcnt([TableName])
Returns the row count for TableName.Do StringTable.$savestringtable(TableName)
Saves a String Table which has been previously created.
Note: A path name is not required when saving. Pathnames are only specified when creating String Tables.
If you had a new String Table in a list, the process of creating the new String Table file on diskDo StringTable.$setcolumn([StringTableName.]ColumnName)
Sets the current column to "ColumnName". This may be either a name or a number.
Note: "ColumnName" is case sensitive. If multiple String Tables are being used then the format needs to be "TableName.ColumnName"
e.g. StringTable.$setcolumn("Table1.French") or StringTable.$setcolumn("Table1.3")Do StringTable.$unloadall()
Unloads all String Tables from memory.
This function is optional, as all String Tables are automatically unloaded when Omnis quits.Do StringTable.$unloadstringtable(TableName)
Unloads a String Table from memory.
This function is optional, as all String Tables are automatically unloaded when Omnis quits.The headed list $columnnames property doesn't seem to evaluate [] square bracket $gettext notation. One workaround is to build the $columnnames string in the $construct method of the headed list.
Click "Run Demo" to see how this can be done.If you are using String Tables for supporting various languages, you may want to use the same language abbreviations used by the web browsers. Why bother figuring out codes for the various languages, when there is already a standard being used.
To find out the code, open the "Preferences" in your web brower and click on the "Languages" preferences setting. Click on the "Add..." button, and you will be prompted with a list of languages and their abbreviations.
English/United States [en-US]
English/United Kingdom [en-GB]
German/Germany [de-DE]
Italian [it]
Dutch [nl] etc.
So for my string tables I'm using "en-US" as the 2nd column name.
Note: Don't include brackets () in your string table column names ... the brackets will mess up your notation.
Many thanks to Pierre Huber for pointing out the web browser language codes.The String Table Editor is a window interface which Omnis has created to assist developers in creating, editting, and saving String Tables.
To open the String Table Editor look under the Tools menu > Add-Ons > String Table Editor... Look through the File and Tools menus. Hold your mouse of the toolbar buttons to review all the String Table Editor functions that Omnis has provided for you to use.
When you "Save" the String Table, Omnis sorts the table on the ID column just prior to saving it. This is done to speed lookups when the String Table is being used.
If you aren't happy with the String Table Editor you can "roll your own". You might find it easier to create your string table in Excel and then import it. For editing an existing String Table, you have the option of exporting it, editing it in Excel and then importing it.
NOTE: The +Plus version of StudioTips includes a String Table editor. Click "String Tables Editor" on the tab strip at the bottom of the StudioTips Browser window to access it.Omnis provides a handy "String Table Label" external component which works well with String Tables. You will find the String Table Label in the Component Store under "Background Components".
The String Table Label has a property $rowid under the "Custom" tab where you enter TableName.ID
The only "gotcha" with "String Table Labels" is that they don't support $fieldstyles. (see General tab > Fonts>#STYLES) This short coming makes the String Table Label unusable for me.
An enhancement request has been put through to Tech Support to add $fieldstyle to String Table Labels.
One alternative is to use "text" background objects and enclose the $gettext() function in [square brackets].So, how do you go about using String Tables in your own application? Well, as usual there are many ways. Some developers choose to create and store the String Tables as lists which are stored in the data file, rather than using actual String Table files stored on disk. In that case you would use $loadtablefromlist() instead of $loadstringtable().
My own preference at this stage is to use the Omnis String Table files stored on disk.
To keep your String Tables manageable, it is recommended that you have a String Tables for various categories. e.g. Labels,Buttons,Menus,Prompts,ErrorMessages.
Store all the String Tables files in a separate sub-folder below the main library of your application. For StudioTips I created a "string_tables" folder located inside the "studiotips_stuff" folder.
Create your String Table using the Tools menu > Add-Ons > String Table Editor... Save it. Then load the String Table(s) in the Startup_Task.$construct and test your labels.
Even if you are currently only developing your application in one language, the use of String Tables can be a good discipline for keeping your terminology consistent throughout your application.
CREATING STRING TABLES MATCHING YOUR SQL TABLES
As I was adding labels to the entry fields on a window the idea occurred to me to create a string table for each server table and to use the server table column name in the string table ID column. Every colum in my database would be mapped to a unique string table row.
I have embarked on a mission to create a String Table for every SQL table that has data which is displayed on a window or report. The String Table file name and Òshort nameÓ is the SQL table name. The ID column of the String Table is the SQL table column names, and the different language columns contain the exact label wording as I want it displayed in window labels for that column name.
For example, a SQL table column name of "ChequeNumber" in the en-US (English-US) column will say "Check Number" and in the en-GB (English-British) column will say "Cheque Number".
Now when I "LoadStringTables" at the time of opening the library, I can simply loop through all the SQL tables and generically load all the corresponding String Tables for each SQL table.
In my window text labels I use the following square bracket notation:
[StringTable.$gettext('SQLTable.ColumnName')].
No further thinking or coding is required! When the window opens the labels will display the correct label text for the language column that was set when the library was opened. If I or my client want to change the label for the SQL table column "LastName" from "Last Name" to "Surname" ... no problem. Change it in the String Table and EVERY place "LastName" is displayed will be changed from "Last Name" to "Surname".
To speed up creation of String Tables which match existing schema classes, I've created a String Table Editor which allows you to select a schema class and then using a context menu call a method which builds a String Table list with the ID and 1st language column already filled in. You can then edit and "Save As..." the String Table.
NOTE: The full version of StudioTips includes a String Table editor with the "Create String Table from SchemaThis section lists the known limitations of Omnis Studio. It is not a negative criticism of Omnis Studio, but rather, simply designed to inform you of places where you can "hit the wall" with Omnis Studio, and possible solutions for working around them.
If you run into any Omnis Studio limitations, please send me an email clearly explaining what the limitation is, and a work around solution, if there is one.
METHODS STACK LIMIT
If you hit the "Too many stacked methods" message (or something like that), you either have to figure out a way to reduce the number of methods being called or increase the $stacklimit.
My own application doesn't work with the default limit, so in the Startup_Task I have added the following code.
; Increase the stacked methods limite from default 10 to 15
Do $root.$modes.$stacklimit.$assign(15)
MAX CACHED CLASSES
At the end of 1999 a month before Y2K as I was going "live" with my application I hit this problem. The longer users had my application running and opening and closing windows the slower Studio would run. Desperate for a solution I sent an urgent S.O.S. email to Tech Support. Within a few days they found the problem and provide the $maxcachedclasses patch to save my bacon.
You will find this property under the "Tools" menu > "Preferences" > "General" tab. For my application, setting $maxcachedclasses to 100 works well.