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