diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui')
17 files changed, 0 insertions, 4503 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/EditableDialogCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/EditableDialogCellEditor.java deleted file mode 100644 index baf8a1039..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/EditableDialogCellEditor.java +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.SdkConstants; - -import org.eclipse.core.runtime.Assert; -import org.eclipse.jface.viewers.DialogCellEditor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.KeyAdapter; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Text; - -import java.text.MessageFormat; - -/** - * Custom DialogCellEditor, replacing the Label with an editable {@link Text} widget. - * <p/>Also set the button to {@link SWT#FLAT} to make sure it looks good on MacOS X. - * <p/>Most of the code comes from TextCellEditor. - */ -public abstract class EditableDialogCellEditor extends DialogCellEditor { - - private Text text; - - private ModifyListener modifyListener; - - /** - * State information for updating action enablement - */ - private boolean isSelection = false; - - private boolean isDeleteable = false; - - private boolean isSelectable = false; - - EditableDialogCellEditor(Composite parent) { - super(parent); - } - - /* - * Re-implement this method only to properly set the style in the button, or it won't look - * good in MacOS X - */ - @Override - protected Button createButton(Composite parent) { - Button result = new Button(parent, SWT.DOWN | SWT.FLAT); - result.setText("..."); //$NON-NLS-1$ - return result; - } - - - @Override - protected Control createContents(Composite cell) { - text = new Text(cell, SWT.SINGLE); - text.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetDefaultSelected(SelectionEvent e) { - handleDefaultSelection(e); - } - }); - text.addKeyListener(new KeyAdapter() { - // hook key pressed - see PR 14201 - @Override - public void keyPressed(KeyEvent e) { - keyReleaseOccured(e); - - // as a result of processing the above call, clients may have - // disposed this cell editor - if ((getControl() == null) || getControl().isDisposed()) { - return; - } - checkSelection(); // see explanation below - checkDeleteable(); - checkSelectable(); - } - }); - text.addTraverseListener(new TraverseListener() { - @Override - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_ESCAPE - || e.detail == SWT.TRAVERSE_RETURN) { - e.doit = false; - } - } - }); - // We really want a selection listener but it is not supported so we - // use a key listener and a mouse listener to know when selection changes - // may have occurred - text.addMouseListener(new MouseAdapter() { - @Override - public void mouseUp(MouseEvent e) { - checkSelection(); - checkDeleteable(); - checkSelectable(); - } - }); - text.addFocusListener(new FocusAdapter() { - @Override - public void focusLost(FocusEvent e) { - EditableDialogCellEditor.this.focusLost(); - } - }); - text.setFont(cell.getFont()); - text.setBackground(cell.getBackground()); - text.setText("");//$NON-NLS-1$ - text.addModifyListener(getModifyListener()); - return text; - } - - /** - * Checks to see if the "deletable" state (can delete/ - * nothing to delete) has changed and if so fire an - * enablement changed notification. - */ - private void checkDeleteable() { - boolean oldIsDeleteable = isDeleteable; - isDeleteable = isDeleteEnabled(); - if (oldIsDeleteable != isDeleteable) { - fireEnablementChanged(DELETE); - } - } - - /** - * Checks to see if the "selectable" state (can select) - * has changed and if so fire an enablement changed notification. - */ - private void checkSelectable() { - boolean oldIsSelectable = isSelectable; - isSelectable = isSelectAllEnabled(); - if (oldIsSelectable != isSelectable) { - fireEnablementChanged(SELECT_ALL); - } - } - - /** - * Checks to see if the selection state (selection / - * no selection) has changed and if so fire an - * enablement changed notification. - */ - private void checkSelection() { - boolean oldIsSelection = isSelection; - isSelection = text.getSelectionCount() > 0; - if (oldIsSelection != isSelection) { - fireEnablementChanged(COPY); - fireEnablementChanged(CUT); - } - } - - /* (non-Javadoc) - * Method declared on CellEditor. - */ - @Override - protected void doSetFocus() { - if (text != null) { - text.selectAll(); - text.setFocus(); - checkSelection(); - checkDeleteable(); - checkSelectable(); - } - } - - /* - * (non-Javadoc) - * @see org.eclipse.jface.viewers.DialogCellEditor#updateContents(java.lang.Object) - */ - @Override - protected void updateContents(Object value) { - Assert.isTrue(text != null && (value == null || (value instanceof String))); - if (value != null) { - text.removeModifyListener(getModifyListener()); - text.setText((String) value); - text.addModifyListener(getModifyListener()); - } - } - - /** - * The <code>TextCellEditor</code> implementation of - * this <code>CellEditor</code> framework method returns - * the text string. - * - * @return the text string - */ - @Override - protected Object doGetValue() { - return text.getText(); - } - - - /** - * Processes a modify event that occurred in this text cell editor. - * This framework method performs validation and sets the error message - * accordingly, and then reports a change via <code>fireEditorValueChanged</code>. - * Subclasses should call this method at appropriate times. Subclasses - * may extend or reimplement. - * - * @param e the SWT modify event - */ - protected void editOccured(ModifyEvent e) { - String value = text.getText(); - if (value == null) { - value = "";//$NON-NLS-1$ - } - Object typedValue = value; - boolean oldValidState = isValueValid(); - boolean newValidState = isCorrect(typedValue); - - if (!newValidState) { - // try to insert the current value into the error message. - setErrorMessage(MessageFormat.format(getErrorMessage(), - new Object[] { value })); - } - valueChanged(oldValidState, newValidState); - } - - /** - * Return the modify listener. - */ - private ModifyListener getModifyListener() { - if (modifyListener == null) { - modifyListener = new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - editOccured(e); - } - }; - } - return modifyListener; - } - - /** - * Handles a default selection event from the text control by applying the editor - * value and deactivating this cell editor. - * - * @param event the selection event - * - * @since 3.0 - */ - protected void handleDefaultSelection(SelectionEvent event) { - // same with enter-key handling code in keyReleaseOccured(e); - fireApplyEditorValue(); - deactivate(); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method returns <code>true</code> if - * the current selection is not empty. - */ - @Override - public boolean isCopyEnabled() { - if (text == null || text.isDisposed()) { - return false; - } - return text.getSelectionCount() > 0; - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method returns <code>true</code> if - * the current selection is not empty. - */ - @Override - public boolean isCutEnabled() { - if (text == null || text.isDisposed()) { - return false; - } - return text.getSelectionCount() > 0; - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method returns <code>true</code> - * if there is a selection or if the caret is not positioned - * at the end of the text. - */ - @Override - public boolean isDeleteEnabled() { - if (text == null || text.isDisposed()) { - return false; - } - return text.getSelectionCount() > 0 - || text.getCaretPosition() < text.getCharCount(); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method always returns <code>true</code>. - */ - @Override - public boolean isPasteEnabled() { - if (text == null || text.isDisposed()) { - return false; - } - return true; - } - - /** - * Check if save all is enabled - * @return true if it is - */ - public boolean isSaveAllEnabled() { - if (text == null || text.isDisposed()) { - return false; - } - return true; - } - - /** - * Returns <code>true</code> if this cell editor is - * able to perform the select all action. - * <p> - * This default implementation always returns - * <code>false</code>. - * </p> - * <p> - * Subclasses may override - * </p> - * @return <code>true</code> if select all is possible, - * <code>false</code> otherwise - */ - @Override - public boolean isSelectAllEnabled() { - if (text == null || text.isDisposed()) { - return false; - } - return text.getCharCount() > 0; - } - - /** - * Processes a key release event that occurred in this cell editor. - * <p> - * The <code>TextCellEditor</code> implementation of this framework method - * ignores when the RETURN key is pressed since this is handled in - * <code>handleDefaultSelection</code>. - * An exception is made for Ctrl+Enter for multi-line texts, since - * a default selection event is not sent in this case. - * </p> - * - * @param keyEvent the key event - */ - @Override - protected void keyReleaseOccured(KeyEvent keyEvent) { - if (keyEvent.character == '\r') { // Return key - // Enter is handled in handleDefaultSelection. - // Do not apply the editor value in response to an Enter key event - // since this can be received from the IME when the intent is -not- - // to apply the value. - // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control - // - // An exception is made for Ctrl+Enter for multi-line texts, since - // a default selection event is not sent in this case. - if (text != null && !text.isDisposed() - && (text.getStyle() & SWT.MULTI) != 0) { - if ((keyEvent.stateMask & SWT.CTRL) != 0) { - super.keyReleaseOccured(keyEvent); - } - } - return; - } - super.keyReleaseOccured(keyEvent); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method copies the - * current selection to the clipboard. - */ - @Override - public void performCopy() { - text.copy(); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method cuts the - * current selection to the clipboard. - */ - @Override - public void performCut() { - text.cut(); - checkSelection(); - checkDeleteable(); - checkSelectable(); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method deletes the - * current selection or, if there is no selection, - * the character next character from the current position. - */ - @Override - public void performDelete() { - if (text.getSelectionCount() > 0) { - // remove the contents of the current selection - text.insert(""); //$NON-NLS-1$ - } else { - // remove the next character - int pos = text.getCaretPosition(); - if (pos < text.getCharCount()) { - text.setSelection(pos, pos + 1); - text.insert(""); //$NON-NLS-1$ - } - } - checkSelection(); - checkDeleteable(); - checkSelectable(); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method pastes the - * the clipboard contents over the current selection. - */ - @Override - public void performPaste() { - text.paste(); - checkSelection(); - checkDeleteable(); - checkSelectable(); - } - - /** - * The <code>TextCellEditor</code> implementation of this - * <code>CellEditor</code> method selects all of the - * current text. - */ - @Override - public void performSelectAll() { - text.selectAll(); - checkSelection(); - checkDeleteable(); - } - - @Override - protected void focusLost() { - if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_LINUX) { - // On Linux, something about the order of focus event delivery prevents the - // callback on the "..." button to be invoked, which means the - // customizer dialog never shows up (see issue #18348). - // (Note that simply trying to Display.asyncRun() the super.focusLost() - // method does not work.) - // - // We can work around this by not deactivating on a focus loss. - // This means that in some cases the cell editor will still be - // shown in the property sheet, but I've tested that the values - // are all committed as before. This is better than having a non-operational - // customizer, but since this issue only happens on Linux the workaround - // is only done on Linux such that on other platforms we deactivate - // immediately on focus loss. - // - if (isActivated()) { - fireApplyEditorValue(); - // super.focusLost calls the following which we're deliberately - // suppressing here: - // deactivate(); - } - } else { - super.focusLost(); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ErrorImageComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ErrorImageComposite.java deleted file mode 100644 index 7085e5d50..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ErrorImageComposite.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.android.ide.eclipse.adt.internal.editors.ui; - -import static org.eclipse.ui.ISharedImages.IMG_DEC_FIELD_ERROR; -import static org.eclipse.ui.ISharedImages.IMG_DEC_FIELD_WARNING; -import static org.eclipse.ui.ISharedImages.IMG_OBJS_ERROR_TSK; -import static org.eclipse.ui.ISharedImages.IMG_OBJS_WARN_TSK; - -import org.eclipse.jface.resource.CompositeImageDescriptor; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.DecorationOverlayIcon; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.Point; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; - -/** - * ImageDescriptor that adds a error marker. - * Based on {@link DecorationOverlayIcon} only available in Eclipse 3.3 - */ -public class ErrorImageComposite extends CompositeImageDescriptor { - - private Image mBaseImage; - private ImageDescriptor mErrorImageDescriptor; - private Point mSize; - - /** - * Creates a new {@link ErrorImageComposite} - * - * @param baseImage the base image to overlay an icon on top of - */ - public ErrorImageComposite(Image baseImage) { - this(baseImage, false); - } - - /** - * Creates a new {@link ErrorImageComposite} - * - * @param baseImage the base image to overlay an icon on top of - * @param warning if true, add a warning icon, otherwise an error icon - */ - public ErrorImageComposite(Image baseImage, boolean warning) { - mBaseImage = baseImage; - ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); - mErrorImageDescriptor = sharedImages.getImageDescriptor( - warning ? IMG_DEC_FIELD_WARNING : IMG_DEC_FIELD_ERROR); - if (mErrorImageDescriptor == null) { - mErrorImageDescriptor = sharedImages.getImageDescriptor( - warning ? IMG_OBJS_WARN_TSK : IMG_OBJS_ERROR_TSK); - } - mSize = new Point(baseImage.getBounds().width, baseImage.getBounds().height); - } - - @Override - protected void drawCompositeImage(int width, int height) { - ImageData baseData = mBaseImage.getImageData(); - drawImage(baseData, 0, 0); - - ImageData overlayData = mErrorImageDescriptor.getImageData(); - if (overlayData.width == baseData.width && overlayData.height == baseData.height) { - overlayData = overlayData.scaledTo(14, 14); - drawImage(overlayData, -3, mSize.y - overlayData.height + 3); - } else { - drawImage(overlayData, 0, mSize.y - overlayData.height); - } - } - - @Override - protected Point getSize() { - return mSize; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/FlagValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/FlagValueCellEditor.java deleted file mode 100644 index 2a1bc36b5..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/FlagValueCellEditor.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiFlagAttributeNode; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** - * DialogCellEditor able to receive a {@link UiFlagAttributeNode} in the {@link #setValue(Object)} - * method. - * <p/>The dialog box opened is the same as the one in the ui created by - * {@link UiFlagAttributeNode#createUiControl(Composite, org.eclipse.ui.forms.IManagedForm)} - */ -public class FlagValueCellEditor extends EditableDialogCellEditor { - - private UiFlagAttributeNode mUiFlagAttribute; - - public FlagValueCellEditor(Composite parent) { - super(parent); - } - - @Override - protected Object openDialogBox(Control cellEditorWindow) { - if (mUiFlagAttribute != null) { - String currentValue = (String)getValue(); - return mUiFlagAttribute.showDialog(cellEditorWindow.getShell(), currentValue); - } - - return null; - } - - @Override - protected void doSetValue(Object value) { - if (value instanceof UiFlagAttributeNode) { - mUiFlagAttribute = (UiFlagAttributeNode)value; - super.doSetValue(mUiFlagAttribute.getCurrentValue()); - return; - } - - super.doSetValue(value); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ListValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ListValueCellEditor.java deleted file mode 100644 index 0c780a8c7..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ListValueCellEditor.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiListAttributeNode; - -import org.eclipse.jface.viewers.ComboBoxCellEditor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.CCombo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** - * ComboBoxCellEditor able to receive a {@link UiListAttributeNode} in the {@link #setValue(Object)} - * method, and returning a {@link String} in {@link #getValue()} instead of an {@link Integer}. - */ -public class ListValueCellEditor extends ComboBoxCellEditor { - private String[] mItems; - private CCombo mCombo; - - public ListValueCellEditor(Composite parent) { - super(parent, new String[0], SWT.DROP_DOWN); - } - - @Override - protected Control createControl(Composite parent) { - mCombo = (CCombo) super.createControl(parent); - return mCombo; - } - - @Override - protected void doSetValue(Object value) { - if (value instanceof UiListAttributeNode) { - UiListAttributeNode uiListAttribute = (UiListAttributeNode)value; - - // set the possible values in the combo - String[] items = uiListAttribute.getPossibleValues(null); - mItems = new String[items.length]; - System.arraycopy(items, 0, mItems, 0, items.length); - setItems(mItems); - - // now edit the current value of the attribute - String attrValue = uiListAttribute.getCurrentValue(); - mCombo.setText(attrValue); - - return; - } - - // default behavior - super.doSetValue(value); - } - - @Override - protected Object doGetValue() { - String comboText = mCombo.getText(); - if (comboText == null) { - return ""; //$NON-NLS-1$ - } - return comboText; - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ResourceValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ResourceValueCellEditor.java deleted file mode 100644 index 8efe294b1..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/ResourceValueCellEditor.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiFlagAttributeNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** - * DialogCellEditor able to receive a {@link UiFlagAttributeNode} in the {@link #setValue(Object)} - * method. - * <p/>The dialog box opened is the same as the one in the ui created by - * {@link UiFlagAttributeNode#createUiControl(Composite, org.eclipse.ui.forms.IManagedForm)} - */ -public class ResourceValueCellEditor extends EditableDialogCellEditor { - - private UiResourceAttributeNode mUiResourceAttribute; - - public ResourceValueCellEditor(Composite parent) { - super(parent); - } - - @Override - protected Object openDialogBox(Control cellEditorWindow) { - if (mUiResourceAttribute != null) { - String currentValue = (String)getValue(); - return mUiResourceAttribute.showDialog(cellEditorWindow.getShell(), currentValue); - } - - return null; - } - - @Override - protected void doSetValue(Object value) { - if (value instanceof UiResourceAttributeNode) { - mUiResourceAttribute = (UiResourceAttributeNode)value; - super.doSetValue(mUiResourceAttribute.getCurrentValue()); - return; - } - - super.doSetValue(value); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/SectionHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/SectionHelper.java deleted file mode 100644 index fdb5d8292..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/SectionHelper.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; - -import org.eclipse.jface.text.DefaultInformationControl; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseTrackListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.forms.SectionPart; -import org.eclipse.ui.forms.widgets.FormText; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.Section; -import org.eclipse.ui.forms.widgets.TableWrapData; -import org.eclipse.ui.forms.widgets.TableWrapLayout; - -import java.lang.reflect.Method; - -/** - * Helper for the AndroidManifest form editor. - * - * Helps create a new SectionPart with sensible default parameters, - * create default layout or add typical widgets. - * - * IMPORTANT: This is NOT a generic class. It makes a lot of assumptions on the - * UI as used by the form editor for the AndroidManifest. - * - * TODO: Consider moving to a common package. - */ -public final class SectionHelper { - - /** - * Utility class that derives from SectionPart, constructs the Section with - * sensible defaults (with a title and a description) and provide some shorthand - * methods for creating typically UI (label and text, form text.) - */ - static public class ManifestSectionPart extends SectionPart { - - /** - * Construct a SectionPart that uses a title bar and a description. - * It's up to the caller to call setText() and setDescription(). - * <p/> - * The section style includes a description and a title bar by default. - * - * @param body The parent (e.g. FormPage body) - * @param toolkit Form Toolkit - */ - public ManifestSectionPart(Composite body, FormToolkit toolkit) { - this(body, toolkit, 0, false); - } - - /** - * Construct a SectionPart that uses a title bar and a description. - * It's up to the caller to call setText() and setDescription(). - * <p/> - * The section style includes a description and a title bar by default. - * You can add extra styles, like Section.TWISTIE. - * - * @param body The parent (e.g. FormPage body). - * @param toolkit Form Toolkit. - * @param extra_style Extra styles (on top of description and title bar). - * @param use_description True if the Section.DESCRIPTION style should be added. - */ - public ManifestSectionPart(Composite body, FormToolkit toolkit, - int extra_style, boolean use_description) { - super(body, toolkit, extra_style | - Section.TITLE_BAR | - (use_description ? Section.DESCRIPTION : 0)); - } - - // Create non-static methods of helpers just for convenience - - /** - * Creates a new composite with a TableWrapLayout set with a given number of columns. - * - * If the parent composite is a Section, the new composite is set as a client. - * - * @param toolkit Form Toolkit - * @param numColumns Desired number of columns. - * @return The new composite. - */ - public Composite createTableLayout(FormToolkit toolkit, int numColumns) { - return SectionHelper.createTableLayout(getSection(), toolkit, numColumns); - } - - /** - * Creates a label widget. - * If the parent layout if a TableWrapLayout, maximize it to span over all columns. - * - * @param parent The parent (e.g. composite from CreateTableLayout()) - * @param toolkit Form Toolkit - * @param label The string for the label. - * @param tooltip An optional tooltip for the label and text. Can be null. - * @return The new created label - */ - public Label createLabel(Composite parent, FormToolkit toolkit, String label, - String tooltip) { - return SectionHelper.createLabel(parent, toolkit, label, tooltip); - } - - /** - * Creates two widgets: a label and a text field. - * - * This expects the parent composite to have a TableWrapLayout with 2 columns. - * - * @param parent The parent (e.g. composite from CreateTableLayout()) - * @param toolkit Form Toolkit - * @param label The string for the label. - * @param value The initial value of the text field. Can be null. - * @param tooltip An optional tooltip for the label and text. Can be null. - * @return The new created Text field (the label is not returned) - */ - public Text createLabelAndText(Composite parent, FormToolkit toolkit, String label, - String value, String tooltip) { - return SectionHelper.createLabelAndText(parent, toolkit, label, value, tooltip); - } - - /** - * Creates a FormText widget. - * - * This expects the parent composite to have a TableWrapLayout with 2 columns. - * - * @param parent The parent (e.g. composite from CreateTableLayout()) - * @param toolkit Form Toolkit - * @param isHtml True if the form text will contain HTML that must be interpreted as - * rich text (i.e. parse tags & expand URLs). - * @param label The string for the label. - * @param setupLayoutData indicates whether the created form text receives a TableWrapData - * through the setLayoutData method. In some case, creating it will make the table parent - * huge, which we don't want. - * @return The new created FormText. - */ - public FormText createFormText(Composite parent, FormToolkit toolkit, boolean isHtml, - String label, boolean setupLayoutData) { - return SectionHelper.createFormText(parent, toolkit, isHtml, label, setupLayoutData); - } - - /** - * Forces the section to recompute its layout and redraw. - * This is needed after the content of the section has been changed at runtime. - */ - public void layoutChanged() { - Section section = getSection(); - - // Calls getSection().reflow(), which is the same that Section calls - // when the expandable state is changed and the height changes. - // Since this is protected, some reflection is needed to invoke it. - try { - Method reflow; - reflow = section.getClass().getDeclaredMethod("reflow", (Class<?>[])null); - reflow.setAccessible(true); - reflow.invoke(section); - } catch (Exception e) { - AdtPlugin.log(e, "Error when invoking Section.reflow"); - } - - section.layout(true /* changed */, true /* all */); - } - } - - /** - * Creates a new composite with a TableWrapLayout set with a given number of columns. - * - * If the parent composite is a Section, the new composite is set as a client. - * - * @param composite The parent (e.g. a Section or SectionPart) - * @param toolkit Form Toolkit - * @param numColumns Desired number of columns. - * @return The new composite. - */ - static public Composite createTableLayout(Composite composite, FormToolkit toolkit, - int numColumns) { - Composite table = toolkit.createComposite(composite); - TableWrapLayout layout = new TableWrapLayout(); - layout.numColumns = numColumns; - table.setLayout(layout); - toolkit.paintBordersFor(table); - if (composite instanceof Section) { - ((Section) composite).setClient(table); - } - return table; - } - - /** - * Creates a new composite with a GridLayout set with a given number of columns. - * - * If the parent composite is a Section, the new composite is set as a client. - * - * @param composite The parent (e.g. a Section or SectionPart) - * @param toolkit Form Toolkit - * @param numColumns Desired number of columns. - * @return The new composite. - */ - static public Composite createGridLayout(Composite composite, FormToolkit toolkit, - int numColumns) { - Composite grid = toolkit.createComposite(composite); - GridLayout layout = new GridLayout(); - layout.numColumns = numColumns; - grid.setLayout(layout); - toolkit.paintBordersFor(grid); - if (composite instanceof Section) { - ((Section) composite).setClient(grid); - } - return grid; - } - - /** - * Creates two widgets: a label and a text field. - * - * This expects the parent composite to have a TableWrapLayout with 2 columns. - * - * @param parent The parent (e.g. composite from CreateTableLayout()) - * @param toolkit Form Toolkit - * @param label_text The string for the label. - * @param value The initial value of the text field. Can be null. - * @param tooltip An optional tooltip for the label and text. Can be null. - * @return The new created Text field (the label is not returned) - */ - static public Text createLabelAndText(Composite parent, FormToolkit toolkit, String label_text, - String value, String tooltip) { - Label label = toolkit.createLabel(parent, label_text); - label.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE)); - Text text = toolkit.createText(parent, value); - text.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE)); - - addControlTooltip(label, tooltip); - return text; - } - - /** - * Creates a label widget. - * If the parent layout if a TableWrapLayout, maximize it to span over all columns. - * - * @param parent The parent (e.g. composite from CreateTableLayout()) - * @param toolkit Form Toolkit - * @param label_text The string for the label. - * @param tooltip An optional tooltip for the label and text. Can be null. - * @return The new created label - */ - static public Label createLabel(Composite parent, FormToolkit toolkit, String label_text, - String tooltip) { - Label label = toolkit.createLabel(parent, label_text); - - TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB); - if (parent.getLayout() instanceof TableWrapLayout) { - twd.colspan = ((TableWrapLayout) parent.getLayout()).numColumns; - } - label.setLayoutData(twd); - - addControlTooltip(label, tooltip); - return label; - } - - /** - * Associates a tooltip with a control. - * - * This mirrors the behavior from org.eclipse.pde.internal.ui.editor.text.PDETextHover - * - * @param control The control to which associate the tooltip. - * @param tooltip The tooltip string. Can use \n for multi-lines. Will not display if null. - */ - static public void addControlTooltip(final Control control, String tooltip) { - if (control == null || tooltip == null || tooltip.length() == 0) { - return; - } - - // Some kinds of controls already properly implement tooltip display. - if (control instanceof Button) { - control.setToolTipText(tooltip); - return; - } - - control.setToolTipText(null); - - final DefaultInformationControl ic = new DefaultInformationControl(control.getShell()); - ic.setInformation(tooltip); - Point sz = ic.computeSizeHint(); - ic.setSize(sz.x, sz.y); - ic.setVisible(false); // initially hidden - - control.addMouseTrackListener(new MouseTrackListener() { - @Override - public void mouseEnter(MouseEvent e) { - } - - @Override - public void mouseExit(MouseEvent e) { - ic.setVisible(false); - } - - @Override - public void mouseHover(MouseEvent e) { - ic.setLocation(control.toDisplay(10, 25)); // same offset as in PDETextHover - ic.setVisible(true); - } - }); - control.addDisposeListener(new DisposeListener() { - @Override - public void widgetDisposed(DisposeEvent e) { - ic.dispose(); - } - }); - } - - /** - * Creates a FormText widget. - * - * This expects the parent composite to have a TableWrapLayout with 2 columns. - * - * @param parent The parent (e.g. composite from CreateTableLayout()) - * @param toolkit Form Toolkit - * @param isHtml True if the form text will contain HTML that must be interpreted as - * rich text (i.e. parse tags & expand URLs). - * @param label The string for the label. - * @param setupLayoutData indicates whether the created form text receives a TableWrapData - * through the setLayoutData method. In some case, creating it will make the table parent - * huge, which we don't want. - * @return The new created FormText. - */ - static public FormText createFormText(Composite parent, FormToolkit toolkit, - boolean isHtml, String label, boolean setupLayoutData) { - FormText text = toolkit.createFormText(parent, true /* track focus */); - if (setupLayoutData) { - TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB); - twd.maxWidth = AndroidXmlEditor.TEXT_WIDTH_HINT; - if (parent.getLayout() instanceof TableWrapLayout) { - twd.colspan = ((TableWrapLayout) parent.getLayout()).numColumns; - } - text.setLayoutData(twd); - } - text.setWhitespaceNormalized(true); - if (isHtml && !label.startsWith("<form>")) { //$NON-NLS-1$ - // This assertion is violated, for example by the Class attribute for an activity - //assert label.startsWith("<form>") : "HTML for FormText must be wrapped in <form>...</form>"; //$NON-NLS-1$ - label = "<form>" + label + "</form>"; //$NON-NLS-1$ //$NON-NLS-2$ - } - text.setText(label, isHtml /* parseTags */, isHtml /* expandURLs */); - return text; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/TextValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/TextValueCellEditor.java deleted file mode 100644 index 3750c3472..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/TextValueCellEditor.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; - -import org.eclipse.jface.viewers.TextCellEditor; -import org.eclipse.swt.widgets.Composite; - -/** - * TextCellEditor able to receive a {@link UiAttributeNode} in the {@link #setValue(Object)} - * method. - */ -public class TextValueCellEditor extends TextCellEditor { - - public TextValueCellEditor(Composite parent) { - super(parent); - } - - @Override - protected void doSetValue(Object value) { - if (value instanceof UiAttributeNode) { - super.doSetValue(((UiAttributeNode)value).getCurrentValue()); - return; - } - - super.doSetValue(value); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/UiElementPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/UiElementPart.java deleted file mode 100644 index db9fa069f..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/UiElementPart.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.Section; - -/** - * Generic page's section part that displays all attributes of a given {@link UiElementNode}. - * <p/> - * This part is designed to be displayed in a page that has a table column layout. - * It is linked to a specific {@link UiElementNode} and automatically displays all of its - * attributes, manages its dirty state and commits the attributes when necessary. - * <p/> - * No derivation is needed unless the UI or workflow needs to be extended. - */ -public class UiElementPart extends ManifestSectionPart { - - /** A reference to the container editor */ - private ManifestEditor mEditor; - /** The {@link UiElementNode} manipulated by this SectionPart. It can be null. */ - private UiElementNode mUiElementNode; - /** Table that contains all the attributes */ - private Composite mTable; - - public UiElementPart(Composite body, FormToolkit toolkit, ManifestEditor editor, - UiElementNode uiElementNode, String sectionTitle, String sectionDescription, - int extra_style) { - super(body, toolkit, extra_style, sectionDescription != null); - mEditor = editor; - mUiElementNode = uiElementNode; - setupSection(sectionTitle, sectionDescription); - - if (uiElementNode == null) { - // This is serious and should never happen. Instead of crashing, simply abort. - // There will be no UI, which will prevent further damage. - AdtPlugin.log(IStatus.ERROR, "Missing node to edit!"); //$NON-NLS-1$ - return; - } - } - - /** - * Returns the Editor associated with this part. - */ - public ManifestEditor getEditor() { - return mEditor; - } - - /** - * Returns the {@link UiElementNode} associated with this part. - */ - public UiElementNode getUiElementNode() { - return mUiElementNode; - } - - /** - * Changes the element node handled by this part. - * - * @param uiElementNode The new element node for the part. - */ - public void setUiElementNode(UiElementNode uiElementNode) { - mUiElementNode = uiElementNode; - } - - /** - * Initializes the form part. - * <p/> - * This is called by the owning managed form as soon as the part is added to the form, - * which happens right after the part is actually created. - */ - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - createFormControls(form); - } - - /** - * Setup the section that contains this part. - * <p/> - * This is called by the constructor to set the section's title and description - * with parameters given in the constructor. - * <br/> - * Derived class override this if needed, however in most cases the default - * implementation should be enough. - * - * @param sectionTitle The section part's title - * @param sectionDescription The section part's description - */ - protected void setupSection(String sectionTitle, String sectionDescription) { - Section section = getSection(); - section.setText(sectionTitle); - section.setDescription(sectionDescription); - } - - /** - * Create the controls to edit the attributes for the given ElementDescriptor. - * <p/> - * This MUST not be called by the constructor. Instead it must be called from - * <code>initialize</code> (i.e. right after the form part is added to the managed form.) - * <p/> - * Derived classes can override this if necessary. - * - * @param managedForm The owner managed form - */ - protected void createFormControls(IManagedForm managedForm) { - setTable(createTableLayout(managedForm.getToolkit(), 2 /* numColumns */)); - - createUiAttributes(managedForm); - } - - /** - * Sets the table where the attribute UI needs to be created. - */ - protected void setTable(Composite table) { - mTable = table; - } - - /** - * Returns the table where the attribute UI needs to be created. - */ - protected Composite getTable() { - return mTable; - } - - /** - * Add all the attribute UI widgets into the underlying table layout. - * - * @param managedForm The owner managed form - */ - protected void createUiAttributes(IManagedForm managedForm) { - Composite table = getTable(); - if (table == null || managedForm == null) { - return; - } - - // Remove any old UI controls - for (Control c : table.getChildren()) { - c.dispose(); - } - - fillTable(table, managedForm); - - // Tell the section that the layout has changed. - layoutChanged(); - } - - /** - * Actually fills the table. - * This is called by {@link #createUiAttributes(IManagedForm)} to populate the new - * table. The default implementation is to use - * {@link #insertUiAttributes(UiElementNode, Composite, IManagedForm)} to actually - * place the attributes of the default {@link UiElementNode} in the table. - * <p/> - * Derived classes can override this to add controls in the table before or after. - * - * @param table The table to fill. It must have 2 columns. - * @param managedForm The managed form for new controls. - */ - protected void fillTable(Composite table, IManagedForm managedForm) { - int inserted = insertUiAttributes(mUiElementNode, table, managedForm); - - if (inserted == 0) { - createLabel(table, managedForm.getToolkit(), - "No attributes to display, waiting for SDK to finish loading...", - null /* tooltip */ ); - } - } - - /** - * Insert the UI attributes of the given {@link UiElementNode} in the given table. - * - * @param uiNode The {@link UiElementNode} that contains the attributes to display. - * Must not be null. - * @param table The table to fill. It must have 2 columns. - * @param managedForm The managed form for new controls. - * @return The number of UI attributes inserted. It is >= 0. - */ - protected int insertUiAttributes(UiElementNode uiNode, Composite table, IManagedForm managedForm) { - if (uiNode == null || table == null || managedForm == null) { - return 0; - } - - // To iterate over all attributes, we use the {@link ElementDescriptor} instead - // of the {@link UiElementNode} because the attributes' order is guaranteed in the - // descriptor but not in the node itself. - AttributeDescriptor[] attr_desc_list = uiNode.getAttributeDescriptors(); - for (AttributeDescriptor attr_desc : attr_desc_list) { - if (attr_desc instanceof XmlnsAttributeDescriptor) { - // Do not show hidden attributes - continue; - } - - UiAttributeNode ui_attr = uiNode.findUiAttribute(attr_desc); - if (ui_attr != null) { - ui_attr.createUiControl(table, managedForm); - } else { - // The XML has an extra attribute which wasn't declared in - // AndroidManifestDescriptors. This is not a problem, we just ignore it. - AdtPlugin.log(IStatus.WARNING, - "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ - attr_desc.getXmlLocalName(), - uiNode.getDescriptor().getXmlName()); - } - } - return attr_desc_list.length; - } - - /** - * Tests whether the part is dirty i.e. its widgets have state that is - * newer than the data in the model. - * <p/> - * This is done by iterating over all attributes and updating the super's - * internal dirty flag. Stop once at least one attribute is dirty. - * - * @return <code>true</code> if the part is dirty, <code>false</code> - * otherwise. - */ - @Override - public boolean isDirty() { - if (mUiElementNode != null && !super.isDirty()) { - for (UiAttributeNode ui_attr : mUiElementNode.getAllUiAttributes()) { - if (ui_attr.isDirty()) { - markDirty(); - break; - } - } - } - return super.isDirty(); - } - - /** - * If part is displaying information loaded from a model, this method - * instructs it to commit the new (modified) data back into the model. - * - * @param onSave - * indicates if commit is called during 'save' operation or for - * some other reason (for example, if form is contained in a - * wizard or a multi-page editor and the user is about to leave - * the page). - */ - @Override - public void commit(boolean onSave) { - if (mUiElementNode != null) { - mEditor.wrapEditXmlModel(new Runnable() { - @Override - public void run() { - for (UiAttributeNode ui_attr : mUiElementNode.getAllUiAttributes()) { - ui_attr.commit(); - } - } - }); - } - - // We need to call super's commit after we synchronized the nodes to make sure we - // reset the dirty flag after all the side effects from committing have occurred. - super.commit(onSave); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/CopyCutAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/CopyCutAction.java deleted file mode 100644 index 3fe98bb23..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/CopyCutAction.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.apache.xml.serialize.Method; -import org.apache.xml.serialize.OutputFormat; -import org.apache.xml.serialize.XMLSerializer; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; -import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.xml.core.internal.document.NodeContainer; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; - - -/** - * Provides Cut and Copy actions for the tree nodes. - */ -@SuppressWarnings({"restriction", "deprecation"}) -public class CopyCutAction extends Action { - private List<UiElementNode> mUiNodes; - private boolean mPerformCut; - private final AndroidXmlEditor mEditor; - private final Clipboard mClipboard; - private final ICommitXml mXmlCommit; - - /** - * Creates a new Copy or Cut action. - * - * @param selected The UI node to cut or copy. It *must* have a non-null XML node. - * @param performCut True if the operation is cut, false if it is copy. - */ - public CopyCutAction(AndroidXmlEditor editor, Clipboard clipboard, ICommitXml xmlCommit, - UiElementNode selected, boolean performCut) { - this(editor, clipboard, xmlCommit, toList(selected), performCut); - } - - /** - * Creates a new Copy or Cut action. - * - * @param selected The UI nodes to cut or copy. They *must* have a non-null XML node. - * The list becomes owned by the {@link CopyCutAction}. - * @param performCut True if the operation is cut, false if it is copy. - */ - public CopyCutAction(AndroidXmlEditor editor, Clipboard clipboard, ICommitXml xmlCommit, - List<UiElementNode> selected, boolean performCut) { - super(performCut ? "Cut" : "Copy"); - mEditor = editor; - mClipboard = clipboard; - mXmlCommit = xmlCommit; - - ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); - if (performCut) { - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_HOVER)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED)); - } else { - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_HOVER)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); - } - - mUiNodes = selected; - mPerformCut = performCut; - } - - /** - * Performs the cut or copy action. - * First an XML serializer is used to turn the existing XML node into a valid - * XML fragment, which is added as text to the clipboard. - */ - @Override - public void run() { - super.run(); - if (mUiNodes == null || mUiNodes.size() < 1) { - return; - } - - // Commit the current pages first, to make sure the XML is in sync. - // Committing may change the XML structure. - if (mXmlCommit != null) { - mXmlCommit.commitPendingXmlChanges(); - } - - StringBuilder allText = new StringBuilder(); - ArrayList<UiElementNode> nodesToCut = mPerformCut ? new ArrayList<UiElementNode>() : null; - - for (UiElementNode uiNode : mUiNodes) { - try { - Node xml_node = uiNode.getXmlNode(); - if (xml_node == null) { - return; - } - - String data = getXmlTextFromEditor(xml_node); - - // In the unlikely event that IStructuredDocument failed to extract the text - // directly from the editor, try to fall back on a direct XML serialization - // of the XML node. This uses the generic Node interface with no SSE tricks. - if (data == null) { - data = getXmlTextFromSerialization(xml_node); - } - - if (data != null) { - allText.append(data); - if (mPerformCut) { - // only remove notes to cut if we actually got some XML text from them - nodesToCut.add(uiNode); - } - } - - } catch (Exception e) { - AdtPlugin.log(e, "CopyCutAction failed for UI node %1$s", //$NON-NLS-1$ - uiNode.getBreadcrumbTrailDescription(true)); - } - } // for uiNode - - if (allText != null && allText.length() > 0) { - mClipboard.setContents( - new Object[] { allText.toString() }, - new Transfer[] { TextTransfer.getInstance() }); - if (mPerformCut) { - for (UiElementNode uiNode : nodesToCut) { - uiNode.deleteXmlNode(); - } - } - } - } - - /** Get the data directly from the editor. */ - private String getXmlTextFromEditor(Node xml_node) { - String data = null; - IStructuredModel model = mEditor.getModelForRead(); - try { - IStructuredDocument sse_doc = mEditor.getStructuredDocument(); - if (xml_node instanceof NodeContainer) { - // The easy way to get the source of an SSE XML node. - data = ((NodeContainer) xml_node).getSource(); - } else if (xml_node instanceof IndexedRegion && sse_doc != null) { - // Try harder. - IndexedRegion region = (IndexedRegion) xml_node; - int start = region.getStartOffset(); - int end = region.getEndOffset(); - - if (end > start) { - data = sse_doc.get(start, end - start); - } - } - } catch (BadLocationException e) { - // the region offset was invalid. ignore. - } finally { - model.releaseFromRead(); - } - return data; - } - - /** - * Direct XML serialization of the XML node. - * <p/> - * This uses the generic Node interface with no SSE tricks. It's however slower - * and doesn't respect formatting (since serialization is involved instead of reading - * the actual text buffer.) - */ - private String getXmlTextFromSerialization(Node xml_node) throws IOException { - String data; - StringWriter sw = new StringWriter(); - XMLSerializer serializer = new XMLSerializer(sw, - new OutputFormat(Method.XML, - OutputFormat.Defaults.Encoding /* utf-8 */, - true /* indent */)); - // Serialize will throw an IOException if it fails. - serializer.serialize((Element) xml_node); - data = sw.toString(); - return data; - } - - /** - * Static helper class to wrap on node into a list for the constructors. - */ - private static ArrayList<UiElementNode> toList(UiElementNode selected) { - ArrayList<UiElementNode> list = null; - if (selected != null) { - list = new ArrayList<UiElementNode>(1); - list.add(selected); - } - return list; - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/ICommitXml.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/ICommitXml.java deleted file mode 100644 index 067d1459e..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/ICommitXml.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -/** - * Interface for an object that can commit its changes to the underlying XML model - */ -public interface ICommitXml { - - /** Commits pending data to the underlying XML model. */ - public void commitPendingXmlChanges(); - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/NewItemSelectionDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/NewItemSelectionDialog.java deleted file mode 100644 index 385a53a5f..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/NewItemSelectionDialog.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog; -import org.eclipse.ui.dialogs.ISelectionStatusValidator; -import org.eclipse.ui.part.FileEditorInput; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -/** - * A selection dialog to select the type of the new element node to - * create, either in the application node or the selected sub node. - */ -public class NewItemSelectionDialog extends AbstractElementListSelectionDialog { - - /** The UI node selected in the tree view before creating the new item selection dialog. - * Can be null -- which means new items must be created in the root_node. */ - private UiElementNode mSelectedUiNode; - /** The root node chosen by the user, either root_node or the one passed - * to the constructor if not null */ - private UiElementNode mChosenRootNode; - private UiElementNode mLocalRootNode; - /** The 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. Can be null. */ - private ElementDescriptor[] mDescriptorFilters; - /** The key for the {@link #setLastUsedXmlName(Object[])}. It corresponds to the full - * workspace path of the currently edited file, if this can be computed. This is computed - * by {@link #getLastUsedXmlName(UiElementNode)}, called from the constructor. */ - private String mLastUsedKey; - /** A static map of known XML Names used for a given file. The map has full workspace - * paths as key and XML names as values. */ - private static final Map<String, String> sLastUsedXmlName = new HashMap<String, String>(); - /** The potential XML Name to initially select in the selection dialog. This is computed - * in the constructor and set by {@link #setInitialSelection(UiElementNode)}. */ - private String mInitialXmlName; - - /** - * Creates the new item selection dialog. - * - * @param shell The parent shell for the list. - * @param labelProvider ILabelProvider for the list. - * @param descriptorFilters The element allows at the root of the tree. Can be null. - * @param ui_node The selected node, or null if none is selected. - * @param root_node The root of the Ui Tree, either the UiDocumentNode or a sub-node. - */ - public NewItemSelectionDialog(Shell shell, ILabelProvider labelProvider, - ElementDescriptor[] descriptorFilters, - UiElementNode ui_node, - UiElementNode root_node) { - super(shell, labelProvider); - mDescriptorFilters = descriptorFilters; - mLocalRootNode = root_node; - - // Only accept the UI node if it is not the UI root node and it can have children. - // If the node cannot have children, select its parent as a potential target. - if (ui_node != null && ui_node != mLocalRootNode) { - if (ui_node.getDescriptor().hasChildren()) { - mSelectedUiNode = ui_node; - } else { - UiElementNode parent = ui_node.getUiParent(); - if (parent != null && parent != mLocalRootNode) { - mSelectedUiNode = parent; - } - } - } - - setHelpAvailable(false); - setMultipleSelection(false); - - setValidator(new ISelectionStatusValidator() { - @Override - public IStatus validate(Object[] selection) { - if (selection.length == 1 && selection[0] instanceof ViewElementDescriptor) { - return new Status(IStatus.OK, // severity - AdtPlugin.PLUGIN_ID, //plugin id - IStatus.OK, // code - ((ViewElementDescriptor) selection[0]).getFullClassName(), //msg - null); // exception - } else if (selection.length == 1 && selection[0] instanceof ElementDescriptor) { - return new Status(IStatus.OK, // severity - AdtPlugin.PLUGIN_ID, //plugin id - IStatus.OK, // code - "", //$NON-NLS-1$ // msg - null); // exception - } else { - return new Status(IStatus.ERROR, // severity - AdtPlugin.PLUGIN_ID, //plugin id - IStatus.ERROR, // code - "Invalid selection", // msg, translatable - null); // exception - } - } - }); - - // Determine the initial selection using a couple heuristics. - - // First check if we can get the last used node type for this file. - // The heuristic is that generally one keeps adding the same kind of items to the - // same file, so reusing the last used item type makes most sense. - String xmlName = getLastUsedXmlName(root_node); - if (xmlName == null) { - // Another heuristic is to find the most used item and default to that. - xmlName = getMostUsedXmlName(root_node); - } - if (xmlName == null) { - // Finally the last heuristic is to see if there's an item with a name - // similar to the edited file name. - xmlName = getLeafFileName(root_node); - } - // Set the potential name. Selecting the right item is done later by setInitialSelection(). - mInitialXmlName = xmlName; - } - - /** - * Returns a potential XML name based on the file name. - * The item name is marked with an asterisk to identify it as a partial match. - */ - private String getLeafFileName(UiElementNode ui_node) { - if (ui_node != null) { - AndroidXmlEditor editor = ui_node.getEditor(); - if (editor != null) { - IEditorInput editorInput = editor.getEditorInput(); - if (editorInput instanceof FileEditorInput) { - IFile f = ((FileEditorInput) editorInput).getFile(); - if (f != null) { - String leafName = f.getFullPath().removeFileExtension().lastSegment(); - return "*" + leafName; //$NON-NLS-1$ - } - } - } - } - - return null; - } - - /** - * Given a potential non-null root node, this method looks for the currently edited - * file path and uses it as a key to retrieve the last used item for this file by this - * selection dialog. Returns null if nothing can be found, otherwise returns the string - * name of the item. - */ - private String getLastUsedXmlName(UiElementNode ui_node) { - if (ui_node != null) { - AndroidXmlEditor editor = ui_node.getEditor(); - if (editor != null) { - IEditorInput editorInput = editor.getEditorInput(); - if (editorInput instanceof FileEditorInput) { - IFile f = ((FileEditorInput) editorInput).getFile(); - if (f != null) { - mLastUsedKey = f.getFullPath().toPortableString(); - - return sLastUsedXmlName.get(mLastUsedKey); - } - } - } - } - - return null; - } - - /** - * Sets the last used item for this selection dialog for this file. - * @param objects The currently selected items. Only the first one is used if it is an - * {@link ElementDescriptor}. - */ - private void setLastUsedXmlName(Object[] objects) { - if (mLastUsedKey != null && - objects != null && - objects.length > 0 && - objects[0] instanceof ElementDescriptor) { - ElementDescriptor desc = (ElementDescriptor) objects[0]; - sLastUsedXmlName.put(mLastUsedKey, desc.getXmlName()); - } - } - - /** - * Returns the most used sub-element name, if any, or null. - */ - private String getMostUsedXmlName(UiElementNode ui_node) { - if (ui_node != null) { - TreeMap<String, Integer> counts = new TreeMap<String, Integer>(); - int max = -1; - - for (UiElementNode child : ui_node.getUiChildren()) { - String name = child.getDescriptor().getXmlName(); - Integer i = counts.get(name); - int count = i == null ? 1 : i.intValue() + 1; - counts.put(name, count); - max = Math.max(max, count); - } - - if (max > 0) { - // Find first key with this max and return it - for (Entry<String, Integer> entry : counts.entrySet()) { - if (entry.getValue().intValue() == max) { - return entry.getKey(); - } - } - } - } - return null; - } - - /** - * @return The root node selected by the user, either root node or the - * one passed to the constructor if not null. - */ - public UiElementNode getChosenRootNode() { - return mChosenRootNode; - } - - /** - * Internal helper to compute the result. Returns the selection from - * the list view, if any. - */ - @Override - protected void computeResult() { - setResult(Arrays.asList(getSelectedElements())); - setLastUsedXmlName(getSelectedElements()); - } - - /** - * Creates the dialog area. - * - * First add a radio area, which may be either 2 radio controls or - * just a message area if there's only one choice (the app root node). - * - * Then uses the default from the AbstractElementListSelectionDialog - * which is to add both a filter text and a filtered list. Adding both - * is necessary (since the base class accesses both internal directly - * fields without checking for null pointers.) - * - * Finally sets the initial selection list. - */ - @Override - protected Control createDialogArea(Composite parent) { - Composite contents = (Composite) super.createDialogArea(parent); - - createRadioControl(contents); - createFilterText(contents); - createFilteredList(contents); - - // We don't want the builtin message area label (we use a radio control - // instead), but if we don't create it, Bad Stuff happens on - // Eclipse 3.8 and later (see issue 32527). - Label label = createMessageArea(contents); - if (label != null) { - GridData data = (GridData) label.getLayoutData(); - data.exclude = true; - } - - // Initialize the list state. - // This must be done after the filtered list as been created. - chooseNode(mChosenRootNode); - - // Set the initial selection - setInitialSelection(mChosenRootNode); - return contents; - } - - /** - * Tries to set the initial selection based on the {@link #mInitialXmlName} computed - * in the constructor. The selection is only set if there's an element descriptor - * that matches the same exact XML name. When {@link #mInitialXmlName} starts with an - * asterisk, it means to do a partial case-insensitive match on the start of the - * strings. - */ - private void setInitialSelection(UiElementNode rootNode) { - ElementDescriptor initialElement = null; - - if (mInitialXmlName != null && mInitialXmlName.length() > 0) { - String name = mInitialXmlName; - boolean partial = name.startsWith("*"); //$NON-NLS-1$ - if (partial) { - name = name.substring(1).toLowerCase(Locale.US); - } - - for (ElementDescriptor desc : getAllowedDescriptors(rootNode)) { - if (!partial && desc.getXmlName().equals(name)) { - initialElement = desc; - break; - } else if (partial) { - String name2 = desc.getXmlLocalName().toLowerCase(Locale.US); - if (name.startsWith(name2) || name2.startsWith(name)) { - initialElement = desc; - break; - } - } - } - } - - setSelection(initialElement == null ? null : new ElementDescriptor[] { initialElement }); - } - - /** - * Creates the message text widget and sets layout data. - * @param content the parent composite of the message area. - */ - private Composite createRadioControl(Composite content) { - - if (mSelectedUiNode != null) { - Button radio1 = new Button(content, SWT.RADIO); - radio1.setText(String.format("Create a new element at the top level, in %1$s.", - mLocalRootNode.getShortDescription())); - - Button radio2 = new Button(content, SWT.RADIO); - radio2.setText(String.format("Create a new element in the selected element, %1$s.", - mSelectedUiNode.getBreadcrumbTrailDescription(false /* include_root */))); - - // Set the initial selection before adding the listeners - // (they can't be run till the filtered list has been created) - radio1.setSelection(false); - radio2.setSelection(true); - mChosenRootNode = mSelectedUiNode; - - radio1.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - chooseNode(mLocalRootNode); - } - }); - - radio2.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - chooseNode(mSelectedUiNode); - } - }); - } else { - setMessage(String.format("Create a new element at the top level, in %1$s.", - mLocalRootNode.getShortDescription())); - createMessageArea(content); - - mChosenRootNode = mLocalRootNode; - } - - return content; - } - - /** - * Internal helper to remember the root node choosen by the user. - * It also sets the list view to the adequate list of children that can - * be added to the chosen root node. - * - * If the chosen root node is mLocalRootNode and a descriptor filter was specified - * when creating the master-detail part, we use this as the set of nodes that - * can be created on the root node. - * - * @param ui_node The chosen root node, either mLocalRootNode or - * mSelectedUiNode. - */ - private void chooseNode(UiElementNode ui_node) { - mChosenRootNode = ui_node; - setListElements(getAllowedDescriptors(ui_node)); - } - - /** - * Returns the list of {@link ElementDescriptor}s that can be added to the given - * UI node. - * - * @param ui_node The UI node to which element should be added. Cannot be null. - * @return A non-null array of {@link ElementDescriptor}. The array might be empty. - */ - private ElementDescriptor[] getAllowedDescriptors(UiElementNode ui_node) { - if (ui_node == mLocalRootNode && - mDescriptorFilters != null && - mDescriptorFilters.length != 0) { - return mDescriptorFilters; - } else { - return ui_node.getDescriptor().getChildren(); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/PasteAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/PasteAction.java deleted file mode 100644 index 6674ba9ca..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/PasteAction.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.action.Action; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; -import org.eclipse.wst.xml.core.internal.document.NodeContainer; -import org.w3c.dom.Node; - - -/** - * Provides Paste operation for the tree nodes - */ -@SuppressWarnings("restriction") -public class PasteAction extends Action { - private UiElementNode mUiNode; - private final AndroidXmlEditor mEditor; - private final Clipboard mClipboard; - - public PasteAction(AndroidXmlEditor editor, Clipboard clipboard, UiElementNode ui_node) { - super("Paste"); - mEditor = editor; - mClipboard = clipboard; - - ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED)); - - mUiNode = ui_node; - } - - /** - * Performs the paste operation. - */ - @Override - public void run() { - super.run(); - - final String data = (String) mClipboard.getContents(TextTransfer.getInstance()); - if (data != null) { - mEditor.wrapEditXmlModel(new Runnable() { - @Override - public void run() { - try { - IStructuredDocument sse_doc = mEditor.getStructuredDocument(); - if (sse_doc != null) { - if (mUiNode.getDescriptor().hasChildren()) { - // This UI Node can have children. The new XML is - // inserted as the first child. - - if (mUiNode.getUiChildren().size() > 0) { - // There's already at least one child, - // so insert right before it. - Node xml_node = mUiNode.getUiChildren().get(0).getXmlNode(); - - if (xml_node instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) xml_node; - sse_doc.replace(region.getStartOffset(), 0, data); - return; // we're done, no need to try the other cases - } - } - - // If there's no first XML node child. Create one by - // inserting at the end of the *start* tag. - Node xml_node = mUiNode.getXmlNode(); - if (xml_node instanceof NodeContainer) { - NodeContainer container = (NodeContainer) xml_node; - IStructuredDocumentRegion start_tag = - container.getStartStructuredDocumentRegion(); - if (start_tag != null) { - sse_doc.replace(start_tag.getEndOffset(), 0, data); - return; // we're done, no need to try the other case - } - } - } - - // This UI Node doesn't accept children. The new XML is inserted as the - // next sibling. This also serves as a fallback if all the previous - // attempts failed. However, this is not possible if the current node - // has for parent a document -- an XML document can only have one root, - // with no siblings. - if (!(mUiNode.getUiParent() instanceof UiDocumentNode)) { - Node xml_node = mUiNode.getXmlNode(); - if (xml_node instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) xml_node; - sse_doc.replace(region.getEndOffset(), 0, data); - } - } - } - - } catch (BadLocationException e) { - AdtPlugin.log(e, - "ParseAction failed for UI Node %2$s, content '%1$s'", //$NON-NLS-1$ - mUiNode.getBreadcrumbTrailDescription(true), data); - } - } - }); - } - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiActions.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiActions.java deleted file mode 100644 index 92ccf2e7d..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiActions.java +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.swt.widgets.Shell; -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import java.util.List; - -/** - * Performs basic actions on an XML tree: add node, remove node, move up/down. - */ -public abstract class UiActions implements ICommitXml { - - public UiActions() { - } - - //--------------------- - // Actual implementations must override these to provide specific hooks - - /** Returns the UiDocumentNode for the current model. */ - abstract protected UiElementNode getRootNode(); - - /** Commits pending data before the XML model is modified. */ - @Override - abstract public void commitPendingXmlChanges(); - - /** - * Utility method to select an outline item based on its model node - * - * @param uiNode The node to select. Can be null (in which case nothing should happen) - */ - abstract protected void selectUiNode(UiElementNode uiNode); - - //--------------------- - - /** - * Called when the "Add..." button next to the tree view is selected. - * <p/> - * This simplified version of doAdd does not support descriptor filters and creates - * a new {@link UiModelTreeLabelProvider} for each call. - */ - public void doAdd(UiElementNode uiNode, Shell shell) { - doAdd(uiNode, null /* descriptorFilters */, shell, new UiModelTreeLabelProvider()); - } - - /** - * 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. - */ - public void doAdd(UiElementNode uiNode, - ElementDescriptor[] descriptorFilters, - Shell shell, ILabelProvider labelProvider) { - // If the root node is a document with already a root, use it as the root node - UiElementNode rootNode = getRootNode(); - if (rootNode instanceof UiDocumentNode && rootNode.getUiChildren().size() > 0) { - rootNode = rootNode.getUiChildren().get(0); - } - - NewItemSelectionDialog dlg = new NewItemSelectionDialog( - shell, - labelProvider, - descriptorFilters, - uiNode, rootNode); - dlg.open(); - Object[] results = dlg.getResult(); - if (results != null && results.length > 0) { - addElement(dlg.getChosenRootNode(), null, (ElementDescriptor) results[0], - true /*updateLayout*/); - } - } - - /** - * Adds a new XML element based on the {@link ElementDescriptor} to the given parent - * {@link UiElementNode}, and then select it. - * <p/> - * If the parent is a document root which already contains a root element, the inner - * root element is used as the actual parent. This ensure you can't create a broken - * XML file with more than one root element. - * <p/> - * If a sibling is given and that sibling has the same parent, the new node is added - * right after that sibling. Otherwise the new node is added at the end of the parent - * child list. - * - * @param uiParent An existing UI node or null to add to the tree root - * @param uiSibling An existing UI node before which to insert the new node. Can be null. - * @param descriptor The descriptor of the element to add - * @param updateLayout True if layout attributes should be set - * @return The new {@link UiElementNode} or null. - */ - public UiElementNode addElement(UiElementNode uiParent, - UiElementNode uiSibling, - ElementDescriptor descriptor, - boolean updateLayout) { - if (uiParent instanceof UiDocumentNode && uiParent.getUiChildren().size() > 0) { - uiParent = uiParent.getUiChildren().get(0); - } - if (uiSibling != null && uiSibling.getUiParent() != uiParent) { - uiSibling = null; - } - - UiElementNode uiNew = addNewTreeElement(uiParent, uiSibling, descriptor, updateLayout); - selectUiNode(uiNew); - - return uiNew; - } - - /** - * 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. - */ - public void doRemove(final List<UiElementNode> nodes, Shell shell) { - - if (nodes == null || nodes.size() == 0) { - return; - } - - final int len = nodes.size(); - - StringBuilder sb = new StringBuilder(); - for (UiElementNode node : nodes) { - sb.append("\n- "); //$NON-NLS-1$ - sb.append(node.getBreadcrumbTrailDescription(false /* include_root */)); - } - - if (MessageDialog.openQuestion(shell, - len > 1 ? "Remove elements from Android XML" // title - : "Remove element from Android XML", - String.format("Do you really want to remove %1$s?", sb.toString()))) { - commitPendingXmlChanges(); - getRootNode().getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - UiElementNode previous = null; - UiElementNode parent = null; - - for (int i = len - 1; i >= 0; i--) { - UiElementNode node = nodes.get(i); - previous = node.getUiPreviousSibling(); - parent = node.getUiParent(); - - // delete node - node.deleteXmlNode(); - } - - // try to select the last previous sibling or the last parent - if (previous != null) { - selectUiNode(previous); - } else if (parent != null) { - selectUiNode(parent); - } - } - }); - } - } - - /** - * Called when the "Up" button is selected. - * <p/> - * If the tree has a selection, move it up, either in the child list or as the last child - * of the previous parent. - */ - public void doUp( - final List<UiElementNode> uiNodes, - final ElementDescriptor[] descriptorFilters) { - if (uiNodes == null || uiNodes.size() < 1) { - return; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiLastNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - getRootNode().getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - for (int i = 0; i < uiNodes.size(); i++) { - UiElementNode uiNode = uiLastNode[0] = uiNodes.get(i); - doUpInternal( - uiNode, - descriptorFilters, - selectXmlNode, - uiSearchRoot, - false /*testOnly*/); - } - } - }); - - assert uiLastNode[0] != null; // tell Eclipse this can't be null below - - if (selectXmlNode[0] == null) { - // The XML node has not been moved, we can just select the same UI node - selectUiNode(uiLastNode[0]); - } else { - // The XML node has moved. At this point the UI model has been reloaded - // and the XML node has been affected to a new UI node. Find that new UI - // node and select it. - if (uiSearchRoot[0] == null) { - uiSearchRoot[0] = uiLastNode[0].getUiRoot(); - } - if (uiSearchRoot[0] != null) { - selectUiNode(uiSearchRoot[0].findXmlNode(selectXmlNode[0])); - } - } - } - - /** - * Checks whether the "up" action can be performed on all items. - * - * @return True if the up action can be carried on *all* items. - */ - public boolean canDoUp( - List<UiElementNode> uiNodes, - ElementDescriptor[] descriptorFilters) { - if (uiNodes == null || uiNodes.size() < 1) { - return false; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - - for (int i = 0; i < uiNodes.size(); i++) { - if (!doUpInternal( - uiNodes.get(i), - descriptorFilters, - selectXmlNode, - uiSearchRoot, - true /*testOnly*/)) { - return false; - } - } - - return true; - } - - private boolean doUpInternal( - UiElementNode uiNode, - ElementDescriptor[] descriptorFilters, - Node[] outSelectXmlNode, - UiElementNode[] outUiSearchRoot, - boolean testOnly) { - // the node will move either up to its parent or grand-parent - outUiSearchRoot[0] = uiNode.getUiParent(); - if (outUiSearchRoot[0] != null && outUiSearchRoot[0].getUiParent() != null) { - outUiSearchRoot[0] = outUiSearchRoot[0].getUiParent(); - } - Node xmlNode = uiNode.getXmlNode(); - ElementDescriptor nodeDesc = uiNode.getDescriptor(); - if (xmlNode == null || nodeDesc == null) { - return false; - } - UiElementNode uiParentNode = uiNode.getUiParent(); - Node xmlParent = uiParentNode == null ? null : uiParentNode.getXmlNode(); - if (xmlParent == null) { - return false; - } - - UiElementNode uiPrev = uiNode.getUiPreviousSibling(); - - // Only accept a sibling that has an XML attached and - // is part of the allowed descriptor filters. - while (uiPrev != null && - (uiPrev.getXmlNode() == null || !matchDescFilter(descriptorFilters, uiPrev))) { - uiPrev = uiPrev.getUiPreviousSibling(); - } - - if (uiPrev != null && uiPrev.getXmlNode() != null) { - // This node is not the first one of the parent. - Node xmlPrev = uiPrev.getXmlNode(); - if (uiPrev.getDescriptor().acceptChild(nodeDesc)) { - // If the previous sibling can accept this child, then it - // is inserted at the end of the children list. - if (testOnly) { - return true; - } - xmlPrev.appendChild(xmlParent.removeChild(xmlNode)); - outSelectXmlNode[0] = xmlNode; - } else { - // This node is not the first one of the parent, so it can be - // removed and then inserted before its previous sibling. - if (testOnly) { - return true; - } - xmlParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlPrev); - outSelectXmlNode[0] = xmlNode; - } - } else if (uiParentNode != null && !(xmlParent instanceof Document)) { - UiElementNode uiGrandParent = uiParentNode.getUiParent(); - Node xmlGrandParent = uiGrandParent == null ? null : uiGrandParent.getXmlNode(); - ElementDescriptor grandDesc = - uiGrandParent == null ? null : uiGrandParent.getDescriptor(); - - if (xmlGrandParent != null && - !(xmlGrandParent instanceof Document) && - grandDesc != null && - grandDesc.acceptChild(nodeDesc)) { - // If the node is the first one of the child list of its - // parent, move it up in the hierarchy as previous sibling - // to the parent. This is only possible if the parent of the - // parent is not a document. - // The parent node must actually accept this kind of child. - - if (testOnly) { - return true; - } - xmlGrandParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlParent); - outSelectXmlNode[0] = xmlNode; - } - } - - return false; - } - - private boolean matchDescFilter( - ElementDescriptor[] descriptorFilters, - UiElementNode uiNode) { - if (descriptorFilters == null || descriptorFilters.length < 1) { - return true; - } - - ElementDescriptor desc = uiNode.getDescriptor(); - - for (ElementDescriptor filter : descriptorFilters) { - if (filter.equals(desc)) { - return true; - } - } - return false; - } - - /** - * 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. - */ - public void doDown( - final List<UiElementNode> nodes, - final ElementDescriptor[] descriptorFilters) { - if (nodes == null || nodes.size() < 1) { - return; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiLastNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - getRootNode().getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - for (int i = nodes.size() - 1; i >= 0; i--) { - final UiElementNode node = uiLastNode[0] = nodes.get(i); - doDownInternal( - node, - descriptorFilters, - selectXmlNode, - uiSearchRoot, - false /*testOnly*/); - } - } - }); - - assert uiLastNode[0] != null; // tell Eclipse this can't be null below - - if (selectXmlNode[0] == null) { - // The XML node has not been moved, we can just select the same UI node - selectUiNode(uiLastNode[0]); - } else { - // The XML node has moved. At this point the UI model has been reloaded - // and the XML node has been affected to a new UI node. Find that new UI - // node and select it. - if (uiSearchRoot[0] == null) { - uiSearchRoot[0] = uiLastNode[0].getUiRoot(); - } - if (uiSearchRoot[0] != null) { - selectUiNode(uiSearchRoot[0].findXmlNode(selectXmlNode[0])); - } - } - } - - /** - * Checks whether the "down" action can be performed on all items. - * - * @return True if the down action can be carried on *all* items. - */ - public boolean canDoDown( - List<UiElementNode> uiNodes, - ElementDescriptor[] descriptorFilters) { - if (uiNodes == null || uiNodes.size() < 1) { - return false; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - - for (int i = 0; i < uiNodes.size(); i++) { - if (!doDownInternal( - uiNodes.get(i), - descriptorFilters, - selectXmlNode, - uiSearchRoot, - true /*testOnly*/)) { - return false; - } - } - - return true; - } - - private boolean doDownInternal( - UiElementNode uiNode, - ElementDescriptor[] descriptorFilters, - Node[] outSelectXmlNode, - UiElementNode[] outUiSearchRoot, - boolean testOnly) { - // the node will move either down to its parent or grand-parent - outUiSearchRoot[0] = uiNode.getUiParent(); - if (outUiSearchRoot[0] != null && outUiSearchRoot[0].getUiParent() != null) { - outUiSearchRoot[0] = outUiSearchRoot[0].getUiParent(); - } - - Node xmlNode = uiNode.getXmlNode(); - ElementDescriptor nodeDesc = uiNode.getDescriptor(); - if (xmlNode == null || nodeDesc == null) { - return false; - } - UiElementNode uiParentNode = uiNode.getUiParent(); - Node xmlParent = uiParentNode == null ? null : uiParentNode.getXmlNode(); - if (xmlParent == null) { - return false; - } - - UiElementNode uiNext = uiNode.getUiNextSibling(); - - // Only accept a sibling that has an XML attached and - // is part of the allowed descriptor filters. - while (uiNext != null && - (uiNext.getXmlNode() == null || !matchDescFilter(descriptorFilters, uiNext))) { - uiNext = uiNext.getUiNextSibling(); - } - - if (uiNext != null && uiNext.getXmlNode() != null) { - // This node is not the last one of the parent. - Node xmlNext = uiNext.getXmlNode(); - // If the next sibling is a node that can have children, though, - // then the node is inserted as the first child. - if (uiNext.getDescriptor().acceptChild(nodeDesc)) { - if (testOnly) { - return true; - } - // Note: insertBefore works as append if the ref node is - // null, i.e. when the node doesn't have children yet. - xmlNext.insertBefore( - xmlParent.removeChild(xmlNode), - xmlNext.getFirstChild()); - outSelectXmlNode[0] = xmlNode; - } else { - // This node is not the last one of the parent, so it can be - // removed and then inserted after its next sibling. - - if (testOnly) { - return true; - } - // Insert "before after next" ;-) - xmlParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlNext.getNextSibling()); - outSelectXmlNode[0] = xmlNode; - } - } else if (uiParentNode != null && !(xmlParent instanceof Document)) { - UiElementNode uiGrandParent = uiParentNode.getUiParent(); - Node xmlGrandParent = uiGrandParent == null ? null : uiGrandParent.getXmlNode(); - ElementDescriptor grandDesc = - uiGrandParent == null ? null : uiGrandParent.getDescriptor(); - - if (xmlGrandParent != null && - !(xmlGrandParent instanceof Document) && - grandDesc != null && - grandDesc.acceptChild(nodeDesc)) { - // This node is the last node of its parent. - // If neither the parent nor the grandparent is a document, - // then the node can be inserted right after the parent. - // The parent node must actually accept this kind of child. - if (testOnly) { - return true; - } - xmlGrandParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlParent.getNextSibling()); - outSelectXmlNode[0] = xmlNode; - } - } - - return false; - } - - //--------------------- - - /** - * Adds a new element of the given descriptor's type to the given UI parent node. - * - * This actually creates the corresponding XML node in the XML model, which in turn - * will refresh the current tree view. - * - * @param uiParent An existing UI node or null to add to the tree root - * @param uiSibling An existing UI node to insert right before. Can be null. - * @param descriptor The descriptor of the element to add - * @param updateLayout True if layout attributes should be set - * @return The {@link UiElementNode} that has been added to the UI tree. - */ - private UiElementNode addNewTreeElement(UiElementNode uiParent, - UiElementNode uiSibling, - ElementDescriptor descriptor, - final boolean updateLayout) { - commitPendingXmlChanges(); - - List<UiElementNode> uiChildren = uiParent.getUiChildren(); - int n = uiChildren.size(); - - // The default is to append at the end of the list. - int index = n; - - if (uiSibling != null) { - // Try to find the requested sibling. - index = uiChildren.indexOf(uiSibling); - if (index < 0) { - // This sibling didn't exist. Should not happen but compensate - // by simply adding to the end of the list. - uiSibling = null; - index = n; - } - } - - if (uiSibling == null) { - // If we don't require any specific position, make sure to insert before the - // first mandatory_last descriptor's position, if any. - - for (int i = 0; i < n; i++) { - UiElementNode uiChild = uiChildren.get(i); - if (uiChild.getDescriptor().getMandatory() == Mandatory.MANDATORY_LAST) { - index = i; - break; - } - } - } - - final UiElementNode uiNew = uiParent.insertNewUiChild(index, descriptor); - UiElementNode rootNode = getRootNode(); - - rootNode.getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - DescriptorsUtils.setDefaultLayoutAttributes(uiNew, updateLayout); - uiNew.createXmlNode(); - } - }); - return uiNew; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java deleted file mode 100644 index 2aa56a826..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart; -import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.ui.forms.IDetailsPage; -import org.eclipse.ui.forms.IFormPart; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.events.ExpansionEvent; -import org.eclipse.ui.forms.events.IExpansionListener; -import org.eclipse.ui.forms.widgets.FormText; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.Section; -import org.eclipse.ui.forms.widgets.SharedScrolledComposite; -import org.eclipse.ui.forms.widgets.TableWrapData; -import org.eclipse.ui.forms.widgets.TableWrapLayout; - -import java.util.Collection; -import java.util.HashSet; - -/** - * Details page for the {@link UiElementNode} nodes in the tree view. - * <p/> - * See IDetailsBase for more details. - */ -class UiElementDetail implements IDetailsPage { - - /** The master-detail part, composed of a main tree and an auxiliary detail part */ - private ManifestSectionPart mMasterPart; - - private Section mMasterSection; - private UiElementNode mCurrentUiElementNode; - private Composite mCurrentTable; - private boolean mIsDirty; - - private IManagedForm mManagedForm; - - private final UiTreeBlock mTree; - - public UiElementDetail(UiTreeBlock tree) { - mTree = tree; - mMasterPart = mTree.getMasterPart(); - mManagedForm = mMasterPart.getManagedForm(); - } - - /* (non-java doc) - * Initializes the part. - */ - @Override - public void initialize(IManagedForm form) { - mManagedForm = form; - } - - /* (non-java doc) - * Creates the contents of the page in the provided parent. - */ - @Override - public void createContents(Composite parent) { - mMasterSection = createMasterSection(parent); - } - - /* (non-java doc) - * Called when the provided part has changed selection state. - * <p/> - * Only reply when our master part originates the selection. - */ - @Override - public void selectionChanged(IFormPart part, ISelection selection) { - if (part == mMasterPart && - !selection.isEmpty() && - selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - - Object first = tree_selection.getFirstElement(); - if (first instanceof UiElementNode) { - UiElementNode ui_node = (UiElementNode) first; - createUiAttributeControls(mManagedForm, ui_node); - } - } - } - - /* (non-java doc) - * Instructs it to commit the new (modified) data back into the model. - */ - @Override - public void commit(boolean onSave) { - - mTree.getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - try { - if (mCurrentUiElementNode != null) { - mCurrentUiElementNode.commit(); - } - - // Finally reset the dirty flag if everything was saved properly - mIsDirty = false; - } catch (Exception e) { - AdtPlugin.log(e, "Detail node failed to commit XML attribute!"); //$NON-NLS-1$ - } - } - }); - } - - @Override - public void dispose() { - // pass - } - - - /* (non-java doc) - * Returns true if the part has been modified with respect to the data - * loaded from the model. - */ - @Override - public boolean isDirty() { - if (mCurrentUiElementNode != null && mCurrentUiElementNode.isDirty()) { - markDirty(); - } - return mIsDirty; - } - - @Override - public boolean isStale() { - // pass - return false; - } - - /** - * Called by the master part when the tree is refreshed after the framework resources - * have been reloaded. - */ - @Override - public void refresh() { - if (mCurrentTable != null) { - mCurrentTable.dispose(); - mCurrentTable = null; - } - mCurrentUiElementNode = null; - mMasterSection.getParent().pack(true /* changed */); - } - - @Override - public void setFocus() { - // pass - } - - @Override - public boolean setFormInput(Object input) { - // pass - return false; - } - - /** - * Creates a TableWrapLayout in the DetailsPage, which in turns contains a Section. - * - * All the UI should be created in a layout which parent is the mSection itself. - * The hierarchy is: - * <pre> - * DetailPage - * + TableWrapLayout - * + Section (with title/description && fill_grab horizontal) - * + TableWrapLayout [*] - * + Labels/Forms/etc... [*] - * </pre> - * Both items marked with [*] are created by the derived classes to fit their needs. - * - * @param parent Parent of the mSection (from createContents) - * @return The new Section - */ - private Section createMasterSection(Composite parent) { - TableWrapLayout layout = new TableWrapLayout(); - layout.topMargin = 0; - parent.setLayout(layout); - - FormToolkit toolkit = mManagedForm.getToolkit(); - Section section = toolkit.createSection(parent, Section.TITLE_BAR); - section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP)); - return section; - } - - /** - * Create the ui attribute controls to edit the attributes for the given - * ElementDescriptor. - * <p/> - * This is called by the constructor. - * Derived classes can override this if necessary. - * - * @param managedForm The managed form - */ - private void createUiAttributeControls( - final IManagedForm managedForm, - final UiElementNode ui_node) { - - final ElementDescriptor elem_desc = ui_node.getDescriptor(); - mMasterSection.setText(String.format("Attributes for %1$s", ui_node.getShortDescription())); - - if (mCurrentUiElementNode != ui_node) { - // Before changing the table, commit all dirty state. - if (mIsDirty) { - commit(false); - } - if (mCurrentTable != null) { - mCurrentTable.dispose(); - mCurrentTable = null; - } - - // To iterate over all attributes, we use the {@link ElementDescriptor} instead - // of the {@link UiElementNode} because the attributes order is guaranteed in the - // descriptor but not in the node itself. - AttributeDescriptor[] attr_desc_list = ui_node.getAttributeDescriptors(); - - // If the attribute list contains at least one SeparatorAttributeDescriptor, - // sub-sections will be used. This needs to be known early as it influences the - // creation of the master table. - boolean useSubsections = false; - for (AttributeDescriptor attr_desc : attr_desc_list) { - if (attr_desc instanceof SeparatorAttributeDescriptor) { - // Sub-sections will be used. The default sections should no longer be - useSubsections = true; - break; - } - } - - FormToolkit toolkit = managedForm.getToolkit(); - Composite masterTable = SectionHelper.createTableLayout(mMasterSection, - toolkit, useSubsections ? 1 : 2 /* numColumns */); - mCurrentTable = masterTable; - - mCurrentUiElementNode = ui_node; - - if (elem_desc.getTooltip() != null) { - String tooltip; - if (Sdk.getCurrent() != null && - Sdk.getCurrent().getDocumentationBaseUrl() != null) { - tooltip = DescriptorsUtils.formatFormText(elem_desc.getTooltip(), - elem_desc, - Sdk.getCurrent().getDocumentationBaseUrl()); - } else { - tooltip = elem_desc.getTooltip(); - } - - try { - FormText text = SectionHelper.createFormText(masterTable, toolkit, - true /* isHtml */, tooltip, true /* setupLayoutData */); - text.addHyperlinkListener(mTree.getEditor().createHyperlinkListener()); - Image icon = elem_desc.getCustomizedIcon(); - if (icon != null) { - text.setImage(DescriptorsUtils.IMAGE_KEY, icon); - } - } catch(Exception e) { - // The FormText parser is really really basic and will fail as soon as the - // HTML javadoc is ever so slightly malformatted. - AdtPlugin.log(e, - "Malformed javadoc, rejected by FormText for node %1$s: '%2$s'", //$NON-NLS-1$ - ui_node.getDescriptor().getXmlName(), - tooltip); - - // Fallback to a pure text tooltip, no fancy HTML - tooltip = DescriptorsUtils.formatTooltip(elem_desc.getTooltip()); - SectionHelper.createLabel(masterTable, toolkit, tooltip, tooltip); - } - } - - Composite table = useSubsections ? null : masterTable; - - for (AttributeDescriptor attr_desc : attr_desc_list) { - if (attr_desc instanceof XmlnsAttributeDescriptor) { - // Do not show hidden attributes - continue; - } else if (table == null || attr_desc instanceof SeparatorAttributeDescriptor) { - String title = null; - if (attr_desc instanceof SeparatorAttributeDescriptor) { - // xmlName is actually the label of the separator - title = attr_desc.getXmlLocalName(); - } else { - title = String.format("Attributes from %1$s", elem_desc.getUiName()); - } - - table = createSubSectionTable(toolkit, masterTable, title); - if (attr_desc instanceof SeparatorAttributeDescriptor) { - continue; - } - } - - UiAttributeNode ui_attr = ui_node.findUiAttribute(attr_desc); - - if (ui_attr != null) { - ui_attr.createUiControl(table, managedForm); - - if (ui_attr.getCurrentValue() != null && - ui_attr.getCurrentValue().length() > 0) { - ((Section) table.getParent()).setExpanded(true); - } - } else { - // The XML has an extra unknown attribute. - // This is not expected to happen so it is ignored. - AdtPlugin.log(IStatus.INFO, - "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ - attr_desc.getXmlLocalName(), - ui_node.getDescriptor().getXmlName()); - } - } - - // Create a sub-section for the unknown attributes. - // It is initially hidden till there are some attributes to show here. - final Composite unknownTable = createSubSectionTable(toolkit, masterTable, - "Unknown XML Attributes"); - unknownTable.getParent().setVisible(false); // set section to not visible - final HashSet<UiAttributeNode> reference = new HashSet<UiAttributeNode>(); - - final IUiUpdateListener updateListener = new IUiUpdateListener() { - @Override - public void uiElementNodeUpdated(UiElementNode uiNode, UiUpdateState state) { - if (state == UiUpdateState.ATTR_UPDATED) { - updateUnknownAttributesSection(uiNode, unknownTable, managedForm, - reference); - } - } - }; - ui_node.addUpdateListener(updateListener); - - // remove the listener when the UI is disposed - unknownTable.addDisposeListener(new DisposeListener() { - @Override - public void widgetDisposed(DisposeEvent e) { - ui_node.removeUpdateListener(updateListener); - } - }); - - updateUnknownAttributesSection(ui_node, unknownTable, managedForm, reference); - - mMasterSection.getParent().pack(true /* changed */); - } - } - - /** - * Create a sub Section and its embedding wrapper table with 2 columns. - * @return The table, child of a new section. - */ - private Composite createSubSectionTable(FormToolkit toolkit, - Composite masterTable, String title) { - - // The Section composite seems to ignore colspan when assigned a TableWrapData so - // if the parent is a table with more than one column an extra table with one column - // is inserted to respect colspan. - int parentNumCol = ((TableWrapLayout) masterTable.getLayout()).numColumns; - if (parentNumCol > 1) { - masterTable = SectionHelper.createTableLayout(masterTable, toolkit, 1); - TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB); - twd.maxWidth = AndroidXmlEditor.TEXT_WIDTH_HINT; - twd.colspan = parentNumCol; - masterTable.setLayoutData(twd); - } - - Composite table; - Section section = toolkit.createSection(masterTable, - Section.TITLE_BAR | Section.TWISTIE); - - // Add an expansion listener that will trigger a reflow on the parent - // ScrolledPageBook (which is actually a SharedScrolledComposite). This will - // recompute the correct size and adjust the scrollbar as needed. - section.addExpansionListener(new IExpansionListener() { - @Override - public void expansionStateChanged(ExpansionEvent e) { - reflowMasterSection(); - } - - @Override - public void expansionStateChanging(ExpansionEvent e) { - // pass - } - }); - - section.setText(title); - section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, - TableWrapData.TOP)); - table = SectionHelper.createTableLayout(section, toolkit, 2 /* numColumns */); - return table; - } - - /** - * Reflow the parent ScrolledPageBook (which is actually a SharedScrolledComposite). - * This will recompute the correct size and adjust the scrollbar as needed. - */ - private void reflowMasterSection() { - for(Composite c = mMasterSection; c != null; c = c.getParent()) { - if (c instanceof SharedScrolledComposite) { - ((SharedScrolledComposite) c).reflow(true /* flushCache */); - break; - } - } - } - - /** - * Updates the unknown attributes section for the UI Node. - */ - private void updateUnknownAttributesSection(UiElementNode ui_node, - final Composite unknownTable, final IManagedForm managedForm, - HashSet<UiAttributeNode> reference) { - Collection<UiAttributeNode> ui_attrs = ui_node.getUnknownUiAttributes(); - Section section = ((Section) unknownTable.getParent()); - boolean needs_reflow = false; - - // The table was created hidden, show it if there are unknown attributes now - if (ui_attrs.size() > 0 && !section.isVisible()) { - section.setVisible(true); - needs_reflow = true; - } - - // Compare the new attribute set with the old "reference" one - boolean has_differences = ui_attrs.size() != reference.size(); - if (!has_differences) { - for (UiAttributeNode ui_attr : ui_attrs) { - if (!reference.contains(ui_attr)) { - has_differences = true; - break; - } - } - } - - if (has_differences) { - needs_reflow = true; - reference.clear(); - - // Remove all children of the table - for (Control c : unknownTable.getChildren()) { - c.dispose(); - } - - // Recreate all attributes UI - for (UiAttributeNode ui_attr : ui_attrs) { - reference.add(ui_attr); - ui_attr.createUiControl(unknownTable, managedForm); - - if (ui_attr.getCurrentValue() != null && ui_attr.getCurrentValue().length() > 0) { - section.setExpanded(true); - } - } - } - - if (needs_reflow) { - reflowMasterSection(); - } - } - - /** - * Marks the part dirty. Called as a result of user interaction with the widgets in the - * section. - */ - private void markDirty() { - if (!mIsDirty) { - mIsDirty = true; - mManagedForm.dirtyStateChanged(); - } - } -} - - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeContentProvider.java deleted file mode 100644 index 14049cf86..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeContentProvider.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -import java.util.ArrayList; - -/** - * UiModelTreeContentProvider is a trivial implementation of {@link ITreeContentProvider} - * where elements are expected to be instances of {@link UiElementNode}. - */ -class UiModelTreeContentProvider implements ITreeContentProvider { - - /** The 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 ElementDescriptor[] mDescriptorFilters; - /** The uiRootNode of the model. */ - private final UiElementNode mUiRootNode; - - public UiModelTreeContentProvider(UiElementNode uiRootNode, - ElementDescriptor[] descriptorFilters) { - mUiRootNode = uiRootNode; - mDescriptorFilters = descriptorFilters; - } - - /* (non-java doc) - * Returns all the UI node children of the given element or null if not the right kind - * of object. */ - @Override - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof UiElementNode) { - UiElementNode node = (UiElementNode) parentElement; - return node.getUiChildren().toArray(); - } - return null; - } - - /* (non-java doc) - * Returns the parent of a given UI node or null if it's a root node or it's not the - * right kind of node. */ - @Override - public Object getParent(Object element) { - if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - return node.getUiParent(); - } - return null; - } - - /* (non-java doc) - * Returns true if the UI node has any UI children nodes. */ - @Override - public boolean hasChildren(Object element) { - if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - return node.getUiChildren().size() > 0; - } - return false; - } - - /* (non-java doc) - * Get root elements for the tree. These are all the UI nodes that - * match the filter descriptor in the current root node. - * <p/> - * Although not documented, it seems this method should not return null. - * At worse, it should return new Object[0]. - * <p/> - * inputElement is not currently used. The root node and the filter are given - * by the enclosing class. - */ - @Override - public Object[] getElements(Object inputElement) { - ArrayList<UiElementNode> roots = new ArrayList<UiElementNode>(); - if (mUiRootNode != null) { - for (UiElementNode ui_node : mUiRootNode.getUiChildren()) { - if (mDescriptorFilters == null || mDescriptorFilters.length == 0) { - roots.add(ui_node); - } else { - for (ElementDescriptor filter : mDescriptorFilters) { - if (ui_node.getDescriptor() == filter) { - roots.add(ui_node); - } - } - } - } - } - - return roots.toArray(); - } - - @Override - public void dispose() { - // pass - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java deleted file mode 100644 index 337319761..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.swt.graphics.Image; - -/** - * UiModelTreeLabelProvider is a trivial implementation of {@link ILabelProvider} - * where elements are expected to derive from {@link UiElementNode} or - * from {@link ElementDescriptor}. - * - * It is used by both the master tree viewer and by the list in the Add... selection dialog. - */ -public class UiModelTreeLabelProvider implements ILabelProvider { - - public UiModelTreeLabelProvider() { - } - - /** - * Returns the element's logo with a fallback on the android logo. - */ - @Override - public Image getImage(Object element) { - ElementDescriptor desc = null; - UiElementNode node = null; - - if (element instanceof ElementDescriptor) { - desc = (ElementDescriptor) element; - } else if (element instanceof UiElementNode) { - node = (UiElementNode) element; - desc = node.getDescriptor(); - } - - if (desc != null) { - Image img = desc.getCustomizedIcon(); - if (img != null) { - if (node != null && node.hasError()) { - return IconFactory.getInstance().addErrorIcon(img); - } else { - return img; - } - } - } - - return AdtPlugin.getAndroidLogo(); - } - - /** - * Uses UiElementNode.shortDescription for the label for this tree item. - */ - @Override - public String getText(Object element) { - if (element instanceof ElementDescriptor) { - ElementDescriptor desc = (ElementDescriptor) element; - return desc.getUiName(); - } else if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - return node.getShortDescription(); - } - return element.toString(); - } - - @Override - public void addListener(ILabelProviderListener listener) { - // pass - } - - @Override - public void dispose() { - // pass - } - - @Override - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - @Override - public void removeListener(ILabelProviderListener listener) { - // pass - } -} - - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java deleted file mode 100644 index d11b8a4c6..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart; -import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; - -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.IMenuListener; -import org.eclipse.jface.action.IMenuManager; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.action.Separator; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.TreeSelection; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.ui.forms.DetailsPart; -import org.eclipse.ui.forms.IDetailsPage; -import org.eclipse.ui.forms.IDetailsPageProvider; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.MasterDetailsBlock; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.Section; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; - -/** - * {@link UiTreeBlock} is a {@link MasterDetailsBlock} which displays a tree view for - * a specific set of {@link UiElementNode}. - * <p/> - * 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. - * <p/> - * 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. - */ -public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml { - - /** Height hint for the tree view. Helps the grid layout resize properly on smaller screens. */ - private static final int TREE_HEIGHT_HINT = 50; - - /** Container editor */ - AndroidXmlEditor mEditor; - /** The 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 UiElementNode mUiRootNode; - /** The 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. Can be null or empty to mean everything - * can be displayed. */ - private ElementDescriptor[] mDescriptorFilters; - /** The title for the master-detail part (displayed on the top "tab" on top of the tree) */ - private String mTitle; - /** The description for the master-detail part (displayed on top of the tree view) */ - private String mDescription; - /** The master-detail part, composed of a main tree and an auxiliary detail part */ - private ManifestSectionPart mMasterPart; - /** The tree viewer in the master-detail part */ - private TreeViewer mTreeViewer; - /** The "add" button for the tree view */ - private Button mAddButton; - /** The "remove" button for the tree view */ - private Button mRemoveButton; - /** The "up" button for the tree view */ - private Button mUpButton; - /** The "down" button for the tree view */ - private Button mDownButton; - /** The Managed Form used to create the master part */ - private IManagedForm mManagedForm; - /** Reference to the details part of the tree master block. */ - private DetailsPart mDetailsPart; - /** Reference to the clipboard for copy-paste */ - private Clipboard mClipboard; - /** Listener to refresh the tree viewer when the parent's node has been updated */ - private IUiUpdateListener mUiRefreshListener; - /** Listener to enable/disable the UI based on the application node's presence */ - private IUiUpdateListener mUiEnableListener; - /** An adapter/wrapper to use the add/remove/up/down tree edit actions. */ - private UiTreeActions mUiTreeActions; - /** - * True 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. - */ - private final boolean mAutoCreateRoot; - - - /** - * Creates a new {@link MasterDetailsBlock} that will display all UI nodes matching the - * given filter in the given root node. - * - * @param editor The parent manifest editor. - * @param uiRootNode The 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 or the - * application UI node. This cannot be null. - * @param autoCreateRoot True 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. - * @param descriptorFilters A list of descriptors of the elements to be displayed as root in - * this tree view. Use null or an empty list to accept any kind of node. - * @param title Title for the section - * @param description Description for the section - */ - public UiTreeBlock(AndroidXmlEditor editor, - UiElementNode uiRootNode, - boolean autoCreateRoot, - ElementDescriptor[] descriptorFilters, - String title, - String description) { - mEditor = editor; - mUiRootNode = uiRootNode; - mAutoCreateRoot = autoCreateRoot; - mDescriptorFilters = descriptorFilters; - mTitle = title; - mDescription = description; - } - - /** @returns The container editor */ - AndroidXmlEditor getEditor() { - return mEditor; - } - - /** @returns The reference to the clipboard for copy-paste */ - Clipboard getClipboard() { - return mClipboard; - } - - /** @returns The master-detail part, composed of a main tree and an auxiliary detail part */ - ManifestSectionPart getMasterPart() { - return mMasterPart; - } - - /** - * Returns the {@link UiElementNode} for the current model. - * <p/> - * This is used by the content provider attached to {@link #mTreeViewer} since - * the uiRootNode changes after each call to - * {@link #changeRootAndDescriptors(UiElementNode, ElementDescriptor[], boolean)}. - */ - public UiElementNode getRootNode() { - return mUiRootNode; - } - - @Override - protected void createMasterPart(final IManagedForm managedForm, 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(Section section, 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*/); - } - - /** - * Creates the tree and its viewer - * @return The tree control - */ - private Tree createTreeViewer(FormToolkit toolkit, Composite grid, - final IManagedForm managedForm) { - // 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 = AndroidXmlEditor.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() { - @Override - 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() { - @Override - public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { - mTreeViewer.refresh(); - } - }; - - mUiEnableListener = new IUiUpdateListener() { - @Override - 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 TargetChangeListener() { - @Override - public IProject getProject() { - if (mEditor != null) { - return mEditor.getProject(); - } - - return null; - } - - @Override - public void reload() { - // 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() { - @Override - public void widgetDisposed(DisposeEvent e) { - if (mUiRootNode != null) { - UiElementNode node = mUiRootNode.getUiParent() != null ? - mUiRootNode.getUiParent() : - mUiRootNode; - - if (node != null) { - 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; - } - - /** - * Changes the UI root node and the descriptor filters of the tree. - * <p/> - * This removes the listeners attached to the old root node and reattaches them to the - * new one. - * - * @param uiRootNode The 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 or the - * application UI node. This cannot be null. - * @param descriptorFilters A list of descriptors of the elements to be displayed as root in - * this tree view. Use null or an empty list to accept any kind of node. - * @param forceRefresh If tree, forces the tree to refresh - */ - public void changeRootAndDescriptors(UiElementNode uiRootNode, - ElementDescriptor[] descriptorFilters, boolean forceRefresh) { - 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. - if (mUiRootNode != null) { - node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode; - - if (node != null) { - 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()); - } - - /** - * Creates the buttons next to the tree. - */ - private void createButtons(FormToolkit toolkit, Composite grid) { - - 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); - } - - private void createTreeContextMenu(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. - */ - @Override - 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); - } - - /** - * Adds the menu actions to the context menu when the given UI node is selected in - * the tree view. - * - * @param manager The context menu manager - * @param selected The UI nodes selected in the tree. Can be null, in which case the root - * is to be modified. - */ - private void doCreateMenuAction(IMenuManager manager, ArrayList<UiElementNode> selected) { - 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. - 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(); - } - }); - } - } - - - /** - * 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. - * - * @param selection The current tree selection (same as mTreeViewer.getSelection()) - */ - private void adjustTreeButtons(ISelection selection) { - mRemoveButton.setEnabled(!selection.isEmpty() && selection instanceof ITreeSelection); - mUpButton.setEnabled(canDoTreeUp(selection)); - mDownButton.setEnabled(canDoTreeDown(selection)); - } - - /** - * An adapter/wrapper to use the add/remove/up/down tree edit actions. - */ - private class UiTreeActions extends UiActions { - @Override - protected UiElementNode getRootNode() { - return mUiRootNode; - } - - @Override - protected void selectUiNode(UiElementNode uiNodeToSelect) { - // Select the new item - if (uiNodeToSelect != null) { - LinkedList<UiElementNode> segments = new LinkedList<UiElementNode>(); - for (UiElementNode ui_node = uiNodeToSelect; ui_node != mUiRootNode; - ui_node = ui_node.getUiParent()) { - segments.add(0, ui_node); - } - if (segments.size() > 0) { - mTreeViewer.setSelection(new TreeSelection(new TreePath(segments.toArray()))); - } else { - mTreeViewer.setSelection(null); - } - } - } - - @Override - public void commitPendingXmlChanges() { - commitManagedForm(); - } - } - - /** - * Filters an ITreeSelection to only keep the {@link UiElementNode}s (in case there's - * something else in there). - * - * @return A new list of {@link UiElementNode} with at least one item or null. - */ - private ArrayList<UiElementNode> filterSelection(ITreeSelection selection) { - ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>(); - - for (Iterator<Object> it = selection.iterator(); it.hasNext(); ) { - Object selectedObj = it.next(); - - if (selectedObj instanceof UiElementNode) { - selected.add((UiElementNode) selectedObj); - } - } - - return selected.size() > 0 ? selected : null; - } - - /** - * 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. - */ - private void doTreeAdd() { - 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()); - } - - /** - * 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. - */ - protected void doTreeRemove() { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - mUiTreeActions.doRemove(selected, mTreeViewer.getControl().getShell()); - } - } - - /** - * Called when the "Up" button is selected. - * <p/> - * If the tree has a selection, move it up, either in the child list or as the last child - * of the previous parent. - */ - protected void doTreeUp() { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - mUiTreeActions.doUp(selected, mDescriptorFilters); - } - } - - /** - * Checks whether the "up" action can be done on the current selection. - * - * @param selection The current tree selection. - * @return True if all the selected nodes can be moved up. - */ - protected boolean canDoTreeUp(ISelection selection) { - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - return mUiTreeActions.canDoUp(selected, mDescriptorFilters); - } - - return false; - } - - /** - * 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. - */ - protected void doTreeDown() { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - mUiTreeActions.doDown(selected, mDescriptorFilters); - } - } - - /** - * Checks whether the "down" action can be done on the current selection. - * - * @param selection The current tree selection. - * @return True if all the selected nodes can be moved down. - */ - protected boolean canDoTreeDown(ISelection selection) { - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - return mUiTreeActions.canDoDown(selected, mDescriptorFilters); - } - - return false; - } - - /** - * Commits the current managed form (the one associated with our master part). - * As a side effect, this will commit the current UiElementDetails page. - */ - void commitManagedForm() { - if (mManagedForm != null) { - mManagedForm.commit(false /* onSave */); - } - } - - /* Implements ICommitXml for CopyCutAction */ - @Override - public void commitPendingXmlChanges() { - commitManagedForm(); - } - - @Override - protected void createToolBarActions(IManagedForm managedForm) { - // Pass. Not used, toolbar actions are defined by createSectionActions(). - } - - @Override - protected void registerPages(DetailsPart inDetailsPart) { - // Keep a reference on the details part (the super class doesn't provide a getter - // for it.) - mDetailsPart = inDetailsPart; - - // 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. - inDetailsPart.setPageLimit(5); - - final UiTreeBlock tree = this; - - inDetailsPart.setPageProvider(new IDetailsPageProvider() { - @Override - public IDetailsPage getPage(Object key) { - if (key instanceof UiElementNode) { - return new UiElementDetail(tree); - } - return null; - } - - @Override - public Object getPageKey(Object object) { - return object; // use node object as key - } - }); - } - - /** - * An alphabetic sort action for the tree viewer. - */ - private class TreeSortAction extends Action { - - private ViewerComparator mComparator; - - public TreeSortAction() { - super("Sorts elements alphabetically.", AS_CHECK_BOX); - setImageDescriptor(IconFactory.getInstance().getImageDescriptor("az_sort")); //$NON-NLS-1$ - - if (mTreeViewer != null) { - boolean is_sorted = mTreeViewer.getComparator() != null; - setChecked(is_sorted); - } - } - - /** - * Called when the button is selected. Toggles the tree viewer comparator. - */ - @Override - public void run() { - if (mTreeViewer == null) { - notifyResult(false /*success*/); - return; - } - - ViewerComparator comp = mTreeViewer.getComparator(); - if (comp != null) { - // Tree is currently sorted. - // Save currently comparator and remove it - mComparator = comp; - mTreeViewer.setComparator(null); - } else { - // Tree is not currently sorted. - // Reuse or add a new comparator. - if (mComparator == null) { - mComparator = new ViewerComparator(); - } - mTreeViewer.setComparator(mComparator); - } - - notifyResult(true /*success*/); - } - } - - /** - * A filter on descriptor for the tree viewer. - * <p/> - * The tree viewer will contain many of these actions and only one can be enabled at a - * given time. When no action is selected, everything is displayed. - * <p/> - * Since "radio"-like actions do not allow for unselecting all of them, we manually - * handle the exclusive radio button-like property: when an action is selected, it manually - * removes all other actions as needed. - */ - private class DescriptorFilterAction extends Action { - - private final ElementDescriptor mDescriptor; - private ViewerFilter mFilter; - - public DescriptorFilterAction(ElementDescriptor descriptor) { - super(String.format("Displays only %1$s elements.", descriptor.getUiName()), - AS_CHECK_BOX); - - mDescriptor = descriptor; - setImageDescriptor(descriptor.getImageDescriptor()); - } - - /** - * Called when the button is selected. - * <p/> - * Find any existing {@link DescriptorFilter}s and remove them. Install ours. - */ - @Override - public void run() { - super.run(); - - if (isChecked()) { - if (mFilter == null) { - // create filter when required - mFilter = new DescriptorFilter(this); - } - - // we add our filter first, otherwise the UI might show the full list - mTreeViewer.addFilter(mFilter); - - // Then remove the any other filters except ours. There should be at most - // one other filter, since that's how the actions are made to look like - // exclusive radio buttons. - for (ViewerFilter filter : mTreeViewer.getFilters()) { - if (filter instanceof DescriptorFilter && filter != mFilter) { - DescriptorFilterAction action = ((DescriptorFilter) filter).getAction(); - action.setChecked(false); - mTreeViewer.removeFilter(filter); - } - } - } else if (mFilter != null){ - mTreeViewer.removeFilter(mFilter); - } - } - - /** - * Filters the tree viewer for the given descriptor. - * <p/> - * The filter is linked to the action so that an action can iterate through the list - * of filters and un-select the actions. - */ - private class DescriptorFilter extends ViewerFilter { - - private final DescriptorFilterAction mAction; - - public DescriptorFilter(DescriptorFilterAction action) { - mAction = action; - } - - public DescriptorFilterAction getAction() { - return mAction; - } - - /** - * Returns true if an element should be displayed, that if the element or - * any of its parent matches the requested descriptor. - */ - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - while (element instanceof UiElementNode) { - UiElementNode uiNode = (UiElementNode)element; - if (uiNode.getDescriptor() == mDescriptor) { - return true; - } - element = uiNode.getUiParent(); - } - return false; - } - } - } - -} |