Converting old procedures to Pano X

I’ve been tasked to finally convert some old but necessary dbs to Pano X and some of the conversion is not fun. In the currrent db, I have 132 procedures like that below. Old style code that worked fine in Pano 6 but no more. The code shows the previously acceptable assignment method. With so much code to walk through and clean up, might this be something that the conversion can do a bit more with for those that will find it daunting to convert?

BTW, I found in the old code SaveAll and OldSelect
I see no replacement for SaveAll and I’ve no clue what OldSelect did. Anyone remember?

If «8271»="Federal Form 8271, Investor Reporting of Tax Shelter Registration Number"
    Field {8271} {} Beep
    Scrapcalc lookup("Forms","Form",8271,"Cost","",0)
    Field {InvTotal}
    Scrapcalc InvTotal-Val(Clipboard())
    PasteCell
else
    Field {8271} {Federal Form 8271, Investor Reporting of Tax Shelter Registration Number}
    Scrapcalc lookup("Forms","Form", 8283, "Cost", "",0)
    Field {InvTotal}
    Scrapcalc InvTotal+Val(Clipboard())
    PasteCell
Endif

i’m in the same boat. i often wish that panorama X help had an option to display deprecated and lost pan6 functions and statements just so i could figure out what i was doing. some of my code is almost 30 years old and astoundingly, sometimes i don’t remember exactly what it does!!

btw you might consider switching to local variables instead of using the clipboard….

The Help does have a list of deprecated commands and functions.

This is what is called an “implicit assignment”. You should change this code to:

«8271» = ""

Until I think Panorama 2.0, there was no assignment statement. If code contained text, it would just plop that into the current cell. The problem was that it was easy to do that accidentally, destroying data in the process without even realizing it was happening. For example, consider this code. In Panorama 1 thru 5.5, this code would display the message hello, then put goodbye into the current cell. An easy mistake to make.

message "hello" "goodbye"

So 20 years after the assignment statement was added, I made use of the assignment statement mandatory in Panorama 6. However, many people howled at the necessary code changes, so a preference was added in Panorama 6 to continue to allow implicit assignments for the time being. At that time it was strongly recommended that anyone still using implicit assignments should remove them from their code, and that a future version of Panorama would not support them at all. Panorama X is that version.

Other than the implicit assignment, this code should all work. I just verified that scrapcalc and pastecell work fine. On the other hand, this code would be a lot easier to understand and maintain if it was written in a single line:

InvTotal = InvTotal-lookup("Forms","Form",8271,"Cost","",0)

The oldselect statement is from Panorama 1.x. In those ancient versions, the select statement didn’t use a boolean formula like it does now. To be honest, I don’t remember exactly how the oldselect statement worked, and I don’t have a Panorama 1.0 manual at my fingertips. I did check the Panorama 2 manual just now and it had already changed to the new boolean formula. Off the top of my head, I think oldselect worked like this:

field Name
oldselect contains "Bob"

Which would need to be changed to

select Name contains "Bob"

But I’m not 100% sure on that, I haven’t actually converted any code with oldselect in decades.

I would just remove all saveall statements, and in most cases save statements as well. With the auto-save system used in Panorama X (and most modern Mac apps) explicit saves aren’t usually necessary.

When Panorama 2.0 was released in 1991 (over 30 years ago!) the Panorama programming language got a major overhaul, with the introduction of concepts like assignment statements and variables. Prior to that Panorama 1.x didn’t really have a proper programming language, and coding required all sorts of hacks like using the clipboard, etc. Panorama carried over the ability to use these hacks for a long time, and many of them still work, but if you have code from 1989 that you’ve been using for 30 years, that was a good run but it’s time to make a bit of an investment in updating your code. Believe me, out in the wider programming world there is very little code from 1989 that still runs with zero modifications.

O ya. All comments are fully understood and it was my working through these 132 procedures that I thought, Hmmm… Can the converter convert the Implicit Assigment to the proper assignment format? I am thinking of others who will kvetch more than me having to take something that was working fine, and now is not. :wink:

Chris… Yep, now that we have variables :wink: , that would be the smarter path but I’m just gunna take the shortcut and get it working again as is.

When I saw ‘oldSelect’, I thought, huh? What the heck is that. And yes, this db was originally created ~1989. 'Tis amazing how well it worked since then.

Dave… I did look in the Deprecated statements but alas, no ‘OldSelect’ or ‘SaveAll’! What’s with that?! LOL

Yes I’m aware. But seeing list of the same statements and functions that are already in my procedure doesn’t do much to illuminate the dim spots in my memory :slight_smile:

Dim… Heck I want to think that happens to everyone.

It is when the lights go all out that someone else has to worry about it.

First of all, there is no “converter” as far as code in procedures goes. Panorama X brings over the code from Panorama 6 databases “as-is”, it doesn’t modify the code in any way.

You’re talking about an AI project that would be more complicated than the entire Panorama programming language, and could probably never work 100%. This hypothetical converter would not only have to identify “orphan” formulas not associated with a statement, but it would also somehow have to figure out what the current field would be at that point in the program. Which may be unknowable without actually running the program.

It’s 2022, Panorama X has been available for years now, some users have been using it for 7 years. Are there possibly a dozen users that have old code from 1989 that must be manually adjusted? Sure, that’s possible. Is spending months of engineering time to save this handful of users a few minutes or hours of time a reasonable business proposition? In my opinion, that would be business malpractice and a disservice to the thousands of Panorama users that have perfectly working modern code. Eliminating all “kvetching” is not a business plan.


Manually fixing implicit assignments is not really a big deal. Let’s take the example you started with. All I have to do is press the Check button, and Panorama shows me exactly where the problem is.

The most simple fix is simply to insert «»= to convert this into a valid assignment. Then I press the Check button again and I see that there is a second implicit assignment.

Again I insert «»=, then I check again and voila, it’s done, you’re ready to go.

For. 132 procedures this might take you a couple of hours. I don’t think two hours of maintenance per 30 years is an unreasonable ask. Sorry I didn’t think of the «»= assignment earlier, but that is not new either (goes back to at least Panorama 5, maybe 4) and it is documented. There were multiple discussions about implicit assignments on the old QNA forum over a decade ago when Panorama 6 was released.

For what it’s worth, I wrote a “conversion” procedure couple years ago when I was upgrading. It looks at all procedures in a database, flags the probable “errors”, suggests what to do with each error, and then does it automatically upon request. I have not posted it before because I know my solution to each error may not be the best solution and I was pretty sure I would be embarrassed should I have to defend my solutions. I’m pretty sure someone could program this better, and everyone is welcome to do just that. But here it is anyway, in case it is of some help.

The “errors” are defined in the SuspectItemLV local variables. I keep this procedure in a separate database called “Macros” and I FarCall it from any database that I want to “convert” to PanX.

;Created September 25, 2020
;Purpose is to perform the necessary tasks for upgrading
;Usually Called by the “Seasonal Special” procedure in any number of databases

/*
search all procedures in current database for all suspect items that may need to be changed, returning the names of those procedures in CR-delimited array
open first procedure window and a form window with a "Resume" button
search for the first suspect item 
if an item is found, Pause DialogPause
when resumed, search for the next suspect item, and so on
when all searches have been done, open the next procedure in the array and search as before
*/

Global ProcedureSearchTextGV, ViewListGV, CombinedListGV, CurrentProcedureGV, CurrentDatabaseGV
Local SuspectItemsLV, TempProcedureTextLV, FileLocationLV
CombinedListGV = ""
CurrentDatabaseGV = info("DatabaseName")
FileLocationLV = “~/Documents/Computer/Panorama/Problems to Fix/Pan 6 —> PanX Repairs.txt”
SuspectItemsLV = {info("changes")
info(“changes”)
not info("empty")
not info(“empty”)
Secret
Define
Globalize
Revert
.Initialize
Loop
Hide
GetText 
Listwindows(
Console }

SuspectItemsLV = {Console }

;get all the procedure names that include the suspect items
LoopArray SuspectItemsLV, ¶, SuspectItemLV
    ProcedureSearchTextGV = SuspectItemLV
    ;FarCall “Macros”, “.Search in All Procedures (Simpler)”
    ShortCall SearchInAllProcedures
    CombinedListGV = CombinedListGV + ViewListGV + ¶
EndLoop
;edit the list
ArrayDeDuplicate CombinedListGV, CombinedListGV, ¶
ArrayStrip CombinedListGV, ¶
OpenForm “Resume”, “Database”, “Macros”
LoopArray CombinedListGV, ¶, CurrentProcedureGV, LoopCounterLV
    ;search for the suspect items
    LoopArray SuspectItemsLV, ¶, SuspectItemLV
        TempProcedureTextLV = GetProcedureText(CurrentDatabaseGV, CurrentProcedureGV)
        If TempProcedureTextLV contains SuspectItemLV
            ;make sure we haven’t already fixed this problem
            Let FixedProblemsLV = FileLoad(FileLocationLV)
            If FixedProblemsLV notcontains (CurrentDatabaseGV + ¬ + CurrentProcedureGV + ¬ + SuspectItemLV)
                OpenProcedure CurrentProcedureGV, “Database”, CurrentDatabaseGV
                ;put it on clipboard
                ScrapCalc SuspectItemLV
                Applescript |||
                    tell application "PanoramaX"
                        activate
                    end tell
                    tell application "System Events"
                        tell process "PanoramaX"
                            --click menu item "Find…"  of menu 1 of menu item "Find"  of menu 1 of menu bar item "Edit"  of menu bar 1
                            delay 1
                            key code 3 using {command down} -- f key
                            delay 1
    
                            keystroke "a" using {command down}
                            delay 1
                            --click menu item "Paste"  of menu 1 of menu bar item "Edit"  of menu bar 1
                            key code 9 using {command down} -- v key
                            delay 1
                            --key code 36 using {command down} -- return Key
                        end tell
                    end tell
                |||
                
                Message “Press Return for the first instance of «” + SuspectItemLV + “». Press Command-G to search for «” + SuspectItemLV + “» again, then click the Resume button to search for the next suspect item.”
                
                ;give instructions for what to do
                If SuspectItemLV contains “not info(”
                    Message {In Pan 6, if Select was unsuccessful, the database would go back to its previous selection. It PanX, ALL records become selected instead.} + ¶ + ¶ + {This is very dangerous, because if you were planning on, say, filling a small subset of records with 0 if they exist and it turns out they don't exist, then you may end up filling every record instead.} + ¶ + ¶ + {So If (not info("empty")) should always be followed by an else.}
                Elseif SuspectItemLV contains {info(”changes")} or SuspectItemLV contains {info(“changes”)}
                    Message {Pan X does automatic saves, so info("changes") does not exist. You can just replace it with Save, which is on the clipboard.}
                    ScrapCalc “Save”
                Elseif SuspectItemLV contains “Secret”
                    Message “Secret windows should be used vary sparingly, because Pan gets confused about which database it is working on.”
                Elseif SuspectItemLV contains “Define”
                    Message {Define CREATES a FileGlobal variable, which is bad if you wanted to define a value for a previously-declared GLOBAL variable instead, like in Pan6.} + ¶ + ¶ + {As an alternative, paste the commands that are currently on the clipboard.}
                    ScrapCalc {AssignGlobal YearlyPayment, catcherror(0, YearlyPayment)} + ¶ + {Use AssignGlobal, AssignLocal, AssignFileGlobal, or AssignWindowGlobal, or MakeFileGlobals ActivityName = catcherror("", ActivityName), ...} + ¶ + {or LetGlobal gVariable = catcherror(“StartingValue”, catcherror(“”, GlobalValue(“gVariable”)))} + ¶
                Elseif SuspectItemLV contains “Globalize”
                    Message {You can replace Globalize with AssignGlobal Variable, FileGlobalValue("", “Variable”)} + ¶ + {or} + ¶ + {MakeFileGlobals AttendDate = catcherror("", AttendDate), …} + ¶ + ¶ + {Those commands are on the clipboard.}
                    ScrapCalc {AssignGlobal Variable, FileGlobalValue("", “Variable”)} + ¶ + {MakeFileGlobals AttendDate = catcherror("", AttendDate), …}
                Elseif SuspectItemLV contains “Revert”
                    Message “Revert no longer works like it did in Pan 6. Instead, paste the commands that are currently on the clipboard.”
                    ScrapCalc {StartDatabaseChange "allrecords","Undo Selection" and sendaction "undo:"} + ¶ + {arraybuild tempdata,cr(),"",exportline()   and   importtext tempdata}
                Elseif SuspectItemLV contains “.Initialize”
                    Message {Make sure one Initialize procedure does not call another Initialize procedure. Also, use try/catch to report errors.} + ¶ + ¶ + {The Message statement won’t work in .Initialize except as part of the catch statement. I like to use as part of the Catch statement the following: Message “Error in ” + info("DatabaseName") + “:.Initialize, ” + info("error"). To display errors in the .Initialize procedure itself, you can also use the zlog statement that is on the clipboard.}
                    ScrapCalc {zlog “   *** Process Calendar / Email Calendar - 1 -  ” + catcherror("", EmailFileName) + " / " + catcherror("", EmailFolder)} + ¶
                Elseif SuspectItemLV contains “Loop”
                    Message {The old format for Loop can be treacherous.} + ¶ + ¶ + {«Until» can no longer CALCULATE the number of times to loop.} + ¶ + ¶ + {You can also use LoopArray, LoopWhile, or For.}
                Elseif SuspectItemLV contains “Hide”
                    Message “Hide should be replaced with NoShow / EndNoShow followed by ShowPage.”
                Elseif SuspectItemLV contains “GetText ”
                    Message {Replace GetText with GetTextSheet “Caption”, Variable  /  If info("trigger") = "Stop" Stop Endif. The second line is on the clipboard so you can paste it.}
                    ScrapCalc {If info("trigger") = "Stop" Stop Endif} + ¶
                Elseif SuspectItemLV contains “Listwindows(”
                    Message {Listwindows( should be replaced with arraysearch(info(“files”), “DatabaseName”, 1, ¶) if you are just trying to determine whether the database is open.}
                Elseif SuspectItemLV contains “Console ”
                    Message {Replace Console with zlog. I put zlog on the clipboard so you can paste it.}
                    ScrapCalc {zlog }
                Endif
    
                Pause ""
                OpenProcedure CurrentProcedureGV, “Database”, CurrentDatabaseGV
                AlertYesNo “Are all the problems with «” + SuspectItemLV + “» fixed in this procedure?”
                If info("dialogtrigger") contains “Yes”
                    FileAppend FileLocationLV, (CurrentDatabaseGV + ¬ + CurrentProcedureGV + ¬ + SuspectItemLV + ¶)
                    Save
                Endif
            Endif
        Endif
    EndLoop
    ;Pause ""
EndLoop
Message “Congrats, you are finished!”
stop



AlertYesNo “Sure you want to update a Pan 6 form to Pan X?”
If info("dialogtrigger") contains “Yes”
    Selectobjects Objectinfo("font") match ".AppleSystemUIFont" ;and Objectinfo("$Fill") = "Hollow"
    Changeobjects "$Fill", "Outline",
        "$TextEditorPopUpEditing", "0",
        "TextSize", "10 px",
        "$TextEditorTerminateReturn","1",
        "$TextEditorTerminateTab","1",
        "$TextEditorTabCycle","1",
        "$TextEditorInsertionPoint","All"
    Selectnoobjects
Endif
Rtn

;********** ShortCalls  ************

SearchInAllProcedures:

Local TempProcedureListLV, TempProcedureTextLV, ProcedureNameLV, ActiveDatabaseLV
Global ProcedureSearchTextGV, ViewListGV

ActiveDatabaseLV = info("DatabaseName")
TempProcedureListLV = dbinfo("Procedures", ActiveDatabaseLV)
TempProcedureTextLV = ""
ViewListGV = ""
LoopArray TempProcedureListLV, ¶, ProcedureNameLV, LoopCounter
    TempProcedureTextLV = GetProcedureText(ActiveDatabaseLV, ProcedureNameLV)
    If TempProcedureTextLV contains ProcedureSearchTextGV
        ViewListGV = sandwich("", ViewListGV, ¶) + ProcedureNameLV
    Endif
EndLoop