/** * Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de> * * Licensed 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 de.codesourcery.jasm16.ide.ui.viewcontainers; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.commons.lang.StringUtils; import de.codesourcery.jasm16.compiler.io.DefaultResourceMatcher; import de.codesourcery.jasm16.compiler.io.IResource; import de.codesourcery.jasm16.compiler.io.IResourceResolver; import de.codesourcery.jasm16.exceptions.ResourceNotFoundException; import de.codesourcery.jasm16.ide.*; import de.codesourcery.jasm16.ide.ui.MenuManager; import de.codesourcery.jasm16.ide.ui.MenuManager.MenuEntry; import de.codesourcery.jasm16.ide.ui.utils.UIUtils; import de.codesourcery.jasm16.ide.ui.views.*; public class EditorContainer extends AbstractView implements IViewContainer , IResourceResolver { public static final String VIEW_ID = "editor-container"; private JPanel panel; private final String title; private final ViewContainerHelper helper = new ViewContainerHelper(); private final EditorFactory editorFactory; private final NavigationHistory navigationHistory = new NavigationHistory(); private final IWorkspace workspace; private final List<ViewWithPanel> views = new ArrayList<ViewWithPanel>(); private final JTabbedPane tabbedPane = new JTabbedPane(); private final ChangeListener changeListener = new ChangeListener() { private int previouslySelectedTab = -1; @Override public void stateChanged(ChangeEvent e) { final int newIndex = tabbedPane.getSelectedIndex(); int oldIndex = previouslySelectedTab; previouslySelectedTab = newIndex; if ( oldIndex != newIndex ) { // selected index changed if ( oldIndex != -1 ) { getViewWithPanelForTabIndex( oldIndex ).tabDeselected(); } getViewWithPanelForTabIndex( newIndex ).tabSelected(); } } }; private MenuEntry saveCurrent = new MenuEntry("File/Save") { @Override public void onClick() { System.out.println("Save current editor contents"); } public boolean isVisible() { return ! mayBeDisposed(); }; }; protected final class ViewWithPanel { public int tabIndex; public final IView view; public final JPanel panel; public ViewWithPanel(IView view,int tabIndex) { this.view = view; this.tabIndex = tabIndex; this.panel = view.getPanel( EditorContainer.this ); } public void tabSelected() { if ( view instanceof IViewStateListener) { ((IViewStateListener) view).viewVisible(); } } public void tabDeselected() { if ( view instanceof IViewStateListener) { ((IViewStateListener) view).viewHidden(); } } public void toFront() { tabbedPane.setSelectedIndex( tabIndex ); } } public EditorContainer(String title, IWorkspace workspace,IViewContainer parent,EditorFactory editorFactory) { this.title = title; this.workspace = workspace; this.editorFactory = editorFactory; } @Override protected JPanel getPanel() { if ( panel == null ) { panel = createPanel(); } return panel; } @Override public void setBlockAllUserInput(boolean yesNo) { if ( panel != null ) { Container parent = panel.getParent(); while( parent != null && !(parent instanceof JFrame ) ) { parent = parent.getParent(); } if ( parent != null && parent instanceof JFrame ) { UIUtils.setBlockAllUserInput( (JFrame) parent , yesNo ); } } } @Override public void toFront(IView view) { } private JPanel createPanel() { final JPanel result = new JPanel(); result.setLayout( new GridBagLayout() ); GridBagConstraints cnstrs = constraints(0 , 0 , true , true , GridBagConstraints.BOTH ); setColors( result ); tabbedPane.setBackground( Color.WHITE ); tabbedPane.setForeground( Color.black ); result.add( tabbedPane ,cnstrs ); if ( getViewContainer().getMenuManager() != null ) { getViewContainer().getMenuManager().addEntry( saveCurrent ); } tabbedPane.addChangeListener( changeListener ); tabbedPane.addKeyListener( new KeyAdapter() { public void keyReleased(KeyEvent e) { if ( e.getKeyCode() == KeyEvent.VK_W && ( e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK ) != 0 ) { int idx = tabbedPane.getSelectedIndex(); if ( idx != -1 ) { disposeView( getViewForTabIndex( idx ) ); } } } } ); return result; } public static final void addEditorCloseKeyListener(Component comp,final IEditorView view) { comp.addKeyListener( new KeyAdapter() { public void keyReleased(KeyEvent e) { if ( e.getKeyCode() == KeyEvent.VK_W && ( e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK ) != 0 ) { System.out.println("*** Closing editor "+view+" ***"); if ( view.hasViewContainer() ) { view.getViewContainer().disposeView( view ); } else { view.dispose(); } } } } ); } @Override public IView addView(IView view) { internalAddView(view); return view; } private ViewWithPanel internalAddView(IView view) { final int index = tabbedPane.getTabCount(); final ViewWithPanel newView = new ViewWithPanel( view , index ); views.add( newView ); tabbedPane.add( view.getTitle() , newView.panel ); return newView; } protected void selectTab(IView view) { for ( ViewWithPanel v : views ) { if ( v.view == view ) { tabbedPane.setSelectedIndex( v.tabIndex ); return; } } } protected ViewWithPanel getViewWithPanelForTabIndex(int tabIndex) { for ( ViewWithPanel v : views ) { if ( v.tabIndex == tabIndex ) { return v; } } throw new IllegalArgumentException("Invalid tab index: "+tabIndex); } protected IView getViewForTabIndex(int tabIndex) { return getViewWithPanelForTabIndex( tabIndex ).view; } @Override public void setTitle(IView view, String title) { for ( ViewWithPanel p : this.views ) { if ( p.view == view ) { final int index = tabbedPane.indexOfComponent( p.panel ); if ( index != -1 ) { tabbedPane.setTitleAt( index , title ); } break; } } } @Override public void disposeHook() { final List<ViewWithPanel> copy = new ArrayList<ViewWithPanel>(this.views); for ( ViewWithPanel v : copy ) { v.view.dispose(); } this.views.clear(); if ( getViewContainer().getMenuManager() != null ) { getViewContainer().getMenuManager().removeEntry( saveCurrent ); } helper.fireViewContainerClosed( this ); } @Override public List<IView> getViews() { final List<IView> result = new ArrayList<IView>(); for ( ViewWithPanel p : this.views ) { result.add( p.view ); } return result; } @Override public void disposeView(IView view) { if (view == null) { throw new IllegalArgumentException("view must not be NULL"); } int disposedTabIndex = -1; final List<ViewWithPanel> copy = new ArrayList<ViewWithPanel>(this.views); for (Iterator<ViewWithPanel> it = copy.iterator(); it.hasNext();) { final ViewWithPanel viewWithPanel = it.next(); if ( viewWithPanel.view == view ) { this.views.remove( viewWithPanel ); disposedTabIndex = viewWithPanel.tabIndex; this.tabbedPane.remove( viewWithPanel.panel ); viewWithPanel.view.dispose(); break; } } if ( disposedTabIndex != -1 ) { // adjust tab indices int previousTabToFocus = -1; int nextTabToFocus = -1; for ( ViewWithPanel v : this.views ) { if ( v.tabIndex >= disposedTabIndex ) { v.tabIndex--; if ( nextTabToFocus == -1 ) { nextTabToFocus = v.tabIndex; } } else { previousTabToFocus = v.tabIndex; } } // focus next/previous tab if ( nextTabToFocus != -1 ) { tabbedPane.setSelectedIndex( nextTabToFocus ); } else if ( previousTabToFocus != -1 ) { tabbedPane.setSelectedIndex( previousTabToFocus ); } } } @Override public String getTitle() { return title; } @Override public void refreshDisplay() { for ( ViewWithPanel p : this.views ) { p.view.refreshDisplay(); } } public IEditorView getEditor(IResource resource) { if (resource == null) { throw new IllegalArgumentException("resource must not be NULL"); } for ( ViewWithPanel p : this.views ) { if ( p.view instanceof IEditorView) { if ( DefaultResourceMatcher.INSTANCE.isSame( ((IEditorView) p.view).getCurrentResource() , resource ) ) { return (IEditorView) p.view; } } } return null; } @Override public boolean mayBeDisposed() { boolean result = false; for ( ViewWithPanel p : this.views ) { if ( p.view instanceof IEditorView) { result |= ((IEditorView) p.view).hasUnsavedContent(); } } return result; } @Override public String getID() { return VIEW_ID; } @Override public IView getViewByID(String viewId) { if (StringUtils.isBlank(viewId)) { throw new IllegalArgumentException("viewId must not be blank/null"); } for ( ViewWithPanel p : this.views ) { if ( p.view.getID().equals( viewId ) ) { return p.view; } } return null; } @Override public MenuManager getMenuManager() { return null; } @Override public void addViewContainerListener(IViewContainerListener listener) { helper.addViewContainerListener( listener ); } @Override public void removeViewContainerListener(IViewContainerListener listener) { helper.removeViewContainerListener( listener ); } public IEditorView openResource(IWorkspace workspace , IAssemblyProject project,IResource resource,int caretPosition) throws IOException { IEditorView editor = getEditor( resource ); if ( editor != null ) { editor.refreshDisplay(); selectTab( editor ); return editor; } editor = editorFactory.createEditor( project , resource , this , navigationHistory ); final ViewWithPanel viewWithPanel = internalAddView( editor ); tabbedPane.setSelectedIndex( viewWithPanel.tabIndex ); // open resource AFTER IView has been added to this container, // view may rely on methods of this container editor.openResource( project , resource , caretPosition ); return editor; } private List<SourceCodeView> getSourceCodeViews() { List<SourceCodeView> result = new ArrayList<SourceCodeView>(); for ( ViewWithPanel view : this.views ) { if ( view.view instanceof SourceCodeView) { result.add( (SourceCodeView) view.view ); } } return result; } @Override public IResource resolve(String identifier) throws ResourceNotFoundException { IResource result = tryResolve(identifier); if ( result == null ) { throw new ResourceNotFoundException("Failed to find resource '"+identifier+"'",identifier); } return result; } private IResource tryResolve(String identifier) { for ( SourceCodeView v : getSourceCodeViews() ) { if ( v.getSourceFromMemory().getIdentifier().equals( identifier ) ) { return v.getSourceFromMemory(); } } return null; } @Override public IResource resolveRelative(String identifier, IResource parent) throws ResourceNotFoundException { IResource result = tryResolve(identifier); if ( result == null ) { throw new ResourceNotFoundException("Failed to find resource '"+identifier+"'",identifier); } return result; } }