|
Introduction
The first part of this three part series walks through the process of creating the actual Palm application. The focus will be on the actual functionality of the application, its UI, and navigation. Learn how to create menu systems, pop-ups, buttons and other UI elements, and also some general syntax for creating and editing databases.
Purpose of the Application
The purpose of the application is to create and edit Web Log (blog) entries. If you are not familiar with blogs, then read http://www.webopedia.com/TERM/b/blog.html. One of the better known Microsoft Community (.NET) blog sites can be found at http://weblogs.asp.net/ including my public blog (http://weblogs.asp.net/rchartier).
Before developing an application, it's always a good idea to start with a quick planning session. The first part of the planning session entails listing all of the desired and needed features. A "want" or desire is something that would be cool to have, where as a "need" is something the application must have in order to actually solve the solution at hand.
The following is a list of the needs and wants that I see for our application:
Needs:
- List current blog entries
- Add/Edit/Delete blog entries
  a. Subject Line
  b. Date/Time
  c. Entry
  d. Upload Settings
- List upload settings
- Add/Edit/Delete upload settings
- Support WebLogApi*
Wants:
- Sort/Filter blog entries
- Support FTP, HTTP uploads
- Pull categories down via WebLogApi and allow entries to be sorted/classified into these categories
*WebLogApi: The WebLogApi is an effort that is in place to offer integration with applications and blogs. For more information refer to http://www.xmlrpc.com/discuss/msgReader$2198.
The "needs" seem like a pretty strong list of requirements for this article's sample application. Feel free to implement the "wants" on your own time.
After investigating all the tools at our disposal for creating applications on Palm devices, I have chosen NS Basic (http://www.nsbasic.com/palm/). I decided against many of the C based (http://www.metrowerks.com/products/palm/, http://www.orbworks.com/pcpalm/download.html) solutions because I want a quick time to market. If you want to investigate a C-based solution or learn how to create an application on the Palm using the C programming language, I do not recommend this article.
If you are familiar with the Visual Basic IDE and programming language, you will be able to learn and use this product quite quickly. The next section walks through setting up an environment for testing our progress with the Palm emulator.
Setting Up the Testing Environment
The first thing you must do is download and install both the NS Basic demonstration from http://www.nsbasic.com/palm/support/downloads.html and also the Palm emulator from http://www.palmos.com/dev/tools/emulator/. Of course you will need to get the ROM Image for your specific device. The directions on that page will explain what's needed to get started.
Once you have the emulator up and running and have downloaded and installed the NS Basic product, you must synchronize the royalty-free NS Basic runtime onto the emulator. If you chose the default installation for NS Basic, NSBRuntime.prc should be located at C:\NSBasic\Download\. Click and drag this PRC file onto the emulator. You can think of the runtime as a reusable library (a package of useful methods, constants, etc.) that all your NS Basic applications leverage. With that said, it is required to be on your Palm. The cool thing too is that you are not limited to just the runtime contents. You can easily call out to other system functions or C libraries that are on the Palm.
Sometimes when opening NS Basic the UI doesn't appear. If this happens, just open the Task Manager, kill the process, and retry it. It usually never happens more than once. Also during the synchronization process from NS Basic to the Palm, pay attention to the dialogs. When it says "Press OK after the device has reset", make sure you wait until the reset is done. Otherwise, it gets a bit buggy and you will have to kill the emulator, restart it, resynchronize the runtime onto it, and try again with NS Basic. Make sure you save the project often, and you should not lose anything.
In the next three sections, I will show you how to translate the "needs" into a fully functioning application.
Screen Layout and Navigation
When our application starts up, we will begin by listing all the current blog entries that we have in our database. This will make it easier for the user to get an idea of what entries he has created and can edit or delete. If you look that the upper right form in figure 1 below you will see that it is essentially a list of items, a button, and a popup list. Take time now to create a new project in NS Basic, and in the default form, add those items. Don't worry too much about the actual code implementation right now because we will cover it in the next section.

Figure 1 Application Forms
Once the person clicks on an item in the list in the "Manage Blogs" screen, he will be taken to that item in the "Edit Blog" screen. Or if he hits the "New" button, it will take him to a blank "Edit Blog" screen. Add the new "Edit Blog" form to your solution and create all of the buttons, labels, fields and popups.
The last two screens are for settings. This is where we will allow our users to tell our application what sites (and usernames, passwords, etc.) they want to upload their entries to. As you can see, you need a list of the available settings, a way to create, edit, and delete individual setting items. Once you have completed these two forms we will move onto the next section where we will actually implement the code for navigating form items and database work.
Before we actually go ahead and implement the code for the entire project, I want to give you a quick explanation of a "Creator ID". Within NS Basic, if you click on the top level of the Project Explorer tree, you will see the properties for that project. Notice that it allows you to enter a creator ID. This is a unique ID that you can use to represent your Palm application and databases. This will become more apparent in the next part of this article series when we deal with the synchronization process of the application and its data to the desktop. For this application I have used "PBlg" (case sensitive) for our creator ID. This entire application and its data will be represented as that. For more information feel free to read up on the Palm developers site (http://dev.palmos.com/creatorid/).
Code Implementation
User Defined Types (UDTs):
When developing any application, I like to start by creating some of the structure with which the data will be held and transformed. NS Basic lets us easily take advantage of UDTs. Essentially this is a miniature version of a class or a custom type, which we can define, instantiate, and destroy. If you have followed the previous sections closely you should be able to immediately recognize at least two UDTs that we can create right away -- one to hold the actual blog entry and another to hold the blog settings. Figure 2 below shows my version of the BlogEntry and the BlogSetting UDTs.
Type BlogEntry
subject as String 'subject of the entry
dDate as String 'date of the entry
entry as String 'the actual full text entry
settingsName as String 'matching settings name
End Type
Type BlogSetting
Name as String 'name of the setting, key
isDefault as Integer 'if this is the default setting
sType as Integer 'type of setting it is
username as String 'username to use during auth
password as String 'password to use during auth
port as Integer 'what port is the remote service running on
domain as String 'what domain/ip is the remote server
path as String 'file path to the save location
filename as String 'ftp file name,
'http variable post name For xml upload
'or url end point
End Type
Figure 1.2 UDTs
I decided to place these UDTs in a convenient location within the project, the startup code for the application. This will ensure that this UDT will be available for the entire project. You can access this by right clicking the project in the Project Explorer and choosing "View Startup Code". I created these UDTs above the "sub main()" definition, which should already be defined. We will work more with the UDTs later.
Let's move into the "sub main()" procedure. As with all applications, there needs to be an initial starting point, and for our NS Basic applications, the sub main() procedure is the place.
Databases:
For this application I wanted to keep things very simple with the database IO and violate some scoping rules. I decided to use two databases in this project, one to keep all of the blog entries and another to keep all of the settings, which I have defined and created globally to the application. I justify this because I don't want to have to worry about creating and opening the database throughout our application. You may want to change that behavior in your application and only create and open your databases on an as needed basis. Figure 3 below demonstrates how to create two global database variables.
Global blogdb as Database
Global settingsDB as Database
Figure 3 Creating global databases
Now that we have these two database variables, let's make sure that the database has been created. If it hasn't, then do so, and then open it so that it is available to the application in an opened state. Figure 4 below is this process for the Settings database, and Figure 5 is this process for the Blog Entry database.
res = dbopen(settingsDB, "blogSettings", 0)
If(res<>0) Then
MsgBox "You have not created any local settings. Nothing will be uploaded until you do so."
res = dbcreate(settingsDB, "blogSettings", 0, "PBlg")
res = dbopen(settingsDB, "blogSettings", 0)
jumptoSettings=1
End If
Figure 4: Ensuring an open Settings database.
'open up the blogdb, else create a new one
res = dbopen(blogdb, "blogdb", 0)
If(res<>0) Then
MsgBox "Default Database not found, creating a new one..."
'failed, so we create a new one
res = dbcreate(blogdb, "blogdb", 0, "PBlg")
If(res<>0) Then
appActive=0
MsgBox "Error creating/opening Database. " + str(res)
Else
res = dbopen(blogdb, "blogdb", 0)
If(res<>0) Then
MsgBox "Error creating/opening Database. " + str(res)
Else
Dim newEntry as BlogEntry
newEntry.Subject="Welcome..."
newEntry.dDate = DateMMDDYY(Today()) + " " + HourMinAMPM(now())
newEntry.Entry="Welcome to the Palm Blog, Written by Robert Chartier (rob@santra.com)"
res=dbinsert (blogdb,newEntry.Subject,newEntry)
End If
End If
End If
Figure 5 Ensuring an open Blog Entry database.
Notice that both blocks of code make a call to the dbopen() method. This is a method that is defined within the NS Basic runtime. It takes a few parameters, the first of which is the variable that will be used to hold that instance of the database. The second is the internal name of the database on the Palm itself. The last parameter allows you to specify on which card to open the database; if your device has more than one card this maybe useful. Once we call this method the result is returned to us. A return of 0 would mean that there were no problems opening the database; anything else means that there was an error. If no database exists, then we will simply create it. Figure 6 below lists all of the database result codes.
-1 EOF on DbGet
0 Operation Successful
1 Operation Failed
2 Key not found - next higher key returned
3 File opened in read only mode
513 Memory Error
514 Index Out Of Range
515 Invalid Parameter
516 Read Only
517 Database Open
518 Can't Open
519 Can't Find
520 Record In Wrong Card
521 Corrupt Database
522 Record Deleted
523 Record Archived
524 Not Record DB
525 Not Resource DB
526 ROM Based or invalid Database name
527 Record Busy
528 Resource Not Found
529 No Open Database
530 Invalid Category
531 Not Valid Record
532 Write Out Of Bounds
533 Seek Failed
534 Already Open For Writes
535 Opened By Another Task
536 UniqueID Not Found
537 Already Exists
538 Invalid Database Name
539 Database Protected
540 Database Not Protected
Figure 6 Database result codes
Also notice that when we call dbCreate(), we pass in our creator ID. This will allow us to capture that data during the synchronization process.
Take time now to explore the other database commands that you see in the code blocks above. Figure 7 shows a summary of each command.
DbClose Closes an open database
DbCreate Creates a database on the Palm OS device
DbCreateDatabasefromResource Creates a database from resource contained
in the project.
DbDelete Deletes a database record by key
DbErase Removes a database from the Palm OS device
DbFind Finds a database record by key
DbGet Reads values from the current database record
DbGetNoRecs Returns the number of records in a database
DbInsert Inserts a new record in a database by key
DbOpen Opens a database and initializes it for processing
DbPosition Locates a record by relative record number
DbPut Writes values to the current database record
DdbRea Reads a database record by key
DbReadNext Reads the next database record
DbReadPrev Reads the previous database record
DbReset Resets a database to the beginning record
DbUpdate Updates the contents of a database record by key
Figure 7: Database commands
Startup Code:
Once we have ensured the databases are created and opened, we can let the sub main() procedure finish normally, which will load the default form. You may want to take time and review the rest of the code in the "Startup Code" section. There are a few handy methods for updating the lists on the various forms, some for translating UDT values to strings, etc. Figure 8 below is a complete listing of my "Startup Code" block.
Type BlogEntry
subject as String 'subject of the entry
dDate as String 'date of the entry
entry as String 'the actuall full text entry
settingsName as String 'matching settings name
End Type
Type BlogSetting
Name as String 'name of the setting, key
isDefault as Integer 'if this is the default setting
sType as Integer 'type of setting it is
username as String 'username to use during auth
password as String 'password to use during auth
port as Integer 'what port is the remote service running on
domain as String 'what domain/ip is the remote server
path as String 'file path to the save location
filename as String 'ftp file name,
'http variable post name For xml upload
'or url end point
End Type
Type sTypes
WebLogApi as Integer
Ftp as Integer
Http as Integer
End Type
Sub main()
Global SettingTypes as sTypes
SettingTypes.WebLogApi=0
SettingTypes.Ftp=1
SettingTypes.Http=2
Dim jumptoSettings as Integer
jumptoSettings=0
Global blogdb as Database
Global settingsDB as Database
Global appActive as Integer
Global editID as String
editID=""
appActive=1
Dim res as Integer
res = dbopen(settingsDB, "blogSettings", 0)
If(res<>0) Then
MsgBox "You have not created any local settings. Nothing will be uploaded until you do so."
res = dbcreate(settingsDB, "blogSettings", 0, "PBlg")
res = dbopen(settingsDB, "blogSettings", 0)
jumptoSettings=1
End If
'open up the blogdb, else create a new one
res = dbopen(blogdb, "blogdb", 0)
If(res<>0) Then
MsgBox "Default Database not found, creating a new one..."
'failed, so we create a new one
res = dbcreate(blogdb, "blogdb", 0, "PBlg")
If(res<>0) Then
appActive=0
MsgBox "Error creating/opening Datbase. " + str(res)
Else
res = dbopen(blogdb, "blogdb", 0)
If(res<>0) Then
MsgBox "Error creating/opening Datbase. " + str(res)
Else
Dim newEntry as BlogEntry
newEntry.Subject="Welcome..."
newEntry.dDate = DateMMDDYY(Today()) + " " + HourMinAMPM(now())
newEntry.Entry="Welcome to the Palm Blog, Written by Robert Chartier (rob@santra.com)"
res=dbinsert (blogdb,newEntry.Subject,newEntry)
End If
End If
End If
If(appActive=1) Then
Call UpdateList()
End If
If(jumptoSettings=1) Then NextForm "SettingsForm"
End Sub
Sub UpdateSettingsList()
Dim x as Integer
Dim res as Integer
Dim key as String
res = dbReset(settingsDB)
SettingsList.clear
For x = 1 to dbGetNoRecs(settingsDB)
Dim existingEntry as BlogSetting
res = DbReadNext(settingsDB, key, existingEntry)
If (res = 0) Then
SettingsList.add existingEntry.name + " ("+GetTypeText(existingEntry.sType)+")"
Else
MsgBox "err:" + str(x)
End If
Next
End Sub
Sub UpdateSettingsListInEntry()
Dim x as Integer
Dim res as Integer
Dim key as String
res = dbReset(settingsDB)
SettingsPopup.clear
For x = 1 to dbGetNoRecs(settingsDB)
Dim existingEntry as BlogSetting
res = DbReadNext(settingsDB, key, existingEntry)
If (res = 0) Then
SettingsPopup.add existingEntry.name + " ("+GetTypeText(existingEntry.sType)+")"
Else
MsgBox "err:" + str(x)
End If
Next
End Sub
Sub UpdateList()
Dim x as Integer
Dim res as Integer
Dim key as String
res = dbReset(blogdb)
List1012.clear
For x = 1 to dbGetNoRecs(blogdb)
Dim existingEntry as BlogEntry
res = DbReadNext(blogdb, key, existingEntry)
If (res = 0) Then
List1012.add existingEntry.subject + " ("+existingEntry.dDate+")"
Else
MsgBox "err:" + str(x)
End If
Next
End Sub
Function GetSelectedSettingsKey() as String
Dim selectedName as String
selectedName= SettingsList.text(SettingsList.selected)
Dim pos as Integer
pos = instr(1,selectedName, "(",1 )
selectedName = left(selectedName, pos-2)
GetSelectedSettingsKey=selectedName
End Function
Function GetSelectedKey() as String
Dim selectedName as String
selectedName= List1012.text(List1012.selected)
Dim pos as Integer
pos = instr(1,selectedName, "(",1 )
selectedName = left(selectedName, pos-2)
GetSelectedKey=selectedName
End Function
Function GetTypeText(TheType as Integer) as String
Select Case TheType
Case SettingTypes.WebLogApi:
GetTypeText="WebLogApi"
Case SettingTypes.Ftp:
GetTypeText="Ftp"
Case SettingTypes.Http:
GetTypeText="Http"
Case Else:
GetTypeText=""
End Select
End Function
Function GetTypeInteger(TheType as String) as Integer
Select Case lcase(TheType)
Case "weblogapi":
GetTypeInteger=SettingTypes.WebLogApi
Case "ftp":
GetTypeInteger=SettingTypes.Ftp
Case "http":
GetTypeInteger=SettingTypes.Http
Case Else:
GetTypeInteger=-1
End Select
End Function
Sub SelectEditBlogSettingsDrop(name as String)
If(name<>"") Then
Dim count as Integer
Dim x as Integer
Dim pos as Integer
Dim selectedName as String
count = SettingsPopup.NoItems
name = trim(name)
For x = 0 to count
selectedName = PullKey(SettingsPopup.ItemText(x))
If(selectedName=name) Then
SettingsPopup.Selected = x
Exit For
End If
Next
End If
End Sub
Function PullKey(selectedName as String) as String
Dim doneName as String
donename=selectedName
Dim pos as Integer
If(selectedName<>"") Then
pos = instr(1,selectedName, "(",1 )
If(pos>0) Then
selectedName = trim(left(selectedName, pos-2))
pos = instr(1,selectedName, "(",1 )
doneName=selectedName
End If
End If
PullKey=doneName
End Function
Figure 8: Startup Code
Manage Blogs Form:
The default form in this application is the "Manage Blogs" form. As you have seen previously it is simply a list of items pulled out of the "blogdb" database. If you right click the list and choose "View Code", you will see that this is where we would want to trap the event where someone clicks an item in the list. Figure 9 is my code to handle a selected list item.
Sub object1012()
If(List1012.selected>0) Then
editID=GetSelectedKey()
EditBlogForm.Clear
NextForm "EditBlogForm"
Else
MsgBox "Select an item to Edit first."
End If
End Sub
Figure 9: Handling a list selection
Most of this should be pretty clear. The "GetSelectedKey()" can be found in the startup code block. There are two items to pay attention to in this code block. The first is the EditBlogForm.Clear member call. This instructs the form to clear all of its internal items. All fields, popups, etc. will be cleared out. Next is the command "NextForm". This instructs the program to change to a different form within the application; you need to only give its name.
The Menu System:
In the Project Explorer you will see "Menus" in the tree. If you right click this, you can choose "Add Menu". Here you can manually enter and configure each menu item you want in your application. For this application I have created one menu system called "MainMenu", which is essentially the parent of other actual menus. Within that I created an "Options" menu which has three actual menu items "Settings", "About", and "Reset". By double clicking each of these items within the Project Explorer, you will be able to specify code in each that will be called when that item is selected.
If you right click on the Managed Blogs form (not on any child object) and choose "View Events Code", you will see the following block of code:
Sub Form1004_Event()
Dim res as Integer
Dim c as String
res=getEventType()
If Not res=nsbKeyOrButton Then Exit Sub 'ignore penup/down events
c=getKey()
If c=&h5 Then
MenuDraw "MainMenu"
SetEventHandled
EndIf
End Sub
Figure 10: Managed Blogs Event Hanlder, the Menu trigger.
This block of code will be executed whenever an event occurs on the form. Let's say the user clicks on the form, or the menu area on the top right, or even the pen events. We will first see the getEventType() method, which simply will return the event number that triggered this special event to process. This gives us the ability to determine which event it was and what to do with it. The "getKey()" method returns a string of the last key or button that was pressed. If you examine figure 11 below, you can see the item we are trying to capture is 5, "menu key on silkscreen area", and if chosen, draw our "MainMenu", and then instruct the system that event was handled.
1 hard button #1 on bottom of case
2 hard button #2 on bottom of case
3 hard button #3 on bottom of case
4 hard button #4 on bottom of case
5 menu key on silkscreen area
6 command in Grafitti area
17 application launch key on silkscreen area
7 find key on silkscreen area
16 calculator key on silkscreen area
11 up button
12 down button
14 power on/off button
15 cradle hotsync button
18 auto power off
Figure 11: Common Event Keys
The last piece of the puzzle is the "New" button. Double click it to view the code (Figure 12). Notice it simply clears the form and then switches back to it.
Sub object1014()
EditBlogForm.clear
NextForm "EditBlogForm"
End Sub
Figure 12: New button implementation
Edit Blog Form:
The edit blog form is the last form to examine for this article. You can review the rest of the project and see exactly what I have done to complete it. On this form we see a few fields for users to enter values, a few buttons to take some action, and a popup at the top so that they can choose with which setting or profile they wish to have this entry uploaded. The only real guts of this form is hidden behind each button, so let's take a closer look.
The "Cancel" button essentially takes no action, except for redirecting the form back to the main "ManageBlogsForm". The "Delete" button will take the existing entry, and based on the subject line, delete it out of the blogsdb database. Figure 13 is the complete source listing for the button event.
Sub object1112()
Dim key as String
Dim res as Integer
Dim be as BlogEntry
key = SubjectField.Text
res = dbReset(blogdb)
res=dbfind(blogdb, key)
res=dbread(blogdb, key, be)
If(res=0) Then
res = dbdelete(blogdb, key)
If(res=0) Then
MsgBox key + " has been deleted."
Else
MsgBox key + " could not be deleted."
End If
Else
MsgBox key + " could not be deleted."
End If
res = dbReset(blogdb)
editid=""
Call UpdateList()
NextForm "ManageBlogsForm"
End Sub
Figure 13: Deleted a Blog Entry
As you should know by now, the dbReset simply resets the database back to the starting pointer; the dbfind() allows us to search in the database based on a key; and the dbread allows us to read the record back into our UDT. I have in place special conditions to handle some of the more common errors and to simply display a message if any occur.
Lastly the "Save" button takes the current items on the form, plugs them into an instance of our UDT, and then calls dbInsert() to save the item into the database. Figure 14 below is the button event handler code.
Sub object1023()
If(SubjectField.Text<>"") Then
Dim newEntry as BlogEntry
newEntry.Subject=SubjectField.text
newEntry.dDate = DateField.text
newEntry.Entry=EntryField.text
newEntry.settingsName = PullKey(SettingsPopup.ItemText(SettingsPopup.Selected))
Dim newid as String
Dim res as Integer
If(editid="") Then
newid = SubjectField.text
res=dbinsert(blogdb, newid, newEntry)
Else
res=dbdelete(blogdb, editid)
editid=SubjectField.text
res = dbinsert(blogdb, editid, newentry)
res = dbReset(blogdb)
End If
If(res=0) Then
editid=""
Call UpdateList()
MsgBox "Saved"
NextForm "ManageBlogsForm"
Else
MsgBox "Not Saved:" + str(res)
End If
Else
MsgBox "You must enter a unique subject"
End If
EditBlogForm.Clear
End Sub
Figure 14: Saving a Blog Entry
Conclusion
In this article you have learned some basics of using NS Basic to create a Palm application. I hope that you find it useful. The next part of this series will show how to work with the synchronization process and how to handle the data transfer, import, and upload to various sites that you specify in the application using C#. Finally in the third part we will cover what is needed to package up the entire Palm application and conduit for distribution in an installer.
References
NSBasic Reviewers Guide: http://www.nsbasic.com/palm/info/ReviewersGuide.htm
What is a blog? http://www.globeandmail.com/series/dot-com/blog02.html
WebLogAPI: http://www.xmlrpc.com/discuss/msgReader$2198
Palm: http://www.palm.com
Palm Creator ID Information: http://dev.palmos.com/creatorid/
About the Author
Robert Chartier has developed IT solutions for more than nine years with a diverse background in both software and hardware development. He is internationally recognized as an innovative thinker and leading IT architect with frequent speaking engagements showcasing his expertise. He's been an integral part of many open forums on cutting-edge technology, including the .NET Framework and Web Services. His current position as vice president of technology for Santra Technology (http://www.santra.com) has allowed him to focus on innovation within the Web Services market space.
He uses expertise with many Microsoft technologies, including .NET, and a strong background in Oracle, BEA Systems, Inc.'s BEA WebLogic, IBM, Java 2 Platform Enterprise Edition (J2EE), and similar technologies to support his award-winning writing. He frequently publishes to many of the leading developer and industry support Web sites and publications. He has a bachelor's degree in Computer Information Systems.
Robert Chartier can be reached at rob@santra.com.
|