= СОБЫТИЯ =

В mc используется система событий, основанная на технологии быстрых бинарных деревьев.

== Вступление ==

Система событий, в первую очередь, призвана отвязать на уровне исходных текстов источник события и обработчик.
Например, в модуле VFS используется функция вывода сообщений, которая определена в исходных текстах (не в lib).
В lib определение этой функции будет затруднительным (потому что функция делает множество вызовов на другие
функции из src). В данном случае можно представить эту функцию как обработчик события, а сам процесс вывода
сообщения от VFS на экран как непосредственно событие. В начале запуска mc функцию вывода сообщений необходимо
зарегистрировать как обработчик события; в последствии при необходимости выдачи сообщений нужно просто вызвать
событие, абсолютно не волнуясь, привязан ли обработчик к событию, а также какая именно функция сейчас является
обработчиком события (вероятна ситуация, при которой позже загрузился плагин и переопределил этот обработчик
событий на себя).

=== Использование ===

Не везде и не всегда применимы события. Самое трудное порой бывает решить, это должна быть простая функция или это
должно быть событие. Наиболее правильным будет заменить все функции (или части switch(){case:}), используемые при
обработке кейбиндингов. Все кейбиндинги описаны в lib/keybind.h

Вторым аргументом при выборе решения (обработчик события или функция) должна стать мысль, что функцию в обработчик
события "превратить" легко; обратный процесс (обработчик в функцию) будет значительно сложнее - хотя бы потому,
что на одном событии может "висеть" множество обработчиков, и каждый может зависеть от работы предыдущего.

Третьим аргументом при выборе в пользу обработчиков событий могут стать плагины (когда появятся). В данном случае
события - это способ дать плагинам доступ к внутренним ресурсам приложения, не "пуская" эти плагины в низкоуровневое
API. Всё, что нужно будет знать "плагинам" - какое событие с какой структурой надо вызвать и как именно вызвать
(#include "lib/event.h").

== Структура ==

В общем виде подсистему событий можно представить так:


   ------------------------------------            }
   |Группа1       Группа2  ...  ГруппаN|            }   Группы событий (GTree)
   -------------------------------------           }
       |             |             |
      /|\           /|\           /|\
     / | \         / | ...       ... событиеN      }
    /  |  \       /  ...                            }
   /   |   \      ...                                } События, разбитые на группы
   |   |    событие3                                 } (GTree для каждой группы)
   |   событие2                                     }
   событие1                                        }
   | | |  |
   f1f2...fN                                       } список обработчиков события (GPtrArray на каждое событие)


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

== Требования к обработчикам событий ==

Любой каллбэк должен быть вида:

gboolean mc_event_callback_func_t (const gchar *event_group, const gchar *event_name, gpointer init_data, gpointer event_data);
где:
 event_group:
    название группы, в которой было инициировано событие

 event_name:
    название события. Вместе с названием группы событий позволяют точно идентифицировать событие.
    Эти параметры могут быть полезны в случае если обработчик события привязан сразу к нескольким событиям
    и необходимо различать различные события (например, в функции логгирования)

 init_data:
    Произвольные данные, предоставляемые обработчикам события. Эти данные указываются при добавлении обработчика
    к событию (инициализационные данные).
 event_data:
    Данные, предоставляемые обработчику событий в момент возникновения события.

Каллбэк должен вернуть TRUE, чтобы разрешить исполниться всем последующим за ним каллбэкам в данном событии; либо
FALSE если необходимо немедленно прекратить дальнейшую обработку события (оставшиеся каллбэки не вызываются).

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

=== Передача параметров в обработчики событий. Возврат результатов ===

Из-за унификации обработчиков событий стало невозможным передать определённое количество параметров и
получить результат выполнения. Если передачу одного параметра (или приём одного результата от обработчика события)
можно произвести простым приведением типа на одну переменную при вызове событий, то при передаче нескольких параметров
(или при получении нескольких результатов) такой метод неприменим.

Для решения этой проблемы можно передавать ранее определённые структуры по универсальному указателю event_data.
Все структуры, используемые в обработчиках событий, должны быть определены в файле lib/event-types.h.

У данного метода (передача параметров указателем на структуру) есть как достоинства, так и недостатки.

Достоинства:
 * произвольное количество параметров и их типов;
 * произвольное количество возвращаемых значений и их типов.

Недостатки:
 * вероятность ошибки: вызов события с неправильной структурой. В данном случае обработчик приведёт указатель к той
   структуре, на которую он рассчитан. При этом возможны весёлые глюки с отладкой (особенно если нет сразу segfault);
 * необходимость иметь ранее определённые структуры для того, чтобы и обработчик события, и инициатор события могли бы
   без проблем "общаться" между собой.

== Примеры использования ==

=== Логгирование ===

Рассмотрим пример временного каллбэка, который просто логгирует порядок вызова определённых событий (например,
для выявления зацикливаний).

Сам каллбэк:

gboolean mc_temp_event_logger (const gchar *event_group, const gchar *event_name, gpointer init_data, gpointer data)
{
    (void) init_data;
    (void) data;

    mc_log("Event: %s:%s",event_group,event_name);
    return TRUE;
}

Добавляем его в src/event_init.c в виде записей к инициализационной структуре
перед строчкой "{NULL, NULL, NULL, NULL}":

{MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", mc_temp_event_logger, NULL},
{MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", mc_temp_event_logger, NULL},
{MCEVENT_GROUP_CORE, "clipboard_text_to_file", mc_temp_event_logger, NULL},
{MCEVENT_GROUP_CORE, "clipboard_text_from_file", mc_temp_event_logger, NULL},
...(тут любые другие события, которые необходимо мониторить)...

