Home | History | Annotate | Download | only in properties
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.ide.eclipse.adt.internal.editors.layout.properties;
     17 
     18 import com.android.ide.eclipse.adt.AdtPlugin;
     19 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     20 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
     21 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
     22 import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory.SortingMode;
     23 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
     24 import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener;
     25 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
     26 
     27 import org.eclipse.jface.action.Action;
     28 import org.eclipse.jface.action.IAction;
     29 import org.eclipse.jface.action.IMenuListener;
     30 import org.eclipse.jface.action.IMenuManager;
     31 import org.eclipse.jface.action.IStatusLineManager;
     32 import org.eclipse.jface.action.IToolBarManager;
     33 import org.eclipse.jface.action.MenuManager;
     34 import org.eclipse.jface.action.Separator;
     35 import org.eclipse.jface.resource.ImageDescriptor;
     36 import org.eclipse.jface.viewers.ISelection;
     37 import org.eclipse.jface.viewers.ISelectionChangedListener;
     38 import org.eclipse.jface.viewers.SelectionChangedEvent;
     39 import org.eclipse.jface.viewers.StructuredSelection;
     40 import org.eclipse.jface.viewers.TreeSelection;
     41 import org.eclipse.swt.SWT;
     42 import org.eclipse.swt.widgets.Composite;
     43 import org.eclipse.swt.widgets.Control;
     44 import org.eclipse.swt.widgets.MenuItem;
     45 import org.eclipse.ui.ISharedImages;
     46 import org.eclipse.ui.IWorkbenchPart;
     47 import org.eclipse.ui.PlatformUI;
     48 import org.eclipse.ui.part.Page;
     49 import org.eclipse.ui.views.properties.IPropertySheetPage;
     50 import org.eclipse.wb.internal.core.editor.structure.IPage;
     51 import org.eclipse.wb.internal.core.model.property.Property;
     52 import org.eclipse.wb.internal.core.model.property.table.IPropertyExceptionHandler;
     53 import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
     54 
     55 import java.util.ArrayList;
     56 import java.util.Arrays;
     57 import java.util.Collections;
     58 import java.util.Iterator;
     59 import java.util.List;
     60 
     61 /**
     62  * Property sheet page used when the graphical layout editor is chosen
     63  */
     64 public class PropertySheetPage extends Page
     65         implements IPropertySheetPage, IUiUpdateListener, IPage {
     66     private PropertyTable mPropertyTable;
     67     private final GraphicalEditorPart mEditor;
     68     private Property mActiveProperty;
     69     private Action mDefaultValueAction;
     70     private Action mShowAdvancedPropertiesAction;
     71     private Action mSortAlphaAction;
     72     private Action mCollapseAll;
     73     private Action mExpandAll;
     74     private List<CanvasViewInfo> mSelection;
     75 
     76     private static final String EXPAND_DISABLED_ICON = "expandall-disabled";          //$NON-NLS-1$
     77     private static final String EXPAND_ICON = "expandall";                            //$NON-NLS-1$
     78     private static final String DEFAULT_ICON = "properties_default";                  //$NON-NLS-1$
     79     private static final String ADVANCED_ICON = "filter_advanced_properties";         //$NON-NLS-1$
     80     private static final String ALPHA_ICON = "sort_alpha";                            //$NON-NLS-1$
     81     // TODO: goto-definition.png
     82 
     83     /**
     84      * Constructs a new {@link PropertySheetPage} associated with the given
     85      * editor
     86      *
     87      * @param editor the editor associated with this property sheet page
     88      */
     89     public PropertySheetPage(GraphicalEditorPart editor) {
     90         mEditor = editor;
     91     }
     92 
     93     private PropertyFactory getPropertyFactory() {
     94         return mEditor.getPropertyFactory();
     95     }
     96 
     97     @Override
     98     public void createControl(Composite parent) {
     99         assert parent != null;
    100         mPropertyTable = new PropertyTable(parent, SWT.NONE);
    101         mPropertyTable.setExceptionHandler(new IPropertyExceptionHandler() {
    102             @Override
    103             public void handle(Throwable e) {
    104                 AdtPlugin.log(e, null);
    105             }
    106         });
    107         mPropertyTable.setDefaultCollapsedNames(Arrays.asList(
    108                 "Deprecated",
    109                 "Layout Parameters",
    110                 "Layout Parameters|Margins"));
    111 
    112         createActions();
    113         setPropertyTableContextMenu();
    114     }
    115 
    116     @Override
    117     public void selectionChanged(IWorkbenchPart part, ISelection selection) {
    118         if (selection instanceof TreeSelection
    119                 && mPropertyTable != null && !mPropertyTable.isDisposed()) {
    120             TreeSelection treeSelection = (TreeSelection) selection;
    121 
    122             // We get a lot of repeated selection requests for the same selection
    123             // as before, so try to eliminate these
    124             if (mSelection != null) {
    125                 if (mSelection.isEmpty()) {
    126                     if (treeSelection.isEmpty()) {
    127                         return;
    128                     }
    129                 } else {
    130                     int selectionCount = treeSelection.size();
    131                     if (selectionCount == mSelection.size()) {
    132                         boolean same = true;
    133                         Iterator<?> iterator = treeSelection.iterator();
    134                         for (int i = 0, n = selectionCount; i < n && iterator.hasNext(); i++) {
    135                             Object next = iterator.next();
    136                             if (next instanceof CanvasViewInfo) {
    137                                 CanvasViewInfo info = (CanvasViewInfo) next;
    138                                 if (info != mSelection.get(i)) {
    139                                     same = false;
    140                                     break;
    141                                 }
    142                             } else {
    143                                 same = false;
    144                                 break;
    145                             }
    146                         }
    147                         if (same) {
    148                             return;
    149                         }
    150                     }
    151                 }
    152             }
    153 
    154             stopTrackingSelection();
    155 
    156             if (treeSelection.isEmpty()) {
    157                 mSelection = Collections.emptyList();
    158             } else {
    159                 int selectionCount = treeSelection.size();
    160                 List<CanvasViewInfo> newSelection = new ArrayList<CanvasViewInfo>(selectionCount);
    161                 Iterator<?> iterator = treeSelection.iterator();
    162                 while (iterator.hasNext()) {
    163                     Object next = iterator.next();
    164                     if (next instanceof CanvasViewInfo) {
    165                         CanvasViewInfo info = (CanvasViewInfo) next;
    166                         newSelection.add(info);
    167                     }
    168                 }
    169                 mSelection = newSelection;
    170             }
    171 
    172             startTrackingSelection();
    173 
    174             refreshProperties();
    175         }
    176     }
    177 
    178     @Override
    179     public void dispose() {
    180         stopTrackingSelection();
    181         super.dispose();
    182     }
    183 
    184     private void startTrackingSelection() {
    185         if (mSelection != null && !mSelection.isEmpty()) {
    186             for (CanvasViewInfo item : mSelection) {
    187                 UiViewElementNode node = item.getUiViewNode();
    188                 if (node != null) {
    189                     node.addUpdateListener(this);
    190                 }
    191             }
    192         }
    193     }
    194 
    195     private void stopTrackingSelection() {
    196         if (mSelection != null && !mSelection.isEmpty()) {
    197             for (CanvasViewInfo item : mSelection) {
    198                 UiViewElementNode node = item.getUiViewNode();
    199                 if (node != null) {
    200                     node.removeUpdateListener(this);
    201                 }
    202             }
    203         }
    204         mSelection = null;
    205     }
    206 
    207     // Implements IUiUpdateListener
    208     @Override
    209     public void uiElementNodeUpdated(UiElementNode node, UiUpdateState state) {
    210         refreshProperties();
    211     }
    212 
    213     @Override
    214     public Control getControl() {
    215         return mPropertyTable;
    216     }
    217 
    218     @Override
    219     public void setFocus() {
    220         mPropertyTable.setFocus();
    221     }
    222 
    223     @Override
    224     public void makeContributions(IMenuManager menuManager,
    225             IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
    226         toolBarManager.add(mShowAdvancedPropertiesAction);
    227         toolBarManager.add(new Separator());
    228         toolBarManager.add(mSortAlphaAction);
    229         toolBarManager.add(new Separator());
    230         toolBarManager.add(mDefaultValueAction);
    231         toolBarManager.add(new Separator());
    232         toolBarManager.add(mExpandAll);
    233         toolBarManager.add(mCollapseAll);
    234         toolBarManager.add(new Separator());
    235     }
    236 
    237     private void createActions() {
    238         ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
    239         IconFactory iconFactory = IconFactory.getInstance();
    240 
    241         mExpandAll = new PropertySheetAction(
    242                 IAction.AS_PUSH_BUTTON,
    243                 "Expand All",
    244                 ACTION_EXPAND,
    245                 iconFactory.getImageDescriptor(EXPAND_ICON),
    246                 iconFactory.getImageDescriptor(EXPAND_DISABLED_ICON));
    247 
    248         mCollapseAll = new PropertySheetAction(
    249                 IAction.AS_PUSH_BUTTON,
    250                 "Collapse All",
    251                 ACTION_COLLAPSE,
    252                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL),
    253                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL_DISABLED));
    254 
    255         mShowAdvancedPropertiesAction = new PropertySheetAction(
    256                 IAction.AS_CHECK_BOX,
    257                 "Show Advanced Properties",
    258                 ACTION_SHOW_ADVANCED,
    259                 iconFactory.getImageDescriptor(ADVANCED_ICON),
    260                 null);
    261 
    262         mSortAlphaAction = new PropertySheetAction(
    263                 IAction.AS_CHECK_BOX,
    264                 "Sort Alphabetically",
    265                 ACTION_SORT_ALPHA,
    266                 iconFactory.getImageDescriptor(ALPHA_ICON),
    267                 null);
    268 
    269         mDefaultValueAction = new PropertySheetAction(
    270                 IAction.AS_PUSH_BUTTON,
    271                 "Restore Default Value",
    272                 ACTION_DEFAULT_VALUE,
    273                 iconFactory.getImageDescriptor(DEFAULT_ICON),
    274                 null);
    275 
    276         // Listen on the selection in the property sheet so we can update the
    277         // Restore Default Value action
    278         ISelectionChangedListener listener = new ISelectionChangedListener() {
    279             @Override
    280             public void selectionChanged(SelectionChangedEvent event) {
    281                 StructuredSelection selection = (StructuredSelection) event.getSelection();
    282                 mActiveProperty = (Property) selection.getFirstElement();
    283                 updateDefaultValueAction();
    284             }
    285         };
    286         mPropertyTable.addSelectionChangedListener(listener);
    287     }
    288 
    289     /**
    290      * Updates the state of {@link #mDefaultValueAction}.
    291      */
    292     private void updateDefaultValueAction() {
    293         if (mActiveProperty != null) {
    294             try {
    295                 mDefaultValueAction.setEnabled(mActiveProperty.isModified());
    296             } catch (Exception e) {
    297                 AdtPlugin.log(e, null);
    298             }
    299         } else {
    300             mDefaultValueAction.setEnabled(false);
    301         }
    302     }
    303 
    304     /**
    305      * Sets the context menu for {@link #mPropertyTable}.
    306      */
    307     private void setPropertyTableContextMenu() {
    308         final MenuManager manager = new MenuManager();
    309         manager.setRemoveAllWhenShown(true);
    310         manager.addMenuListener(new IMenuListener() {
    311             @Override
    312             public void menuAboutToShow(IMenuManager m) {
    313                 // dispose items to avoid caching
    314                 for (MenuItem item : manager.getMenu().getItems()) {
    315                     item.dispose();
    316                 }
    317                 // apply new items
    318                 fillContextMenu();
    319             }
    320 
    321             private void fillContextMenu() {
    322                 manager.add(mDefaultValueAction);
    323                 manager.add(mSortAlphaAction);
    324                 manager.add(mShowAdvancedPropertiesAction);
    325             }
    326         });
    327 
    328         mPropertyTable.setMenu(manager.createContextMenu(mPropertyTable));
    329     }
    330 
    331     /**
    332      * Shows {@link Property}'s of current objects.
    333      */
    334     private void refreshProperties() {
    335         PropertyFactory factory = getPropertyFactory();
    336         mPropertyTable.setInput(factory.getProperties(mSelection));
    337         updateDefaultValueAction();
    338     }
    339 
    340     // ---- Actions ----
    341 
    342     private static final int ACTION_DEFAULT_VALUE = 1;
    343     private static final int ACTION_SHOW_ADVANCED = 2;
    344     private static final int ACTION_COLLAPSE = 3;
    345     private static final int ACTION_EXPAND = 4;
    346     private static final int ACTION_SORT_ALPHA = 5;
    347 
    348     private class PropertySheetAction extends Action {
    349         private final int mAction;
    350 
    351         private PropertySheetAction(int style, String label, int action,
    352                 ImageDescriptor imageDesc, ImageDescriptor disabledImageDesc) {
    353             super(label, style);
    354             mAction = action;
    355             setImageDescriptor(imageDesc);
    356             if (disabledImageDesc != null) {
    357                 setDisabledImageDescriptor(disabledImageDesc);
    358             }
    359             setToolTipText(label);
    360         }
    361 
    362         @Override
    363         public void run() {
    364             switch (mAction) {
    365                 case ACTION_COLLAPSE: {
    366                     mPropertyTable.collapseAll();
    367                     break;
    368                 }
    369                 case ACTION_EXPAND: {
    370                     mPropertyTable.expandAll();
    371                     break;
    372                 }
    373                 case ACTION_SHOW_ADVANCED: {
    374                     boolean show = mShowAdvancedPropertiesAction.isChecked();
    375                     mPropertyTable.setShowAdvancedProperties(show);
    376                     break;
    377                 }
    378                 case ACTION_SORT_ALPHA: {
    379                     boolean isAlphabetical = mSortAlphaAction.isChecked();
    380                     getPropertyFactory().setSortingMode(
    381                         isAlphabetical ? SortingMode.ALPHABETICAL : PropertyFactory.DEFAULT_MODE);
    382                     refreshProperties();
    383                     break;
    384                 }
    385                 case ACTION_DEFAULT_VALUE:
    386                     try {
    387                         mActiveProperty.setValue(Property.UNKNOWN_VALUE);
    388                     } catch (Exception e) {
    389                         // Ignore warnings from setters
    390                     }
    391                     break;
    392                 default:
    393                     assert false : mAction;
    394             }
    395         }
    396     }
    397 
    398     @Override
    399     public void setToolBar(IToolBarManager toolBarManager) {
    400         makeContributions(null, toolBarManager, null);
    401         toolBarManager.update(false);
    402     }
    403 }
    404