Palmphi
A MemoPad application step by step


The MemoPad application with Palmphi

In this tutorial we will try to recreate the functionality of the MemoPad standard application provided with each Palm.
I will assume that this is not your first program with Palmphi and that you have already tried some tutorials and know what I mean when I say form, object inspector, etc.

To follow this tutorial please get at least the version 1.10

Step 1. The main list

First, create a new TForm and change the caption to 'Memo list'. Try to recreate the layout of the original MemoPad. Use 10 VisibleItems in the listbox. Add also  a "New" button under the list to allow the user insert new memos. When ready go to the File menu and save the form as "FrmMemoList.c". The name of the Form changes accordingly.

Define a global variable (I know you don't like it, but for this example is easier so) for the memo :

TPalmDB MemoDB;

Place it before anything else on the first form (A global variable on the top of the first form is visible for all forms).

Now we have to open the database when starting the program. Go to the event list of the FrmMemoList and double click on the value of OnOpen.

To change the items of the list at runtime, we need to assign a comma delimited list of options to the Items property of the TListBox. So we will need a holder for the items. Declare this on the top of the list:

Char Memos[500]="";

500 bytes should be enough to contain all the headers of the memos. You can use more if you want. You can allocate it dynamically if you are for elegancy instead of for easiness.

We'll need a temporary holder for each Title :

Char Title[50];

Now open the database using what you learnt on Tutorial 3:

MemoDB.OpenRW('memo');

Now we iterate every record to extract the title, which is the first line of the memo. A line ends always with the character 0xA. So following code should do it :

       Int16 size=MemoDB.RecordSize(i);     // Get the memo size
       Char *cut,TmpStr[35];
       MemoDB.Read(i,Title,50);             // We read the first 50 characters of the memo
       cut=StrChr(Title,10);                // We look for the end of the line
       if (cut) *cut=0;                     // If found, we set the end of the string at this point
       if (size>28)                         // For too long lines we cut at the 28th character
          Title[28]=0;
       if (StrLen(Memos)>0) StrCat(Memos,",");    // We append a comma to delimite the last item from this one  
       sprintf(TmpStr,"%d. %s",i+1,Title);        // We create a header, which is the number of the record and the title
       StrCat(Memos,TmpStr);                // We append what we got to our Memo items string

The iteration occurs by looping from 0 to MemoDB.RecordCount()-1

After we are ready we assign the result to the TListBox :

    ListBox6.Items=Memos;


And we redraw the list to show the changes :

    ListBox6.Draw();


NOTES

As you probably guessed, if the title of the memo contains a comma it will be interpreted as an item separator, to avoid this, you should replace the commas :
       for(j=0;j<StrLen(Title);j++)
          if (Title[j]==',') Title[j]=' ';


So the code could look like this after writing everything :

EVENT FrmMemoList_OnOpen(EventPtr event)
{
    Char Memos[500]="";
    Char Title[50];
    Int16 i,j;
    MemoDB.OpenRW('memo');
    for(i=0;i<MemoDB.RecordCount();i++)
    {
       Int16 size=MemoDB.RecordSize(i);
       Char *cut,TmpStr[35];
       MemoDB.Read(i,Title,50);
       cut=StrChr(Title,10);
       if (cut) *cut=0;
       if (size>28)
          Title[28]=0;
       for(j=0;j<StrLen(Title);j++)
          if (Title[j]==',') Title[j]=' ';
       if (StrLen(Memos)>0) StrCat(Memos,",");
       sprintf(TmpStr,"%d. %s",i+1,Title);
       StrCat(Memos,TmpStr);
    }
    ListBox6.Items=Memos;
    ListBox6.Draw();
}


You can now run the application and see that all the memos are listed in your new program.


Step 2. The Memo contents


Now we need another form to show the contents of a memo.

Press the "new form" button labelled as frm in the object inspector. Save this form as FrmMemoContents.c

This Form must have SaveBehind property set to true, as we want to come back and forth showing memos and coming back to the list. Add a new TEdit component with MultiLine and Underlined set to True. Make it long enough to be able to display several lines.

Add a button to come back to the first form, label it "Done". This button should call FrmMemoContents.Back() to go back to the first form.
On the first form clicking on a item in the list (OnClick event) will call FrmMemoContents.PopUp()

Create again a global variable to hold the value of the clicked item. Name it SelectedItem and before opening the FrmMemoContents TForm assign the selected item to this variable:
    SelectedItem=ListBox6.ItemIndex();

You shouldn't use member functions of controls in one form from another form, hence we use a temporary global variable to do this.

Loading the contents

You've done this before ... remember loading the titles? This is easier, because we don't have to process the contents, just display them. Just do following :

   Char MemoContents[2048];
   MemoDB.Read(SelectedItem,MemoContents,2048);
   Edit8.Text=MemoContents;


Once again, you could make it better, by reserving the necessary memory for the record instead of reserving the maximum size (2048). Dynamic memory is handled with MemPtrNew and MemPtrFree.

Edit8 is the TEdit in the second TForm. For your program the numeration can vary.

If you run now the program, you should be able to see the contents of each memo by clicking on the list.

Step 3. Saving changes

I recommend you backing up your data if you have something important before continuing

As with the standard MemoPad, we want to get our changes saved when pressing "Done". To do this we just have to change the record size of the current memo to fit the new length of the TEdit. This can be done so :
   MemoDB.RecordSize(SelectedItem)=Edit8.Length();
Now the record is prepared to hold the new data. The function Modify uses the same parameters as Read. And this would look like this :
   MemoDB.Modify(SelectedItem,Edit8.Text(),Edit8.Length());

If you add this to lines before the call to Back your changes will be saved when you press "Done"

You can now run the program and see if the data is saved when pressing done.

Step 4. Button new

Now we have to add the last functionality. Creating a new memo when pressing "New" button.

There are many ways to do this. I will just set SelectedItem to the number of records and check in the second form for this value. You could as well use another value or a flag.

So the code for pressing the new button would be :
   SelectedItem=MemoDB.RecordCount();
   FrmMemoContents.PopUp();

When opening the second form, we shouldn't try to load a record which does not exist :
   Char MemoContents[2048];
   if(SelectedItem==MemoDB.RecordCount()) {
      Edit8.Text="";
   } else {
      MemoDB.Read(SelectedItem,MemoContents,2048);
      Edit8.Text=MemoContents;
   }

Similarly, when pressing the button "Done" we shouldn't try to call Modify on an unexistent record :
   if(SelectedItem==MemoDB.RecordCount()) {
               MemoDB.Insert(SelectedItem,Edit8.Text(),StrLen(Edit8.Text()));
   } else {
               MemoDB.RecordSize(SelectedItem)=Edit8.Length();
               MemoDB.Modify(SelectedItem,Edit8.Text(),Edit8.Length());
   }
   FrmMemoContents.Back();


I don't know why StrLen(Edit8.Text()) is not the same as Edit8.Length(). I just tried it and FldGetTextLength didn't return the right value. If anyone knows why please tell me ...

Anyway, now we have a full working memo. The full listing of the project can you see here :


EVENT ListBox6_OnClick(EventPtr event)
{
    SelectedItem=ListBox6.ItemIndex();
    FrmMemoContents.PopUp();
}

EVENT FrmMemoList_OnOpen(EventPtr event)
{
    Char Memos[500]="";
    Char Title[50];
    Int16 i,j;
    MemoDB.OpenRW('memo');
    for(i=0;i<MemoDB.RecordCount();i++)
    {
       Int16 size=MemoDB.RecordSize(i);
       Char *cut,TmpStr[35];
       MemoDB.Read(i,Title,50);
       cut=StrChr(Title,10);
       if (cut) *cut=0;
       if (size>28)
          Title[28]=0;
       for(j=0;j<StrLen(Title);j++)
          if (Title[j]==',') Title[j]=' ';
       if (StrLen(Memos)>0) StrCat(Memos,",");
       sprintf(TmpStr,"%d. %s",i+1,Title);
       StrCat(Memos,TmpStr);
    }
    ListBox6.Items=Memos;
    ListBox6.Draw();
}

EVENT Button7_OnClick(EventPtr event)
{
   SelectedItem=MemoDB.RecordCount();
   FrmMemoContents.PopUp();
}
EVENT Button10_OnClick(EventPtr event)
{
   if(SelectedItem==MemoDB.RecordCount()) {
       MemoDB.Insert(SelectedItem,Edit8.Text(),StrLen(Edit8.Text()));
   } else {
       MemoDB.RecordSize(SelectedItem)=Edit8.Length();
       MemoDB.Modify(SelectedItem,Edit8.Text(),Edit8.Length());
   }
   FrmMemoContents.Back();
}


EVENT FrmMemoContents_OnOpen(EventPtr event)
{
   Char MemoContents[2048];
   if(SelectedItem==MemoDB.RecordCount()) {
      Edit8.Text="";
   } else {
      MemoDB.Read(SelectedItem,MemoContents,2048);
      Edit8.Text=MemoContents;
   }
}



Step 5. Scrolling (thanks Bill McAllister for the idea)

You may have noticed than looking the memos without a scroll bar is not very comfortable.
From the version 1.10 there is a possibility to link easily a TScrollBar and a TEdit. Simply create a TScrollBar and place it next to the TEdit.
Then write in the OnOpen event :

   ScrollBar9.GetValuesFrom(Edit8);


This method initializes the values of the ScrollBar according the length and page size of the TEdit

And now in the OnScroll event of the ScrollBar write following :

   Edit8.ScrollFrom(ScrollBar9);

This gets the values of the TScrollBar and scrolls the TEdit consequently.

UPDATE
From version 1.11 you have the possibility to update the TScrollBar indicator each time the user press a key (or introduces a character through graffiti). The code would look like this :

EVENT FrmMemoContents_OnKeyPress(WChar chr,UInt16 Modifiers)
{
   if (FrmMemoContents.FocusedControl()==Edit8) {
      ScrollBar9.GetValuesFrom(Edit8);
   }
}




The source code of this project can be downloaded here