blob: 5a2275b3062e002c1a7e130fce7824cc3eb860e3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.core.multiview;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import org.netbeans.core.multiview.MultiViewModel.ActionRequestObserverFactory;
import org.netbeans.core.spi.multiview.CloseOperationHandler;
import org.netbeans.core.spi.multiview.MultiViewDescription;
import org.netbeans.core.spi.multiview.MultiViewElement;
import org.netbeans.core.spi.multiview.MultiViewElementCallback;
import org.netbeans.core.spi.multiview.SourceViewMarker;
import org.openide.awt.UndoRedo;
import org.openide.text.CloneableEditorSupport;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.windows.CloneableTopComponent;
import org.openide.windows.TopComponent;
/** Special subclass of TopComponent which shows and handles set of
* MultiViewElements, shows them in switchable toggle buttons style, along
* with toolbars and actions associated with individual view elements.
*
*
* @author Dafe Simonek, Milos Kleint
*/
public final class MultiViewCloneableTopComponent extends CloneableTopComponent
implements ActionRequestObserverFactory,
CloneableEditorSupport.Pane,
Splitable {
MultiViewPeer peer;
public MultiViewCloneableTopComponent() {
super();
peer = new MultiViewPeer(this, this);
// initializes the multiview component.
peer.initComponents();
setFocusCycleRoot(false);
setName("");
// assocuate lookup needs to come after the init.. initComponents() initializes actionMap
associateLookup(peer.getLookup());
// setFocusable(false);
}
public <T extends Serializable & Lookup.Provider> void setMimeLookup(String mimeType, T context) {
peer.setMimeLookup(mimeType, context);
}
public void setMultiViewDescriptions(MultiViewDescription[] descriptions, MultiViewDescription defaultDesc) {
peer.setMultiViewDescriptions(descriptions, defaultDesc);
}
public void setCloseOperationHandler(CloseOperationHandler handler) {
peer.setCloseOperationHandler(handler);
}
private void setDeserializedMultiViewDescriptions(MultiViewDescription[] descriptions,
MultiViewDescription defaultDesc, Map existingElements) {
peer.setDeserializedMultiViewDescriptions(-1, descriptions, defaultDesc, null, existingElements);
}
MultiViewModel getModel() {
return peer.getModel();
}
@Override
public Lookup getLookup() {
peer.assignLookup((MultiViewTopComponentLookup)super.getLookup());
return super.getLookup();
}
protected void componentClosed() {
super.componentClosed();
peer.peerComponentClosed();
}
protected void componentShowing() {
super.componentShowing();
peer.peerComponentShowing();
}
protected void componentHidden() {
super.componentHidden();
peer.peerComponentHidden();
}
protected void componentDeactivated() {
super.componentDeactivated();
peer.peerComponentDeactivated();
}
protected void componentActivated() {
super.componentActivated();
peer.peerComponentActivated();
}
protected void componentOpened() {
super.componentOpened();
peer.peerComponentOpened();
}
/**
* delegate to the apppropriate active element's component
*/
public boolean requestFocusInWindow() {
return peer.requestFocusInWindow();
}
/**
* delegate to the apppropriate active element's component
*/
public void requestFocus() {
peer.requestFocus();
}
/**
* merge action for the topcomponent and the enclosed MultiViewElement..
*
*/
@Override
public Action[] getActions() {
//TEMP don't delegate to element's actions..
Action[] superActions = super.getActions();
List<Action> acts = new ArrayList<Action>(Arrays.asList(peer.peerGetActions(superActions)));
if( !acts.isEmpty() )
acts.add(null);
acts.add( new EditorsAction() );
if( canSplit() ) {
acts.add( new SplitAction(true) );
}
return acts.toArray(new Action[acts.size()]);
}
@Override
public boolean canSplit() {
return null != peer.model && peer.model.canSplit();
}
public MultiViewHandlerDelegate getMultiViewHandlerDelegate() {
// TODO have one handler only or create a new one each time?
return peer.getMultiViewHandlerDelegate();
}
/**
* Delegates the value to the element descriptions.
*/
public int getPersistenceType() {
return peer.getPersistenceType();
}
protected String preferredID() {
return peer.preferredID();
}
@Override
protected CloneableTopComponent createClonedObject() {
MultiViewCloneableTopComponent tc = new MultiViewCloneableTopComponent();
tc.setMultiViewDescriptions(peer.model.getDescriptions(), getActiveDescription());
tc.setCloseOperationHandler(peer.closeHandler);
tc.peer.copyMimeContext(peer);
if(getSplitOrientation() != -1) {
tc.splitComponent(getSplitOrientation(), -1);
tc.updateName();
}
return tc;
}
private MultiViewDescription getActiveDescription() {
int splitOrientation = getSplitOrientation();
MultiViewDescription activeDescription = peer.model.getActiveDescription();
if (splitOrientation != -1) {
boolean isSplitDescription = false;
if (activeDescription instanceof ContextAwareDescription) {
isSplitDescription = ((ContextAwareDescription) activeDescription).isSplitDescription();
}
if (isSplitDescription) {
for (MultiViewDescription descr : peer.model.getDescriptions()) {
if (descr.getDisplayName().equals(activeDescription.getDisplayName())) {
activeDescription = descr;
break;
}
}
}
}
return activeDescription;
}
@Override
public TopComponent splitComponent(int orientation, int splitPosition) {
peer.peerSplitComponent(orientation, splitPosition);
return this;
}
@Override
public TopComponent clearSplit(int splitElementToActivate) {
peer.peerClearSplit(splitElementToActivate);
return this;
}
@Override
public int getSplitOrientation() {
return peer.getSplitOrientation();
}
/** Serialize this top component.
* Subclasses wishing to store state must call the super method, then write to the stream.
* @param out the stream to serialize to
*/
public void writeExternal (ObjectOutput out) throws IOException {
super.writeExternal(out);
peer.peerWriteExternal(out);
}
/** Deserialize this top component.
* Subclasses wishing to store state must call the super method, then read from the stream.
* @param in the stream to deserialize from
*/
public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
peer.peerReadExternal(in);
}
Action[] getDefaultTCActions() {
return super.getActions();
}
public MultiViewElementCallback createElementCallback(MultiViewDescription desc) {
return SpiAccessor.DEFAULT.createCallback(new ActReqObserver(desc));
}
public CloneableTopComponent getComponent() {
return this;
}
public javax.swing.JEditorPane getEditorPane() {
if (peer == null || peer.model == null) {
return null;
}
MultiViewElement paneEl = findPaneElement();
if (paneEl != null) {
CloneableEditorSupport.Pane pane = (CloneableEditorSupport.Pane)paneEl.getVisualRepresentation();
return pane.getEditorPane();
}
// hopeless case, don't try to create new elements. it's users responsibility to
// switch to the editor element before getEditorPane()
return null;
}
private MultiViewElement findPaneElement() {
MultiViewElement el = peer.model.getActiveElement(false);
if (el != null && el.getVisualRepresentation() instanceof CloneableEditorSupport.Pane) {
return el;
}
// now try a best guess.. iterate the already created elements and check if any of
// them is a Pane
Collection<MultiViewElement> col = peer.model.getCreatedElements();
Iterator<MultiViewElement> it = col.iterator();
while (it.hasNext()) {
el = it.next();
if (el.getVisualRepresentation() instanceof CloneableEditorSupport.Pane) {
// fingers crossed and hope for the best... could result in bad results once
// we have multiple editors in the multiview component.
return el;
}
}
MultiViewDescription[] descs = peer.model.getDescriptions();
for (MultiViewDescription desc : descs) {
if (isSourceView(desc)) {
el = peer.model.getElementForDescription(desc);
if (el.getVisualRepresentation() instanceof CloneableEditorSupport.Pane) {
return el;
} else {
Logger.getLogger(getClass().getName()).info("MultiViewDescription " + desc.getDisplayName() + "(" + desc.getClass() +
") claimed to contain sources, but it's MutliViewElement.getVisualRepresentation() didn't return a valid CloeanbleEditorSupport.Pane instance.");
}
}
}
// hopeless case, don't try to create new elements. it's users responsibility to
// switch to the editor element before getEditorPane()
return null;
}
public HelpCtx getHelpCtx() {
return peer.getHelpCtx();
}
public String toString() {
return "MVCTC[name=" + getDisplayName() + ", peer=" + peer + "]"; // NOI18N
}
/**
* Get the undo/redo support for this component.
* The default implementation returns a dummy support that cannot
* undo anything.
*
* @return undoable edit for this component
*/
public UndoRedo getUndoRedo() {
UndoRedo retValue;
retValue = peer.peerGetUndoRedo();
if (retValue == null) {
retValue = super.getUndoRedo();
}
return retValue;
}
/**
* This method is called when last <code>CloneableTopComponent</code> is about to close.
* Delegates to CloseOperationHandler.
*/
@Override
protected boolean closeLast() {
return peer.canClose();
}
// from CloneableEditor.Pane
public void updateName() {
// ensure to trigger update name from AWT -> #44012 - will ultimately trigger winsys.
if (peer != null) {
if (SwingUtilities.isEventDispatchThread() ) {
peer.updateName();
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
peer.updateName();
}
});
}
}
}
/**
* callback for the Pane implementation to adjust itself to the openAt() request.
*/
public void ensureVisible() {
MultiViewElement paneEl = findPaneElement();
if (paneEl != null) {
open();
MultiViewElementCallback call = peer.getModel().getCallbackForElement(paneEl);
call.requestVisible();
}
}
static boolean isSourceView(MultiViewDescription desc) {
if (desc instanceof SourceViewMarker) {
return true;
}
if (desc instanceof SourceCheckDescription) {
return ((SourceCheckDescription)desc).isSourceView();
}
return false;
}
@Override
public SubComponent[] getSubComponents() {
return MultiViewTopComponent.getSubComponents( peer );
}
/**
* implementation of the MultiViewElement.ActionRequestObserver, manages activatation of the elements
* and the TC itself based on requests from the elements.
*/
class ActReqObserver implements Serializable, MultiViewElementCallbackDelegate {
private static final long serialVersionUID =-3126744916624172415L;
private MultiViewDescription description;
ActReqObserver(MultiViewDescription desc) {
description = desc;
}
public void requestActive() {
boolean activated = peer.isActivated();
if (!activated) {
MultiViewCloneableTopComponent.this.requestActive();
}
if (peer.model.getActiveDescription() != description) {
if (activated) {
peer.model.getActiveElement().componentDeactivated();
}
peer.tabs.changeActiveManually(description);
if (activated) {
peer.model.getActiveElement().componentActivated();
}
}
}
public void requestVisible() {
peer.tabs.changeVisibleManually(description);
}
public Action[] createDefaultActions() {
return MultiViewCloneableTopComponent.this.getDefaultTCActions();
}
public void updateTitle(String title) {
MultiViewCloneableTopComponent.this.setDisplayName(title);
}
/** replace as null - should not be stored and read..*/
public Object writeReplace() throws ObjectStreamException {
return null;
}
/** Resolve as null -should not be stored and read..*/
public Object readResolve() throws ObjectStreamException {
return null;
}
public boolean isSelectedElement() {
return (description.equals(peer.model.getActiveDescription()));
}
public TopComponent getTopComponent() {
return MultiViewCloneableTopComponent.this;
}
}
}