Статьи

Быстрый и грязный контекст


Несколько дней назад я портировал приложение Swing на платформу NetBeans.
(На самом деле, я уже давно портирую его).

Затем я понял, что на платформе NetBeans отсутствует очень полезное представление Nodes: ContextChoiceView. Существует BeanTreeView, ChoiceView, ContextTreeView … но нет ни одного ContextChoiceview.

Таким образом, это не должно быть очень трудным для просмотра. Почти все уже сделано. Возьмите ChoiceView, используйте модель ContextTreeView, добавьте несколько трубопроводов, чтобы сложить все вместе, и вуаля, ContextChoiceView:

ContextChoiceView с корневым узлом:

ContextTreeView расширен и показывает контекст. Кроме того, ListView показывает содержимое контекста:

 

К сожалению, было несколько закрытых и видимых классов, которые я не мог просто расширить. Я должен был обратиться к исходному коду и использовать магию копирования / вставки. Во-первых, модель:

static final class NodeContextModel extends NodeTreeModel
                                        implements MutableComboBoxModel,
                                                   PropertyChangeListener{
        private Node selectedItem = null;
        //
        // Event filtering
        //
        private int[] newIndices;
        private Object[] newChildren;
        public NodeContextModel(Node root) {
            super(root);
        }
        public NodeContextModel() {
        }
        @Override
        public Object getChild(Object parent, int index) {
            int childCount = super.getChildCount(parent);
            int contextIndex = -1;
            for(int i = 0; ((contextIndex < index) || (i < childCount)); i++){
                Object child = super.getChild(parent, i);
                if(!isLeaf(child)){
                    contextIndex++;
                    if(contextIndex == index)
                        return child;
                }
            }
            return null;
        }
        @Override
        public int getChildCount(Object parent) {
            int childCount = super.getChildCount(parent);
            int contextIndex = 0;
            for(int i = 0; i < childCount; i++){
                Object child = super.getChild(parent, i);
                if(!isLeaf(child)){
                    contextIndex++;
                }
            }
            return contextIndex;
        }
        
        @Override
        public Object getSelectedItem() {
            return selectedItem == null?
                root:
                Visualizer.findVisualizer(selectedItem);
        }
        @Override
        public void setSelectedItem(Object anItem) {
            selectedItem = Visualizer.findNode(anItem);
        }
        @Override
        public void addListDataListener(ListDataListener l) {
            listenerList.add(ListDataListener.class, l);
        }
        @Override
        public Object getElementAt(int index) {
            TreeNode[] nodesPath = getPathToRoot((TreeNode)getSelectedItem());
            try {
                if (index > (nodesPath.length - 1)) {
                    return getChild(getSelectedItem(), index-nodesPath.length);
                }
                else {
                    return nodesPath[index];
                }
            } catch (Exception e) {
                return null;
            }
        }
        @Override
        public int getSize() {
            TreeNode[] nodesPath = getPathToRoot((TreeNode)getSelectedItem());
            return nodesPath.length + getChildCount(getSelectedItem());
        }
        @Override
        public void removeListDataListener(ListDataListener l) {
            listenerList.remove(ListDataListener.class, l);
        }
        @Override
        public void addElement(Object obj) {
            throw new UnsupportedOperationException("Not allowed.");
        }
        @Override
        public void insertElementAt(Object obj, int index) {
            throw new UnsupportedOperationException("Not allowed.");
        }
        @Override
        public void removeElement(Object obj) {
            throw new UnsupportedOperationException("Not allowed.");
        }
        @Override
        public void removeElementAt(int index) {
            throw new UnsupportedOperationException("Not allowed.");
        }
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (ExplorerManager.PROP_ROOT_CONTEXT.equals(
                    evt.getPropertyName()
                ) ||
                ExplorerManager.PROP_EXPLORED_CONTEXT.equals(
                    evt.getPropertyName()
                )) {
                for (ListDataListener listener : listenerList.getListeners(
                                                    ListDataListener.class)) {
                    listener.contentsChanged(
                        new ListDataEvent(
                            this, ListDataEvent.CONTENTS_CHANGED, 0, getSize()
                        )
                    );
                }
                return;
            }
        }
    }
     // end of NodeContextModel

По сути, это модель ContextTreeView, которая реализует MutableComboBoxModel, поэтому ComboBox может показывать свои данные и изменять их во время выполнения. Я понял, как трудно изменить данные ComboBox, если вы не используете MutableComboBoxModel. Теперь давайте инициализируем представление. Как обычно, подождите, пока не будет добавлено представление, затем получите ExplorerManager, чтобы мы могли знать, что мы показываем, и добавили несколько слушателей (не забудьте выпустить то, что у нас есть):

@Override
    public void addNotify() {
        manager = ExplorerManager.find(this);
        manager.addVetoableChangeListener(iListener);
        manager.addPropertyChangeListener(iListener);
        manager.addPropertyChangeListener((NodeContextModel)model);
        updateChoice();
        addActionListener(iListener);
        super.addNotify();
    }
 
    @Override
    public void removeNotify() {
        super.removeNotify();
        removeActionListener(iListener);
        if (manager != null) {
          manager.removeVetoableChangeListener(iListener);
          manager.removePropertyChangeListener(iListener);
          manager.removePropertyChangeListener((NodeContextModel)model);
       }
    }

 Затем не забудьте синхронизировать Проводник и ComboBox:

    private void updateSelection() {
        for(ListDataListener listener : 
            listenerList.getListeners(ListDataListener.class)){
            listener.contentsChanged(
                new ListDataEvent(
                    this,
                    ListDataEvent.CONTENTS_CHANGED,
                    0, 
                    model.getSize()
                )
            );
        }
        setSelectedItem(manager.getExploredContext());
    }
    private void updateChoice() {
        model.setSelectedItem(manager.getExploredContext());
        updateSelection();
    }

 Прослушивая изменения как в проводнике, так и в ComboBox:

/*
 * The inner adaptor class for listening to the ExplorerManager's property
 * and vetoable changes.
 */
    final class PropertyIL implements PropertyChangeListener,
                                      VetoableChangeListener,
                                      ActionListener {
        @Override
        public void vetoableChange(PropertyChangeEvent evt)
        throws PropertyVetoException {
            if (ExplorerManager.PROP_SELECTED_NODES.equals(
                    evt.getPropertyName()
                )) {
                Node[] nodes = (Node[]) evt.getNewValue();
                if (nodes.length > 1) {
                    // we do not allow multiple selection // NOI18N
                    throw new PropertyVetoException("", evt); 
                }
            }
        }
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            ContextChoiceView.this.removeActionListener(this);
            try {
                if (ExplorerManager.PROP_ROOT_CONTEXT.equals(
                    evt.getPropertyName())) {
                    updateChoice();
                    return;
                }
                if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(
                    evt.getPropertyName())) {
                    updateChoice();
                    return;
                }
            } finally {
                ContextChoiceView.this.addActionListener(this);
            }
        }
        @Override
        public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
            int s = getSelectedIndex();
            if ((s < 0) || (s >= model.getSize())) {
                return;
            }
            manager.removeVetoableChangeListener(this);
            manager.removePropertyChangeListener(this);
            manager.setExploredContext(
                Visualizer.findNode(getSelectedItem()));
            manager.addVetoableChangeListener(this);
            manager.addPropertyChangeListener(this);
        }
    }

И после того, как вы сложите все это вместе, вот что вы получите:

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
 * Other names may be trademarks of their respective owners.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */
package org.myorg.nodes.views;
import java.awt.event.ActionListener;
import javax.swing.event.ListDataListener;
import org.openide.explorer.*;
import org.openide.explorer.ExplorerManager.Provider;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import java.beans.*;
import java.io.*;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.tree.TreeNode;
import org.openide.explorer.view.ChoiceView;
import org.openide.explorer.view.ContextTreeView;
import org.openide.explorer.view.NodeRenderer;
import org.openide.explorer.view.NodeTreeModel;
import org.openide.explorer.view.Visualizer;
/** Explorer view based on a combo box.
 *  Allows to select the selected Context for the Nodes hierarchy.
 *  No leaf nodes are shown.
 * 

* This class is a view * to use it properly you need to add it into a component which implements * {@link Provider}. Good examples of that can be found * in {@link ExplorerUtils}. Then just use * {@link Provider#getExplorerManager} call to get the {@link ExplorerManager} * and control its state. *

*

* There can be multiple views under one container * implementing {@link Provider}. Select from * range of predefined ones or write your own: *

*


    *

  • {@link org.openide.explorer.view.BeanTreeView} —
    * показывает дерево узлов

  • *

  • {@link org.openide.explorer.view.ContextTreeView} —
    * показывает дерево узлов без листовых узлов

  • *

  • {@link org.openide.explorer.view.ListView} —
    * показывает список узлов

  • *

  • {@link org.openide.explorer.view.IconView} —
    * показывает ряды узлов с большими иконками

  • *

  • {@link org.openide.explorer.view.ChoiceView} —
    * создает поле со списком на основе исследованных узлов

  • *

  • {@link org.openide.explorer.view.TreeTableView} —
    * показывает дерево узлов вместе с набором их {@link Property}

  • *

  • {@link org.openide.explorer.view.MenuView} —
    * может создать структуру {@link JMenu} на основе структуры {@link Node} s

  • *


*


* Все эти представления используют {@link ExplorerManager # find}, чтобы пройти вверх

* AWT иерархия и найдите {@link ExplorerManager} для использования в качестве контроллера.

* Они присоединяются как слушатели к нему, а также вызывают его методы установки для обновления

* общее состояние на основе действий пользователя.
Не все взгляды имеют смысл вместе,

* но например {@link org.openide.explorer.view.ContextTreeView} и

* {@link org.openide.explorer.view.ListView} были разработаны для дополнения

* сами и ведут себя как Windows Explorer.

* {@Link org.openide.explorer.propertysheet.PropertySheetView}

* Например, должна быть возможность работать с любым другим представлением.

*


* @author Ярослав Тулач

* /

открытый класс ContextChoiceView расширяет JComboBox реализует Externalizable {

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

static final Logger LOG = Logger.getLogger (

ContextChoiceView.class.getName ());

/ ** Локальная ссылка на explorerManager.
Это переходный процесс

* потому что он будет сброшен в initializeManager () после десериализации. * /

временный приватный менеджер ExplorerManager;

/ ** Прослушивает ExplorerManager.
* /

временный частный PropertyIL iListener;

/ ** модель для использования * /

временная частная модель ComboBoxModel;

/ ** Значение свойства showExploredContext.
* /

приватный логический showExploredContext = true;

// в этом ……………………………………….. …………………….

/ ** Конструктор по умолчанию.
* /

public ContextChoiceView () {

супер();

initializeChoice ();

}

/ ** Инициализировать вид.
* /

private void initializeChoice () {

setRenderer (новый NodeRenderer ());


setModel (model = createModel ());

iListener = новый PropertyIL ();

}

// XXX [PENDING] установка новой модели с помощью setModel () фактически игнорируется, см. Модель

// поле -> которое «заменяет» нормальную комбо-модель

// бесполезный.

/ **

* Запись состояния просмотра в выходной поток.

* /

@Override

public void writeExternal (ObjectOutput out) выбрасывает IOException {

out.writeObject (showExploredContext? Boolean.TRUE: Boolean.FALSE);

}

/ **

* Читает поток вывода формы состояния представления.

* /

@Override

public void readExternal (ObjectInput in) выдает IOException,

ClassNotFoundException {

showExploredContext = ((Boolean) in.readObject ()). booleanValue ();

}

//

// Переопределить

//

/ ** Создает модель, которую должно отображать это представление.

* /

Защищенный ComboBoxModel createModel () {

вернуть новый NodeContextModel ();

}

// основные методы ………………………………………. ………………

/ ** Установить показ исследуемых контекстов.

* @param b true, чтобы показать исследуемый контекст,

* ложный корневой контекст

* /

public void setShowExploredContext (логическое b) {

showExploredContext = b;

updateChoice ();

}

/ **

* Получить исследуемый контекстный переключатель.

* @ return, показывает ли в данный момент исследуемый контекст

* (по умолчанию false)

* /

public boolean getShowExploredContext () {

return showExploredContext;

}

// основные методы ………………………………………. ………………

/ * Инициализирует вид.

* /

@Override

public void addNotify () {

manager = ExplorerManager.find (this);

manager.addVetoableChangeListener (iListener);

manager.addPropertyChangeListener (iListener);

manager.addPropertyChangeListener ((NodeContextModel) модель);

updateChoice ();

addActionListener (iListener);

super.addNotify ();

}

/ * Деинициализирует представление.

* /

@Override

public void removeNotify () {

super.removeNotify ();

removeActionListener (iListener);

if (manager! = null) {

manager.removeVetoableChangeListener (iListener);

manager.removePropertyChangeListener (iListener);

manager.removePropertyChangeListener ((NodeContextModel) модель);

}

}

private void updateSelection () {

для (ListDataListener слушатель: listenerList.getListeners (

ListDataListener.class)) {

listener.contentsChanged (

новый ListDataEvent (

это,

ListDataEvent.CONTENTS_CHANGED,

0,

model.getSize ()

)

);

}

setSelectedItem (manager.getExploredContext ());

}

private void updateChoice () {

model.setSelectedItem (manager.getExploredContext ());

updateSelection ();

}

// внутренние классы ……………………………………….. ……………..

/ *

* Внутренний класс адаптера для прослушивания свойства ExplorerManager

* и вето изменений.

* /

последний класс PropertyIL реализует PropertyChangeListener,

VetoableChangeListener,

ActionListener {

@Override

public void vetoableChange (PropertyChangeEvent evt)

бросает PropertyVetoException {

if (ExplorerManager.PROP_SELECTED_NODES.equals (

evt.getPropertyName ())

) {

Node [] node = (Node []) evt.getNewValue ();

if (node.length> 1) {

// мы не разрешаем множественный выбор // NOI18N

бросить новое PropertyVetoException («», evt);

}

}

}

@Override

public void propertyChange (PropertyChangeEvent evt) {

ContextChoiceView.this.removeActionListener (это);

пытаться {

if (ExplorerManager.PROP_ROOT_CONTEXT.equals (

evt.getPropertyName ())) {

updateChoice ();

возвращение;

}

if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals (

evt.getPropertyName ())) {

updateChoice ();

возвращение;

}

} наконец {

ContextChoiceView.this.addActionListener (это);

}

}

@Override

public void actionPerformed (java.awt.event.ActionEvent actionEvent) {

int s = getSelectedIndex ();

if ((s <0) || (s> = model.getSize ())) {

возвращение;

}

manager.removeVetoableChangeListener (это);

manager.removePropertyChangeListener (это);

manager.setExploredContext (Visualizer.findNode (getSelectedItem ()));

manager.addVetoableChangeListener (это);

manager.addPropertyChangeListener (это);

}

}

/ ** Исключает листья из модели.

* /

статический финальный класс NodeContextModel расширяет NodeTreeModel

реализует MutableComboBoxModel,

PropertyChangeListener {

закрытый узел selectedItem = null;

//

// Фильтрация событий

//

private int [] newIndices;

закрытый объект [] newChildren;

public NodeContextModel (Node root) {

супер (корень);

}

public NodeContextModel () {

}

@Override

public Object getChild (Object parent, int index) {

int childCount = super.getChildCount (parent);

int contextIndex = -1;

for (int i = 0; ((contextIndex <index) || (i <childCount)); i ++) {

Object child = super.getChild (parent, i);

если (! IsLeaf (ребенок)) {

contextIndex ++;

if (contextIndex == индекс)

вернуть ребенка;

}

}

вернуть ноль;

}

@Override

public int getChildCount (Object parent) {

int childCount = super.getChildCount (parent);

int contextIndex = 0;

for (int i = 0; i <childCount; i ++) {

Object child = super.getChild (parent, i);

если (! IsLeaf (ребенок)) {

contextIndex ++;

}

}

вернуть contextIndex;

}


@Override

public Object getSelectedItem () {

вернуть selectedItem == ноль?

корень:

Visualizer.findVisualizer (SelectedItem);

}

@Override

public void setSelectedItem (Object anItem) {

selectedItem = Visualizer.findNode (anItem);

}

@Override

public void addListDataListener (ListDataListener l) {

listenerList.add (ListDataListener.class, l);

}

@Override

public Object getElementAt (int index) {

TreeNode [] nodePath = getPathToRoot ((TreeNode) getSelectedItem ());

пытаться {

if (index> (nodePath.length — 1)) {

вернуть getChild (

getSelectedItem (), index — nodePath.length);

}

еще {

возвращать nodePath [индекс];

}

} catch (исключение e) {

вернуть ноль;

}

}

@Override

public int getSize () {

TreeNode [] nodePath = getPathToRoot ((TreeNode) getSelectedItem ());

return nodePath.length + getChildCount (getSelectedItem ());

}

@Override

public void removeListDataListener (ListDataListener l) {

listenerList.remove (ListDataListener.class, l);

}

@Override

public void addElement (Object obj) {

бросить новое исключение UnsupportedOperationException («Не разрешено»);

}

@Override

public void insertElementAt (Object obj, int index) {

бросить новое исключение UnsupportedOperationException («Не разрешено»);

}

@Override

public void removeElement (Object obj) {

бросить новое исключение UnsupportedOperationException («Не разрешено»);

}

@Override

public void removeElementAt (int index) {

бросить новое исключение UnsupportedOperationException («Не разрешено»);

}

@Override

public void propertyChange (PropertyChangeEvent evt) {

if (ExplorerManager.PROP_ROOT_CONTEXT.equals (

evt.getPropertyName ()) ||

ExplorerManager.PROP_EXPLORED_CONTEXT.equals (

evt.getPropertyName ())) {

для (ListDataListener слушатель: listenerList.getListeners (

ListDataListener.class)) {

listener.contentsChanged (

новый ListDataEvent (

это,

ListDataEvent.CONTENTS_CHANGED,

0,

GETSIZE ()

)

);

}

возвращение;

}

}

}

// конец NodeContextModel

}

Есть некоторые недостающие желательные функции, такие как выравнивание узлов по их уровню и показ родительских узлов со значком «открыто», в то время как узлы-братья сохраняют свой закрытый значок; что-то вроде FileChooser.

Возможно, вы можете назвать некоторые другие отсутствующие функции, но суть здесь. Это была моя первая статья здесь. Если вы читаете это, то это было по крайней мере не скучно. ? Пожалуйста, оставьте отзыв. Это будет очень высоко ценится. Большое спасибо. Muchas gracias por su paciencia!