Field Control - это штатный контрол PalmOS для редактируемого поля. Как обычно, умение работать с аналогичными контролами в других ОС как помогают, так и мешают работе с field.
Field традиционно проще своих десктопных аналогов.
==== Общие замечания ====
Отдельные символы в поле задаются **позициями**. **позиция** - смещение символа в байтах от начала поля.
Можно задать **диапазон**. Диапазон задается позицией первого символа диапазона и позицией символа **после** диапазона. Если позиции начала и конца совпадают, то диапазон пустой.
==== Описание в ресурсах ====
Приведем описание поля в файле ресурсов. Описание отдельных параметров будет приведено позже в разделе атрибутов
FIELD ID AT ( ) [USABLE] [NONUSABLE] [DISABLED]
[LEFTALIGN] [RIGHTALIGN] [[FONT|]] [EDITABLE] [NONEDITABLE] [UNDERLINED] [SINGLELINE]
[MULTIPLELINES] [DYNAMICSIZE] [[MAXCHARS|]] [AUTOSHIFT]
[NUMERIC] [HASSCROLLBAR]
==== Атрибуты ====
В этом разделе перечислены атрибуты поля. После описания обычно даются функции по получению и установке атрибута, а также имя атрибута в файле ресурсов.
||**Имя поля**||**Описание**||**Функции извлечения**||**Функции модификации**||**Параметр в ресурсах**||
||id||идентификатор поля на форме|| || ||ID ||
||rect||стандартные координаты поля типа RectangleType||FldGetBounds||FldSetBounds||AT ( )||
||fontID||шрифт, используемый в поле||FldGetFont||FldSetFont||FONT )||
||text||указатель на текст, содержащийся в поле. текст завершается нулевым байтом.||FldGetTextPtr||FldSetTextPtr||||
||textHandle||хендл текстового блока||FldGetTextHandle||FldSetTextHandle FldSetText||||
||textLen||реальная длина текста, используемая блоком, StrLen(text).||FldGetTextLength||||||
||textBlockSize||длина блока, выделенного для текста. По сути дела максимальная длина текста, которую поле будет использовать до перевыделения памяти.||FldGetTextAllocatedSize||FldSetTextAllocatedSize||||
||maxChars||максимальное допустимое число вводимых символов. Учтите, что для мультибайтных кодировок это не будет совпадать с максимальной длиной блока в байтах. Предельный размер поля - 32767 байт.||FldGetMaxChars||FldSetMaxChars||MAXCHARS ||
||selFirstPost, selLastPos||позиции выделенного текста.||FldGetSelection||FldSetSelection ||||
||insert position||позиция ввода. реально в структуре FieldType не хранится, вместо него хранятся следующие два поля.||FldGetInsPtPosition|| FldSetInsPtPosition (с перерисовкой) FldSetInsertionPoint (без перерисовки)||||
||insPtXPos||смещение в байтах позиции ввода от начала строки. А если текущая строка не видна, то абсолютная позиция.||||||||
||insPtYPos||строка на экране, в которой находится позиция ввода. Если позиция невидима, то в поле хранится 0x8000||||||||
||maxVisibleLines||максимальное число видимых строк|| ||FldSetMaxVisibleLines||||
||lines||информация разбиения текста по строкам. Поле указывает на массив информации о началах строк и числе символов в каждой. Число строк можно получить с помощью функции FldGetVisibleLines.||||||||
Следующие поля входят в структуру FieldAttrType. Для работы с этими полями используются функции FldGetAttributes и FldSetAttributes
||**Имя поля**||**Описание**||**Функции извлечения**||**Функции модификации**||**Параметр в ресурсах**||
||usable||признак использования поля в форме|| ||FldSetUsable||USABLE NONUSABLE||
||visible||признак видимости поля.|| ||FldDrawField FldEraseField||DISABLED||
||editable||признак возможности редактирования поля пользователем|| || ||EDITABLE NONEDITABLE||
||singleLine||Признак однострочного или многострочного поля|| || ||SINGLELINE MULTIPLELINES||
||hasFocus||поле держит фокус. Если поле в фокусе, то в нем мерцает курсор|| ||FldGrabFocus FldReleaseFocus||||
||dynamicSize||Обманчивое имя признака. На самом деле, если флаг выставлен, то при изменении числа строк генерируется событие fldHeightChangedEvent. Приложение может отслеживать событие и изменять видимые границы поля. Если признак не выставлен, то событие не генерируется. Никакого автоматического изменения размера не происходит. Для однострочных полей смысла не имеет.|| || ||DYNAMICSIZE||
||insPtVisible||внутренний признак того, что строка с позицией ввода видна на экране.||||||||
||dirty||признак "грязного" поля. Поле является грязным, когда изменения в содержимом не отображены на экране.|||FldDirty||FldSetDirty||||
||underlined||Признак указывает на тип подчеркивания строк. PilRC позволяет только стандартный способ - grayUnderline. Программно можно задать noUnderline, solidUnderline, colorUnderline.|| || ||UNDERLINED||
||justification||задает выравнивание поля: leftAlign, rightAlign. centerAlign не поддерживается.|| || ||LEFTALIGN RIGHTALIGN||
||autoShift||если признак задан, то поддерживаются правила autoShift (автоматическое выставление заглавных букв в начале предложений). || || ||AUTOSHIFT||
||hasScrollBar||Еще одно обманчивое имя. При выставлении поле начинает чаще посылать fldChangedEvent. Слово "чаще" нужно понимать как "поле чаще пересчитывает разбиение на строки".|| || ||HASSCROLLBAR||
||numeric||К версии PalmOS 3.5 поле научилось воспринимать только цифры, разделитель тысяч и десятичную точку.|| || ||NUMERIC||
==== Работа с памятью ====
Первое, что следует усвоить при работе с полями - это стратегии выделения памяти для содержимого поля.
* Фиксированное поле. textHandle содержит NULL, а пользователь функциями FldGetTextPtr и FldSetTextPtr работает с содержимым поля (text). При освобождении контрола память не освобождается. Такая техника должна применяться только к read-only полям.
* Динамическое поле в куче. Эта стратегия подразумевает, что памятью управляет контрол. Память выделяется контролом по необходимости в виде чанка и перевыделяется при превышении объема чанка. Хэндл чанка хранится в textHandle, связанный с ним указатель в text. Атрибут textBlockSize указывает максимальный размер памяти, доступный для использования без перевыделения.
* Динамическое поле в storage. Эта стратегия предназначена для модификации текста в базе. Именно так реализованы заметки во всех стандартных приложениях. Функция FldSetText содержит дополнительный параметр offset, который указывает смещение строки в чанке. Подразумевается что offset первых байт записи содержат прочую информацию. В остальном способ использования очень похож на предыдущий.
Функция FldFreeMemory освобождает память, выделенную для поля. В случае фиксированного поля ничего не освобождается. Если поле хранит содержимое в куче, то все освобождение происходит автоматически. В третьем случае функция попытается освободить память связанную с записью, что приведет к краху. Чтобы его избежать следует отвязать запись от поля вызовом FldSetTextHandle(fldP, NULL)
.
Таким образом, аккуратного программирования требует только третья стратегия. Две первые ведут себя ожидаемо и предсказуемо. Не рекомендуется смешивать стратегии работы без необходимости.
Также не стоит сохранять указатель на текст в двух последних стратегиях, поскольку указатель может измениться.
Функция FldCompactText уменьшает размер чанка до текущего размера поля.
==== Рисование поля ====
Обычная отрисовка поля происходит автоматически. Программировать перерисовку руками нужно только есть вы изменяете содержимое поля функциями FldSetTextPtr/FldSetText/FldSetTextHandle. В этом случае используйте следующие функции:
* FldDrawField - отрисовка поля
* FldEraseField - очистка области, занимаемой полем. Признак visible сбрасывается и дальнейшие автоматические перерисовки будут запрещены до следующего вызова FldDrawField.
* FldRecalculateField - пересчитывает структуру lines и опционально перерисовывает поле. После вызова функций FldSet* вызов этой функции необходим.
Сверяйтесь со справочником при использовании функций, модифицирующих поле. Некоторые функции обновляют поле, а некоторые этого не делают.
==== Операции над полем ====
Для непосредственной работы с полем существуют привычные функции: FldCopy, FldCut, FldPaste. Эти функции реализуют стандартные функции взаимодействия поля с клипбордом.
Функция FldUndo откатывает последние операции. Функции отката могут откатить до 100 действий, сделанных над текущим полем.
Существует удобная функция для вставки в текущую позицию (или замены выделенного текста) - FldInsert. Пользоваться этой функцией удобно, но нужно учесть, что она заместит выделение и запостит в очередь сообщений fldChangedEvent. Сотня вставок с помощью FldInsert просто вызовет переполнение очереди. В этом случае проще получить хэндл текста, модифицировать содержимое и установить новый хэндл.
Функция FldDelete удаляет символы из указанного диапазона.
==== Взаимодействие со скроллбаром ====
Взаимодействие со скролл баром должно быть реализовано вручную. К счастью технология взаимодействия достаточно простая.
Описание поля и скроллбара в ресурсах:
FIELD ID MainDescriptionField AT (0 20 151 122) EDITABLE MULTIPLELINES LEFTALIGN MAXCHARS 8192 HASSCROLLBAR
SCROLLBAR ID MainDescriptionScl AT (152 20 7 122) VALUE 0 MIN 0 MAX 100 PAGESIZE 8
Для обновления скроллбара нужно получить текущую позицию текста в поле с помощью функции FldGetScrollValues. Функция для обновления позиции скроллбара:
static void UpdateScrollBar(void){
UInt16 scrollPos;
UInt16 textHeight;
UInt16 fieldHeight;
Int16 maxValue;
FieldPtr fld;
ScrollBarPtr bar;
fld = GetObjectPtr (MainDescriptionField);
bar = GetObjectPtr (MainDescriptionScl);
FldGetScrollValues (fld, &scrollPos, &textHeight, &fieldHeight);
if (textHeight > fieldHeight)
{
// On occasion, such as after deleting a multi-line selection of text,
// the display might be the last few lines of a field followed by some
// blank lines. To keep the current position in place and allow the user
// to "gracefully" scroll out of the blank area, the number of blank lines
// visible needs to be added to max value. Otherwise the scroll position
// may be greater than maxValue, get pinned to maxvalue in SclSetScrollBar
// resulting in the scroll bar and the display being out of sync.
maxValue = (textHeight - fieldHeight) + FldGetNumberOfBlankLines (fld);
}
else if (scrollPos)
maxValue = scrollPos;
else
maxValue = 0;
SclSetScrollBar (bar, scrollPos, 0, maxValue, fieldHeight-1);
}
Для точного позиционирования поля по первому видимому байту используются функции FldGetScrollPosition и FldSetScrollPosition. Но для грубых позиционирований достаточно использовать FldScrollField.
Выяснить, скроллится ли поле в указанном направлении можно с помощью функции FldScrollable.
Функция для скролла поля по изменению скролл бара:
static void ViewScroll (Int16 linesToScroll, Boolean updateScrollbar)
{
UInt16 blankLines;
FieldPtr fld;
fld = GetObjectPtr (MainDescriptionField);
blankLines = FldGetNumberOfBlankLines (fld);
if (linesToScroll < 0)
FldScrollField (fld, -linesToScroll, winUp);
else if (linesToScroll > 0)
FldScrollField (fld, linesToScroll, winDown);
// If there were blank lines visible at the end of the field
// then we need to update the scroll bar.
if ((blankLines && (linesToScroll < 0)) || updateScrollbar){
UpdateScrollBar();
}
}
Фрагмент обработчика событий формы:
case fldChangedEvent:
UpdateScrollBar();
break;
case sclRepeatEvent:
ViewScroll (e->data.sclRepeat.newValue - e->data.sclRepeat.value, false);
break;
==== Динамический размер поля ====
PalmOS позволяет отслеживать высоту поля и изменять его размеры на форме. Изменение размеров нужно писать самому, PalmOS позволяет только отделить изменение поля от перерасчета размеров. Для отслеживания необходимо:
* Добавить в описание ресурса признак DYNAMICSIZE.
* Добавить в обработчик событий формы обработку fldHeightChangedEvent. Это сообщение указывает на необходимость изменения размеров поля.
* После ручных изменений содержимого поля следует вызвать FldMakeFullyVisible. Функция пересчитает число строк и добавит в очередь соответствующее событие.
* Функция FldSendHeightChangeNotification позволяет задать размеры поля вручную.
==== Как отследить изменение поля? ====
К сожалению, событие fldChangedEvent посылается при всех визуальных изменениях поля, в том числе и просто скроллах. Не существует надежного механизма для изменения переменной, связанной с полем при выходе из поля. Можно посоветовать только изменять переменную на все fldChangedEvent.
==== Прочие функции ====
Оставшиеся функции нужны редко. В основном они вызываются другими модулями PalmOS.
* FldHandleEvent обрабатывает события, посылаемые полю.
* FldNewField добавляет новое поле на форму.
* FldSendChangeNotification вручную посылает полю оповещение о перерисовке.
* FldGetTextHeight возвращает высоту непустых видимых строк.
* FldGetNumberOfBlankLines возвращает число видимых пустых строк в конце поля.
Следующие функции полезны при "ручной" реализации статических полей, они позволяют разбить длинную текстовую строку на отдельные строки заданной ширины:
* FldCalcFieldHeight - рассчитывает сколько строк займет текст в прямоугольнике указанной ширины.
* FldWordWrap - рассчитывает число байт, умещающихся в одну строку указанной ширины.
==== Полезные ссылки ====
http://www.palmosters.com/back/palmstranges.shtm - на странице описаны недокументированные нюансы программирования в PalmOS, в том числе некоторые грабли, связанные с филдами.