• last update at 26.10.2006, any questions mailto:wtwsoft@yandex.ru, ICQ: 225-410-492

  • How to access to Edit
    (copyleft) Denis Kirin

    Адаптация редактора текстов (на примере Edit)


    скачать

    Примечание:

    Документ ссылается на "Win32 Developer's references" желательно иметь его под рукой.

    Примеры приводимые в этом документе рассчитаны на использование Delphi 7, однако автор будет стараться как можно более отстранится от VCL классов TMemo и TEdit представляющих в этой библиотеке EDIT window class.

    Так же автор по возможности реализует универсальный документ, освещающий основные принципы адаптации компонент графического интерфейса. Класс RichEdit специально небыл выбран, по причине его относительной усложненности

    (Шрифты, таблицы, рисунки). Обратите внимание на WebBrowser как возможный компонент для редактирования, просмотра текста.

    Введение (Абстракция):

    У нас всегда есть два пути, в данном случае - реализовывать собственный редактор текста с "нуля", либо воспользоваться уже готовым построив оболочку вокруг него.

    Оба пути обладают своими плюсами и минусами, в этом документе мы обратимся ко второму.

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

    Путь номер два:

    Edit реализован и поставляется в месте с Windows, ваше Win32 приложение может использовать его для отображения и редактирования текстов. Разработчики Edit заложили в него определенное поведение (реакцию на события), как правило программист использующий Edit почти не задумывается о его поведение, Edit делает все сам, причем это происходит привычным и интуитивно понятно для пользователя (ведь он уже имел дело с Edit ранее).

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

    Охватить все поведение Edit сразу, практически не возможно, наша программа должна постепенно наращивать свои возможности. Поэтому давайте начнем программировать.

    Пример 1.

    В Edit расположена каретка курсора (не путайте ее с указателем мыши). У каретки есть несколько задач:

    Каретка показывает пользователю его текущее положение в тексте.

    Каретка показывает место в тексте куда будут помещается набранные пользователем символы.

    Каретка послужит началом выделения текста.

    На положение каретки в тексте влияют следующие события:

    Клавиши управления кареткой и их комбинации.

    Ввод новых символов.

    Нажатие клавиши Enter.

    Клик мыши над текстом.

    Так же нельзя забывать о том что каретка может быть перемещена самим приложением, например при загрузке нового текста.

    Возможно это не все события, которые переместят каретку.

    В первом примере рассмотрим очень упрощенную ситуацию:

    В Edit содержится три строчки текста, пользователь может нажимать только клавиши курсора вверх и вниз.

    Изначально каретка находится на первой строчке текста.

    Вот текст:

    "Это простой текст
    для демонстрации
    простого примера"

    Начнем с описания того что мы хотим получить от нашей программы.

    - Программа ожидает действия пользователя (события).

    - Программа анализирует изменения произошедшие в Edit после того как пользователь произвел действие.

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

    Просто сценарий (как это должно быть на мой взгляд):
    Событие - Пользователь нажимает клавишу вниз.
    Реакция Edit - каретка перемещается на вторую сточку.
    Реакция программы - вывод голосового сообщения "для демонстрации"
    События - еще раз клавиша вниз.
    Реакция Edit - каретка на третей строчки
    Реакция программы - вывод голосом "простого примера" звуковой сигнал, вывод текста "конец текста"
    Событие - пользователь снова нажимает вниз.
    Реакция Edit - каретка остается на месте
    Реакция программы - вывод голосом "простого примера" звуковой сигнал, вывод текста "конец текста"
    Событие - пользователь нажимает клавишу вверх.
    Реакция Edit - каретка на строчку вверх.
    Реакция программы - вывод голосового сообщения "для демонстрации"
    Событие - пользователь нажимает клавишу вверх.
    Реакция Edit - каретка на строчку вверх.
    Реакция программы - вывод текста голосом "Это простой пример", специальный звуковой сигнал, "начало текста"
    Событие - пользователь нажимает клавишу вверх.
    Реакция Edit - каретка остается на месте.
    Реакция программы - вывод текста голосом "Это простой пример", специальный звуковой сигнал, "начало текста"

    Это сценарий, попробуем его запрограммировать.

    От Edit нам необходимы два события:

    - Нажатие клавиши.

    - Изменения позиции каретки.

    Для нажатия клавиш Edit предоставляет (как и любое правильное окно) два события WM_KEYDOWN и WM_KEYUP. Эти события происходят одно за другим - когда пользователь нажимает клавишу и когда пользователь отпускает клавишу, соответственно.

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

    SendMessage(Handle, EM_GETSEL, 0, 0)

    Причем нас интересует только старшее значение результата, положение каретки (младшая часть - длина выделенной области):

    X := LongRec(SendMessage(Handle, EM_GETSEL, 0, 0)).Hi;

    X это еще не номер колонки, это количество символов от начала текста до начала выделенной области (по сути текст рассматривается как одномерный массив) Воспользуемся сообщением EM_LINEFROMCHAR, если мы отправим Edit это сообщение, указав в качестве параметра количество символов от начала текста, то Edit вернет нам номер строки в которой находится последний символ. Мы знаем положение каретки в количестве символов от начала текста, это значение сохранено в переменой X. Если мы передадим X в качестве параметра, то мы получим Y (номер строки в которой находится каретка)

    Y := SendMessage(Handle, EM_LINEFROMCHAR, X, 0)

    В данном случае нам нужен только Y, однако рассмотрим как получить номер столбца: X := X - SendMessage(Handle, EM_LINEINDEX, -1, 0); EM_LINEINDEX возвращает количество символов от начала текста до указанной строки. Однако если в качестве параметра указать -1, EM_LINEINDEX в качестве номера строки будет использовать позицию каретки.

    Подготовка закончена, теперь у нас есть номер строки в которой находится каретка (Line). Что бы получить текст из определенной строки Edit, необходимо передать ему команду EM_GETLINE, это можно сделать примерно так:

    var
    Result: string;
    Text: array[0..4095] of Char;
    begin
    Word((@Text)^) := SizeOf(Text);
    SetString(Result, Text, SendMessage(Handle, EM_GETLINE, Y, Longint(@Text)));
    end;

    В результате в переменой Result появится текст из строки Y.

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

    Поэтому наш обработчик должен обрабатывать WM_KEYDOWN.

    Сайт создан в системе uCoz