Глава 6. Разработка программ для Palm OS

В предыдущих частях книги рассматривались собственно карманные компьютеры и различные программы для них. Однако, рассказывая об эффективном использовании карманных компьютеров, нельзя не коснуться вопроса о написании собственных программ. В некоторых случаях возможностей стандартных программ не хватает для какой-либо задачи. Особенно это может быть актуально для российских пользователей, поскольку российских разработок очень мало — российские программисты, разрабатывающие программы для КПК, существуют, но разрабатывают программные продукты в основном для зарубежных заказчиков. Собственно говоря, это и неудивительно, поскольку рынок карманных компьютеров в России почти не развит, и на серьезный сбыт продукции в России рассчитывать не приходится. Например, по информации из конференции fido7.ru.palmtop в 2001 году всего было продано около 20 000 карманных компьютеров, причем, преобладали три различных типа — Palm (около 40%), PocketPC (около 40%) и Psion (15%), плюс некоторое количество других моделей (например, Casio Pocket Viewer со своей операционной системой).
Вообще говоря, разработка программ для КПК сопряжена с рядом сложностей.

Тем не менее, программирование само по себе является очень интересным занятием, и изучение принципов разработки программ для карманных компьютеров будет весьма интересно и полезно. Возможно, кому-то это позволит интересно провести за карманным компьютером свое свободное время, кому-то позволит найти новую, более высокооплачиваемую работу. В общем, новые знания никогда не помешают, и есть надежда, что даже принесут пользу.

По традиции стоит начать часть III книги именно с рассмотрения средств разработки для карманных компьютеров Palm, поскольку эти компьютеры исторически появились первыми.
Сразу стоит отметить, что эта книга ни в коем случае не призвана заменить "классические" книги по программированию. Эта книга вряд ли заменит такие объемные труды, как трехтомник Кнута "Искусство программирования" или описание языка C++, сделанное Страуструпом. Все-таки программирование — это не только ремесло, но в большей степени, искусство, и даже только одно" описание языка C++ и всех его возможностей может занять целую книгу или даже не один том. Поэтому те, кто захочет изучать главы о программировании более подробно, могут приобрести соответствующую литературу.
Для понимания этой части книги от читателя не требуется каких-либо специальных знаний. Единственное, что нужно знать для понимания написанного кода, — это язык С. Все фрагменты кода написаны с предположением о том, что язык С знаком читателю. Цель этой части книги — рассказать об особенностях написания программ для Palm OS и привести конкретные примеры, которые значительно облегчат читателю процесс создания собственной программы для Palm. К сожалению, специальной русскоязычной литературы по программированию для Palm пока еще нет, поэтому при самостоятельном изучении пришлось бы потратить немало времени для изучения особенностей Palm OS. Цель данной главы как раз состоит в том, чтобы рассказать об этих особенностях и сэкономить читателю время. Представленной здесь информации достаточно, чтобы создать вполне законченную и работоспособную на карманном компьютере Palm программу.
А тем, кто захочет всерьез заняться программированием для карманных компьютеров Palm, можно посоветовать найти какие-либо книги из приведенных в табл. 6.1. Для поиска цен использовался популярный зарубежный интернет-магазин www.amazon.com.

Таблица 6.1. Англоязычные книги по программированию для Palm Стоимость Описание

Название Стоимость Описание
Neil Rhodes, Julie McKeehan $27,97 Объем: 702 страницы.
Palm OS Programming: The Developer's Guide, 2-е издание                    Издательство: O'Reilly & Associates, ISBN: 1565928563
Greg Winton, Troy Mott $27,97 Объем: 456 страниц.
Palm OS Network Programming                        Издательство: O'Reilly & Associates, ISBN: 0596000057
Lonnon R. Foster $31,49 Объем: 893 страницы.
Palm OS Programming Bible                       Издательство: John Wiley & Sons, ISBN: 0764546767.
                                    В книгу включен CD-ROM
Eric Giguere $38,50 Объем: 432 страницы.
Palm Database Programming: The Complete Developer's Guide                      Издательство: O'Reilly & Associates, ISBN: 0596002009
Nicholas Pleis $49,99 Объем: 423 страницы.
Palm OS Game Programming                       Издательство: Premier Pr, ISBN: 1931841195.
                                                      В книгу включен CD-ROM

Можно лишь с сожалением отметить очень высокий уровень цен на книги, продаваемые за рубежом — цены на книги "там" значительно превосходят стоимость книг в России. Также нужно иметь в виду, что при необходимости доставки книг в Россию их цена возрастет еще больше. Определенные сложности могут возникнуть, наверное, и с оплатой книг, но вроде через российский интернет-магазин www.books.ru можно заказать и купить англоязычную литературу.

6.1. Средства разработки для Palm OS
Говоря о разработке программ, естественно надо начать с главного — средств разработки. Ведь именно с их помощью исходный код на языке высокого уровня преобразуется в исполняемый программный файл. Конечно, когда-то давно программы разрабатывали прямо в кодах процессора (автору довелось немного заниматься этим еще на компьютерах ZX Spectrum), но к счастью, в настоящее время это уже в прошлом — сейчас имеются весьма приличные среды для разработки, да и возможности операционных систем уже гораздо выше. За последние несколько лет средства разработки программ прошли немалый путь. В целом, можно выделить несколько этапов.

Говоря о программировании для Palm, можно заметить, что средства разработки пока еще находятся лишь во второй фазе развития — возможности операционной системы Palm OS примерно соответствуют уровню развития системы Windows 3.1 для настольных компьютеров. Сами системы разработки следует рассмотреть более подробно.

6.1.1. GCC
Это единственное бесплатное средство разработки, доступное для Palm OS. Компилятор GCC — это бесплатный компилятор, распространяемый по лицензии GNU. Этот компилятор вызывается из командной строки и позволяет создавать программы, используя языки С и C++. Понятно, что подобный способ создания программ очень неудобен, но к сожалению, это единственный бесплатный компилятор для Palm OS.
Переписать этот компилятор можно с сайта www.palmos.com.

6.1.2. Code Warrior
Это вполне современная интегрированная среда разработки для Palm. CodeWarrior предлагает пользователю встроенные редактор, отладчик, визуальную систему конструирования форм, что позволяет создавать программы гораздо более легко и удобно. К сожалению, эта система является платной и довольно дорогой — ее стоимость составляет несколько сотен долларов. На лотках иногда можно встретить версию CodeWarrior 6.0 (на компакт-дисках серии "Компьютер на ладони"), но эти диски бывают довольно редко, к тому же, сейчас давно уже выпущена новая версия 8.0.
Недостатком CodeWarrior можно назвать "несовместимость" по внешнему виду и "горячим" клавишам управления со средой разработки Microsoft Visual Studio, поэтому тем, кто привык к Visual Studio, пользоваться CodeWarrior не столь удобно.

6.1.3. Falch.net Developer Studio
Система Falch.net Developer Studio сразу понравилась автору этой книги тем, что она похожа на систему разработки программ от Microsoft. Создатели сделали свою систему внешне очень похожей на Visual Studio, что довольно удобно. В дальнейшем все программы, представленные в книге, будут создаваться именно при помощи этой системы.
Взять последнюю версию программы можно по адресу ftp://ftp.falch.net/pub,
на компакт-диске, прилагаемом к книге, представлена версия 2.5.2.0. Она является демонстрационной и ограничена 30-дневным сроком использования.

6.2. Основные понятия, используемые при разработке
6.2.1. Элементы пользовательского интерфейса

Давным-давно, еще во время использования MS-DOS, операционная система предоставляла пользователям лишь самые основные возможности — работу с файлами (чтение, запись), простейший вывод информации на экран (вывод текста в определенную позицию, вывод точки на экран в графическом режиме) и т. д. Все более сложные элементы программист разрабатывал самостоятельно или брал из уже написанных библиотек.
В настоящее время подобные сложности уже позади — операционная система предоставляет разработчикам довольно большое количество стандартных элементов пользовательского интерфейса. Благодаря этому были достигнуты два основных преимущества: во-первых, разработка программ стала значительно проще, а во-вторых, пользовательский интерфейс стал стандартным. Поэтому освоить незнакомую программу стало гораздо проще, например, известно, что все программы для Windows имеют меню в верхней части, строку подсказки в нижней и кнопку закрытия справа сверху. В системе Palm OS ситуация примерно такая же.
Из основных, наиболее часто используемых элементов можно выделить несколько.

Полный список элементов интерфейса можно посмотреть в документации по операционной системе.

6.2.2. Некоторые базовые термины
Событие (Event). Событие определяет некоторое изменение состояния, произошедшее с программой. В Palm OS различают низкоуровневые (low-level) события, пришедшие от аппаратуры, например, от нажатия пером на экране или нажатия кнопки на карманном компьютере, и высокоуровневые события, например, выбор пользователем некоторого пункта меню. Событие является одним из основных моментов, требуемых для понимания работы программ в операционной системе Palm OS — при запуске программа постоянно выполняет цикл обработки событий, воспринимающий все, что происходит в окружении программы.
Ресурс (Resource). Ресурс представляет собой блок данных, содержащий тип ресурса и уникальный идентификатор. Очень многие элементы программы хранятся в виде ресурсов — имя приложения, формы, элементы интерфейса, значки, меню и т. д. При "ручном" составлении программы с использованием компилятора командной строки все описания ресурсов пришлось бы создавать самостоятельно, но современные среды разработки могут брать это на себя.
База данных (Database). В Palm OS различают базы данных ресурсов и записей. Как нетрудно догадаться, база данных ресурсов хранит в себе ресурсы, используемые программой. База данных записей хранит данные, с которыми работает программа. Особенность файловой системы Palm OS состоит в том, что в ней нет файловой системы в традиционном понимании — все данные хранятся лишь в виде записей базы данных.

6.3. Исследование простейшей программы для Palm OS
6.3.1. Настройка среды разработки Falch.net Developer Studio

Для начала необходимо установить на компьютер саму среду разработки. Дистрибутив демонстрационной версии представлен на компакт-диске, прилагаемом к этой книге. Для установки программы необходимо получить на сайге www.falch.net регистрационный ключ. Для этого нужно в разделе сайта Demo Download ввести имя пользователя и адрес электронной почты, после этого на указанный адрес будет выслан регистрационный ключ. После активизации ключа программа будет работоспособной в течение месяца. Лицензионная версия стоит $200, но для ознакомительных целей можно продолжить использование программы, просто переведя часы компьютера назад перед запуском программы, и вернув прежнее время после окончания работы.
Вторым шагом необходимо установить соответствующие файлы, необходимые для успешной компиляции программы. Для этого нужно переписать с сайта www.palmos.com так называемый Palm OS SDK, который находится по адресу http://www.palmos.com/dev/tools/sdk/sdk40.html. По этому адресу находится лицензионное соглашение, которое нужно принять, нажав кнопку Accept, после чего откроется страница, содержащая ссылки на загрузку. На этой странице нужно выбрать Palm OS SDK 4.0. Размер инсталляционного файла составляет около 4 Мбайт. После распаковки архива будет создано несколько каталогов, нас будет интересовать подкаталог Sdk-4\Include.
Заключительным шагом в настройках Falch.net (Tools-Settings-Library Presets) необходимо указать пути к созданному каталогу. Нужно добавить каталоги Include, Include\Core, Include\Core\Hardware, Include\Core\System и Include\ Core\UI.

6.3.2. Создание проекта в Falch.net Developer Studio
На первый раз рассмотрим процесс создания программы подробно, по шагам.
В меню File выбираем пункт New. Поскольку нам нужна отдельная программа, выбираем Palm OS Framework Project. После этого выдаются различные запросы, выбираем имя проекта FirstApp, указываем путь на диске, по которому проект будет создан. Далее идет запрос на создание форм, но одна форма в проекте уже есть, поэтому просто нажимаем кнопку Next. Далее следует отметить, в каких формах должно быть меню, устанавливаем флажок напротив единственной формы frmMain. Следующим шагом идет запрос об информационных окнах (Alerts). Добавляем одно информационное сообщение с названием Proglnfo. Следующим шагом идет запрос версии операционной системы (Target Palm SDK). По умолчанию стоит версия 4.0, но большая часть карманных компьютеров работает под управлением 3.5, поэтому выбираем SDK 3.5. Здесь же можно выбрать значок (Icon) приложения, для примера был выбран значок с именем Wheel. Отмечаем заодно флажок Create C++ project, т. к. для серьезных проектов использование языка C++ все-таки предпочтительнее.
Вот и все, простейшая программа создана. Можно заметить, что в папке проекта в подкаталоге Debug появился файл FirstApp.prc, который является исполняемым файлом, готовым к запуску на карманном компьютере Palm. Функциональность программы пока минимальна — имеются пустое окно программы и меню с различными пунктами, которое можно открывать, но никаких действий при этом пока не происходит. Но в любом случае это уже готовая программа, способная к работе под операционной системой Palm OS, различные компоненты программы стоит рассмотреть более подробно.

6.3.3. Компоненты программы
Рассмотрим последовательно различные файлы, созданные автоматически при помощи Falch.net.

FirstApp_res.hpp
Файл, описывающий ресурсы программы. Как уже упоминалось выше, с помощью ресурсов описываются все элементы, используемые программой (кнопки, элементы меню и т. д.). Такие описания хранятся именно в этом файле. Его строки показаны в листинге 6.1.

Листинг 6.1. Описание ресурсов

#define frmMain 1
#define Proglnfo 1
#def ine mnufrmMain 1
#define mnuClose 1000
#def ine mnuUndo 1001
#def ine mnuCut 1002
# define mnuCopy 1003
#define mnuPaste 1004
#define mnuSelectAll 1005
#define mnuKeyboard 1006
#define mnuGraf f itiHelp 1007
#define mnuAbout 1008

FirstApp.hpp
В файле FirstApp.hpp описаны функции ApplicationHandleMenu И rmMain_HandieEvent, которые будут рассмотрены ниже.

Main.cpp
Этот файл содержит ряд важных основных функций, которые стоит рассмотреть более подробно.

Функция PilotMain
Вид функции PilotMain показан в листинге 6.2. Эта функция является основной точкой входа в программу — с выполнения этой функции начинается работа программы в операционной системе Palm OS. Как нетрудно видеть по листингу функции, в ней выполняются три основные задачи — startAppiication (запуск программы), EventLoop (цикл обработки сообщений) и stopAppiication (завершение работы программы).

Листинг 6.1. Описание ресурсов

UInt32 PilotMain(UIntl6 cmd, void *cmdPBP, UIntl6 launchFlags)
{
Err error ;
switch (cmd)
{
case sysAppLaunchCmdNormalLaunch :
// Application start code error = StartApplication () ;
if (error)
return error;
// Maintain event loop EventLoop ( ) ;
// Stop application StopApplication ( ) ;
break;
default:
break;
}
return 0;
}

Функции StartApplication и StopApplication
Функции StartApplication и StopApplication показаны в листинге 6.3.

Листинг 6.3. Функции StartApplication и StopApplication

static int StartApplication(void)
{
FrmGotoForm ( f rtnMa in ) ;
return 0;
}
static void StopApplication (void)
{
// Insert stop code here
FnnCloseAllForms ( ) ;
}

Видно, что назначение этих функций простое — первая функция активизирует основную форму приложения, а вторая, наоборот, закрывает все формы.

Функция EventLoop
Вид этой функции показан в листинге 6.4. Как уже упоминалось, эта функция является одной из основных. Из описания функции видно, что в ней происходит бесконечный цикл, в котором выбираются сообщения трех видов: системных (в функции SysHandieEvent), сообщений от меню (в функции MenuHandieEvent) и сообщений, создаваемых внутри самой программы (обрабатываемых в функции ApplicationHandleEvent).

Листинг 6.4. Функция EventLoop


static void EventLoop(void)
{
Err error;
EventType event;
// Main event loop
do
{
// Get next event
EvtGetEvent(&event, evtWaitForever);
// Handle event
if (!SysHandieEvent(&event))
{
if (!MenuHandieEvent(0, &event, &error))
{
if (!ApplicationHandleEvent(Sevent))
FrmDispatchEvent(sevent);

}

}

}

while (event.eType != appStopEvent);
}

Функция SysHandieEvent всегда должна присутствовать первой. Она обеспечивает обработку системных сообщений, например, корректную обработку нажатий кнопок карманного компьютера. Без вызова этой функции программа не сможет работать корректно.

Функция ApplicationHandleEvent
Вид этой функции представлен в листинге 6.5.

Листинг 6.5. Функция ApplicationHandleEvent

static Boolean ApplicationHandleEvent(EventPtr event)
{
UIntl6 formID;
FormPtr form;
Boolean handled = false;
// Application event loop switch (event->eType)
{
case menuEvent:
// Set event handler for application
handled = ApplicationHandleMenu(event->data.menu.itemID);
break;
case frmLoadEvent:
// Handle form load events
formID = event->data.frmLoad.formID;
form = FrmlnitFormfformID);
FrmSetActiveForm(form);
switch (formID)
{
case frmMain:
// Установить обработчик сообщений для frmMain
FrmSetEventHandler(form,
(FormEventHandlerPtr)frmMain_HandleEvent),
break;
default:
break;
}
handled = true;
break;
default:
break;
}
return handled;
}

Важно обратить внимание на вызовы функции AppiicationHandieMenu, которая отвечает за активизацию различных пунктов меню программы, и функции FrmsetEventHandier, которая устанавливает обработчик для сообщений, приходящих к форме frmMain. Как будет показано ниже, при добавлении в программу новых форм и пунктов меню придется обращаться именно к этим функциям.

Menu.cpp
Как нетрудно догадаться, этот файл отвечает за поддержку функционирования меню. В созданной автоматически "пустой" программе этот файл содержит лишь одну функцию, показанную в листинге 6.6.

Листинг 6.6. Функция ApplicationHandleMenu

Boolean AppiicationHandieMenu(UIntl6 menuID)
{
Boolean handled = false;
switch (menuID)
{
default:
break;
}
return handled;
}

Видно, что эта функция пока ничего не делает. Сюда будут добавляться обработчики, соответствующие вызовам различных пунктов меню.
Здесь рассмотрены не все функции, описанные в проекте. Полностью весь проект можно посмотреть непосредственно в среде разработки, на прилагаемом к книге компакт-диске он представлен под названием FirstApp.

6.4. Дальнейшая модификация программы
Продолжим развивать и совершенствовать этот проект, чтобы программа обретала все больше и больше новых возможностей. В конце работы мы получим вполне интересную и функциональную программу.

6.4.1. Добавление новой формы в программу
Для начала добавим в программу важнейшую часть — пункт About. Чтобы никто не мог усомниться, откуда же эта программа взялась. Меню программы уже заботливо создано при помощи Falch.net, и в нем уже есть пункт About, при выборе этого пункта как раз и нужно будет выводить наше окно.
Рассмотрим этот процесс последовательно.

Добавление обработчика меню
Как уже упоминалось выше, за обработку сообщений от меню отвечает функция ApplicationHandleMenu, показанная в листинге 6.6. Пока еще эта функция пустая и ничего не делает. Добавим в файл menu.cpp следующие строки, как показано в листинге 6.7.

Листинг 6.7. Обработка выбора пункта меню About в файле Menu.cpp
// --- этот код мы добавили:
static Boolean mnuAbout_OnSelect(UIntl6 menuID)
{
// The "About" menu item was selected.
// Pop up frmAbout.
FrmPopupForm(frmAbout);
return true;
}// этот код мы добавили.
Boolean ApplicationHandleMenu(UIntl6 menuID)
{
Boolean handled = false;
switch (menuID)
// --- этот код мы добавили:
// Choose Menu->About:
case mnuAbout:
handled = mnuAbout_OnSelect(menuID);
break; // этот код мы добавили.
default:
break;
}
return handled;
}

Видно, что в функцию ApplicationHandieMenu добавлена проверка соответствующего идентификатора меню, и если идентификатор равен mnuAbout, то вызывается наша функция mnuAbout_onseiect, которая и загружает нужную форму frmAbout. Но если сейчас откомпилировать программу, то компилятор выдаст лишь сообщение об ошибке, т. к. такой формы пока еще нет. Ее созданием мы сейчас и займемся.

Добавление ресурсов в программу
Использование редактора ресурсов
Как будет показано ниже, все ресурсы программы хранятся в виде текстового файла, который, в принципе, можно редактировать самостоятельно при помощи любого текстового редактора. Но этот способ очень длительный и неудобный, гораздо проще пользоваться встроенным в среду разработки Falch.net редактором ресурсов. Дня открытия редактора ресурсов нужно во вкладке проекта Project Explorer выбрать подраздел Resources и там дважды щелкнуть на файле FirstApp.rcp.
С помощью этого редактора очень удобно модифицировать различные ресурсы программы — добавлять или изменять пункты меню, создавать новые формы, добавлять на них различные элементы. Например, в редакторе форм можно добавлять различные объекты (кнопки, списки, надписи и т. д.), просто рисуя их, как в обычном графическом редакторе.
Например, для создания формы можно в качестве шаблона воспользоваться первой, уже созданной. Для этого нужно выделить мышкой название f rmMain. из контекстного меню выбрать Сору и затем выбрать из меню Paste.
Появится форма с такими же параметрами, из которых нужно будет поменять следующие:

При этом мы получим окно, размер которого чуть меньше экрана, с заголовком About program. Свойство модальности говорит о том, что окно является на данный момент основным — пока окно не закрыто, пользователь не может выбирать что-либо другое в программе.
Последним шагом добавляем кнопку Close на форму. Делаем это также при помощи редактора ресурсов — рисуем кнопку и присваиваем ей идентификатор cmdciose. Все остальные текстовые элементы (информация об авторе и т. д.) создаем по вкусу, на функциональность программы это не повлияет.

Использование файла ресурсов
Выше уже упоминалось, что все идентификаторы хранятся в заголовочном файле FirstApp_res.hpp, листинг которого уже приводился с номером 6.1. Например, в том файле описан идентификатор mnuAbout, который мы использовали для проверки выбора соответствующего пункта меню. Также понятно, что новые идентификаторы можно добавлять самостоятельно, припомощи любого текстового редактора, а не только автоматически, при помощи Falch.net (хотя автоматический способ гораздо проще и быстрее).
Но не только идентификаторы, но и сами ресурсы, используемые программой, хранятся в аналогичном файле. Рассмотрим этот файл более подробно в листинге 6.8.

Листинг 6.8. Файл ресурсов FirstApp.rcp
/*********************************************
* Created with Falch.net DeveloperStudio
* http://www.falch.net/
**********************************************/
// Подключается заголовочный файл с идентификаторами ресурсов
#include "firstapp_res.hpp"
// Описания значков программы
ICONFAMILY "wheel_22x22xl.bmp" "wheel_22x22x2.bmp"
"wheel_22x22x4.bmp" "wheel_22x22x8.bmp"
SMALLICONFAMILY "wheel_15x9xl.bmp"
"wheel_15x9x2.bmp" "wheel_15x9x4.bmp"
"wheel_15x9x8.bmp"
// Описание формы "frmMain"
FORM ID frmMain AT (0 0 160 160}
USABLE
MENU ID nvnuf rmMain
BEGIN
TITLE "frmMain"
END
// Описание меню программы
MENU ID mnufrmMain
BEGIN
PULLDOWN "File"
BEGIN
MENUITEM "Close "
ID mnuClose END
PULLDOWN "Edit" BEGIN
MENUITEM "Undo"
ID mnuUndo "Z"
MENUITEM "Cut"
ID mnuCut "X"
MENUITEM "Copy"
ID mnuCopy "C"
MENUITEM "Paste"
ID mnuPaste "P"
MENUITEM "Select All"
ID mnuSelectAll "S"
MENUITEM SEPARATOR
MENUITEM "Keyboard"
ID mnuKeyboard "K"
MENUITEM "GraffitiHelp"
ID mnuGraffitiHelp "G"
END
PULLDOWN "Help"
BEGIN
MENUITEM "About..."
ID imuAbout
END
END
// Описание сообщения (пока оно так и не использовалось)
ALERT Proglnfo
CONFIRMATION
BEGIN
TITLE "Proglnfo"
MESSAGE "Enter your message"
BUTTONS "OK"
"Cancel"
END
// Описание формы "frmAbout"
FORM ID frmAbout AT (2 2 156 156)
USABLE
MODAL
BEGIN
TITLE "About program"
BUTTON "Close"
ID cmdClose
AT (CENTER 116 AUTO AUTO)
LABEL "First App"
AUTOID AT (CENTER 38)
LABEL "Sample project"
AUTOID AT (46 52)
LABEL "Written by"
AUTOID AT (56 88)
LABEL "Dmitry ELjuseev"
AUTOID AT (44 100)
END

Как видно из листинга этого файла, описание всех ресурсов программы вполне наглядно и очевидно. При очень большом желании эти ресурсы можно создавать и редактировать вручную, но естественно, что в графическом редакторе делать это гораздо более наглядно. Особенно это актуально для тех элементов, расположение которых видно визуально (кнопки, надписи и т. д.).
Рассмотрим теперь еще раз фрагмент файла описания ресурсов FirstApp_res.hpp (листинг 6.9), показанный в первоначальном виде в листинге 6.1.

Листинг 6.9. Описание идентификаторов ресурсов в файле FirstApp_res.hpp

#define frmMain 1000
#define frmAbout 1001
#define cmdClose 1000
#define Proglnfo 1000
#define
...
mnufrmMain 1000

Если сравнить данный файл с листингом 6.1, то видно, что в этот файл автоматически были добавлены идентификаторы новой формы frmAbout и кнопки cmdciose. В нашем случае среда разработки Falch.net сделала это самостоятельно, но при необходимости новые идентификаторы также можно добавить вручную.
Все это упоминается не зря. Во-первых, подобное рассмотрение полезно для понимания принципов работы программы, чтобы не казалось, что все это делается каким-либо фантастическим образом. А во-вторых, как уже говорилось выше, система разработки Falch.net является платной и не очень дешевой, и не каждый сможет себе ее приобрести. Поэтому можно пользоваться бесплатным компилятором, работающим из командной строки. Но при этом все необходимые действия по добавлению новых ресурсов и идентификаторов придется выполнять самостоятельно.

Добавление обработчика формы
Новую форму мы уже создали, но во время работы такой программы при выборе пункта меню About программа зависает и не реагирует далее ни на какие действия. Для корректной работы формы необходимо, чтобы она имела собственный обработчик сообщений, созданием которого мы сейчас и займемся.
Собственно говоря, обработчик сообщений формы frmAbout по своей структуре очень похож на аналогичный, используемый для основной формы frmMain.
Создадим файл frmAbout.cpp, содержимое которого показано в листинге 6.10.

Листинг 6.10. Обработчик собщений формы frmAbout (файл frmAbout.cpp)
static Boolean frmAbout_cmdClose_OnSelect (EventPtr event)
{
// Была нажата кнопка "Close"
// Вернуться к основной форме.
FnnReturnToForm(fnnMain) ;
return true;
Boolean frmAbout_HandleEvent (EventPtr event)
{
FormPtr form;
Boolean handled = false
switch ( event ->eType)
{
case ctlSelectEvent :
switch (event->data . ctlSelect . controlID)
{
// сообщение от кнопки с идентификатором
cmdClose case
cmdClose:
handled = frmAbout_cmdClose_OnSelect (event) ;
break;
}
break;
case f rmOpenEvent :
// Открытие или перерисовка формы
form = FrmGetActiveForm ( ) ;
FnriDrawForm ( form) ;
handled = true;
break;
default :
break;
}
return handled;
}

Здесь следует обратить внимание на добавление обработки сообщения нажатия кнопки с идентификатором cmdciose. В остальном этот код ничем не отличается от аналогичного, используемого в функции frmMain_HandleEvent.
Последний шаг — необходимо прописать наш новый обработчик в базовой функции ApplicationHandieEvent, "направляющей" сообщения во все остальные обработчики. Для этого следует добавить в функцию код, показанный в листинге 6.11.

Листинг 6.11. Вид функции ApplicationHandieEvent после добавления формы frmAbout
static Boolean AppiicationHandieEvent(EventPtr event)
{
// Application event loop
switch (event->eType)
{
case menuEvent:
...
case frmLoadEvent:
// Handle form load events
formID = event->data.frmLoad.formID;
form = FrmlnitFormfformID);
FrmSetActiveForm(form);
switch (formID)
{
case frmMain:
// Обработка сообщений формы frmMain
FrmSetEventHandler(form, (FormEventHandlerPtr)
frmMain_HandleEvent);
break;
// --- Этот код мы добавили
case frmAbout:
// Обработка сообщений формы frmAbout
FrmSetEventHandler(form, (FormEventHandlerPtr)
frmAbout_HandleEvent);
break;
// --- этот код мы добавили
default:
break;
}
handled = true;
break;
default:
break;
}
return handled;
}

Видно, что в функцию был добавлен код, отвечающий за перенаправление сообщений в функцию frmAbout_HandleEvent. Теперь, после этого, форма функционирует полностью корректно, и программа может функционировать совершенно нормально.
Получившаяся программа вряд ли займет первое место в рейтинге сайта PalmGear, но как первое самостоятельное творение, вполне сойдет.

6.4.2. Использование элементов управления
Рассмотрим теперь, как можно использовать в программе собственные элементы пользовательского интерфейса — кнопки, поля ввода и т. д. Для примера создадим простейший калькулятор, который будет выполнять всего лишь одно действие — складывать два числа, введенных в соответствующие поля, и выводить результат. Конечно, программу MathCAD наш калькулятор вряд ли заменит, но работу с элементами управления на этом примере вполне можно освоить.

Добавление соответствующих ресурсов
Как упоминалось выше, нужные ресурсы вполне можно добавить вручную. Но поскольку это весьма неудобно и пользоваться этим имеет смысл лишь в крайнем случае, то воспользуемся средой разработки Falch.net. Тем более, что, как будет показано ниже, написание кода в этом случае также можно сильно упростить.

Добавим (на основную форму) в редакторе ресурсов три поля ввода (Field resource), три поля текстовых меток (Label resource), и одну кнопку (Button resource).
Идентификаторы присвоим следующим образом:

Добавление кода
Основной код, выполняющий вычисление, будет находиться в обработчике нажатия кнопки Count. В листинге 6.10 уже показывалось использование обработчика кнопки Close в форме, выводящей информацию о программе. Для создания этого обработчика нужно было вписать соответствующую проверку В функцию frmAbout_HandleEvent и ввести новую функцию
frmAbout_cmdciose_onSeiect, выполняющуюся при нажатии.
Но не зря упоминалось, что использование качественной среды разработки может значительно облегчить процесс написания кода. Для создания функции обработки нажатия кнопки необязательно прописывать соответствующий код вручную — достаточно щелкнуть правой кнопкой мыши на кнопке Count, и в контекстом меню выбрать пункт Code Templates | Control Select Event. При этом все необходимые функции создадутся автоматически, в код будут внесены следующие изменения: в функцию frmMain_HandieEvent будут добавлены проверка нажатия кнопки с идентификатором btnCount И функция frmMain_btnCount_OnSelect, выполняющаяся при нажатии на кнопку. Естественно, что все это можно добавить и вручную, но при автоматическом способе это гораздо быстрее и удобнее.
Теперь рассмотрим собственно процесс расчета. Для вычисления необходимо выполнить следующие действия:
1. Получить текст из полей ввода.
2. Преобразовать данные из текстового представления в числовое.
3. Вычислить результат.
4. Преобразовать результат в строку и вывести в соответствующее поле вывода. Программа, выполняющая все эти операции, показана в листинге 6.12.

Листинг 6.12. Функция выплнения расчета
static Boolean frmMain_btnCount_OnSelect(EventPtr event)
{
// Получить указатель на активную форму:
FormType *formPtr = FrmGetActiveForm ();
// Получить числовые индексы двух полей ввода с идентификаторами
// editNmbl и edit№nb2:
UIntl6 fieldlndexl = FrmGetObjectIndex(formPtr, editNmb1),
fieldlndex2 = FrmGetObjectlndexfformPtr, editNmb2);
// Получить указатель на соответствующие поля:
FieldType *fieldPtrl = (FieldType*) FrmGetObjectPtr(formPtr,
fieldlndexl),
*fieldPtr2 = (FieldType*) FrmGetObjectPtr(formPtr,
fieldlndex2);
// Получить указатель на текстовые данные полей ввода:
Char *namePtrl = FldGetTextPtr(fieldPtrl),
*namePtr2 = FldGetTextPtr(fieldPtr2);
// Преобразовать полученные строки в числа:
Int32 nmbl = StrAToI(namePtrl) , nmb2 = StrAToI(namePtr2) ;
// Преобразовать результат "nmb1+ nmb2" в строку "result"
Char result[20];
StrIToA(result, nmbl + nmb2);
// Получить указатель на текстовое поле ввода с идентификатором
// editRes:
UInt.16 fieldlndex3 = FrraGetObjectIndex(formPtr, editRes);
FieldType *fieldPtr3 = (FieldType*) FrmGetObjectPtr(formPtr,
fieldlndexS)
// Вывести результат: FldSetTextPtr(fieldPtr3, result);
// Перерисовать текстовое поле вывода
FldDrawField(fieldPtr3) ;
return true;
}

Рассмотрим используемые функции более подробно.

Работа с элементами управления
Для каких-либо действий с любым объектом на форме используется указатель на этот объект. Для получения указателя используется функция FrmGetObjectPtr, в качестве параметров которой нужно передать указатель на текущую форму и индекс объекта. Для получения этих данных используется соответствующие функции FrmGetActiveForm и FrmGetObjectlndex.
Если говорить точнее, то на самом деле, указатель "на объект" ссылается на системную структуру данных, описывающую этот объект. К примеру, структура описания текстового поля (Field) имеет вид, показанный в листинге 6.13.

Листинг 6.13. Структура, описывающая объект Field

typedef stxuct {
Ulntl6 id;
RectangleType rect;
FieldAttrType attr;
Char *text;
MemHandle textHandle;
LinelnfoPtr lines;
UIntl6 textLen;
UIntl6 textBlockSize;
UIntl6 maxChars;
UIntl6 selFirstPos;
UIntl6 selLastPos;
UIntl6 insPtXPos;
UIntl6 insPtYPos;
FontID fontID;
UlntS reserved;
} FieldType;

Следует отметить, что использовать эти структуры напрямую вряд ли целесообразно, да в большинстве случаев это и не нужно. Например, для получения текста используется функция FidGetTextPtr, а для занесения текста в соответствующее поле — функция FidsetTextPtr. После изменения каких-либо параметров объекта, его следует принудительно перерисовать, для этого вызывается функция FidDrawFieid. В противном случае внесенные изменения, например, изменившийся текст, отображены на экране не будут.

Работа со строками
В данном примере используются два вида преобразований — из строки в число, и наоборот, из числа в строку. Для этого применяются функции strAToi и striToA. Первая функция возвращает число, принимая в качестве параметра текстовую строку, а вторая выполняет обратное преобразование — заполняет строку, занося в нее число, передаваемое вторым параметром.
Помимо этого, в разделе String Manager Functions описано весьма большое количество функций, предназначенных для работы со строками. Некоторые из них можно выделить, как наиболее широко используемые.

Полный список функций, используемых для работы со строками, гораздо больше, целиком его можно посмотреть в документации по операционной системе.
Для примера модифицируем наш "калькулятор" так, чтобы он выдавал значения как в десятичном, так и в шестнадцатеричном виде. Например, при сложении чисел "123" и "456" должен выдаваться результат вида "579 (243h)".
Для этого модифицируем код так, как показано в листинге 6.14.

Листинг 6.14. Модифицированная процедура вычисления значения
Char result[30], resultHex[20];
// Преобразовать строку в число:
StrIToA(result, nmbl + nmb2);
// Преобразовать строку в число в шестнадцатеричном виде:
StrIToH(resultHex, nmbl + nmb2);
// Прибавить к исходной строке фрагмент " (":
StrCat(result, " (");
// Прибавить к строке вторую подстроку:
StrCat(result, resultHex);
// Прибавить к строке подстроку "h)":
StrCat(result, "h)");

Приведенный фрагмент кода как раз обеспечивает подобный вывод. Единственная проблема — функция формирования шестнадцатеричной строки прибавляет в начале незначимые нули, поэтому результат выглядит примерно, как "579 (00000243h)". Но это не является большой проблемой, при желании лишние нули в начале можно убрать, читатели могут сделать это в качестве самостоятельного задания.
Получившийся проект представлен на прилагаемом к книге компакт-диске в каталоге dev_Palm\ FirstApp2.

6.4.3. Работа с графикой
Добавим в программу немного графики и научимся использовать графические возможности Palm OS. Создадим новую форму, в которой пользователь сможет добавлять пары чисел, и по ним будет строиться диаграмма. Сделаем заодно, чтобы вводимые пары чисел отражались в списке. Внешний вид того, что должно будет получиться.

Таким образом, в этой главе мы сможем изучить методы работы со списками в Palm OS и несложные операции, используемые для вывода графики.
Добавление формы и построение обработчика
При помощи редактора ресурсов добавим в проект новую форму с идентификатором frmGraph. Выше уже описывалось, как добавлять обработчик данных для формы вручную, но теперь мы пойдем более простым путем — среда разработки Falch.net многое может сделать самостоятельно. Щелкаем правой кнопкой мыши на форме и в контекстном меню выбираем Code Templates | Insert Form Event Handler. При этом будут произведены следующие действия:
1. Создан файл frmGraph.cpp, содержащий обработчик сообщений для новой формы.
2. Добавлены соответствующие фрагменты в файлы main.cpp, FirstApp.hpp и файл ресурсов.
То, на что при "ручном" создании программист потратил бы 2—3 минуты, среда разработки Falch.net создала автоматически за пару секунд. Воистину, при необходимости профессиональной разработки программ для Palm OS среда разработки Falch.net достойна тех 200 долларов, которые за нее просят разработчики.
Добавим также в основную форму кнопку с идентификатором btnstartGraph и названием Open graph. Создадим обработчик этой кнопки (для этого нужно щелкнуть правой кнопкой мыши по кнопке Open graph, и в меню выбрать Code Templates — Control Select Event), и в него впишем код для активизации новой формы "FrmPopupForm(frmGraph),-" (без кавычек, естественно).

Добавим теперь в форму f rmGraph следующие элементы:

Написание кода
Код, отвечающий за функционирование нашего модуля, показан в листинге 6.15. Ниже он будет рассмотрен более подробно.

Листинг 6.15. Подпрограмма построения диаграмм.
// массивы для хранения данных
списка static Char ListData[255] ;
static Char* ListPtrs [255] ;
Boolean f rmGraph_HandleEvent (EventPtr event)
{
FormPtr form;
Boolean handled = false;
switch (event->eType)
{
case ctlSelectEvent :
...
break;
case frmOpenEvent :
// Перерисовка или открытие формы
form = FrmGetActiveForm ( ) ;
FrmDrawForm ( form) ;
{ // нарисовать линии осей
const Intl6 xl=8, yl=31,'х2=88, у2=120;
// нарисовать вертикальную линию
WinDrawLine(xl, yl-5, xl, у2);
// нарисовать горизонтальную линию
WinDrawLine(xl-5, у2, х2, у2) ;
for(Int16 i=0; i<8;
{
WinDrawLine (xl-5, y2-10*i, xl, y2-10*i) ;
WinDrawLine (xl+10*i, y2, xl+10*i, y2+5) ;
}
}
handled = true;
break;
default:
break;
}
return handled;
}
static Boolean frmGraph_btnAdd_OnSelect (EventPtr event)
{
// Получить указатель на активную форму:
FormType *formPtr = FrmGetActiveForm ( ) ;
// Получить индексы элементов управления:
UIntl6 fieldlnpl = FrmGetObjectIndex(formPtr, editlnpl) ,
fieldlnp2 = FrmGetObjectIndex(foinnPtr, editlnp2) ;
// Получить указатели на элементы:
FieldType *fieldPtrl = (FieldType*)
FrmGetObjectPtr (formPtr,
fieldlnpl) *fieldPtr2 = (FieldType*)
FrmGetObjectPtr (formPtr,
fieldlnp2)
// Получить текст из полей ввода:
Char *namePtrl = FldGetTextPtr (f ieldPtrl) ,
*namePtr2 = FldGetTextPtr (fieldPtr2) ;
// Преобразовать данные из строки в число:
Int32 nmbl = StrAToI (namePtrl) , nmb2 = StrAToI (namePtr2) ;
Char result [30], numb2_str [20] ;
// Подготовить данные, которые будут добавляться в список
StrIToA( result, nmbl) ;
// Записать 1-е число в строку StrCat( result, ", ") ;
// Добавить разделитель StrIToA(numb2_str, nmb2) ;
// Записать 2-е число в строку StrCat (result, numb2_str) ;
// Добавить 2-ю строку к 1-й
// Добавить данные в список
UIntl6 listlndex = FrmGetObjectlndex (formPtr, itemsList) ;
ListType *listPtrl = (ListType *) FrmGetObjectPtr (formPtr,
listlndex) ;
Intl6 nCount = listElemsNmb;
// Пропустить уже имеющиеся значения
Intl6 str_pos = 0;
if (nCount > 0)
{
for(Intt6 i=0; i<listElemsNmb;
{
str_pos += StrLen(ListPtrs[i])
}
}
ListPtrs[listElemsNmb] = SListData[str_pos];
StrCopy(SListData[str_pos], result);
ListData[str_pos + strlen(result)] = 0;
// Занести в список массив
LstSetListChoices(listPtrl, (Char **)ListPtrs, listElemsNmb+1)
// Перерисовать список
LstDrawList(listPtrl);
listElemsNmb++;
// Нарисовать прямоугольники гистограммы
const Intl6 xl=8, yl=31, x2=88, y2=120;
RectangleType rect;
rect.topLeft.x = xl+curDrawPos+1;
rect.topLeft.y = y2 — nmb2;
rect.extent.x = nmbl;
rect.extent.у = nmb2;
WinSetForeColor(14);
WinDrawRectangle(& rect, 0);
WinSetForeColor(5);
rect.extent.x— ;
WinDrawRectangleFrame(simpleFrame, Srect);
curDrawPos += nmbl;
return true;
}

Рассмотрим используемые функции более подробно.

Получение данных от элементов интерфейса
Работа с полями ввода уже рассматривалась ранее, ничего нового здесь не использовалось. Для получения указателя на объекты интерфейса использовались функции FrmGetObjectIndex и FrmGetObjectPtr. Для получения данных из полей ввода использовалась функция FidGetTextPtr, а сами строки обрабатывались с помощью функций StrAToI, StrlToA и StrCat.

Работа с графикой
Для вывода графики в Palm OS существует ряд функций с названиями, начинающимися на winoraw*. В этом примере использовались функции WinDrawLine для рисования линий, WinDrawRectangle для рисования закрашенного прямоугольника и winorawRectangieFrame для рисования прямоугольника, ограниченного рамкой.
Из различных функций, необходимых для работы с графикой, можно выделить следующие.

Структура RectangieType описывается в файле Rect.h следующим образом:
typedef struct PointType {
Coord x; Coord y;
} PointType;
typedef struct RectangieType {
PointType topLeft; PointType extent;
} RectangieType;

Из описания видно, что прямоугольник описывается верхней левой точкой и значениями ширины и высоты.
Помимо функций семейства winDraw*, существует еще семейство функций winErase*, задача которых — рисовать те же примитивы, но применяя при этом цвет фона. Это может использоваться, к примеру, для вывода анимации.
Для установки соответствующих цветов можно применять функции winSetForeCoior и winSetBackcoior. Для задания цвета выводимого текста можно использовать функцию winSetTextcoior. Следует лишь учитывать, что эти функции были введены только с Palm OS версии 3.5.

Работа со списком
Работа с элементом пользовательского интерфейса список (List) пока еще не рассматривалась. Поэтому эту часть стоит рассмотреть более детально, тем более, что в Palm OS работа со списком реализована довольно неудобно, по сравнению со списком (ListBox), используемым в Windows.
Сложность состоит в том, что список (List) в Palm OS не хранит в себе никаких данных. Структура данных списка имеет вид, показанный в листинге 6.16.

Листинг 6.16. Структура описания элемента List

typedef struct {
UIntl6 id;
RectangieType bounds;
ListAttrType attr;
Char ** itemsText;
Intl6 numl terns;
Intl6 currentltem;
Intl6 topltem;
FontID font;
UInt8 reserved;
WinHandle popupWin;
ListDrawDataFuncPtr drawItemCallback; } ListType;

Для нас сейчас наиболее интересен член структуры itemsText, который указывает на массив указателей на строки. Такая вот непростая структура необходима для заполнения списка.
Возможно, понять это "с ходу" не так легко, поэтому можно привести простой пример добавления в список четырех элементов (лишний код опущен).

// описание 4-х текстовых строк, которые будут добавлены в список
static Char strl[10] = "eleml", str2[10] = "elem2",
str3[10] = "elemS", str4[10] = "elem4";
// массив из четырех указателей на строки static
Char *strPtr[4] = (strl, str2, str3, str4);
// инициализировать список значениями
LstSetListChoicesflistPtrl, (Char **) strPtr, 4);
// перерисовать список
LstDrawList(listPtrl) ;

Нужно иметь в виду, что в отличие от списка в Windows, список в Palm OS не хранит данные внутри себя, поэтому массив указателей на строки (как и сами строки) должны существовать во все время существования списка. Именно поэтому в данном примере строки и массив указателей были объявлены как static. Особенно это актуально, если, к примеру, список заполняется при нажатии на кнопку: переменные формально перестают существовать после завершения работы функции-обработчика. Поэтому, если не учитывать это, то список может начать отображать всякий "мусор", а в худшем случае программа может аварийно завершить работу.
В создаваемом примере было сделано примерно то же самое. Был создан массив указателей на строки с названием Listptrs и буфер для хранения символов ListData. В буфер последовательно заносятся строки, и соответствующим образом на них настраиваются указатели в массиве ListPtrs.
Справедливости ради стоит отметить, что существует другой способ для работы со списками — переопределение функции отображения списка при помощи функции LstsetDrawFunction. При этом способе пользователь сам обеспечивает формирование списка, соответственно может выбирать тот способ хранения данных, который для него более удобен. Но рассмотрение различных особенностей программирования под Palm OS выходит за рамки этой книги, а интересующиеся читатели могут обратиться к этому вопросу самостоятельно.

6.4.4. Работа с таблицами
Помимо использования списков, весьма часто нужно выводить на экран данные различных типов, представленные в виде таблицы. Рассмотрим пример таблицы, содержащей список встреч: номер встречи, наименование и дату.


Для работы с такими структурами в Palm OS специально был введен элемент пользовательского интерфейса таблица (Table).
Различные типы данных, которые могут храниться в этом элементе, показаны в табл. 6.2.

Таблица 6.2. Типы данных, поддерживаемые элементом Table

Элемент Описание
checkboxTableltem Элемент типа checkBox может быть в состояниях "включено" и "выключено". Для инициализации используется целочисленное значение
customTableltem Этот элемент определяется программистом. Прорисовка элемента происходит при помощи функции, задаваемой программистом, поэтому, в принципе, может выводиться все, что угодно. Но все это требует со стороны программиста дополнительных затрат
DateTableltem Элемент, хранящий дату. К сожалению, по непонятной причине может выводиться только день и месяц, год не выводится. Формат даты задается в виде 16-битного числа вида "yyyyyyymmmmddddd". Поле "УУУУУУУ" задает год, начиная с 1904. Месяц и день задаются обычным образом
LabelTableltem Нередактируемый текст. Для инициализации требуется указатель
numericTableltem Нередактируемое число. Инициализируется целочисленным значением
popupTriggerTableltem Раскрывающийся список. Инициализируется двумя значениями: целочисленным индексом списка и указателем на сам список
text Table I tern Редактируемое текстовое поле
textWithNoteTableltem Редактируемое текстовое поле со значком note справа
timeTableltem Поле для хранения времени. Описано, но пока не реализовано
narrowText Table I tern Редактируемое текстовое поле с зарезервированным местом в правой части. Написав свою функцию TableDrawltemFuncType, программист может выводить в этой области все, что возможно

В таблице не зря упоминалось, чем инициализируется то или иное значение, числом или указателем. Для инициализации различных значений таблицы используются две функции: Tbisetitemint, которая устанавливает в нужной ячейке целочисленное значение, и TbisetitemPtr, которая служит для установки указателя на более "габаритное" значение, например, на текстовую строку.
Все описание работы с таблицами может занять весьма много времени, поэтому рассмотрим более подробно лишь инициализацию таблицы и заполнение ее различными значениями.
Для работы с таблицами в проект была добавлена форма frmTabie, на которую была помещена таблица TableID1. Для таблицы в редакторе ресурсов были заданы следующие параметры: rows = 8, Coiumnwidths = {20, so, 40}. Далее с помощью пункта меню Code Templates был автоматически сформирован файл frmTable.cpp, содержащий обработчик сообщений таблицы (как это делается, более подробно рассматривалось выше).
Рассмотрим теперь функцию инициализации таблицы, представленную в листинге 6.17.

Листинг 6.17. Подпрограмма инициализации таблицы и ее вызов

// Функция инициализации таблицы
void Tablelnit ( )
FormType *pFrm = FrmGetActiveForm ( ) ;
TableType *pTable = (ТаЫеТуре*) FrmGetObjectPtr (pFrm,
FrmGetObjectlndex (pFrm, TablelDl ) ) ;
Intl6 i; for (i = 0; i < TblGetNumberOfRows(pTable) ; i++)
{
// установить тип для первой колонки
TblSetItemStyle(pTable, i, 0, numericTableltem)
// установить данные для первой колонки
TblSetltamlnttpTable, i, 0, i+1);
// установить тип "текст" для второй колонки
TblSetItemStyle(pTable, i, I, textTableltem);
// установить тип для третьей колонки
TblSetltemStyletpTable, i, 2, dateTableltem);
// установить данные для третьей колонки
DateType date;
date.year = 2000 + i - 1904;
date.month = 8;
date.day = 29;
TblSetltemlnt(pTable, i, 2, DateToInt(date));
}
// установить все строки и столбцы, как используемые
for (i = 0; i < pTable->numRows; i++)
TblSetColunmOsable(pTable, i, true);
Boolean frmTable_HandleEvent(EventPtr event)
{
...
switch (event->eType)
case ctlSelectEvent:
...
break;
case frmOpenEvent:
form = FrmGetActiveForm();
// инициализация таблицы при открытии формы
Tablelnit() ;
FnriDrawForm(form) ;
handled = true;
break;
default:
break;
}
return handled;
}

Рассмотрим используемый код более подробно.

В ней заданы соответствующие поля и занимаемые ими биты, так что эта структура занимает только 16 бит, несмотря на то, что в ней используется три числа. Такое описание весьма удобно, т. к. структура занимает мало места, но в то же время с ее битовыми полями можно работать так же, как и с обычными целыми числами.
Именно поэтому для преобразования структуры в двубайтный формат практически ничего не нужно, и описание макроса DateToint в файле DateTime.h выглядит очень просто:
#define DateToint(date) (*(UIntl6 *) Sdate)
Работа с датами в таблице имеет еще одну интересную особенность. Если внимательно посмотреть на рис. 6.5, то можно заметить, что часть дат имеет справа восклицательный знак. Этим знаком обозначаются даты, более ранние, чем текущая. Если посмотреть на текст подпрограммы инициализации, то можно увидеть, что даты в строках инициализируются значениями 29.08.2000, 29.08.2001, 29.08.2002 и т. д. Именно две первые даты, которые во время запуска программы были уже в прошлом, отмечены восклицательными знаками. Непонятно лишь, почему для поля даты не может выводиться год, это было бы весьма удобно.
В целом, нужно отметить, что использование таблиц в Palm OS в плане удобства далеко от совершенства. С одной стороны, представление разнообразных типов данных в таблице обеспечивает довольно большую гибкость, но, с другой стороны, для полнофункционального использования таблиц программист должен дописывать весьма много дополнительного кода. Здесь можно отметить сложности собственной прорисовки таблицы, невозможность автоматической прокрутки, отсутствие нормальных заголовков для таблиц с возможностью автоматического изменения ширины столбцов, как это сделано в элементе List Control Microsoft Windows.
Собственно говоря, эти проблемы характерны не только для таблиц, но и для всей операционной системы Palm OS в целом. Отсутствие нормальных полнофункциональных элементов управления, отсутствие многих удобных средств наподобие MFC, ATL, весьма ограниченные возможности для работы с графикой и т. д. Конечно, новая версия Palm OS 5.0 наверное будет иметь
гораздо большие возможности в отношении работы с графикой и со звуком, но вряд ли ситуация изменится принципиально — все-таки Microsoft имеет гораздо больше финансовых возможностей, и может тратить гораздо больше средств на развитие своих систем. Поэтому в настоящее время разрабатывать программы для карманных компьютеров, работающих с Windows СЕ, намного удобнее, чем под любую из других операционных систем. И вряд ли в будущем ситуация изменится. Впрочем, программирование для Windows СЕ будет рассмотрено подробнее в следующей главе.

6.4.5. Работа с файлами
В этом разделе настала пора перейти к хранению данных. Помимо текущей работы с данными, пользователям необходимо сохранять эти данные для будущего использования. В противном случае возможности компьютера не сильно бы отличались от возможностей программируемого калькулятора. Поэтому возможность сохранения данных и последующего их открытия является очень важной.
Собственно говоря, этот раздел книги назван не совсем правильно. Название "Работа с файлами" подчеркивает лишь то, что здесь будет рассматриваться запись данных для долговременного хранения, а почти на всех системах данные хранятся в виде файлов. "Неправильность" названия состоит в том, что в файловой системе Palm OS файловой системы в нормальном ее понимании не существует. В операционной системе Palm OS вообще отсутствуют такие понятия, как "файл" или "папка". Тем не менее, задача сохранения данных не становится от этого менее актуальной.
Данные в Palm OS хранятся в виде баз данных и записей. База данных содержит в себе некоторое количество записей. Объем одной записи не может превышать 64 Кбайт, но суммарный объем базы данных такого ограничения не имеет. Впрочем, где-то проходила информация, что Palm OS не может работать с памятью, превышающей 16 Мбайт, но из-за неимения рядом устройства с еще большим объемом памяти проверить это сложно.
База данных хранит в себе разнообразную информацию:

База данных также может размещаться в основной памяти или на внешней карте памяти. Принципиально логика работы с ней от этого не изменяется.
Рассмотрим работу с базами данных на примере формирования гистограммы, описанной ранее. Добавим в форму frmGraph две кнопки Save и Load. Назначим этим двум кнопкам идентификаторы btnSave И btnLoad соответственно.

Сохранение данных
Рассмотрим для начала процесс записи данных. Подпрограмма, сохраняющая данные в базе, представлена в листинге 6.18.

Листинг 6.18. Подпрограмма записи результатов в базу данных

/ / Структура, описывающая одну запись
struct PZEntryType
{
Char StrData[20];
Intl6 nmbl, nmb2;
} PZDataEntry;
#define dbType 'Data'
#define AppID 'F472'
#define dbName "F APP"
// Обработчик нажатия кнопки Save
static Boolean frmGraph_btnSave_OnSelect(EventPtr event)
{
// Удалить прежнюю базу данных
LocallD dbID = DmFindDatabasefO, dbName);
if (dbID)
DmDeleteDatabase(0, dbID);
// Создать базу данных
if (DmCreateDatabase(0, dbName, AppID, dbType, false) != errNone)
return 1;
// Открыть базу данных на запись
DmOpenRef dbPtr = DmOpenDatabaseByTypeCreator(dbType, AppID,
dmModeWrite);
for(Int!6 1=0; KlistElemsNmb; i++)
{
UIntl6 index = dmMaxRecordlndex;
// Добавить запись
MemHandle recH = DmNewRecord(dbPtr, &index, sizeof(PZEntryType));
void *recP = MemHandleLock(recH);
// Заполнить структуру данных
StrCopy(PZDataEntry.StrData, ListPtrs[i]);
PZDataEntry.nmbl = ListNmbl[i];
PZDataEntry.nmb2 = ListNmb2[i];
DmWrite(recP, OL, iPZDataEntry, sizeof(PZEntryType));
MemHandleUnlock(recH); DmReleaseRecordfdbPtr, index, true);
}
// Закрыть базу данных
DmCloseDatabase(dbPtr);
return true;
}

Как видно из программы, процесс записи в базу данных можно подразделить на несколько этапов.

Относительно последних параметров можно сделать ряд замечаний. Первым параметром функции DmCreateDatabase является номер карты памяти — данные ведь могут сохраняться не только в оперативной памяти КПК, но и на внешней карте. В данном примере указана константа "О", и данные сохраняются в основной памяти карманного компьютера. Соответсвенно, с картами памяти могут быть связаны такие ошибки, как memErrCardNotPresent — карта памяти может быть не вставлена в карманный компьютер, или memErrRAMOnlyCard — карта не предназначена для хранения данных (например, в виде карты могут быть GPS-приемник или цифровая фотокамера). И, естественно, ошибка нехватки места может возникать как при наличии карт, так и при хранении данных в оперативной памяти.

Проверить, создана ли база данных, очень просто. Для этого можно выбрать пункт Info в контекстном меню Palm OS, там можно увидеть созданную базу данных и количество записей в ней.
На рисунке видна база данных с названием F_APP, которую мы создавали.

Чтение данных
Очевидно, что после того как данные сохранены, желательно было бы их корректно прочитать. Подпрограмма, считывающая информацию из созданной базы данных, показана в листинге 6.19.

Листинг 6.19. Подпрограмма чтения результатов из базы данных

static Boolean frmGraph_btnLoad_OnSelect(EventPtr event)
{
// Очистить список
listElemsNmb = 0;
curDrawPos = 0;
// Перерисовать форму и отобразить оси координат
FrmDrawForm(FrmGetActiveForm ());
DrawAxes();
// Найти идентификатор базы
LocalID dbID = DmFindDatabase(0, dbName);
if (dbID == 0) return false;
// Открыть базу данных для чтения
DmOpenRef dbPtr = DmOpenDatabase(0, dbID, draModeReadOnly)
// Получить количество элементов в базе
UIntl6 rec_numb = DmNumRecords(dbPtr);
// Считать последовательно все элементы
for(Int!6 i=0; i<rec numb;
{
// Запросить запись из базы
MemHandle recH = DmQueryRecord(dbPtr, i) ;
void *recP = MemHandleLock(recH) ;
PZEntryType* pData = (PZEntryType*) recP;
AddDataToList (pData->StrData, pData->nrribl, pData->nmb2) ;
MamHandleUnlock (recH) ;
}
// Закрыть базу данных
DmCloseDatabase(dbPtr) ;
return true;
}

Процесс чтения из базы данных можно подразделить на несколько этапов.

Удаление данных
Данные нужно не только добавлять, но также и удалять. Эту операцию можно выполнить двумя способами.

Другие операции
Помимо вышерассмотренных, существует ряд функций, которые могут быть полезными для работы с данными.

Естественно, что логику работы этой функции программист должен написать самостоятельно, т. к. критерии сортировки могут быть различными.

6.5. Особенности операционной системы Palm OS
6.5.1. Аппаратно-программные особенности

Несмотря на свои возможности, Palm OS является весьма ограниченной системой, при работе с которой следует учитывать ряд немаловажных ограничений. Некоторые из них весьма полезно перечислить.

Впрочем, можно предположить, что в последних версиях операционной системы эти проблемы несколько уменьшены, т. к. сам процессор является 32-разрядным и может адресовать до 4 Гбайт памяти. Также можно с уверенностью сказать, что эти проблемы наверное будут решены в новых карманных компьютерах, построенных на операционной системе Palm OS 5 и процессорах Strong Arm.

6.5.2. Особенности операционной системы и языка
Помимо особенностей, связанных с аппаратурой и памятью, можно выделить ряд чисто программных проблем, с которыми могут столкнуться программисты, изучающие до этого программирование на более серьезных системах.

6.6. Специальные возможности Palm OS
Изучение основ программирования для Palm OS в этой книге постепенно подходит к концу. Естественно, что в таком небольшом объеме невозможно рассмотреть все тонкости этой системы, да это и не нужно — помимо Palm OS существует гораздо более интересная и мощная система Windows СЕ, описанию которой тоже нужно уделить некоторое время. Те же, кто захотят серьезно заняться программированием для Palm OS, могут обратиться к англоязычной литературе и различным форумам по программированию. К сожалению, еще раз можно отметить, что на русский язык никакой англоязычной литературы по программированию для Palm OS пока еще не переведено. Особо ищущие знаний читатели (плюс, имеющие кредитную карту) могут заказать соответствующую литературу на сайте www.amazon.com или обратиться в раздел англоязычной литературы на сайте www.books.ru.
Говоря о специальных возможностях Palm OS, нужно отметить два основных момента — передача данных через инфракрасный порт (beaming) и обеспечение синхронизации с настольным компьютером. Эти возможности отнесены здесь к специальным, т. к. их реализация специфична именно для этой операционной системы.

6.6.1. Передача данных через инфракрасный порт
Передача данных между компьютерами — вполне актуальное и распространенное действие. Инфракрасный порт удобен тем, что для его работы не требуется никаких дополнительных устройств, соединителей и проводов, поэтому он может быть использован в любое время. Это может быть актуально, например, при необходимости передачи данных с одного компьютера на другой. Конечно, у инфракрасного порта есть свои недостатки — небольшая дальность связи, относительно небольшая скорость передачи и необходимость расположения портов в пределах прямой видимости. Возможно, в будущем все такие устройства будут заменены стандартом Bluetooth, но пока еще это слишком дорогое удовольствие, поэтому можно с высокой вероятностью предположить, что в течение как минимум нескольких лет инфракрасные порты еще будут использоваться. А учитывая их очень низкую стоимость, долговечность и невысокое энергопотребление, можно предположить, что для некоторых устройств ИК-связь будет использоваться еще очень долго.
В операционной системе Palm OS за передачу данных через инфракрасный порт отвечает компонент Exchange Manager. Он был введен в Palm OS, начиная с версии 3.0. При помощи Exchange Manager обеспечивается реализация стандарта ObEx Infrared Data Association (IrDA), поддерживаемого многими устройствами. Благодаря этому, возможна передача данных между различным оборудованием, например, можно пересылать данные на карманный компьютер Palm с настольного компьютера, работающего под управлением Windows 2000, или с карманного компьютера PocketPC, работающего под управлением PocketPC 2002.
С точки зрения пользователя, процесс передачи данных подразделяется на три этапа.

Если за определенное время другое устройство не найдено, то процесс прекращается, пользователь получает соответствующее сообщение.

Стек протокола IrDA
Стек протокола IrDA, использующийся для передачи данных через инфракрасный порт, показан на рис. 6.1.


Рис. 6.1. Стек протокола инфракрасной связи

На самом нижнем уровне располагаются уровни SIP и FIR. Они отвечают за аппаратную реализацию средств передачи данных. Протокол SIR (Serial IR) поддерживает скорость передачи до 115 Кбит/с, в то время как FIR (Fast IR) поддерживает скорость передачи до 4 Мбит/с.
Протокол IrLAP (IR Link Access Protocol) обеспечивает логический канал данных между различными устройствами, поддерживающими IrDA. Протокол IrLMP (IR Link Management Protocol) обеспечивает логическое управление сессией передачи данных, запуская/останавливая передачу данных через IrLAP.
Протокол Tiny TP представляет собой небольшую надстройку над IrLMP. Его используют протоколы высокого уровня.
Протоколы верхнего уровня (IrComm, IrLAN и ОВЕХ) используются для обеспечения доступа к соответствующим ресурсам. В карманных компьютерах Palm поддерживаются протоколы SIR, IrLAP, IrLMP, Tiny TP и ОBEX. Протокол ОBEX (Object Exchange protocol) используется Palm OS с помощью Exchange Manager для приема/передачи различной информации. К сожалению, в КПК Palm поддерживается только протокол передачи SIR, так что скорость обмена данными ограничена величиной в 115 Кбит/с.
Более подробную информацию о стандартах инфракрасной передачи данных можно получить на сайте www.irda.org.

Использование Exchange Manager
Передача данных

Рассмотрим использование функций Семейства exchange manager API из программы. Для передачи данных необходимо выполнить ряд действий.

  1. Подключить заголовочный файл ExgMgr.h, в котором находятся описания всех необходимых функций.
  2. Создать в программе объект типа ExgSocketType. Эта структура описывает различные параметры соединения, такие как идентификатор программы, инициирующей соединение, количество передаваемых объектов и т. д. Для корректной передачи необходимо заполнить ряд полей:
  1. Вызвать функцию ExgPut для инициирования соединения. Если все нормально, функция должна вернуть константу errNone.
  2. Вызвать функцию ExgSend для пересылки данных. Основными исходными данными для этой функции являются указатель на объект, который нужно переслать, и размер этого объекта. После выполнения функция возвращает количество реально переданных байтов. Анализируя это число, можно определить, когда передача завершилась.
  3. Вызвать функцию ExgDisconnect для завершения сеанса связи.

Прием данных
Для обеспечения приема данных необходимо выполнить следующие действия.

  1.  Добавить в функцию PilotMain проверку для кода запуска sysAppLaunchCmdExgReceiveData. Программа запускается с этим кодом при приеме данных.
    UInt32 PilotMain(UIntl6 cmd, void *cmdPBP, UIntl6 launchFlags)
    {
    Err error;
    switch (cmd) . .
    case sysAppLaunchCmdNorraalLaunch:
    break;
    case sysAppLaunchCmdExgReceiveData:
    // здесь будет код приема данных
    ...
    break;
    default:
    break;
    }
    return 0;
    }
  2.  Написать функцию приема данных, которая будет выполнять следующие действия:

6.6.2. Синхронизация настольного и карманного компьютеров
Карманный компьютер — очень удобное устройство для просмотра различной информации, но вводить большое количество данных в КПК не очень удобно. Как уже упоминалось выше, наиболее оптимальным является именно совместное использование карманного и настольного компьютеров.
В операционной системе Palm OS модуль, отвечающий за синхронизацию данных, называется conduit, что в переводе означает "трубопровод". Поскольку русское название не очень благозвучно по отношению к программному обеспечению, будем пользоваться оригинальным английским наименованием.
Программа, выполняющая синхронизацию данных, должна уметь выполнять следующие действия:

Создание модуля conduit подразделяется на несколько этапов, которые кратко будут рассмотрены ниже.

Создание модуля синхронизации
Для создания модулей синхронизации существует специальный Conduit Development Kit (CDK). С его помощью можно создавать модули синхронизации при помощи среды разработки Visual C++.
Модуль, отвечающий за синхронизацию данных, представляет собой подгружаемую библиотеку (DLL), которая автоматически вызывается во время синхронизации. Для корректной работы DLL должна предоставлять ряд необходимых функций.

Помимо вышеперечисленных, существует еще несколько функций, назначение которых не столь принципиально. Например, функция LogAddEntry добавляет строку в файл истории синхронизации. Обычно пользователь вряд ли будет изучать этот файл, но в случае некорректной синхронизации выдача дополнительной информации может быть весьма полезна.
Таким образом, для создания модуля, отвечающего за синхронизацию данных, необходимо:

  1. В среде разработки Visual Studio создать новый проект, например, при помощи MFC AppWizard (dll).
  2. Подключить к проекту необходимые библиотеки hslog20d.lib, pdcmn21d.lib и sync20d.lib, которые должны поставляться вместе с Conduit Development Kit.
  3. Реализовать в DLL как минимум три функции: GetconduitName, GetConduitVersion И OpenConduit.
    Более подробно об этом можно прочесть в специальной литературе, например, в книге "Palm programming: The Developers Guide"3.

Регистрация модуля синхронизации
Очевидно, что для того, чтобы при синхронизации вызывался созданный модуль, его необходимо каким-либо образом зарегистрировать. Регистрация производится с помощью занесения ряда записей в реестр, но делать это самостоятельно не рекомендуется, т. к. существует набор специальных функций, предназначенных для решения этой задачи.
Для регистрации модуля, необходимо установить три обязательных параметра.

Помимо этого, можно задать ряд необязательных параметров, например, имя модуля, показываемое пользователю, приоритет модуля синхронизации (модули с более высоким приоритетом синхронизируются первыми) и т. д.
Для установки всех этих параметров используются функции Cminstaiicreator, CmSetCreatorDirectory и т. д. Более подробно об этом можно прочитать в уже упомянутой книге "Palm OS Programming: The Developer's Guide". А в Интернете, по адресу http://www.handx.net/devtool/conduitlnstaller можно найти бесплатный инсталлятор для модулей, поставляющийся в исходных кодах по лицензии Open Source.
На этом изучение основ программирования для Palm OS подходит к концу. Естественно, что в таком объеме материала удалось рассмотреть только малую часть того, что может пригодиться программисту, разрабатывающему программное обеспечение для Palm OS. Но и приведенный материал вполне сойдет в качестве начальной точки для старта. Для более глубокого изучения придется воспользоваться специальной литературой.

Hosted by uCoz