UiTreeBlockpublic final class UiTreeBlock extends org.eclipse.ui.forms.MasterDetailsBlock implements ICommitXml{@link UiTreeBlock} is a {@link MasterDetailsBlock} which displays a tree view for
a specific set of {@link UiElementNode}.
For a given UI element node, the tree view displays all first-level children that
match a given type (given by an {@link ElementDescriptor}. All children from these
nodes are also displayed.
In the middle next to the tree are some controls to add or delete tree nodes.
On the left is a details part that displays all the visible UI attributes for a given
selected UI element node. |
Fields Summary |
---|
private static final int | TREE_HEIGHT_HINTHeight hint for the tree view. Helps the grid layout resize properly on smaller screens. | com.android.ide.eclipse.editors.AndroidEditor | mEditorContainer editor | private com.android.ide.eclipse.editors.uimodel.UiElementNode | mUiRootNodeThe root {@link UiElementNode} which contains all the elements that are to be
manipulated by this tree view. In general this is the manifest UI node. | private com.android.ide.eclipse.editors.descriptors.ElementDescriptor[] | mDescriptorFiltersThe descriptor of the elements to be displayed as root in this tree view. All elements
of the same type in the root will be displayed. | private String | mTitleThe title for the master-detail part (displayed on the top "tab" on top of the tree) | private String | mDescriptionThe description for the master-detail part (displayed on top of the tree view) | private com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart | mMasterPartThe master-detail part, composed of a main tree and an auxiliary detail part | private org.eclipse.jface.viewers.TreeViewer | mTreeViewerThe tree viewer in the master-detail part | private org.eclipse.swt.widgets.Button | mAddButtonThe "add" button for the tree view | private org.eclipse.swt.widgets.Button | mRemoveButtonThe "remove" button for the tree view | private org.eclipse.swt.widgets.Button | mUpButtonThe "up" button for the tree view | private org.eclipse.swt.widgets.Button | mDownButtonThe "down" button for the tree view | private org.eclipse.ui.forms.IManagedForm | mManagedFormThe Managed Form used to create the master part | private org.eclipse.ui.forms.DetailsPart | mDetailsPartReference to the details part of the tree master block. | private org.eclipse.swt.dnd.Clipboard | mClipboardReference to the clipboard for copy-paste | private com.android.ide.eclipse.editors.uimodel.IUiUpdateListener | mUiRefreshListenerListener to refresh the tree viewer when the parent's node has been updated | private com.android.ide.eclipse.editors.uimodel.IUiUpdateListener | mUiEnableListenerListener to enable/disable the UI based on the application node's presence | private UiTreeActions | mUiTreeActionsAn adapter/wrapper to use the add/remove/up/down tree edit actions. | private final boolean | mAutoCreateRootTrue if the root node can be created on-demand (i.e. as needed as
soon as children exist). False if an external entity controls the existence of the
root node. In practise, this is false for the manifest application page (the actual
"application" node is managed by the ApplicationToggle part) whereas it is true
for all other tree pages. |
Methods Summary |
---|
private void | adjustTreeButtons(org.eclipse.jface.viewers.ISelection selection)This is called by the tree when a selection is made.
It enables/disables the buttons associated with the tree depending on the current
selection.
mRemoveButton.setEnabled(!selection.isEmpty() && selection instanceof ITreeSelection);
mUpButton.setEnabled(!selection.isEmpty() && selection instanceof ITreeSelection);
mDownButton.setEnabled(!selection.isEmpty() && selection instanceof ITreeSelection);
| public void | changeRootAndDescriptors(com.android.ide.eclipse.editors.uimodel.UiElementNode uiRootNode, com.android.ide.eclipse.editors.descriptors.ElementDescriptor[] descriptorFilters, boolean forceRefresh)Changes the UI root node and the descriptor filters of the tree.
This removes the listeners attached to the old root node and reattaches them to the
new one.
UiElementNode node;
// Remove previous listeners if any
if (mUiRootNode != null) {
node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode;
node.removeUpdateListener(mUiRefreshListener);
mUiRootNode.removeUpdateListener(mUiEnableListener);
}
mUiRootNode = uiRootNode;
mDescriptorFilters = descriptorFilters;
mTreeViewer.setContentProvider(new UiModelTreeContentProvider(mUiRootNode, mDescriptorFilters));
// Listen on structural changes on the root node of the tree
// If the node has a parent, listen on the parent instead.
node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode;
node.addUpdateListener(mUiRefreshListener);
// Use the root node to listen to its presence.
mUiRootNode.addUpdateListener(mUiEnableListener);
// Initialize the enabled/disabled state
mUiEnableListener.uiElementNodeUpdated(mUiRootNode, null /* state, not used */);
if (forceRefresh) {
mTreeViewer.refresh();
}
createSectionActions(mMasterPart.getSection(), mManagedForm.getToolkit());
| void | commitManagedForm()Commits the current managed form (the one associated with our master part).
As a side effect, this will commit the current UiElementDetails page.
if (mManagedForm != null) {
mManagedForm.commit(false /* onSave */);
}
| public void | commitPendingXmlChanges()
commitManagedForm();
| private void | createButtons(org.eclipse.ui.forms.widgets.FormToolkit toolkit, org.eclipse.swt.widgets.Composite grid)Creates the buttons next to the tree.
mUiTreeActions = new UiTreeActions();
Composite button_grid = SectionHelper.createGridLayout(grid, toolkit, 1);
button_grid.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
mAddButton = toolkit.createButton(button_grid, "Add...", SWT.PUSH);
SectionHelper.addControlTooltip(mAddButton, "Adds a new element.");
mAddButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL |
GridData.VERTICAL_ALIGN_BEGINNING));
mAddButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
doTreeAdd();
}
});
mRemoveButton = toolkit.createButton(button_grid, "Remove...", SWT.PUSH);
SectionHelper.addControlTooltip(mRemoveButton, "Removes an existing selected element.");
mRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mRemoveButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
doTreeRemove();
}
});
mUpButton = toolkit.createButton(button_grid, "Up", SWT.PUSH);
SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element up.");
mUpButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mUpButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
doTreeUp();
}
});
mDownButton = toolkit.createButton(button_grid, "Down", SWT.PUSH);
SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element down.");
mDownButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mDownButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
doTreeDown();
}
});
adjustTreeButtons(TreeSelection.EMPTY);
| protected void | createMasterPart(org.eclipse.ui.forms.IManagedForm managedForm, org.eclipse.swt.widgets.Composite parent)
FormToolkit toolkit = managedForm.getToolkit();
mManagedForm = managedForm;
mMasterPart = new ManifestSectionPart(parent, toolkit);
Section section = mMasterPart.getSection();
section.setText(mTitle);
section.setDescription(mDescription);
section.setLayout(new GridLayout());
section.setLayoutData(new GridData(GridData.FILL_BOTH));
Composite grid = SectionHelper.createGridLayout(section, toolkit, 2);
Tree tree = createTreeViewer(toolkit, grid, managedForm);
createButtons(toolkit, grid);
createTreeContextMenu(tree);
createSectionActions(section, toolkit);
| private void | createSectionActions(org.eclipse.ui.forms.widgets.Section section, org.eclipse.ui.forms.widgets.FormToolkit toolkit)
ToolBarManager manager = new ToolBarManager(SWT.FLAT);
manager.removeAll();
ToolBar toolbar = manager.createControl(section);
section.setTextClient(toolbar);
ElementDescriptor[] descs = mDescriptorFilters;
if (descs == null && mUiRootNode != null) {
descs = mUiRootNode.getDescriptor().getChildren();
}
if (descs != null && descs.length > 1) {
for (ElementDescriptor desc : descs) {
manager.add(new DescriptorFilterAction(desc));
}
}
manager.add(new TreeSortAction());
manager.update(true /*force*/);
| protected void | createToolBarActions(org.eclipse.ui.forms.IManagedForm managedForm)
// Pass. Not used, toolbar actions are defined by createSectionActions().
| private void | createTreeContextMenu(org.eclipse.swt.widgets.Tree tree)
MenuManager menuManager = new MenuManager();
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(new IMenuListener() {
/**
* The menu is about to be shown. The menu manager has already been
* requested to remove any existing menu item. This method gets the
* tree selection and if it is of the appropriate type it re-creates
* the necessary actions.
*/
public void menuAboutToShow(IMenuManager manager) {
ISelection selection = mTreeViewer.getSelection();
if (!selection.isEmpty() && selection instanceof ITreeSelection) {
ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection);
doCreateMenuAction(manager, selected);
return;
}
doCreateMenuAction(manager, null /* ui_node */);
}
});
Menu contextMenu = menuManager.createContextMenu(tree);
tree.setMenu(contextMenu);
| private org.eclipse.swt.widgets.Tree | createTreeViewer(org.eclipse.ui.forms.widgets.FormToolkit toolkit, org.eclipse.swt.widgets.Composite grid, org.eclipse.ui.forms.IManagedForm managedForm)Creates the tree and its viewer
// Note: we *could* use a FilteredTree instead of the Tree+TreeViewer here.
// However the class must be adapted to create an adapted toolkit tree.
final Tree tree = toolkit.createTree(grid, SWT.MULTI);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.widthHint = AndroidEditor.TEXT_WIDTH_HINT;
gd.heightHint = TREE_HEIGHT_HINT;
tree.setLayoutData(gd);
mTreeViewer = new TreeViewer(tree);
mTreeViewer.setContentProvider(new UiModelTreeContentProvider(mUiRootNode, mDescriptorFilters));
mTreeViewer.setLabelProvider(new UiModelTreeLabelProvider());
mTreeViewer.setInput("unused"); //$NON-NLS-1$
// Create a listener that reacts to selections on the tree viewer.
// When a selection is made, ask the managed form to propagate an event to
// all parts in the managed form.
// This is picked up by UiElementDetail.selectionChanged().
mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
managedForm.fireSelectionChanged(mMasterPart, event.getSelection());
adjustTreeButtons(event.getSelection());
}
});
// Create three listeners:
// - One to refresh the tree viewer when the parent's node has been updated
// - One to refresh the tree viewer when the framework resources have changed
// - One to enable/disable the UI based on the application node's presence.
mUiRefreshListener = new IUiUpdateListener() {
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
mTreeViewer.refresh();
}
};
mUiEnableListener = new IUiUpdateListener() {
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
// The UiElementNode for the application XML node always exists, even
// if there is no corresponding XML node in the XML file.
//
// Normally, we enable the UI here if the XML node is not null.
//
// However if mAutoCreateRoot is true, the root node will be created on-demand
// so the tree/block is always enabled.
boolean exists = mAutoCreateRoot || (ui_node.getXmlNode() != null);
if (mMasterPart != null) {
Section section = mMasterPart.getSection();
if (section.getEnabled() != exists) {
section.setEnabled(exists);
for (Control c : section.getChildren()) {
c.setEnabled(exists);
}
}
}
}
};
/** Listener to update the root node if the target of the file is changed because of a
* SDK location change or a project target change */
final ITargetChangeListener targetListener = new ITargetChangeListener() {
public void onProjectTargetChange(IProject changedProject) {
if (changedProject == mEditor.getProject()) {
onTargetsLoaded();
}
}
public void onTargetsLoaded() {
// If a details part has been created, we need to "refresh" it too.
if (mDetailsPart != null) {
// The details part does not directly expose access to its internal
// page book. Instead it is possible to resize the page book to 0 and then
// back to its original value, which has the side effect of removing all
// existing cached pages.
int limit = mDetailsPart.getPageLimit();
mDetailsPart.setPageLimit(0);
mDetailsPart.setPageLimit(limit);
}
// Refresh the tree, preserving the selection if possible.
mTreeViewer.refresh();
}
};
// Setup the listeners
changeRootAndDescriptors(mUiRootNode, mDescriptorFilters, false /* refresh */);
// Listen on resource framework changes to refresh the tree
AdtPlugin.getDefault().addTargetListener(targetListener);
// Remove listeners when the tree widget gets disposed.
tree.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
UiElementNode node = mUiRootNode.getUiParent() != null ?
mUiRootNode.getUiParent() :
mUiRootNode;
node.removeUpdateListener(mUiRefreshListener);
mUiRootNode.removeUpdateListener(mUiEnableListener);
AdtPlugin.getDefault().removeTargetListener(targetListener);
if (mClipboard != null) {
mClipboard.dispose();
mClipboard = null;
}
}
});
// Get a new clipboard reference. It is disposed when the tree is disposed.
mClipboard = new Clipboard(tree.getDisplay());
return tree;
| private void | doCreateMenuAction(org.eclipse.jface.action.IMenuManager manager, java.util.ArrayList selected)Adds the menu actions to the context menu when the given UI node is selected in
the tree view.
if (selected != null) {
boolean hasXml = false;
for (UiElementNode uiNode : selected) {
if (uiNode.getXmlNode() != null) {
hasXml = true;
break;
}
}
if (hasXml) {
manager.add(new CopyCutAction(getEditor(), getClipboard(),
null, selected, true /* cut */));
manager.add(new CopyCutAction(getEditor(), getClipboard(),
null, selected, false /* cut */));
// Can't paste with more than one element selected (the selection is the target)
if (selected.size() <= 1) {
// Paste is not valid if it would add a second element on a terminal element
// which parent is a document -- an XML document can only have one child. This
// means paste is valid if the current UI node can have children or if the
// parent is not a document.
UiElementNode ui_root = selected.get(0).getUiRoot();
if (ui_root.getDescriptor().hasChildren() ||
!(ui_root.getUiParent() instanceof UiDocumentNode)) {
manager.add(new PasteAction(getEditor(), getClipboard(), selected.get(0)));
}
}
manager.add(new Separator());
}
}
// Append "add" and "remove" actions. They do the same thing as the add/remove
// buttons on the side.
Action action;
IconFactory factory = IconFactory.getInstance();
// "Add" makes sense only if there's 0 or 1 item selected since the
// one selected item becomes the target.
if (selected == null || selected.size() <= 1) {
manager.add(new Action("Add...", factory.getImageDescriptor("add")) { //$NON-NLS-1$
@Override
public void run() {
super.run();
doTreeAdd();
}
});
}
if (selected != null) {
if (selected != null) {
manager.add(new Action("Remove", factory.getImageDescriptor("delete")) { //$NON-NLS-1$
@Override
public void run() {
super.run();
doTreeRemove();
}
});
}
manager.add(new Separator());
manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-1$
@Override
public void run() {
super.run();
doTreeUp();
}
});
manager.add(new Action("Down", factory.getImageDescriptor("down")) { //$NON-NLS-1$
@Override
public void run() {
super.run();
doTreeDown();
}
});
}
| private void | doTreeAdd()Called when the "Add..." button next to the tree view is selected.
Displays a selection dialog that lets the user select which kind of node
to create, depending on the current selection.
UiElementNode ui_node = mUiRootNode;
ISelection selection = mTreeViewer.getSelection();
if (!selection.isEmpty() && selection instanceof ITreeSelection) {
ITreeSelection tree_selection = (ITreeSelection) selection;
Object first = tree_selection.getFirstElement();
if (first != null && first instanceof UiElementNode) {
ui_node = (UiElementNode) first;
}
}
mUiTreeActions.doAdd(
ui_node,
mDescriptorFilters,
mTreeViewer.getControl().getShell(),
(ILabelProvider) mTreeViewer.getLabelProvider());
| protected void | doTreeDown()Called when the "Down" button is selected.
If the tree has a selection, move it down, either in the same child list or as the
first child of the next parent.
ISelection selection = mTreeViewer.getSelection();
if (!selection.isEmpty() && selection instanceof ITreeSelection) {
ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection);
mUiTreeActions.doDown(selected);
}
| protected void | doTreeRemove()Called when the "Remove" button is selected.
If the tree has a selection, remove it.
This simply deletes the XML node attached to the UI node: when the XML model fires the
update event, the tree will get refreshed.
ISelection selection = mTreeViewer.getSelection();
if (!selection.isEmpty() && selection instanceof ITreeSelection) {
ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection);
mUiTreeActions.doRemove(selected, mTreeViewer.getControl().getShell());
}
| protected void | doTreeUp()Called when the "Up" button is selected.
If the tree has a selection, move it up, either in the child list or as the last child
of the previous parent.
ISelection selection = mTreeViewer.getSelection();
if (!selection.isEmpty() && selection instanceof ITreeSelection) {
ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection);
mUiTreeActions.doUp(selected);
}
| private java.util.ArrayList | filterSelection(org.eclipse.jface.viewers.ITreeSelection selection)Filters an ITreeSelection to only keep the {@link UiElementNode}s (in case there's
something else in there).
ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>();
for (Iterator it = selection.iterator(); it.hasNext(); ) {
Object selectedObj = it.next();
if (selectedObj instanceof UiElementNode) {
selected.add((UiElementNode) selectedObj);
}
}
return selected.size() > 0 ? selected : null;
| org.eclipse.swt.dnd.Clipboard | getClipboard()
return mClipboard;
| com.android.ide.eclipse.editors.AndroidEditor | getEditor()
return mEditor;
| com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart | getMasterPart()
return mMasterPart;
| public com.android.ide.eclipse.editors.uimodel.UiElementNode | getRootNode()Returns the {@link UiElementNode} for the current model.
This is used by the content provider attached to {@link #mTreeViewer} since
the uiRootNode changes after each call to
{@link #changeRootAndDescriptors(UiElementNode, ElementDescriptor[], boolean)}.
return mUiRootNode;
| protected void | registerPages(org.eclipse.ui.forms.DetailsPart detailsPart)
// Keep a reference on the details part (the super class doesn't provide a getter
// for it.)
mDetailsPart = detailsPart;
// The page selection mechanism does not use pages registered by association with
// a node class. Instead it uses a custom details page provider that provides a
// new UiElementDetail instance for each node instance. A limit of 5 pages is
// then set (the value is arbitrary but should be reasonable) for the internal
// page book.
detailsPart.setPageLimit(5);
final UiTreeBlock tree = this;
detailsPart.setPageProvider(new IDetailsPageProvider() {
public IDetailsPage getPage(Object key) {
if (key instanceof UiElementNode) {
return new UiElementDetail(tree);
}
return null;
}
public Object getPageKey(Object object) {
return object; // use node object as key
}
});
|
|