# Советы, подсказки и хитрости для разработчиков gEDA

## Структура данных схемы

Внутри gaf схема представляется набором двунаправленно-связанных списков. Центральным типом связи в списках является OBJECT. Он может быть представлением какого-нибудь символа, строки текста, графического примитива, соединения или атрибута.

Обзор структуры данных схемы можно найти здесь. Этот набросок сделан Стюартом Брорсоном (Stuart Brorson) в 2005 году.

## Комментарии и стили Doxygen

Doxygen представляет собой утилиту для извлечения документации по API из комментариев в исходном коде. В комментариях может указываться разметка, которая впоследствии может быть преобразована программой Doxygen в код HTML или LaTeX. Это позволяет, к примеру, одной функции сослаться на другую, связанную с ней, а также даёт возможность приводить документацию об аргументах и возвращаемых значениях.

Некоторые части исходных текстов gaf уже подготовлены для работы с doxygen. Сейчас сюда входят libgeda, gschem, gnetlist, gsymcheck и gattrib. Файлы Makefile этих утилит в каталоге `docs` содержат цель “doxygen”. Или же вывод doxygen можно посмотреть на этой странице, подготовленной Бертом Тиммерманом (Bert Timmerman).

Если вы хотите узнать заложенные в Doxygen идеи о том, как надо форматировать документацию, см. веб-сайт Doxygen. Отдельные команды документированы здесь. По Doxygen есть также несколько очень удобных кратких справочных страниц.

В следующих разделах предлагается вводная информация о том, как обычно документируются gschem и libgeda. Обратите внимание, что для комментариев, разрешённых в Doxygen, предпочтительным является стиль QT вида /*! здесь идут комментарии */.

### Документирование файлов

При создании нового файла в нём, очевидно, должен быть обычный текст лицензии GNU. После лицензии нужно включить комментарий о файле с описанием того, для чего этот файл, и с любым другим текстом, относящимся ко всему файлу как целому1).

      /*! \file <filename.ext>
          \brief Здесь краткое изложение того, для чего этот файл...
          \par Описание
          Более длинное описание того, для чего этот файл (необязательно).
       */

### Документирование переменных/определений/описаний типов

Глобальные переменные в файле можно документировать с помощью команды \var или просто написав комментарий с помощью команды \brief прямо перед определением.

      /*! \brief fill style of objects like cirle, rect, path */
      typedef enum {FILLING_HOLLOW, FILLING_FILL, FILLING_MESH, FILLING_HATCH, FILLING_VOID} OBJECT_FILLING;

### Документирование функций

Функции можно документировать точно так же, как и переменные и т. д. Достаточно вставить блок комментария выше документируемой им функции и начать его командой \brief.

Обычно для длинного описания назначения функции используется дополнительный параграф Function Description. Также, для определения того, будут ли параметры функции изменяться в ней самой, в командах \param используются атрибуты [in] или [out].

      /*! \brief "Save" a file into a string buffer
       *  \par Function Description
       *  This function saves a whole schematic into a buffer in libgeda
       *  format. The buffer should be freed when no longer needed.
       *
       *  \param [in] toplevel    The current TOPLEVEL.
       *  \param [in] object_list The head of a GList of OBJECTs to save.
       *  \return a buffer containing schematic data or NULL on failure.
       */

### Документирование структур

Структуры документируются так же, как указано в предыдущих разделах. Учтите, что комментарии для членов структур могут как быть встроенными в их собственные строки, так и указываться с помощью того же синтаксиса \brief, что и для переменных. Для встраивания документации в строку в её конце должен помещаться комментарий особого вида, начинающийся с /*!<.

      /*! \brief Structure for connections between OBJECTs
       *
       * The st_conn structure contains a single connection
       * to another object.
       * The connection system in s_conn.c uses this struct
       */
      struct st_conn {
        OBJECT *other_object; /*!< The "other" object connected to this one */
        /*! \brief type of connection. Always in reference to how the "other"
            object is connected to the current one */
        int type;
        int x; /*!< x coord of the connection position */
        int y; /*!< y coord of the connection position */
        int whichone; /*!< which endpoint of the current object caused this connection */
        int other_whichone; /*!< which endpoint of the "other" object caused this connection */
      };

### Команды Bug/Todo

\bug и \todo полезны для отметки мест с недостатками или отсутствующими возможностями в коде. Эти команды могут быть вставлены в любое место внутри комментариев Doxygen, и служат они для создания записей на специальных страницах в документации, позволяющих легко найти соответствующее место.

## Диалоговые окна: дизайн и поведение

### Создание диалоговых окон

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

Дизайн диалоговых окон — это, в общем-то, дело вкуса:

- выравнивание элементов. См. Window Layout
- правое выравнивание кнопок диалога
- некоторое расстояние вокруг диалога (но какое?)
- некоторое расстояние между элементами (по вертикали и по горизонтали)
- группы переключателя в рамках или со сдвигом?
- метки рамок или жирные заголовки?

### Модальные и немодальные диалоговые окна

Модальное окно диалога требуется во всех тех случаях, когда данные для окна обеспечиваются основным приложением.

    Пример:
      Окно диалога вызывается со списком выбора и должно работать только
      с данными этого списка.

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

Модальное окно не подходит, если пользователь много взаимодействует с этим окном. Хороший пример — выбор компонентов.

### Где размещать диалоговое окно

Диалоговое окно можно поместить в различные места экрана. Список возможных мест можно найти в GtkReference

В настоящее время диалоги помещаются или в позиции мыши (GTK_WIN_POS_MOUSE) или ни в какой предустановленной позиции (GTK_WIN_POS_NONE). В Gnome HID по этой теме ничего не сказано.

Настройкой по умолчанию для GtkWindow является GTK_WIN_POS_NONE, см. GtkWindow. Настройкой по умолчанию для GtkDialog является GTK_WIN_POS_CENTER_ON_PARENT ( taken from the GtkDialog source).

### Помещение диалоговых окон перед их родительскими окнами

Большинство диалоговых окон размещается перед их родительскими окнами с помощью свойства transient_for (см. GtkReference). Это свойство должно быть установлено для всех модальных диалоговых окон.

Для немодальных диалоговых окон установка свойства transient_for не очевидна. В то время как, например, в gschem диалоговое окно координат должно находиться над родительским окном, окно журнала вешать перед ним нужды нет.

Примечание: Существует более старый механизм, удерживающий эти окна перед gschem. Если переменная raise-dialog-boxes-on-expose устанавливается в enabled в одном из файлов настроек gschem, она может вызвать проблемы с некоторыми оконными менеджерами. Если диалоговые окна мерцают при 100%-ной загрузке CPU, запретите эту настройку.

    ; raise-dialog-boxes-on-expose string
    ;
    ; Должны ли диалоговые окна подниматься всякий раз, когда появится
    ; событие expose
    ; По умолчанию включено (enabled)
    ;
    ;(raise-dialog-boxes-on-expose "enabled")
    (raise-dialog-boxes-on-expose "disabled")

### Порядок кнопок в диалоговых окнах

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

Альтернативный порядок кнопок задаётся только с помощью одного вызова GTK-функции:

    /* Настройка альтернативного порядка кнопок (ok, no, cancel, help) для других систем */
    gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
    			                GTK_RESPONSE_OK,
    					GTK_RESPONSE_NO,
    					GTK_RESPONSE_CANCEL,
    					GTK_RESPONSE_HELP,
    					-1);

Это должно быть сделано перед запуском каждого нового создаваемого диалогового окна.

### Дизайн текущих диалогов

- Вокруг всего окна есть некоторое пространство (DIALOG_BORDER_SPACING).
- Некоторый сдвиг для выделения тематической группы (DIALOG_INDENTATION) под её жирным заголовком.
- Вертикальное и горизонтальное разделение производится с помощью DIALOG_H_SPACING и DIALOG_V_SPACING.

### Шаблон исходного кода для простых диалоговых окон

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

    void dialog (TOPLEVEL *w_current)
    {
      GtkWidget *vbox, *label, *alignment, *table;
      GtkWidget *dialog;

      /* Создавать диалоговое окно только тогда, когда его ещё нет. Обычно
         это указатель на виджет в структуре w_current:
         dialog = w_current->tewindow */
      if (!dialog) {
        dialog = gtk_dialog_new_with_buttons(_("Dialog title"),
    					 /* родительское окно или NULL */
    					 GTK_WINDOW(w_current->main_window),
    					 /* свойства диалога */
    					 GTK_DIALOG_MODAL, /* 0 для немодальных диалоговых окон */
    					 /* кнопки окна и сигналы ответа */
    					 GTK_STOCK_CANCEL,
    					 GTK_RESPONSE_REJECT,
    					 GTK_STOCK_OK,
    					 GTK_RESPONSE_ACCEPT,
    					 NULL);

        /* Настройка альтернативного порядка кнопок (ok, no, cancel, help) для других систем */
        gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
    	  				    GTK_RESPONSE_OK,
    					    GTK_RESPONSE_NO,
    					    GTK_RESPONSE_CANCEL,
    					    GTK_RESPONSE_HELP,
    					    -1);

        /* Установить сигнал ответа по умолчанию. Он обычно вызывается
           по нажатию "Return" */
        gtk_dialog_set_default_response(GTK_DIALOG(dialog),
    				    GTK_RESPONSE_ACCEPT);

        /* Задать функцию для обработки ответов кнопок и закрытия диалогового окна,
           для немодальных окон можно также использовать dialog_run().*/
        gtk_signal_connect(GTK_OBJECT(dialog), "response",
    		       GTK_SIGNAL_FUNC(dialog_response), w_current);

        /* Где размещать диалоговое окно: GTK_WIN_POS_MOUSE или GTK_WIN_POS_NONE */
        gtk_window_position(GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);

        /* Задать расстояние от границы окна и расстояние между элементами по вертикали */
        vbox = GTK_DIALOG(dialog)->vbox;
        gtk_container_set_border_width(GTK_CONTAINER(dialog),DIALOG_BORDER_SPACING);
        gtk_box_set_spacing(GTK_BOX(vbox), DIALOG_V_SPACING);

        /* Создать метку (со спецразметкой) и упаковать её в диалоговое окно */
        label = gtk_label_new(_("<b>Section label</b>"));
        gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
        gtk_misc_set_alignment(GTK_MISC(label),0,0);
        gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

        /* Создать контейнер выравнивания с DIALOG_INDENTATION слева */
        alignment = gtk_alignment_new(0,0,1,1);
        gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
    			      DIALOG_INDENTATION, 0);
        gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);

        /* Таблица может содержать несколько записей. Она сохраняется в контейнере выравнивания.
           Примечание: расстояния между ячейками по вертикали и горизонтали */
        table = gtk_table_new (3, 2, FALSE);
        gtk_table_set_row_spacings(GTK_TABLE(table), DIALOG_V_SPACING);
        gtk_table_set_col_spacings(GTK_TABLE(table), DIALOG_H_SPACING);
        gtk_container_add(GTK_CONTAINER(alignment), table);

        /* Простая текстовая метка в одной ячейке таблицы с левым выравниванием.
           Примечание: GTK_FILL в третьей строке обязательно */
        label = gtk_label_new(_("Text:"));
        gtk_misc_set_alignment(GTK_MISC(label),0,0);
        gtk_table_attach(GTK_TABLE(table), label, 0,1,0,1, GTK_FILL,0,0,0);

        /* Простое поле ввода текста завершает ряд опций */
        textentry = gtk_entry_new_with_max_length (10);
        gtk_table_attach_defaults(GTK_TABLE(table), textentry, 1,2,0,1);
        gtk_entry_set_activates_default(GTK_ENTRY(textentry), TRUE);

        /* ..... ещё строки таблицы с опциями или новые разделы */

        /* Создать ссылки на все виджеты, которые потребуются в дальнейшем */
        GLADE_HOOKUP_OBJECT(dialog, sizeentry,"textentry");

        /* Показывать все значения рекурсивно */
        gtk_widget_show_all(dialog);
      }

      else {
        /* Окно уже здесь. Показать его пользователю.
           Это необходимо только для немодального диалогового окна */
        gtk_window_present(GTK_WINDOW(dialog));
      }

      /* Всегда задавать в диалоговом окне текущие значения
         Если поместить эту часть в конец функции диалогового окна, это
         позволит легко создавать диалоги, которые можно будет
         вызывать, даже если они уже открыты */
      textentry = g_object_get_data(G_OBJECT(dialog), "textentry");
      gtk_entry_set_text(GTK_ENTRY(textentry), string);
      /* Выделение области текста, которую пользовать обычно любит заменять */
      gtk_entry_select_region(GTK_ENTRY(textentry), 0, strlen(string));
    }

Функция ответа для такого диалогового окна может выглядеть так:

    void dialog_response(GtkWidget *widget, gint response, TOPLEVEL *w_current)
    {
      switch (response) {
      case GTK_RESPONSE_ACCEPT:
        /* Применить установки диалогового окна:
           просто вставьте свой код здесь, если он короткий;
           или вызовите внешнюю функцию применения, если требуемый код длинный */
        break;
      case GTK_RESPONSE_REJECT:
      case GTK_RESPONSE_DELETE_EVENT:
        /* Для модальных окон просто ничего не делаем,
           для немодальных окон, уничтожаем диалог и прибираемся */
        break;
      default:
        /* Ловим ошибочные сигналы (параноидальная проверка ошибок ;-)) */
        printf("dialog_response(): strange signal %d\n", response);
      }

      /* Для немодальных окон просто ничего не делаем,
         для модальных окон всегда уничтожаем диалог и прибираемся */
    }

### Текущие проблемы с диалоговыми окнами в gschem

- у каждого диалогового окна свой собственный дизайн
- размещение окон: в позиции мыши или в непредопределённой позиции?
- диалоговые окна не запоминают своего последнего размера, положения и содержимого
- отсутствуют “горячие клавиши”

Здесь список вещей, которые могли бы быть улучшены:

##### Диалоговое окно печати

- Изменить дизайн?

##### Сохранение изображений

- В окне выбора файла отсутствует имя файла по умолчанию, если файл не существует
- Клавиша Return не работает в поле записи имени файла

##### Выполнить скрипт

##### Правка текста

- отсутствует несколько сочетаний клавиш
- добавить теги *unmodified*, если есть несколько выделенных объектов
- может быть добавить несколько цветных pixbuf для выбора цвета
- может быть заменить выравнивание текста девятью селективными кнопками, переключать кнопки с иконками или …

##### Правка цвета

- может быть добавить несколько цветных pixbuf для выбора цвета

##### Свойства линии

- отсутствуют “горячие клавиши”
- иконки для типов линии

##### Тип заполнения

- отсутствуют “горячие клавиши”
- иконки для типов заполнения

##### Смещение символа

##### Менеджер страниц

- неправильный порядок кнопок? Зависит от того, считаете ли вы кнопку обновления основной действующей кнопкой или просто дополнительной кнопкой
- может быть клавиша “Return” должна вызывать обновление

##### Выбор компонентов

- странный виджет редактирования, когда производится набор текста при выделенном дереве (этот виджет — помощник в поиске, запретить его?!)

##### Редактор атрибута

- некоторые проблемы при выделении нескольких элементов и вызове функции редактирования (ee):
  - если первый объект является текстом, то открывается это диалоговое окно (но с неправильным параметром списка)
  - если первый объект является комплексным (complex), то вызывается Правка атрибутов

##### Правка атрибутов

##### Ввод текста

##### Параметры дуги

- Добавить в диалоговое окно диаметр, но выбирать поле ввода начального угла (increment = grid)
- пусть “ee” вызывает диалог, если выделена только одна дуга
- может быть добавить метку раздела

##### Вставка изображения

##### Замена изображения

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

##### Найти текст...

- манипулирует указателем мыши (может быть виновником является код изменения масштаба). Просто нажать “Return” чтобы вызвать FindNext (найти следующий)
- если вы выбираете поиск по иерархии и найденный текст находится на другой схеме, то имя файла в заголовке окна не обновляется
- может быть добавить опцию: “Выделить все соответствующие выражению текстовые объекты”, запретить иерархию для этого случая!
- gschem виснет, если это диалоговое окно используется с иерархической схемой, в которой есть циклические зависимости (например, автонумерация тестовой схемы)
- добавить опцию “поиск только в видимом тексте”
- может быть для поиска текста вместо части строки использовать регулярные выражения

##### Скрыть текст...

- использовать регулярные выражения вместо начала строки

##### Показать текст...

- использовать регулярные выражения вместо начала строки
- может быть объединить это окно с окном “Скрыть текст…”

##### Автонумерация...

- может быть запретить опцию пропуска, если областью перенумерации является “Выделенные объекты”. Другие варианты пропуска (“Текущая страница” и “Вся иерархия”) чесслово глупые.

##### Размер текста

##### Шаг сетки привязки...

##### Окно координат...

- может быть перенести координаты всей рабочей области в панель состояния основного окна?

##### Окно О программе

##### Горячие клавиши

1) Здесь и далее в коде всё конечно же должно быть по-английски. — Прим. перев.