Статьи

Как сделать платформу NetBeans чувствительной к клиентам

В вашем приложении на платформе NetBeans вы часто можете оказаться в ситуации, когда вам нужно создать действия, чувствительные к их контексту. Простой пример показан ниже. У нас есть клиентское приложение с действием, вызываемым из панели инструментов, а также из пункта меню в главной строке меню и из узла в виде проводника. На приведенном ниже снимке экрана действие вызывается из кнопки с раздраженным лицом (типичное выражение клиента) на панели инструментов, из пункта меню «Сведения о клиенте» на узле в окне просмотра, а также из пункта меню в меню Файл (которое вы не видите ниже). В этом случае есть объект Customer, для которого можно вызвать действие …

… в то время как в этой ситуации пользовательский интерфейс для вызова действия отключен (как вы можете видеть, посмотрев на кнопку на панели инструментов, которая отключена, и обратите внимание, что другие действия включены в этом контексте, так как эти действия связаны в редактор, который является окном, которое выбрано в данный момент), потому что здесь у нас больше нет объекта Customer в контексте приложения, поскольку курсор находится в данный момент в окне редактора, а не в окне представления, которое предоставляет объект Customer из текущего выбранного узла:

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

Сделайте следующие шаги:

  1. В мастере New Action в модуле, в котором вы хотите создать свое действие, укажите, что вы хотите создать контекстно-зависимое действие, которое должно быть чувствительным к объектам Customer, что является частью модели в моем приложении:

    Примечание: Мы (т.е. команде NetBeans) необходимо изменить строки в приведенном выше диалоговом окне (т. е. в исходном коде IDE NetBeans), поскольку в обоих случаях будет создан ActionListener. То есть, когда вы выбираете радиокнопку «Условно включено», вы не получите CookieAction. Вместо этого вы получите ActionListener, который зарегистрирован в слое, так что он внедряется с учетом контекста к объектам Customer, как будет показано ниже.

  2. Нажмите Далее, а затем укажите, что вы хотите создать пункт меню и кнопку панели инструментов:

  3. Снова нажмите кнопку Далее и выберите значок на диске, а также укажите префикс имени класса и отображаемое имя:

    Совет. Прочитайте этот полезный совет о значках и приложениях платформы NetBeans!

  4. Завершив работу над этим мастером, вы увидите, что у вас есть старый класс ActionListener, что является хорошей новостью, поскольку это означает, что вы можете переносить свои собственные ActionListeners из своего собственного приложения на платформу NetBeans без необходимости переписывать их каким-либо образом. Другими словами, платформа NetBeans обрабатывает ActionListeners изначально и не требует использования какого-либо специального API-интерфейса NetBeans для создания действий. Вот ActionListener, созданный из вышеупомянутого, который имеет доступ к текущему объекту Customer (довольно удобно!):
    package org.shop.ui;

    import demo.Customer;
    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;

    public final class CustomerDetailsAction implements ActionListener {

        private final Customer context;

        public CustomerDetailsAction(Customer context) {
            this.context = context;
        }

        public void actionPerformed(ActionEvent ev) {
            // TODO use context
        }

    }

    Между тем, ваш файл layer.xml содержит следующие записи, созданные вышеупомянутым мастером, превращая ваш скромный ActionListener в контекстно-зависимое действие, чувствительное к объектам Customer:

    <folder name="Actions">
        <folder name="Build">
            <file name="org-shop-ui-CustomerDetailsAction.instance">
                <attr name="delegate" methodvalue="org.openide.awt.Actions.inject"/>
                <attr name="displayName" bundlevalue="org.shop.ui.Bundle#CTL_CustomerDetailsAction"/>
                <attr name="iconBase" stringvalue="org/shop/ui/customer.png"/>
                <attr name="injectable" stringvalue="org.shop.ui.CustomerDetailsAction"/>
                <attr name="instanceCreate" methodvalue="org.openide.awt.Actions.context"/>
                <attr name="noIconInMenu" boolvalue="false"/>
                <attr name="selectionType" stringvalue="EXACTLY_ONE"/>
                <attr name="type" stringvalue="demo.Customer"/>
            </file>
        </folder>
    </folder>
    <folder name="Menu">
        <folder name="File">
            <file name="org-shop-ui-CustomerDetailsAction.shadow">
                <attr name="originalFile" stringvalue="Actions/Build/org-shop-ui-CustomerDetailsAction.instance"/>
                <attr name="position" intvalue="1300"/>
            </file>
        </folder>
    </folder>
    <folder name="Toolbars">
        <folder name="File">
            <file name="org-shop-ui-CustomerDetailsAction.shadow">
                <attr name="originalFile" stringvalue="Actions/Build/org-shop-ui-CustomerDetailsAction.instance"/>
            </file>
        </folder>
    </folder>

    Примечание: я подправил строку 11 выше. По умолчанию для этого типа установлено значение «Клиент», в то время как на самом деле это должно быть полностью определенное имя класса «Клиент», то есть «demo.Customer», поскольку объект «Клиент» находится в пакете с именем «demo». Может быть, в мастере New Action следует ввести полное имя, а не просто имя класса домена. Нужно проверить это. Элемент «type» в слое определяет контекст, доступный ActionListener, т. Е. Текущий объект Customer.

    Кстати, выше, в строке 10 мы гарантируем, что действие будет отключено, если выбрано более одного узла (благодаря выбору «Пользователь выбирает один узел» на первой странице мастера создания нового действия), что может быть видел здесь:

  5. Наконец, давайте добавим действие в качестве элемента контекстного меню на нашем узле. В этом случае я знаю, что у меня есть два действия в папке «Actions / Build», одно из текущего модуля, а другое из другого. Вы также можете получить все действия в определенной папке, а не в определенных, или вы можете получить действия из разных папок. Вам решать.
    private class CustomerNode extends AbstractNode {

        public CustomerNode(Customer c) {
            super(Children.LEAF, Lookups.singleton(c));
            setDisplayName(c.getName());
            setShortDescription(c.getCity());
        }

        @Override
        public Action[] getActions(boolean context) {
            return new Action[]{
                Utilities.actionsForPath("Actions/Build/").get(0),
                Utilities.actionsForPath("Actions/Build/").get(1),
            };
        }

    }

И это действительно все. Вы можете использовать свой старый класс ActionListener. Недостатком является то, что у вас есть куча тегов в файле слоя для работы, хотя (кроме небольшого изменения) все это было сгенерировано для вас. Жду аннотации для действий, так что мой ActionListener может быть просто украшен аннотацией, которая создаст необходимые записи слоя при компиляции модуля.

Тем не менее, теперь у вас есть контекстно-зависимое действие для объектов заказчика.