Tips_classictostudio   >   StudioTips - Classic to Studio (All Contents)

Classic to Studio

There are many differing opinions on how to make the move from Omnis Classic to Omnis Studio. The documentation in this section provides suggestions and tips for moving from Omnis Classic to Omnis Studio.

If you have never used Omnis Classic, the information in this section is of no value to you.

My personal opinion is that you should rewrite your application in Omnis Studio rather than convert your Omnis Classic application to Omnis Studio.

Omnis Studio is an object-oriented programming development tool. If you convert an Omnis Classic app to Omnis Studio using the converter tool you will end up with non object-oriented code inside of an object-oriented programming development tool.

Also, if you aren't already using SQL, do yourself a favor and move to SQL at the same time.

My Story

Back in 1999 I was faced with the decision of whether or not to convert my existing Omnis Classic app to Omnis Studio. I tried using the Classic converter tool which Omnis Studio provided, but I ended up with all my (ugly) Omnis Classic code inside Omnis Studio and was faced with fixing, debugging, and supporting old legacy code. I thought long and hard about it and in my heart I knew that a rewrite was the right thing to do.

The other issue I faced was that the Omnis Classic app was not using SQL, it was limited to the Omnis data file. A fellow Omnis developer told me to make the move to SQL. Learning SQL took some time, but looking back, switching to SQL was a very important decision. Several years later my Omnis data file had grown to 4 segements and I had to move from to a real RDBMS (FrontBase). Because I had switched to using SQL when I started with Omnis Studio, moving the Omnis data file to FrontBase was very easy to do.

Non-OO Classes

Omnis Studio has Omnis Classic roots because Omnis wanted to make it possible for Omnis developers to convert their existing Omnis Classic libraries to Omnis Studio.

There are several non-object-oriented types of classes in Omnis Studio that you should not use.

If you are coming from Omnis Classic, the code classes are very tempting to use. Trust me, they are evil. Code classes are a slippery slope that leads you away from writing object-oriented code. Anything you can do with a code class, you can do better with an object class.

Non-OO Variables

The hash variables, #F, #S1, #L1, are variables which I used extensively in my old Omnis Classic app.

Don't use hash variables in your Omnis Studio app. Hash variables have global scope. The object-oriented police frown on the use of global variables... for good reason.

If you open two Omnis Studio applications with the same instance of Omnis Studio, or you move your code to a multi-threaded web app, or if Omnis Studio becomes multi-threaded, those global variables are going to give you grief. Trust me, it takes very little time to create a local variable or instance variable, and your code will be stronger for it.

Use the narrowest scope of variable possible for each situation. If you can use a local variable in a method do so.

Avoid using class variables unless you have a good reason for doing so. The only place I use class variables is for remembering user prompt values. If I prompt the user for a date range of records to include in a report I'll use the class variables cDateFrom and cDateTo. If the user decides to reprint the report, when the prompt window reopens the last cDateFrom and cDateTo values that they entered will be displayed because class variables hold their values.

Instances

In Omnis Classic we could instantly flip from editing a window class to viewing it. This does not hold true in Omnis Studio. You never really see a window class, you only see instances of a window class.

An instance has to live inside of a task. Because there can be multiple tasks, Omnis Studio doesn't know which task to instantiate the window class instance inside of. At first this can be frustrating, but don't fight with it. Get used to the fact that you must open a window class as an instance inside of a task instance.

I always include a Programmer Test Method menu line in the main menu of any applications I write. Go ahead and select the Programmer Test Method menu line in the StudioTips Tips menu. You will hit a Breakpoint followed by a Quit method. When you are at the Breakpoint you are inside the tipsBase task instance. If you add the following line of code...

Do $clib.$window.wTipsBrowser.$open('*')

...below the Quit method and double-click on that line to set the Go point, and then click the Step Over button, you will open a second instance of the StudioTips Browser window. Go ahead and try it.

Using a Programmer Test Method in a main menu which is opened by your library's Startup_Task $construct method you will be able to instantiate any class (window, report, table, toolbar) within your application's task, and send messages (call public methods), to test code.

Being able to open instances of classes is an important feature of object-oriented programming. Instances allow you to reuse classes and have multiple instances of the same window open with different records in each window.

Set current

There are a series of Omnis commands that begin with the word Set.

Set current list
Set current session
Set current data file
Set timer method sec

Generally you should avoid Omnis commands that begin with the word Set. The reason is because they are global. As you read previously in the Non-OO Variables topic, the object-oriented programming police frown on the use of anything global. Even if you get away with it, some day these global Set commands will come back to haunt you.

In Omnis Classic if were looping through one list, and inside the loop we would go off an search another list, it was crucial to remember to set the current list at the beginning of each method in a reversible block.

Begin reversible block
Set current list List
End reversible block

The reversible block at the beginning of the method will restore the previous current list at the end of the method. Having to keep track of current list, current data file, is a pain, and the code is fragile.

Omnis Studio is an object-oriented programming development tool so it provides you with non-global ways to loop through lists and do the things you were accustomed to doing with the Omnis Set commands.

For examples, here is how you can loop through a list using Omnis notation.

For List.$line from 1 to List.$linecount step 1
   
   ; The current line in the list is incremented each time through the loop.
   ; You could call 100 other methods which loop thorugh other lists
   ; and never have to worry about which list is the 'current' list.
   
End For

Modal vs. Modeless

In Omnis Classic I was used to writing window classes which were modal. Modal meant that the user was trapped with the window until they finished whatever they were doing and closed the window. The built-in OK, Yes/No, No/Yes, Prompt for Input messages are modal prompts. Writing code using modal prompts is fairly easy. Code execution is halted until the user deals with the prompt, after which code execution continues where it left off.

Modeless windows do not trap the user. The user can bring them to the front, move them to the back, open an other window, run a report, and return to the modeless window whenever they like. Users like modeless windows. Web applications are modeless.

When I switched from Omnis Classic to Omnis Studio I immediately switched to modeless windows, but I still used modal prompts. After 5 years of writing code in Omnis Studio I started writing Omnis Studio web apps (using JavaScript on the client side). As I started to write these web apps I finally saw the importance, and benefits, of writing code that uses modeless prompts. The trick with modeless prompts is that you have to give the modeless prompt a call back method. If and when the user clicks the Continue button on the modeless prompt, the prompt sends a call back message passing along the input parameters.

You have to break your old modal code into two parts (method).

  1. The method which prepares the input variables and opens the modeless prompt window passing in the call back method information.
  2. The call back method which receives the user input values and continues the method execution.

Typically I add the suffix _continue to the call back method. Here is an example.

  1. $printAddressLabels - This method fetches a batch of contacts and opens the modeless prompt which asks the user to select the contacts they want to print labels for.
  2. $printAddressLabels_continue - This is the call back method which receives the list of records from the modeless prompt. This method removes the non-selected records, and then prints the address labels for the selected records.

Visual vs. Non-Visual

The object-oriented world makes a distinction between visual class and non-visual classes. The distinction is very important in helping you decide what code and things happen in which classes, and more importantly what doesn't happen in non-visual classes. Non-visual class methods tend to be easier to reuse, so you want to put the majority of your code in non-visual classes.

In Omnis Classic I would put lots of code in menu classes and window classes. These are visual classes.

In Omnis Studio you want to put the majority of your code in object classes. Object classes can be instantiated by all the other visual and non-visual classes that can be instantiated.

If you haven't already done so, go through the Studio 100 series tutorials. Studio 103 gives you some practical hands on experience with visual vs. non-visual classes and writing non-visual code.

StudioWorks

If you are moving from Omnis Classic to Omnis Studio I encourage you to take a look at StudioWorks. I switched from Omnis Classic to Omnis Studio in 1999 and have been writing applications full time with Omnis Studio ever since. With the StudioWorks framework you take advantage of the collective experience of the StudioWorks developer community. Many of the Omnis Studio developers in the StudioWorks community have been, or are going through, the transition from Omnis Classic to Omnis Studio. An Omnis data file converter has been built to convert non-SQL file slots to SQL tables adding primary keys and foreign keys. There are StudioWorks developers, including myself, who can help you with the transition.

StudioWorks provides you with a pre-built, tested, and debugged set of classes that are ready to go. StudioWorks ships with a StartNewApp set of files that is a ready to go Omnis Studio application. It includes a test database, sign-in window, main menu, main window, error handler, autoconfig list and edit windows, and tons of other features. You create schema classes, add meta-data, declare the window instances, and the StudioWorks framework creates the database tables and generates the windows for you. When you create an application in StudioWorks it automatically supports deployment in multiple languages without changing a single line of code.

StudioWorks will make the move from Omnis Classic to Omnis Studio, object-oriented programming, and SQL much easier to accomplish.

For more information visit
www.studioworks-dev.net.

Omnis List Commands

If you are familiar with the Omnis commands for looping through lists, searching lists, etc., this section gives you the Omnis notation equivalents.

#L - Current Line

* $line tells you the current line number in the list. Using $assign or Calculate you can set the current line.

Either of the following will tell you the current line.

Calculate %L as List.$line
Do List.$line Returns %L

Set the current line to the value of LineN, provided LineN is between 1 and List.$linecount.

Do List.$line.$assign(LineNum)

Set the current line to zero so that there is no current line.

Do List.$line.$assign(0)

Set the current line to the last line in the list.

Do List.$line.$assign($ref.$linecount)

Remember that the selected line and the current line are not the same thing, they can be different.

#LM - Maximum Lines

Set the maximum number of lines in the list.

Do List.$linemax.$assign(100)

#LN - Linecount

Either of the following will tell you the total number of lines in the list.

Calculate %LN as List.$linecount
Do List.$linecount Returns %LN

Add line to list

The $add function is used all over the place in Omnis Studio. With respect to lists it is used to add rows or add columns. Also see $remove for various ways to delete line(s) from a list.

Do List.$add([Col1Value,Col2Value,...]) Returns rLine

Adds an empty row to the end of the list. If you include values, they will be added by column number.
$add does not change the current line in the list.

Tip

Avoid immediately setting the column values in the $add line. Code that depends on the column order is fragile. If the schema class or method that defined the list changes the order of the columns your code is broken. It is much safer to add the line to the list, set the current line, and then calculate the column values by name.

You can use rLine in your calculations immediately following $add

Do List.$add() Returns rLine
Calculate rLine.ColName as 'ABC'

or you can use rLine.$line to assign the current line immediately following $add

Do List.$add() Returns rLine
Do List.$line.$assign(rLine.$line)
Calculate List.ColName as 'ABC'

or you can use $ref.$linecount to assign the current line immediately following $add since it will always the last line.

Do List.$add()
Do List.$line.$assign($ref.$linecount)

To add a line before another line, use $addbefore.

Do List.$addbefore(LineNum [,Col1Value,Col2Value,...]) Returns rLine

This adds an empty row before LineNum in the list. If LineNum=0, the current line will be used. $addbefore does not change the current line in the list!

To add a line after another line, use $addafter.

Do List.$addafter(LineNum [,Col1Value,Col2Value,...]) Returns rLine

This adds an empty row after LineNum in the list. If LineNum=0, the current line will be used. $addafter does not change the current line in the list.

Build list columns list

To build a list of columns of a list.

Do List.$cols.$makelist($ref().$name,$ref.$objtype,$ref.$objsubtype,$ref.$objsublen) Returns ColsList

This produces a four column list. The columns are not named. You can reference the columns by the column aliases C1,C2,C3,C4 or $assign the column names.

Clear line in list

Do List.[LineNum].$clear()

Where LineNum is the line number to be cleared. You can use zero for the current line.

Clear list

Do List.$clear()

Clears the list. The list definition remains.

Warning

You must include the open and close () parenthesis with Do List.$clear().

Copy list definition

Do List2.$copydefinition(List1)

Define list

There are many ways to define a list in Omnis Studio.

The most common methods which I use are:

The SQL classes in Omnis Studio are schema classes, query classes, and table classes. Table classes have a $sqlclassname property which points the table class to a schema class or query class to be used for the table class' list definition. There are several ways to define a list from a SQL class.

Do ListRow.$definefromsqlclass([LibName.] TableSchemaOrQueryClassName)
Do List.$definefromsqlclass('t_author')
Do List.$definefromsqlclass(TableSchemaOrQueryClassRef)

The list is defined to match all the columns listed in the schema or query class.

The $definefromsqlclass has become a favourite for me. Fast and easy, a very handy command. I will sometimes create a dummy schema class which I just use for defining a list. The dummy schema class is never used for database communication.

If you use table classes (and you should), $definefromsqlclass binds an instance of the table class to the list/row variable. Once the variable is bound to the table class you have direct access to all of the table class methods from the variable. This is similar to object type variables where the object class is bound to the object type variable.

You can add columns to a list or row variable use $cols.$add notation.

Do ListRow.$cols.$add(variable|'ColName' [,kDataType,kDataSubtype,iMaxLen]) Returns ColRef

It took me a while to catch on to this one, but now I use it all the time. Use the F9 Catalog > Constants tab > Data types and Data subtypes for find the correct kContant variables.

; Define a 3 column list.
Do List.$cols.$add('name',kCharacter,kSimplechar,200)
Do List.$cols.$add('total',kInteger,kLongint)
Do List.$cols.$add('Active',kBoolean)

You can also use a variable which you've already defined. The column name becomes the variable name.

Do List.$cols.$add(Name)

You can also use $addbefore, $addafter on $cols.

Do List.$cols.$addbefore(1,'Test',kBoolean)
Do List.$cols.$addafter(2,'Test2',kBoolean)

$addbefore and $addafter will not work with lists or rows that were defined using $definefromsqlclass. You can only $add columns to those lists.

You can move the column to a new position using the $ident .

Do List.$cols.1.$ident.$assign(3)

You can also use $cols.$remove to remove columns, except on list/rows which were $definefromsqlclass.

Do List.$cols.$remove(rCol)

You can make a copy of a list using Calculate.

Calculate ListB as ListA

This copies the list definition and data from one list to another.

You can copy the current row in a list to a row variable.

Calculate Row as List

Warning

If the list is large, Calculate Row as List, is very slow. You are better to Calculate Row as List when the list is empty, and then use Row.$assignrow(List) in the loop.

Delete line in list

* $remove can be used for removing line(s) in a list.

Remove the current line.

Do List.$remove(0)

Remove LineNum.

Do List.$remove(LineNum)

Delete selected lines

Delete the selected lines, remove the rest.

Do List.$remove(kListDeleteSelected)

Keep the selected lines, remove the rest.

Do List.$remove(kListKeepSelected)

$remove is one of my favourite list methods. Used with the $search, and $merge, you can do some very powerful work.

Deselect list line(s)

Deselect the current line.

Do List.0.$selected.$assign(kFalse)

Deselect line 3.

Do List.3.$selected.$assign(kFalse)

Deselect all lines.

Do List.$search(kFalse)

For each line in list

There are several methods for looping through a list of records using notation. The best ones I've found are listed below.

Loop through all the lines in a list.

For List.$line from 1 to List.$linecount step 1

  ; The current line is automatically set by the For loop

End For

Loop through the selected lines in a list.

Do List.$first([bOnlySelectedNY,bBackwardsNY])
Do List.$next(LineN,[bOnlySelectedNY,bBackwardsNY])

Do List.$first(kTrue) ;; Only Selected Lines
While List.$line

  ; The current line is automatically set by $first, then $next
  Do List.$next(0,kTrue) ;; 0=starting with the current line, kTrue=next selected line

End While

Go to next selected line

Along with $first, $next is useful for looping through selected lines in a list.

Do List.$first([bOnlySelectedNY,bBackwardsNY])
Do List.$next(LineN,[bOnlySelectedNY,bBackwardsNY])

Do List.$first(kTrue) ;; Only Selected Lines
While List.$line

  ; The current line is automatically set by $first, then $next
  Do List.$next(0,kTrue) ;; 0=starting with the current line, kTrue=next selected line

End While

Tip

Do not use this for looping through all lines in a list. A For loop is faster if you have to loop through all the lines.

Insert line in list

See $addafter and $addbefore in the Add line to list topic.

Invert selection for line(s)

Invert selection for the current line.

Do List.0.$selected.$assign(not($ref.$selected))

Replace zero with a specific line number to select any line in the list.

Do List.3.$selected.$assign(not($ref.$selected))

You can invert selection for all lines in a list using $sendall.

Do List.$sendall($ref.$selected.$assign(not($ref.$selected)))

Load from list

Load the columns into the specfied variables.

Do List.$loadcols(VARIABLE1,VARIABLE2,...)

Warning

Use of this command tends towards fragile code. If the list column order changes, your code will break.

Merge list

Merge the data from List2 into List1. List2 must be defined before the $merge is executed. Omnis Studio won't define the list for you.

Do List1.$merge(List2 [,bMatchColNamesNY,bOnlySelectedNY])

Match by column number. Merge all lines.

Do List1.$merge(List2)

Match by column name. Merge all lines.

Do List1.$merge(List2,kTrue)

Match by column name. Merge selected lines only.

Do List1.$merge(List2,kTrue,kTrue)

Match by column number. Merge selected lines only.

Do List1.$merge(List2,kFalse,kTrue)

You can merge a Row into a List.

Do List1.$merge(Row)

Redefine list

There are various methods you can use to redefine a list. $cols.$add, $cols.$addbefore, $cols.$addafter.

Do ListRow.$cols.$add(variable|'ColName' [,kDataType,kDataSubtype,iMaxLen]) Returns rCol

To rename a column.

Do List.$cols.2.$name.$assign('NewColName')

To move column 1 to column 3

Do List.$cols.1.$ident.$assign(3)

To remove a column.

Do List.$cols.$remove(rCol)

Warning

You can not move or remove a column in a list that has been defined using $definefromsqlclass.

Replace line in list

Assign row values to current line in the list without matching column names.

Do List.$assignrow(Row)

Assign row values to current line in the list and matching column names.

Do List.$assignrow(Row,kTrue)

Assign values from the current line in List2 to the current line in List1. Match the column names.

Do List1.$assignrow(List2,kTrue)

Adds a line to the list and immediately assign the values from the row to the added line. Match the column names.

Do List.$add().$assignrow(Row,kTrue)

If you want the added line to be the current line you must set it after adding the line.

Search list

The $search method is very useful. Remembering the 4 parameters is the harder part of using this method. I keep forgetting the order and have to check back with the documentation to make sure I've got them right.

Do List.$search(SearchCriteria [,kFromStartYN,kOnlySelectedNY,kSelectMatchesYN,kDeselectNonMatchesYN])

If parameters 3 and 4 are set to kFalse, $search will set the current line in the list to the first matched line, otherwise $search does not change the current line.

To select or deselect all the lines in a list.

Do List.$search(kTrue) ;; Select all lines
Do List.$search(kFalse) ;; Deselect all line

To select all the lines in a list which have the CityName of Orlando.

Do List.$search($ref.CityName='Orlando')

To do a case-insensitive search for Orlando, orlando, or ORLANDO.

Do List.$search(low($ref.CityName)='orlando')

The following $search will select the first matching line, set it to be the current line, and return the line number.

Do List.$search($ref.ColName=Value,kTrue,kFalse,kFalse,kFalse) Returns LineNum

The above search is a common one to use. I got tired of typing the kTrue,kFalse,kFalse,kFalse, and switched to using ones (1) and zeroes (0).

Do List.$search($ref.ColName=Value,1,0,0,0) Returns LineNum

You can use this $search in an If statement to test if a line matching your search criteria exist. If the test is true, the current line is immedately set for you as well.

If List.$search($ref.ColName=Value,1,0,0,0)
  
  ; The value was found and the current line is set

Else

  ; The value was not found in the list and the current line is zero.

End if

You can get quite creative with the search criteria. You can use arguments like:

pos('abc',$ref.ColName)>0
len($ref.ColName)>5

Do not use $ref in the comparison value. The following search would fail.

Do List.$search($ref.Col1Name=$ref.Col2Name)

This following search would succeed.

Do List.$search($ref.Col1Name=List.Col2Name)

Select list line(s)

Select the current line.

Do List.0.$selected.$assign(kTrue)

Replace zero with a specific line number to select a specific line in the list.

Do List.3.$selected.$assign(kTrue)

Select all lines.

Do List.$search(kTrue)

Set current list

You shouldn't be using this command. See the topic Set Current.

Set final line number

Use $linemax to set the final line number.

Do List.$linemax.$assign(%LN)

Where %LN = the maximum number of lines for the list.

Sort list

You can have up to 9 columns using $sort.

Do List.$sort($ref.ColName|$ref.C# [,bDescendingNY,$ref.ColName,bDescendingNY,...])

Sort the list by the ColName column, ascending.

Do List.$sort($ref.ColName)

Sort the list by the ColName column, descending.

Do List.$sort($ref.name,kTrue)

Sort the list by the Col1Name column, ascending, Col2Name column, descending.

Do List.$sort($ref.Col1Name,kFalse,$ref.Col2Name,kTrue)

Sort the list on column number 3 without having to specify the column name.

Do List.$sort($ref.C3)

Tip

C# is a column alias which you can use anywhere to refer to a column by its column number, rather than the column name. Be forewarned that code which depends on the column number is fragile code and is harder for read. Only use the column number if the list is not being passed in or out of the method where you are are defining the list and using C#.

Sort the list on the uppercase values of the ColName column, descending

Do List.$sort(upp($ref.name))

Swap lists

Not sure how often you'd use this. I think you'd have to use a 3rd temp list to accomplish this.

Calculate TempList as List1
Calculate List1 as List2
Calculate List2 as TempList
Do TempList.$clear()

Test for list line selected

$selected

tells you whether or not a line is currently selected.

Either of the following will set the bSelected boolean variable based on the current line in the list is selected.

Calculate bSelected as List.0.$selected
Do List.0.$selected Returns bSelected

Replace zero with a specific line number to find the selection state any line in the list.

Do List.3.$selected Returns bSelected

tot(#LSEL)

The tot(#LSEL) function is used to return the number of lines selected in a list. The tot() function defaults to the current list, but since we want to avoid the Omnis command Set current list, we need to specify the list.

Calculate LinesSelected as tot(List,#LSEL)

Even though this command uses a hash variable the object-oriented police are okay with it because we can specify the list and the hash variable value is immediately calculated to a local variable.