- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF97:GtkSourceView
Материал из Linuxformat.
Движок текстового редактора
- Если возможностей чистого GtkTextView для вашего приложения уже недостаточно или вы просто решили по-быстрому написать лучшую IDE всех времен и народов – начните с урока GtkSourceView от Петра Семилетова.
В состав библиотеки Gtk входит отличный виджет текстового редактора, воплощенный в виде взаимодополняющих классов GtkTextBuffer, GtkSourceView и ряда других, вспомогательных (мы писали про них в LXF93). Но ради универсальности этого виджета его разработчики пожертвовали некоторыми вещами. Например, нет встроенного механизма redo/undo, нет также подсветки синтаксиса различных языков программирования, хотя виджет обладает способностью задавать цвет и параметры шрифта при отображении отдельных участков текста.
Однако среди разрабочиков известного редактора GEdit вырос виджет-надстройка над GetTextView – и называется он GtkSourceView (http://gtksourceview.sourceforge.net). В нем есть все то, чего недостает в стандартном виджете: и движок redo/undo, и подсветка синтаксиса, и отображение правой границы – так называемого margin’а, и многое другое.
Сейчас развитие GtkSourceView стоит на пороге перехода на новую ветку – под номером два. Вторая версия еще считается нестабильной, а API ее не устоялось. В этой статье я постараюсь дать представление и о современной версии библиотеки, и о грядущей. Замечу также, что я предполагаю ваше умение работать с обычным GtkTextView. Как я упоминал, GtkSourceView – лишь надстройка над GtkTextView, поэтому работа с GtkSourceView подразумевает использование и «стандартных» функций из Gtk.
Чтобы создать новый виджет GtkSourceView, используется функция Gtk_source_view_new:
GtkWidget *text_view = gtk_source_view_new ();
Классу буфера для GtkSourceView, вместо GtkTextBuffer, соответствует GtkSourceBuffer. Для его создания надо вызвать функцию gtk_source_buffer_new:
GtkSourceBuffer *text_buffer = gtk_source_buffer_new (table);
В качестве параметра мы передаем этой функции экземпляр таблицы с определениями тэгов пользовательской подсветки (вне движка подсветки GtkSourceView). Напомню, что в текстовом движке Gtk, тэг – это объект, содержащий параметры форматирования текста, как то: шрифт, цвет и тому подобное.
Для движка GtkSourceView первой версии, таблица имеет тип GtkSourceTagTable. Во второй версии GtkSourceView надо использовать стандартный тип из Gtk – GtkTextTagTable. Можно передавать в параметре значение NULL. Тэги вам понадобятся, только если вы захотите неким образом размечать текст – например, подчеркивать слова с ошибками. Создание тэгов, помещение их в таблицу и последующее применение (тэги применяются в тексте к месту, ограниченному итераторами) – тема, выходящая за рамки данной статьи.
Для получения текста из GtkSourceBuffer и помещения его туда применяются те же функции, что и для GtkTextBuffer – gtk_text_buffer_get_text и gtk_text_buffer_set_text. Передавая экземпляр GtkSourceBuffer в качестве параметра, можете приводить его к типу (макросом GTK_TEXT_BUFFER), а можете не приводить – все равно передается указатель, а компилятор может выдавать предупреждения в зависимости от своих настроек. Однако ничего дурного не случится, если вы вместо:
gchar *text = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer));
напишете такое:
gchar *text = gtk_text_buffer_get_text (buffer);
В общем, здесь и далее, ради экономии журнального места, я не буду использовать в примерах приведение к типу.
Итак, с текстовым содержимым GtkSourceBuffer мы разобрались. Теперь давайте применим к загруженному тексту подсветку синтаксиса – если таковая, конечно же, предусмотрена для конкретного языка движком: создания подсветки «пользовательского» языка мы касаться не будем. Используем готовые, «встроенные» модули подсветки – физически это XML-файлы, в которых заданы правила подсветки.
Само собой, виджет не догадывается по помещенному в него тексту, подсветку синтаксиса какого языка надо использовать. Эту процедуру необходимо запрограммировать Работать будем опять-таки с буфером.
В GtkSourceView 1:
gtk_source_buffer_set_language (buffer, language); gtk_source_buffer_set_highlight (buffer, TRUE);
В GtkSourceView 2:
gtk_source_buffer_set_language (buffer, language); gtk_source_buffer_set_highlight_syntax (buffer, TRUE);
Функция gtk_source_buffer_set_language устанавливает нужную подсветку (удаляя при этом все тэги типа GtkSourceTag из таблицы тэгов переданного буфера), а gtk_source_buffer_set_highlight (либо gtk_source_buffer_set_highlight_syntax) включает ее. А вот что такое переменная language в параметре первой функции? Определена она так:
GtkSourceLanguage *language = NULL;
Каким образом связать переменную language с подсветкой файлов определенного типа? В первой версии GtkSourceView есть функция с очень длинным названием: gtk_source_languages_manager_get_language_from_mime_type. Она получает два параметра – указатель на менеджер языков и строку, задающую тип MIME (для загруженного файла).
Менеджер языков – это переменная типа GtkSourceLanguagesManager (в GtkSourceView 1') и GtkSourceLanguageManager (в GtkSourceView 2). В первом случае получаем экземпляр нужного класса с помощью функции gtk_source_languages_manager_new, во втором лучше использовать новое средство, функцию gtk_source_language_manager_get_default. Вручную уничтожать полученные объекты не нужно.
Итак, для получения механизма подсветки, соответствующего некоторому типу MIME, используется примерно следующее:
language = gtk_source_languages_manager_get_language_from_mime_type (source_languages_manager, mime_string);
Однако тут возникают два вопроса. Как быть в GtkSourceView 2, где покамест нет функции, «отдающей» нам язык по переданному типу MIME? И как вообще получить MIME для файла?
На первый вопрос ответ очевиден – надо писать свою функцию. В каталоге tests исходных текстов GtkSourceView 2 есть образец – смотрите файл test-widget.c, функцию get_language_for_mime_type. Не самое удачное воплощение алгоритма, но оно работает, и вы будете иметь представление, куда двигаться дальше.
Теперь о типах MIME. Есть несколько библиотек, позволяющих работать с ними. Самый естественный способ для программы, основанной на Gtk – обратиться к библиотеке GnomeVFS (LXF94):
GnomeVFSFileInfo *info = gnome_vfs_file_info_new (); GnomeVFSResult r = gnome_vfs_get_file_info (filename, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE); gchar *mime = gnome_vfs_file_info_get_mime_type (info); // тут что-то делаем со строкой mime, затем освобождаем память, выделенную под структуру info: gnome_vfs_file_info_unref (info);
Итого – в переменной mime, покуда память для info не освобождена, имеем значение, соответствующее типу MIME для файла, чье имя передано в переменной filename.
Не забудьте, что перед использованием функций GnomeVFS надо инициализировать эту библиотеку (gnome_vfs_init), а после завершения работы вызвать gnome_vfs_shutdown.
Итак, подсветка заработала. Что еще мы можем с ней сделать? Изменить цвета. В GtkSourceView 1 можно, с помощью довольно громоздкого кода, назначить параметры подсветки для каждого элемента каждого доступного языка, например, для строки в HTML или CSS. В GtkSourceView 2 появился более удобный способ изменения цветов – использование схем. Схема здесь – это, грубо говоря, предустановленный набор цветов. Во время написания этих строк, в составе GtkSourceView идет 3 схемы – classic, kate и tango. Как выбрать схему?
Сначала раздобудем список идентификаторов установленных в системе схем:
GtkSourceStyleSchemeManager *manager = gtk_source_style_scheme_manager_get_default (); gchar **scheme_ids; g_object_get (manager, “scheme-ids”, &scheme_ids, NULL); GList *ids = NULL; gint c = g_strv_length (scheme_ids) - 1; gint i; for (i = 0; i <= c; i++) ids = g_list_prepend (ids, scheme_ids[i]);
Здесь мы получаем менеджер схем по умолчанию, а затем считываем из него свойство scheme-ids в массив scheme_ids. После чего, для удобства, перемещаем идентификаторы в список ids. Со списком GList удобнее работать, если у вас есть готовые функции для создания виджетов-меню или виджетов-списков на основе данных из GList. А массивы представляются мне чем-то старорежимным.
Далее, зная идентификаторы доступных схем, мы можем затребовать нужную схему по ее идентификатору:
GtkSourceStyleScheme *scheme = gtk_source_style_scheme_manager_get_scheme (manager, cheme_id);
Осталось лишь назначить схему буферу GtkSourceBuffer:
gtk_source_buffer_set_style_scheme (buffer, scheme);
Чем еще полезным нас может порадовать GtkSourceView2? Механизмом Undo/Redo. По умолчанию любое изменение текста в буфере подлежит отмене либо обратному действию. Если вам надо временно этот механизм отключить, используйте функции gtk_source_buffer_begin_not_undoable_action и gtk_source_buffer_end_not_undoable_action. Любые действия с текстом в буфере между их вызовами не будут включены в очередь отмены. Так, например, следует поступить при первой вставке текста из загруженного файла – иначе, когда пользователь будет применять отмену, он рано или поздно дойдет до пустого документа.
Для программного вызова отмены и отмены отмены (redo, как вы догадались) нам даны функции gtk_source_buffer_undo и gtk_source_buffer_redo. Можно также проверить, доступны ли эти операции, и установить наибольшую длину очереди движка замен – указать, сколько в ней может быть элементов.
Кроме того, движок GtkSourceView предоставляет много функций, полезных разработчикам редакторов для программистов. Это управление автоматическим отступом и параметрами табуляции, отображение номеров строк слева от текста, работа с маркерами (полезно для показа каких-нибудь закладок) и многое другое.
Обе версии GtkSourceView – первую и вторую – можно установить одновременно, они не мешают одна другой. Если вы пишете программу с использованием GtkSourceView, то стоит подумать над поддержкой обеих версий, ведь в старых дистрибутивах (да и в стабильной ветке Debian) вторая версия появится не скоро.
Проверить наличие установленной библиотеки и включить ее в параметры сборки можно разными способами. Приведенный ниже код (предназначенный для размещения в configure.in) проверяет, установлены ли в системы библиотеки GtkSourceView2 и GnomeVFS 2. Если да, то они добавляются к параметрам сборки, а в файле config.h будет определена константа gtksourceview2_SUPPORTED, проверяя которую, мы сможем узнать, есть поддержка GtkSourceView2 или нет:
echo -n “checking for GtkSourceView2... “ if pkg-config --exists gtksourceview-2.0 ; then LIBS=”$LIBS `pkg-config --libs gtksourceview-2.0 `” CFLAGS=”$CFLAGS `pkg-config --cflags gtksourceview-2.0 `” AC_DEFINE(GTKSOURCEVIEW2_SUPPORTED, 1, [GTKSOURCEVIEW2_SUPPORTED]) echo “yes” echo -n “checking for gnome-vfs-2.0... “ if pkg-config --exists gnome-vfs-2.0 ; then LIBS=”$LIBS `pkg-config --libs gnome-vfs-2.0`” CFLAGS=”$CFLAGS `pkg-config --cflags gnome-vfs-2.0`” echo “yes” else echo “no” fi else echo “no” fi
Сходным способом можно проверить и наличие GtkSourceView 1.
Все это касается стандартной системы сборки/установки с помощью Autotools – Scons, CMake и прочие настраиваются аналогично (с поправкой на синтаксис, разумеется). Для удобной работы с пользовательской настройкой подсветки в первой версии GtkSourceView вам еще понадобится библиотека GConf, чтобы держать настройки цветов в хранилище. Все подробности можно найти в исходных текстах GEdit или TEA. LXF