aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java394
1 files changed, 394 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
new file mode 100644
index 000000000..56b86aa85
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2012 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.layout.gle2;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
+import com.google.common.collect.Maps;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+
+import java.util.Map;
+
+/**
+ * The {@link LayoutWindowCoordinator} keeps track of Eclipse window events (opening, closing,
+ * fronting, etc) and uses this information to manage the propertysheet and outline
+ * views such that they are always(*) showing:
+ * <ul>
+ * <li> If the Property Sheet and Outline Eclipse views are showing, it does nothing.
+ * "Showing" means "is open", not necessary "is visible", e.g. in a tabbed view
+ * there could be a different view on top.
+ * <li> If just the outline is showing, then the property sheet is shown in a sashed
+ * pane below or to the right of the outline (depending on the dominant dimension
+ * of the window).
+ * <li> TBD: If just the property sheet is showing, should the outline be showed
+ * inside that window? Not yet done.
+ * <li> If the outline is *not* showing, then the outline is instead shown
+ * <b>inside</b> the editor area, in a right-docked view! This right docked view
+ * also includes the property sheet!
+ * <li> If the property sheet is not showing (which includes not showing in the outline
+ * view as well), then it will be shown inside the editor area, along with the outline
+ * which should also be there (since if the outline was showing outside the editor
+ * area, the property sheet would have docked there).
+ * <li> When the editor is maximized, then all views are temporarily hidden. In this
+ * case, the property sheet and outline will show up inside the editor.
+ * When the editor view is un-maximized, the view state will return to what it
+ * was before.
+ * </ul>
+ * </p>
+ * There is one coordinator per workbench window, shared between all editors in that window.
+ * <p>
+ * TODO: Rename this class to AdtWindowCoordinator. It is used for more than just layout
+ * window coordination now. For example, it's also used to dispatch {@code activated()} and
+ * {@code deactivated()} events to all the XML editors, to ensure that key bindings are
+ * properly dispatched to the right editors in Eclipse 4.x.
+ */
+public class LayoutWindowCoordinator implements IPartListener2 {
+ static final String PROPERTY_SHEET_PART_ID = "org.eclipse.ui.views.PropertySheet"; //$NON-NLS-1$
+ static final String OUTLINE_PART_ID = "org.eclipse.ui.views.ContentOutline"; //$NON-NLS-1$
+ /** The workbench window */
+ private final IWorkbenchWindow mWindow;
+ /** Is the Eclipse property sheet ViewPart open? */
+ private boolean mPropertiesOpen;
+ /** Is the Eclipse outline ViewPart open? */
+ private boolean mOutlineOpen;
+ /** Is the editor maximized? */
+ private boolean mEditorMaximized;
+ /**
+ * Has the coordinator been initialized? We may have to delay initialization
+ * and perform it lazily if the workbench window does not have an active
+ * page when the coordinator is first started
+ */
+ private boolean mInitialized;
+
+ /** Map from workbench windows to each layout window coordinator instance for that window */
+ private static Map<IWorkbenchWindow, LayoutWindowCoordinator> sCoordinators =
+ Maps.newHashMapWithExpectedSize(2);
+
+ /**
+ * Returns the coordinator for the given window.
+ *
+ * @param window the associated window
+ * @param create whether to create the window if it does not already exist
+ * @return the new coordinator, never null if {@code create} is true
+ */
+ @Nullable
+ public static LayoutWindowCoordinator get(@NonNull IWorkbenchWindow window, boolean create) {
+ synchronized (LayoutWindowCoordinator.class){
+ LayoutWindowCoordinator coordinator = sCoordinators.get(window);
+ if (coordinator == null && create) {
+ coordinator = new LayoutWindowCoordinator(window);
+
+ IPartService service = window.getPartService();
+ if (service != null) {
+ // What if the editor part is *already* open? How do I deal with that?
+ service.addPartListener(coordinator);
+ }
+
+ sCoordinators.put(window, coordinator);
+ }
+
+ return coordinator;
+ }
+ }
+
+
+ /** Disposes this coordinator (when a window is closed) */
+ public void dispose() {
+ IPartService service = mWindow.getPartService();
+ if (service != null) {
+ service.removePartListener(this);
+ }
+
+ synchronized (LayoutWindowCoordinator.class){
+ sCoordinators.remove(mWindow);
+ }
+ }
+
+ /**
+ * Returns true if the main editor window is maximized
+ *
+ * @return true if the main editor window is maximized
+ */
+ public boolean isEditorMaximized() {
+ return mEditorMaximized;
+ }
+
+ private LayoutWindowCoordinator(@NonNull IWorkbenchWindow window) {
+ mWindow = window;
+
+ initialize();
+ }
+
+ private void initialize() {
+ if (mInitialized) {
+ return;
+ }
+
+ IWorkbenchPage activePage = mWindow.getActivePage();
+ if (activePage == null) {
+ return;
+ }
+
+ mInitialized = true;
+
+ // Look up current state of the properties and outline windows (in case
+ // they have already been opened before we added our part listener)
+ IViewReference ref = findPropertySheetView(activePage);
+ if (ref != null) {
+ IWorkbenchPart part = ref.getPart(false /*restore*/);
+ if (activePage.isPartVisible(part)) {
+ mPropertiesOpen = true;
+ }
+ }
+ ref = findOutlineView(activePage);
+ if (ref != null) {
+ IWorkbenchPart part = ref.getPart(false /*restore*/);
+ if (activePage.isPartVisible(part)) {
+ mOutlineOpen = true;
+ }
+ }
+ if (!syncMaximizedState(activePage)) {
+ syncActive();
+ }
+ }
+
+ static IViewReference findPropertySheetView(IWorkbenchPage activePage) {
+ return activePage.findViewReference(PROPERTY_SHEET_PART_ID);
+ }
+
+ static IViewReference findOutlineView(IWorkbenchPage activePage) {
+ return activePage.findViewReference(OUTLINE_PART_ID);
+ }
+
+ /**
+ * Checks the maximized state of the page and updates internal state if
+ * necessary.
+ * <p>
+ * This is used in Eclipse 4.x, where the {@link IPartListener2} does not
+ * fire {@link IPartListener2#partHidden(IWorkbenchPartReference)} when the
+ * editor is maximized anymore (see issue
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=382120 for details).
+ * Instead, the layout editor listens for resize events, and upon resize it
+ * looks up the part state and calls this method to ensure that the right
+ * maximized state is known to the layout coordinator.
+ *
+ * @param page the active workbench page
+ * @return true if the state changed, false otherwise
+ */
+ public boolean syncMaximizedState(IWorkbenchPage page) {
+ boolean maximized = isPageZoomed(page);
+ if (mEditorMaximized != maximized) {
+ mEditorMaximized = maximized;
+ syncActive();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isPageZoomed(IWorkbenchPage page) {
+ IWorkbenchPartReference reference = page.getActivePartReference();
+ if (reference != null && reference instanceof IEditorReference) {
+ int state = page.getPartState(reference);
+ boolean maximized = (state & IWorkbenchPage.STATE_MAXIMIZED) != 0;
+ return maximized;
+ }
+
+ // If the active reference isn't the editor, then the editor can't be maximized
+ return false;
+ }
+
+ /**
+ * Syncs the given editor's view state such that the property sheet and or
+ * outline are shown or hidden according to the visibility of the global
+ * outline and property sheet views.
+ * <p>
+ * This is typically done when a layout editor is fronted. For view updates
+ * when the view is already showing, the {@link LayoutWindowCoordinator}
+ * will automatically handle the current fronted window.
+ *
+ * @param editor the editor to sync
+ */
+ private void sync(@Nullable GraphicalEditorPart editor) {
+ if (editor == null) {
+ return;
+ }
+ if (mEditorMaximized) {
+ editor.showStructureViews(true /*outline*/, true /*properties*/, true /*layout*/);
+ } else if (mOutlineOpen) {
+ editor.showStructureViews(false /*outline*/, false /*properties*/, true /*layout*/);
+ editor.getCanvasControl().getOutlinePage().setShowPropertySheet(!mPropertiesOpen);
+ } else {
+ editor.showStructureViews(true /*outline*/, !mPropertiesOpen /*properties*/,
+ true /*layout*/);
+ }
+ }
+
+ private void sync(IWorkbenchPart part) {
+ if (part instanceof AndroidXmlEditor) {
+ LayoutEditorDelegate editor = LayoutEditorDelegate.fromEditor((IEditorPart) part);
+ if (editor != null) {
+ sync(editor.getGraphicalEditor());
+ }
+ }
+ }
+
+ private void syncActive() {
+ IWorkbenchPage activePage = mWindow.getActivePage();
+ if (activePage != null) {
+ IEditorPart editor = activePage.getActiveEditor();
+ sync(editor);
+ }
+ }
+
+ private void propertySheetClosed() {
+ mPropertiesOpen = false;
+ syncActive();
+ }
+
+ private void propertySheetOpened() {
+ mPropertiesOpen = true;
+ syncActive();
+ }
+
+ private void outlineClosed() {
+ mOutlineOpen = false;
+ syncActive();
+ }
+
+ private void outlineOpened() {
+ mOutlineOpen = true;
+ syncActive();
+ }
+
+ // ---- Implements IPartListener2 ----
+
+ @Override
+ public void partOpened(IWorkbenchPartReference partRef) {
+ // We ignore partOpened() and partClosed() because these methods are only
+ // called when a view is opened in the first perspective, and closed in the
+ // last perspective. The outline is typically used in multiple perspectives,
+ // so closing it in the Java perspective does *not* fire a partClosed event.
+ // There is no notification for "part closed in perspective" (see issue
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=54559 for details).
+ // However, the workaround we can use is to listen to partVisible() and
+ // partHidden(). These will be called more often than we'd like (e.g.
+ // when the tab order causes a view to be obscured), however, we can use
+ // the workaround of looking up IWorkbenchPage.findViewReference(id) after
+ // partHidden(), which will return null if the view is closed in the current
+ // perspective. For partOpened, we simply look in partVisible() for whether
+ // our flags tracking the view state have been initialized already.
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPartReference partRef) {
+ // partClosed() doesn't get called when a window is closed unless it has
+ // been closed in *all* perspectives. See partOpened() for more.
+ }
+
+ @Override
+ public void partHidden(IWorkbenchPartReference partRef) {
+ IWorkbenchPage activePage = mWindow.getActivePage();
+ if (activePage == null) {
+ return;
+ }
+ initialize();
+
+ // See if this looks like the window was closed in this workspace
+ // See partOpened() for an explanation.
+ String id = partRef.getId();
+ if (PROPERTY_SHEET_PART_ID.equals(id)) {
+ if (activePage.findViewReference(id) == null) {
+ propertySheetClosed();
+ return;
+ }
+ } else if (OUTLINE_PART_ID.equals(id)) {
+ if (activePage.findViewReference(id) == null) {
+ outlineClosed();
+ return;
+ }
+ }
+
+ // Does this look like a window getting maximized?
+ syncMaximizedState(activePage);
+ }
+
+ @Override
+ public void partVisible(IWorkbenchPartReference partRef) {
+ IWorkbenchPage activePage = mWindow.getActivePage();
+ if (activePage == null) {
+ return;
+ }
+ initialize();
+
+ String id = partRef.getId();
+ if (mEditorMaximized) {
+ // Return to their non-maximized state
+ mEditorMaximized = false;
+ syncActive();
+ }
+
+ IWorkbenchPart part = partRef.getPart(false /*restore*/);
+ sync(part);
+
+ // See partOpened() for an explanation
+ if (PROPERTY_SHEET_PART_ID.equals(id)) {
+ if (!mPropertiesOpen) {
+ propertySheetOpened();
+ assert mPropertiesOpen;
+ }
+ } else if (OUTLINE_PART_ID.equals(id)) {
+ if (!mOutlineOpen) {
+ outlineOpened();
+ assert mOutlineOpen;
+ }
+ }
+ }
+
+ @Override
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ }
+
+ @Override
+ public void partActivated(IWorkbenchPartReference partRef) {
+ IWorkbenchPart part = partRef.getPart(false);
+ if (part instanceof AndroidXmlEditor) {
+ ((AndroidXmlEditor)part).activated();
+ }
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPartReference partRef) {
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ IWorkbenchPart part = partRef.getPart(false);
+ if (part instanceof AndroidXmlEditor) {
+ ((AndroidXmlEditor)part).deactivated();
+ }
+ }
+} \ No newline at end of file