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, в том числе некоторые грабли, связанные с филдами.