/******************************************************************************* * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. *******************************************************************************/ package com.liferay.ide.layouttpl.ui.editor; import com.liferay.ide.core.ILiferayConstants; import com.liferay.ide.core.ILiferayPortal; import com.liferay.ide.core.ILiferayProject; import com.liferay.ide.core.LiferayCore; import com.liferay.ide.core.util.CoreUtil; import com.liferay.ide.layouttpl.core.model.LayoutTplElement; import com.liferay.ide.layouttpl.core.model.LayoutTplElementsFactory; import com.liferay.ide.layouttpl.core.util.LayoutTplUtil; import com.liferay.ide.project.core.descriptor.LiferayDescriptorHelper; import java.lang.reflect.Method; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.text.IDocument; import org.eclipse.sapphire.Element; import org.eclipse.sapphire.Event; import org.eclipse.sapphire.Listener; import org.eclipse.sapphire.ui.SapphireEditor; import org.eclipse.sapphire.ui.def.DefinitionLoader; import org.eclipse.sapphire.ui.def.DefinitionLoader.Reference; import org.eclipse.sapphire.ui.def.EditorPageDef; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.ui.StructuredTextEditor; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; import org.osgi.framework.Version; /** * @author Kuo Zhang * @author Joye Luo */ @SuppressWarnings( "restriction" ) public class LayoutTplEditor extends SapphireEditor implements IExecutableExtension { private static final int SOURCE_PAGE_INDEX = 0; private static final int PREVIEW_PAGE_INDEX = 1; private static final int DESIGN_PAGE_INDEX = 2; private static final String SOURCE_PAGE_TITLE = "Source"; private static final String PREVIEW_PAGE_TITLE = "Preview"; private static final String DESIGN_PAGE_TITLE = "Design"; private StructuredTextEditor sourcePage; private LayoutTplPreviewEditor previewPage; private DefinitionLoader.Reference<EditorPageDef> definition; private IDOMModel sourceModel; private boolean isDesignPageChanged; private boolean isSourceModelChanged; private boolean isBootstrapStyle; private boolean is62; @Override protected void createEditorPages() throws PartInitException { this.sourcePage = new StructuredTextEditor(); this.sourcePage.setEditorPart( this ); addPage( SOURCE_PAGE_INDEX, this.sourcePage, getEditorInput() ); setPageText( SOURCE_PAGE_INDEX, SOURCE_PAGE_TITLE ); initSourceModel(); addDeferredPage( 1, PREVIEW_PAGE_TITLE, "preview" ); addDeferredPage( 2, DESIGN_PAGE_TITLE, "designPage" ); } @Override protected IEditorPart createPage( String pageDefinitionId ) { if( "preview".equals( pageDefinitionId ) ) { if( this.previewPage == null ) { Element element = getModelElement(); if( element instanceof LayoutTplElement ) { this.previewPage = new LayoutTplPreviewEditor( (LayoutTplElement) element ) { @Override public String getTitle() { return "Preview"; } }; } } return this.previewPage; } return super.createPage( pageDefinitionId ); } @Override protected Reference<EditorPageDef> getDefinition( String pageDefinitionId ) { if( "preview".equals( pageDefinitionId ) ) { if( this.definition == null ) { this.definition = DefinitionLoader.sdef( LayoutTplEditor.class ).page( "preview" ); } return this.definition; } return super.getDefinition( pageDefinitionId ); } protected LayoutTplElement createEmptyDiagramModel() { LayoutTplElement layoutTpl = LayoutTplElement.TYPE.instantiate(); layoutTpl.setBootstrapStyle( isBootstrapStyle() ); layoutTpl.setClassName( getEditorInput().getName().replaceAll( "\\..*$", "" ) ); layoutTpl.setIs62( is62() ); return layoutTpl; } @Override protected Element createModel() { final IFile file = getFile(); isBootstrapStyle = isBootstrapStyle(); is62 = is62(); LayoutTplElement layoutTpl = LayoutTplElementsFactory.INSTANCE.newLayoutTplFromFile( file, isBootstrapStyle, is62 ); if( layoutTpl == null ) { layoutTpl = createEmptyDiagramModel(); } layoutTpl.attach( new Listener() { @Override public void handle( Event event ) { setDesignPageChanged( true ); firePropertyChange( PROP_DIRTY ); } }, "*" ); return layoutTpl; } @Override public void dispose() { super.dispose(); this.definition = null; this.sourcePage = null; this.previewPage = null; if( this.sourceModel != null ) { sourceModel.releaseFromEdit(); } } @Override public void doSave( final IProgressMonitor monitor ) { final int activePage = getActivePage(); if( activePage == PREVIEW_PAGE_INDEX ) { if( this.sourcePage.isDirty() ) { this.sourcePage.doSave( monitor ); } } else if( activePage == SOURCE_PAGE_INDEX ) { if( this.sourcePage.isDirty() ) { this.sourcePage.doSave( monitor ); refreshDiagramModel(); } } else if( activePage == DESIGN_PAGE_INDEX ) { if( isDesignPageChanged ) { refreshSourceModel(); this.sourcePage.doSave( monitor ); } } setSourceModelChanged( false ); setDesignPageChanged( false ); firePropertyChange( PROP_DIRTY ); } @Override public IContentOutlinePage getContentOutline( final Object page ) { if( page == this.sourcePage ) { return (IContentOutlinePage) this.sourcePage.getAdapter( IContentOutlinePage.class ); } return super.getContentOutline( page ); } protected void initSourceModel() { if( sourceModel == null ) { if( this.sourcePage != null && this.sourcePage.getDocumentProvider() != null ) { final IDocumentProvider documentProvider = this.sourcePage.getDocumentProvider(); final IDocument doc = documentProvider.getDocument( getEditorInput() ); sourceModel =(IDOMModel) StructuredModelManager.getModelManager().getExistingModelForEdit( doc ); sourceModel.addModelStateListener( new IModelStateListener() { public void modelAboutToBeChanged( IStructuredModel model ){} public void modelAboutToBeReinitialized( IStructuredModel structuredModel ){} public void modelChanged( IStructuredModel model ) { setSourceModelChanged( true ); } public void modelDirtyStateChanged( IStructuredModel model, boolean isDirty ){} public void modelReinitialized( IStructuredModel structuredModel ){} public void modelResourceDeleted( IStructuredModel model ){} public void modelResourceMoved( IStructuredModel oldModel, IStructuredModel newModel ){} } ); } } } private boolean isBootstrapStyle() { boolean retval = true; try { final ILiferayProject lrproject = LiferayCore.create( getFile().getProject() ); final ILiferayPortal portal = lrproject.adapt( ILiferayPortal.class ); final Version version = new Version( portal.getVersion() ); if( CoreUtil.compareVersions( version,ILiferayConstants.V620 ) < 0 ) { retval = false; } } catch( Exception e ) { } return retval; } private boolean is62() { final IProject project = getFile().getProject(); final Version version = new Version( LiferayDescriptorHelper.getDescriptorVersion( project ) ); return ( CoreUtil.compareVersions( version, ILiferayConstants.V620 ) == 0 ); } @Override public boolean isDirty() { return isDesignPageChanged || this.sourcePage.isDirty(); } @Override protected void pageChange( int pageIndex ) { final int[] lastActivePage = new int[1]; try { final Method getLastActivePage = SapphireEditor.class.getDeclaredMethod( "getLastActivePage" ); getLastActivePage.setAccessible( true ); lastActivePage[0] = (Integer) getLastActivePage.invoke( this ); } catch( Exception e ) { } if( lastActivePage[0] == SOURCE_PAGE_INDEX && pageIndex == PREVIEW_PAGE_INDEX ) { // if the source page is dirty, but the model didn't get changed, // then don't refresh the model element if( this.sourcePage.isDirty() && isSourceModelChanged ) { refreshDiagramModel(); } refreshPreviewPage(); } if( lastActivePage[0] == SOURCE_PAGE_INDEX && pageIndex == DESIGN_PAGE_INDEX ) { if( this.sourcePage.isDirty() && isSourceModelChanged ) { refreshDiagramModel(); } } if( lastActivePage[0] == DESIGN_PAGE_INDEX && pageIndex == SOURCE_PAGE_INDEX ) { if( isDesignPageChanged ) { refreshSourceModel(); } } if( lastActivePage[0] == DESIGN_PAGE_INDEX && pageIndex == PREVIEW_PAGE_INDEX ) { if( isDesignPageChanged ) { refreshSourceModel(); } refreshPreviewPage(); } try { super.pageChange( pageIndex ); } catch( Exception e ) { // catch the NPE caused by null content outline } } protected void refreshDiagramModel() { LayoutTplElement newElement = LayoutTplElementsFactory.INSTANCE.newLayoutTplFromFile( getFile(), isBootstrapStyle, is62 ); if( newElement == null ) { // create an empty model for diagram in memory, but not write to source newElement = createEmptyDiagramModel(); } Element model = getModelElement(); model.clear(); model.copy( newElement ); } protected void refreshPreviewPage() { if( this.previewPage != null ) { this.previewPage.refreshVisualModel( (LayoutTplElement) getModelElement() ); } } protected void refreshSourceModel() { refreshSourceModel( (LayoutTplElement) getModelElement() ); } protected void refreshSourceModel( LayoutTplElement modelElement ) { if( this.sourceModel != null ) { final String templateSource = LayoutTplUtil.getTemplateSource( modelElement ); sourceModel.aboutToChangeModel(); sourceModel.getStructuredDocument().setText( this, templateSource ); sourceModel.changedModel(); } setSourceModelChanged( false ); } protected void setDesignPageChanged( boolean changed ) { isDesignPageChanged = changed; } protected void setSourceModelChanged( boolean changed ) { this.isSourceModelChanged = changed; } }