Углубленное изучение АПИ плагинов Р7-Офис
Занятие 5
Создание контекстного меню
Введение

Контекстное меню является одним из основных способов быстрого взаимодействия пользователя с документом. Оно используется в случае внесения каких-либо изменений текущего документа. Оно может быть вызвано при выборе выделенного текста, рисунка, диаграммы и другого элемента (контекста). Пункты меню изменяются редактором в зависимости от контекста.
Для картинки:
Для диаграммы:
С версии Р7-Офис 7.4 в АПИ плагинов появилась возможность добавлять в контекстное меню редактора свои пункты меню (в том числе и иерархические). Разберём механизм создания и реакции на выбор собственных пунктов в контекстном меню.

Прежде всего, нужно в плагине указать в его конфигурации, что он будет использовать контекстное меню. Далее в код скрипта плагина нужно добавить функции создания и функции реакции на нажатие пунктов контекстного меню.
Файл конфигурации config.json.
Если в файле конфигурации не предусматривалась обработка системных событий, следует в него добавить раздел events.

Пример кода файла config.json с добавленным разделом events:

    "name" : "ContextMenu",    
    "guid" : "asc.{0956b700-7940-4b53-c563-aa77cf624ac5}",
    "minVersion":"7.4",
    "version": "0.0.1",
    "description":"Тестовый плагин проверки работы контекстного меню",

    "variations" : [
        {
            "description" : "Пример Starfair Studio",
            "url"         : "index.html",

            "icons": [ "resources/icon.png" ],           
            "isViewer"        : true,
            "EditorsSupport"  : ["cell"],

            "isVisual"        : false,
            "isModal"         : false,
            "isInsideMode"    : false,            

            "initDataType"    : "",
            "initData"        : "",

            "isUpdateOleOnResize" : false,
            "events": ["onContextMenuShow", "onContextMenuClick"],            
            "buttons"         : []            
        }
    ]
}

В примере добавлена реакция на два события:

1.onContextMenuShow - вызов контекстного меню по правому клику мыши.

2.onContextMenuClick" - выбор пункта меню (необходима регистрация, смотри далее).
Создание контекстного меню
В качестве образца используем минимальный вариант скрипта плагинов (plugin.js):

(function (window, undefined) {
    window.Asc.plugin.init = function () {
//Здесь пишется код, выполняемый при запуске
    };
//Обработка нажатия на системные кнопки (если их добавили в конфигурации)
    window.Asc.plugin.button = function (id) {};
//---  Далее вставляем код начиная с этого места
})(window, undefined);

Добавим обработчик системного события, предшествующего открытию контекстного меню:

//Присоединение к системному событию создания пунктов своего контекстного меню в момент его       показа в редакторе
    window.Asc.plugin.attachEvent('onContextMenuShow', function(options) {
        if (!options) return;
        //Присоединение к контекстному меню через системный вызов 
        if (options.type === 'Selection' || options.type === 'Target')
            this.executeMethod('AddContextMenuItem', [getContextMenuItems()]);
    });
   //Заполнение пунктов контекстного меню
    function getContextMenuItems() {
        let settings = {
            guid: window.Asc.plugin.guid,
            items: [
                {
                    id : 'onCellActionContextMenuMethod',
                    text : 'Тестовый пункт'
                }
            ]
        };
        return settings;
    };

Для понимания работы кода, опишем функции, используемые в нем.
function getContextMenuItems()
Функция задает имя пункта меню и уникальный описатель события, по которому в дальнейшем в макросе будет отрабатываться выбор пункта меню пользователем. Уникальность необходима для однозначности реакции на выбор одного из пунктов контекстного меню, в том числе и иерархического.

Структура settings описывается в формате JSON:

guid – GUID код, связанный с модулем контекстного меню. Его можно получить с помощью window.Asc.plugin.guid из поля guid файла config.json.

items - массив объектов, каждый из которых содержит описание пункта меню text (в нашем случае 'Тестовый пункт') и его уникальное id сигнала, которое передаётся внутрь плагина при выборе этого пункта меню. Здесь же могут быть включены и вложенные массивы items для построения иерархического меню.

Имеется возможность формирования пунктов меню без использования функции, но другие варианты рассматривать не будем.
window.Asc.plugin.attachEvent('onContextMenuShow')
Работа плагинов в основном строится на асинхронном слежении за системными или пользовательскими событиями. Функция API плагинов window.Asc.plugin.attachEvent() позволяет присоединиться к отслеживанию системных событий, к которым плагин может иметь доступ, в том числе события onContextMenuShow. Это событие вызывается каждый раз при вызове пользователем контекстного меню.

API позволяет достаточно точно понять, что делается пользователем в момент вызова меню (для какого типа элементов или части документа происходит вызов контекстного меню). Это позволяет плагину соответствующим образом настраивать пункты меню.

Для учебных целей не будем делать дополнительные проверки, только добавим пункт меню 'Тестовый пункт'. Он будет добавляться при выборе текстовой ячейки (плагин для табличного редактора Р7-Офис) или выделения текста в ней.

Сначала проверяется контекст события в документе через передаваемый объект options, который содержит строковое поле type с его описанием. Полного описания значений этого поля в документации нет, но в примерах других плагинов наиболее часто используются 'Selection' (выбрано выделение) и 'Target'(выбран блок). Если эти контексты подходят, происходит присоединение нашего пункта 'Тестовый пункт' к существующему контекстному меню самого редактора.
Делается это с помощью функции API плагинов executeMethod('AddContextMenuItem', [getContextMenuItems()]). Она присоединяет к текущему меню перечисленные в виде массива наши пункты меню. Каждый пункт меню является объектом, структура которого уже описывалась выше.

Обработка выбора пунктов контекстного меню
За обработку события выбора пользователем нашего пункта меню отвечает системное событие onContextMenuClick, обработчик которого необходимо добавить в текст нашего базового скрипта. Обработка этого события идёт с помощью специальной функции АПИ плагинов window.Asc.plugin.attachContextMenuClickEvent(). В качества аргумента в неё передаётся идентификатор-описатель нашего пункта меню в строковом формате.

Для обработки данного события можно так же воспользоваться тем же методом window.Asc.plugin.attachEvent(), передавая событие onContextMenuClick. В этом случае придётся использовать более сложный механизм проверки того, какой именно пункт меню вызывается, а в остальном всё будет работать аналогично.

Проще использовать указанный выше специализированный метод АПИ:

//Присоединяется к созданному пользовательскому сообщению нажатия на кнопку контекстного меню
window.Asc.plugin.attachContextMenuClickEvent('onCellActionContextMenuMethod', function(data) { 
        window.Asc.plugin.executeMethod ("GetSelectionType", [], function(sType) { 
            switch (sType) {                
                case "text":
                    window.Asc.plugin.callCommand(function(){                                                    
                       var sel=Api.GetSelection();                                            
                       if(sel.Text=='test'){// Если содержимое ячейки == 'test', заменяем его на "done!"
                            sel.SetValue("done!");
                       }
                     },undefined,undefined);
                    break;
                default:
                    break;  
            }
        });             
    });

window.Asc.plugin.attachContextMenuClickEvent
Функция присоединяет сообщение 'onCellActionContextMenuMethod' из заданного нами пункта 'Тестовый пункт' контекстного меню в список обрабатываемых и отслеживаемых событий контекстных меню редактора Р7-Офис. Она привязывает созданную нами функцию-обработчик. Внутри этого обработчика мы делаем весь функционал, связанный с добавляемыми пунктами меню. Для нашего пункта контекстного меню осуществляется проверка текста в выбранной ячейке. Если ячейка содержит слово “test”, то заменяем его на “done!”.

С помощью функции API плагинов window.Asc.plugin.executeMethod получаем из редактора тип текущего выделения (вызов системного метода ‘GetSelectionType’). Если тип содержимого ячейки текстовый, можно сравнивать содержимое. Так как напрямую из плагина доступ к содержимому документа получить нельзя, вызываем ещё одну функцию API window.Asc.plugin.callCommand(), в аргументе которой передаём свою функцию работы внутри документа.

Сделано так, видимо, по соображеням безопасности. Наша функция проверяет текущую выбранную пользователем ячейку и если в ней есть текст, полученный с помощью функции Api.GetSelection() и он равен требуемому 'test', то текст в ячейке заменяется на 'done!'.