/*******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     WindRiver - Bug 192028 [Memory View] Memory view does not 
 *                 display memory blocks that do not reference IDebugTarget
 *     ARM - Bug 192028 [Memory View] Memory view does not 
 *                 display memory blocks that do not reference IDebugTarget
 *     WindRiver - Bug 216509 [Memory View] typo, s/isMeomryBlockRemoved/isMemoryBlockRemoved
 *     Wind River Systems - Ted Williams - [Memory View] Memory View: Workflow Enhancements (Bug 215432)
 *******************************************************************************/
package org.eclipse.debug.internal.ui.views.memory;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.views.memory.renderings.CreateRendering;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.memory.IMemoryRendering;
import org.eclipse.debug.ui.memory.IMemoryRenderingContainer;
import org.eclipse.debug.ui.memory.IMemoryRenderingSite;
import org.eclipse.debug.ui.memory.IMemoryRenderingSynchronizationService;
import org.eclipse.debug.ui.memory.IResettableMemoryRendering;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabFolder2Adapter;
import org.eclipse.swt.custom.CTabFolderEvent;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.UIJob;



/**
 * Represents a rendering view pane in the Memory View.
 * This hosts the memory view tabs in the view.
 * @since 3.1
 *
 */
public class RenderingViewPane extends AbstractMemoryViewPane implements IMemoryRenderingContainer{

	public static final String RENDERING_VIEW_PANE_ID = DebugUIPlugin.getUniqueIdentifier() + ".MemoryView.RenderingViewPane"; //$NON-NLS-1$
	
	private Hashtable fTabFolderForMemoryBlock = new Hashtable();
	private Hashtable fMemoryBlockFromTabFolder = new Hashtable();

	private ViewPaneRenderingMgr fRenderingMgr;
	
	private IMemoryRenderingSite fRenderingSite;
	private Set fAddedRenderings = new HashSet();
	private Set fAddedMemoryBlocks = new HashSet();
	
	private boolean fCanAddRendering = true;
	private boolean fCanRemoveRendering = true;

	private boolean fIsDisposed = false;
	
	/**
	 * @param parent is the view hosting this view pane
	 * @param paneId is the identifier assigned by the Memory View
	 * 
	 * Pane id is assigned with the following format.  
	 * Rendering view pane created has its id assigned to 
	 * org.eclipse.debug.ui.MemoryView.RenderingViewPane.#.  
	 * # is a number indicating the order of which the rendering view
	 * pane is created.  First rendering view pane created will have its
	 * id assigned to org.eclipse.debug.ui.MemoryView.RenderingViewPane.1.
	 * Second rendering view pane created will have its id assigned to
	 * org.eclipse.debug.ui.MemoryView.RenderingViewPane.2. and so on.
	 * View pane are created from left to right by the Memory View.
	 * 
	 */
	public RenderingViewPane(IViewPart parent) {
		super(parent);
		
		if (parent instanceof IMemoryRenderingSite)
			fRenderingSite = (IMemoryRenderingSite)parent;
		else
		{
			DebugUIPlugin.logErrorMessage("Parent for the rendering view pane is invalid."); //$NON-NLS-1$
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.memory.IMemoryBlockListener#MemoryBlockAdded(org.eclipse.debug.core.model.IMemoryBlock)
	 */
	public void memoryBlocksAdded(final IMemoryBlock[] memoryBlocks) {
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				
				if (isDisposed())
					return;
						
				// check condition before doing anything
				if (memoryBlocks == null || memoryBlocks.length <= 0)
					return;
				
				for (int i=0; i<memoryBlocks.length; i++)
				{
					IMemoryBlock memory = memoryBlocks[i];
				
					if (!fTabFolderForMemoryBlock.containsKey(memory))
					{
						createFolderForMemoryBlock(memory);						
					}	
					fAddedMemoryBlocks.add(memory);
					updateToolBarActionsEnablement();
				}
			}});
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.memory.IMemoryBlockListener#MemoryBlockRemoved(org.eclipse.debug.core.model.IMemoryBlock)
	 */
	public void memoryBlocksRemoved(final IMemoryBlock[] memoryBlocks) {
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				for (int j=0; j<memoryBlocks.length; j++)
				{
					IMemoryBlock mbRemoved = memoryBlocks[j];
					if (fTabFolderForMemoryBlock == null)
					{
						return;
					}
					
					// get all renderings from this memory block and remove them from the view
					IMemoryRendering[] renderings = fRenderingMgr.getRenderingsFromMemoryBlock(mbRemoved);
					
					for (int k=0; k<renderings.length; k++)
					{
						removeMemoryRendering(renderings[k]);
					}
					
					// remove a the tab folder if the memory block is removed
					CTabFolder tabFolder =
						(CTabFolder) fTabFolderForMemoryBlock.get(mbRemoved);
					
					if (tabFolder == null)
						continue;
					
					fTabFolderForMemoryBlock.remove(mbRemoved);
					fMemoryBlockFromTabFolder.remove(tabFolder);
					IMemoryBlockRetrieval retrieve = MemoryViewUtil.getMemoryBlockRetrieval(mbRemoved);
					if (retrieve != null)
					{
						if (fTabFolderForDebugView.contains(tabFolder))
						{					
							fTabFolderForDebugView.remove(retrieve);
						}
					}
					
					if (!tabFolder.isDisposed()) {						
						// dispose all view tabs belonging to the tab folder
						CTabItem[] items = tabFolder.getItems();
						
						for (int i=0; i<items.length; i++)
						{	
							disposeTab(items[i]);
						}
						
						// dispose the tab folder
						tabFolder.dispose();						
						
						// if this is the top control
						if (tabFolder == fStackLayout.topControl)
						{	
							
							// if memory view is visible and have a selection
							// follow memory view's selection
							
							ISelection selection = DebugUIPlugin.getActiveWorkbenchWindow().getSelectionService().getSelection(IDebugUIConstants.ID_MEMORY_VIEW);
							IMemoryBlock mbToSelect = getMemoryBlock(selection);
							
							if (mbToSelect != null)
							{									
								// memory view may not have got the event and is still displaying
								// the deleted memory block
								if (mbToSelect != mbRemoved)
									handleMemoryBlockSelection(null, mbToSelect);
								else if ((MemoryViewUtil.getMemoryBlockManager().getMemoryBlocks(retrieve).length > 0))
								{
									mbToSelect = MemoryViewUtil.getMemoryBlockManager().getMemoryBlocks(retrieve)[0];
									handleMemoryBlockSelection(null, mbToSelect);										
								}
								else
								{
									emptyFolder();
								}
							}
							else if (MemoryViewUtil.getMemoryBlockManager().getMemoryBlocks(retrieve).length > 0)
							{	// get to the next folder
								mbToSelect = MemoryViewUtil.getMemoryBlockManager().getMemoryBlocks(retrieve)[0];
								handleMemoryBlockSelection(null, mbToSelect);
							}
							else
							{
								emptyFolder();
			
							}
						}
						
						// if not the top control
						// no need to do anything
					}
					
					fAddedMemoryBlocks.remove(mbRemoved);
					updateToolBarActionsEnablement();
				}
			}
		});

	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public void selectionChanged(final IWorkbenchPart part, final ISelection selection) {
		if (fIsDisposed)
			return;
		
		// do not schedule job if any of these conditions are true
		if(part == RenderingViewPane.this)
			return;
		
		if (!(selection instanceof IStructuredSelection))
			return;
		
		if (selection == AbstractMemoryViewPane.EMPTY)
			return;
		
		UIJob job = new UIJob("RenderingViewPane selectionChanged"){ //$NON-NLS-1$

			public IStatus runInUIThread(IProgressMonitor monitor) {
				try {

					if (isDisposed())
						return Status.OK_STATUS;
					
					if (selection.isEmpty())
					{
						// if the event comes from Memory View
						// pick empty tab folder as the memory view is no longer displaying anything
						if (part.getSite().getId().equals(IDebugUIConstants.ID_MEMORY_VIEW))
						{
							if (part == getMemoryRenderingSite().getSite().getPart())
							{
								IMemoryViewTab lastViewTab = getTopMemoryTab();
								
								if (lastViewTab != null)
									lastViewTab.setEnabled(false);
								
								emptyFolder();
							}
						}
						
						// do not do anything if there is no selection
						// In the case when a debug adpater fires a debug event incorrectly, Launch View sets
						// selection to nothing.  If the view tab is disabled, it erases all the "delta" information
						// in the content.  This may not be desirable as it will cause memory to show up as
						// unchanged when it's actually changed.  Do not disable the view tab until there is a 
						// valid selection.
						
						return Status.OK_STATUS;
					}
					
					// back up current view tab
					IMemoryViewTab lastViewTab = getTopMemoryTab();
					
					if (!(selection instanceof IStructuredSelection))
						return Status.OK_STATUS;

					Object elem = ((IStructuredSelection)selection).getFirstElement();
					
					if (elem instanceof IMemoryBlock)
					{	
						// if the selection event comes from this view
						if (part == getMemoryRenderingSite())
						{
							// find the folder associated with the given IMemoryBlockRetrieval
							IMemoryBlock memBlock = (IMemoryBlock)elem;
							
							// should never get here... added code for safety
							if (fTabFolderForMemoryBlock == null)
							{
								if (lastViewTab != null)
									lastViewTab.setEnabled(false);
								
								emptyFolder();
								return Status.OK_STATUS;		
							}
			
							handleMemoryBlockSelection(lastViewTab, memBlock);
						}
					}
				}
				catch(SWTException se)
				{
					DebugUIPlugin.log(se);
				}
				return Status.OK_STATUS;
			}};
		job.setSystem(true);
		job.schedule();
	}
	
	public void handleMemoryBlockSelection(final IMemoryViewTab lastViewTab, final IMemoryBlock memBlock) {
		// Do not check if the debug target of mb is removed
		// We should not get into this method if the debug target of the memory block is terminated
		// Memory Block Manager gets the terminate event and would have removed all memory blocks
		// associated with the debug target
		// Therefore, we will never try to set a selection to a memory block whose target is terminated

		// check current memory block
		CTabFolder currentFolder = (CTabFolder) fStackLayout.topControl;
		if (currentFolder != null && !currentFolder.isDisposed()) {
			IMemoryBlock currentBlk = (IMemoryBlock) fMemoryBlockFromTabFolder.get(currentFolder);
			if (currentBlk != null) {
				if (currentBlk == memBlock)
					return;
			}
		}

		if (getTopMemoryTab() != null) {
			if (getTopMemoryTab().getRendering().getMemoryBlock() == memBlock) {
				return;
			}
		}

		// if we've got a tabfolder to go with the IMemoryBlock, display
		// it
		if (fTabFolderForMemoryBlock.containsKey(memBlock)) {
			if (fStackLayout.topControl != (CTabFolder) fTabFolderForMemoryBlock.get(memBlock)) {
				setTabFolder((CTabFolder) fTabFolderForMemoryBlock.get(memBlock));
				fViewPaneCanvas.layout();
			}
		} else { // otherwise, add a new one
			CTabFolder folder = createTabFolder(fViewPaneCanvas);
			
			fTabFolderForMemoryBlock.put(memBlock, folder);
			fMemoryBlockFromTabFolder.put(folder, memBlock);
			setTabFolder((CTabFolder) fTabFolderForMemoryBlock.get(memBlock));
			fViewPaneCanvas.layout();
			fAddedMemoryBlocks.add(memBlock);
			
			newCreateRenderingForFolder(memBlock, folder);
		}

		// restore view tabs
		IMemoryRendering[] renderings = fRenderingMgr.getRenderingsFromMemoryBlock(memBlock);
		CTabFolder toDisplay = (CTabFolder) fStackLayout.topControl;
		
		// if only CreateRendering is present, restore renderings
		if (isRestoreViewTabs(toDisplay)) {
			restoreViewTabs(renderings);
		}

		// disable last view tab as it becomes hidden
		IMemoryViewTab newViewTab = getTopMemoryTab();

		if (lastViewTab != null && lastViewTab != newViewTab) {
			lastViewTab.setEnabled(false);
		}

		if (newViewTab != null) {
			// if new view tab is not already enabled, enable it
			if (!newViewTab.isEnabled()) {
				// if the view tab is visible, enable it
				if (fVisible) {
					newViewTab.setEnabled(fVisible);
				}
			}
		}

		IMemoryViewTab viewTab = getTopMemoryTab();
		if (viewTab != null)
			setRenderingSelection(viewTab.getRendering());

		//set toolbar actions enabled/disabled
		updateToolBarActionsEnablement();
	}
	
	private boolean isRestoreViewTabs(CTabFolder folder)
	{
		if (canAddRendering())
			return (folder.getItemCount() == 1 && getTopMemoryTab().getRendering() instanceof CreateRendering);
		else
			return (folder.getItemCount() == 0);
	}
	
	private int getIndexOfCreateRenderingTab(CTabFolder folder)
	{
		for(int i = 0; i < folder.getItemCount(); i++)
			if(folder.getItem(i).getData() instanceof MemoryViewTab && 
					((MemoryViewTab) folder.getItem(i).getData()).getRendering() instanceof CreateRendering)
				return i;
		
		return -1;
	}

	public void memoryBlockRenderingAdded(final IMemoryRendering rendering) {
 
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				
				if (isDisposed())
					return;
				
				if (fAddedRenderings.contains(rendering))
					return;

				IMemoryBlock memoryblk = rendering.getMemoryBlock();

				CTabFolder tabFolder = (CTabFolder) fTabFolderForMemoryBlock.get(memoryblk);
				
				if (tabFolder == null)
				{
					tabFolder = createFolderForMemoryBlock(memoryblk);
				}
				
				if (tabFolder == fStackLayout.topControl)
				{
					// disable current view tab
					if (getTopMemoryTab() != null) {
						deactivateRendering(getTopMemoryTab());
						getTopMemoryTab().setEnabled(false);
					}						
				}
				fAddedRenderings.add(rendering);
				
				int index = getIndexOfCreateRenderingTab(tabFolder);
				if (index < 0)
					index = 0;				
				CTabItem tab = createTab(tabFolder, index);
				
				MemoryViewTab viewTab = new MemoryViewTab(tab, rendering,getInstance());
				tabFolder.setSelection(tabFolder.indexOf(tab));
				
				if (tabFolder == fStackLayout.topControl)
				{
					setRenderingSelection(viewTab.getRendering());

					// disable top view tab if the view pane is not visible
					IMemoryViewTab top = getTopMemoryTab();
					if (top != null)
						top.setEnabled(fVisible);
				}
				else
				{
					deactivateRendering(viewTab);
					viewTab.setEnabled(false);
				}

				updateToolBarActionsEnablement();
			}
		});
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.memory.IMemoryRenderingListener#MemoryBlockRenderingRemoved(org.eclipse.debug.internal.core.memory.IMemoryRendering)
	 */
	public void memoryBlockRenderingRemoved(final IMemoryRendering rendering) {
		final IMemoryBlock memory = rendering.getMemoryBlock();
		
		// need to run the following code on the UI Thread to avoid invalid thread access exception
		Display.getDefault().asyncExec(new Runnable()
		{
			public void run()
			{
				if (!fAddedRenderings.contains(rendering))
					return;
				
				fAddedRenderings.remove(rendering);
				
				CTabFolder tabFolder = (CTabFolder) fStackLayout.topControl;
				
				if (tabFolder.isDisposed())
					return;
								
				CTabItem[] tabs = tabFolder.getItems();
				boolean foundTab = false;
				for (int i = 0; i < tabs.length; i++)
				{
					IMemoryViewTab viewTab = (IMemoryViewTab) tabs[i].getData();
					
					if (tabs[i].isDisposed())
						continue;
					

					if (viewTab.getRendering().getMemoryBlock() == memory)
					{
						if (viewTab.getRendering() == rendering)
						{
							foundTab = true;
							disposeTab(tabs[i]);
							break;
						}
						
					}
				}

				// if a tab is not found in the current top control
				// this deletion is a result of a debug target termination
				// find memory from other folder and dispose the view tab
				if (!foundTab)
				{
					Enumeration enumeration = fTabFolderForMemoryBlock.elements();
					while (enumeration.hasMoreElements())
					{
						CTabFolder otherTabFolder = (CTabFolder) enumeration.nextElement();
						tabs = otherTabFolder.getItems();
						IMemoryViewTab viewTab = null;
						for (int i = 0; i < tabs.length; i++)
						{
							viewTab = (IMemoryViewTab) tabs[i].getData();
							if (viewTab.getRendering().getMemoryBlock() == memory)
							{
								if (viewTab.getRendering() == rendering)
								{
									foundTab = true;
									disposeTab(tabs[i]);
									break;
								}
							}
						}
					}
				}
				IMemoryViewTab top = getTopMemoryTab();
				
				// update selection
				if (top != null)
					setRenderingSelection(top.getRendering());
				
				updateToolBarActionsEnablement();
			}
		});
		
	}	
	
	/**
	 * @param viewTab
	 */
	protected void setRenderingSelection(IMemoryRendering rendering) {

	 	if (rendering != null)
	 	{	
	 		fSelectionProvider.setSelection(new StructuredSelection(rendering));
	 	}
	}
	
	private void restoreViewTabs(IMemoryRendering[] renderings)
	{
		for (int i=0; i<renderings.length; i++)
		{
			memoryBlockRenderingAdded(renderings[i]);
		}
	}
	
	private void handleDebugElementSelection(final IMemoryViewTab lastViewTab, final IAdaptable element)
	{	
		// get current memory block retrieval and debug target
		IMemoryBlockRetrieval currentRetrieve = null;
		
		// get tab folder
		CTabFolder tabFolder = (CTabFolder) fStackLayout.topControl;
		
		// get memory block
		IMemoryBlock currentBlock = (IMemoryBlock)fMemoryBlockFromTabFolder.get(tabFolder);
		
		if (currentBlock != null)
		{	
			currentRetrieve = MemoryViewUtil.getMemoryBlockRetrieval(currentBlock);
			
			// backup current retrieve and tab folder
			if (currentRetrieve != null && tabFolder != null)
			{
				fTabFolderForDebugView.put(currentRetrieve, tabFolder);
			}
		}
		
		// find the folder associated with the given IMemoryBlockRetrieval
		IMemoryBlockRetrieval retrieve = MemoryViewUtil.getMemoryBlockRetrieval(element);

		// if debug target has changed
		// switch to that tab folder
		if (retrieve != null && retrieve != currentRetrieve)
		{	
			CTabFolder folder = (CTabFolder)fTabFolderForDebugView.get(retrieve);
			
			if (folder != null)
			{	
				setTabFolder(folder);
				fTabFolderForDebugView.put(retrieve, folder);
				fViewPaneCanvas.layout();
			}
			else
			{	
				// find out if there is any memory block for this debug target
				// and set up tab folder for the memory blocks
				IMemoryBlock blocks[] = MemoryViewUtil.getMemoryBlockManager().getMemoryBlocks(retrieve);
				
				if (blocks.length > 0)
				{	
					handleMemoryBlockSelection(null, blocks[0]);
				}
				else
				{	
					emptyFolder();
					fTabFolderForDebugView.put(retrieve, fEmptyTabFolder);
					fViewPaneCanvas.layout();
				}
			}
		}
		
		// disable last view tab as it becomes hidden
		IMemoryViewTab newViewTab = getTopMemoryTab();

		if (lastViewTab != null && lastViewTab != newViewTab)
		{
			lastViewTab.setEnabled(false);
		}

		if (newViewTab != null)
		{
			// if new view tab is not already enabled, enable it
			if (!newViewTab.isEnabled())
			{
				// if the view tab is visible, enable it
				if (fVisible)
				{
					newViewTab.setEnabled(fVisible);
				}					
			}
			
			// should only change selection if the new view tab is different
			if (lastViewTab != newViewTab)
				setRenderingSelection(newViewTab.getRendering());
		}	
		//set toolbar actions enabled/disabled
		updateToolBarActionsEnablement();
	}
	
	protected void addListeners() {
		super.addListeners();
		
		// must directly listen for selection events from parent's selection provider
		// to ensure that we get the selection event from the tree viewer pane even
		// if the view does not have focuse
		fParent.getSite().getSelectionProvider().addSelectionChangedListener(this);
	}
	protected void removeListeners() {
		super.removeListeners();
		fParent.getSite().getSelectionProvider().removeSelectionChangedListener(this);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
	 */
	public void widgetSelected(SelectionEvent e) {
		
		if (getTopMemoryTab() == null)
			return;
		
		IMemoryRendering rendering = getTopMemoryTab().getRendering();
		
		if (rendering != null)
		{
			fSelectionProvider.setSelection(new StructuredSelection(rendering));
		}
		
	}

	/* (non-Javadoc)
	 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
	 */
	public void widgetDefaultSelected(SelectionEvent e) {
	}
	
	public Object getCurrentSelection() {
		if (getTopMemoryTab() != null)
			if (getTopMemoryTab().getRendering() != null)
				return getTopMemoryTab().getRendering();
		return null;
	}

	/* (non-Javadoc)
	 * @see com.ibm.debug.extended.ui.IMemoryView#getAllViewTabs()
	 */
	public IMemoryViewTab[] getAllViewTabs() {
		
		// otherwise, find the view tab to display
		CTabFolder folder = (CTabFolder) fStackLayout.topControl;
		CTabItem[] items = folder.getItems();
		
		IMemoryViewTab[] viewTabs = new IMemoryViewTab[folder.getItemCount()];
		
		for(int i=0; i<items.length; i++){
			viewTabs[i] = (IMemoryViewTab)items[i].getData();
		}
		
		return viewTabs;
	}

	/* (non-Javadoc)
	 * @see com.ibm.debug.extended.ui.IMemoryView#moveToTop(com.ibm.debug.extended.ui.IMemoryViewTab)
	 */
	public void moveToTop(IMemoryViewTab viewTab) {
		
		IMemoryViewTab lastViewTab = getTopMemoryTab();
		
		if (viewTab == lastViewTab)
			return;
		
		// otherwise, find the view tab to display
		CTabFolder folder = (CTabFolder) fStackLayout.topControl;
		CTabItem[] items = folder.getItems();

		for (int i = 0; i < items.length; i++) {
			IMemoryViewTab tab =
				(IMemoryViewTab) items[i].getData();
			if (viewTab == tab) {

				boolean isEnabled = lastViewTab.isEnabled();

				// switch to that viewTab
				lastViewTab.setEnabled(false);
				folder.setSelection(i);
				
				setRenderingSelection(tab.getRendering());
				
				getTopMemoryTab().setEnabled(isEnabled && fVisible);
				break;
			}
		}
	}
	
	private CTabFolder createTabFolder(Composite parent)
	{
		CTabFolder folder = new CTabFolder(parent, SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM | SWT.FLAT);
		
		ColorRegistry reg = JFaceResources.getColorRegistry();
		Color c1 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"), //$NON-NLS-1$
			  c2 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END"); //$NON-NLS-1$
		folder.setSelectionBackground(new Color[] {c1, c2},	new int[] {100}, true);
		folder.setSelectionForeground(reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR")); //$NON-NLS-1$
		folder.setSimple(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS));
		folder.setBorderVisible(true);
		folder.setFont(fViewPaneCanvas.getFont());
		
		folder.addCTabFolder2Listener(new CTabFolder2Adapter() {
			public void close(CTabFolderEvent event) {
				if(event.item.getData() instanceof MemoryViewTab)
					RenderingViewPane.this.removeMemoryRendering(((MemoryViewTab) event.item.getData()).getRendering());
				event.doit = false;
			}
		});
		return folder;
	}

	public void restoreViewPane() {
		
		// get current selection from memory view
		
		ISelection selection = null;
		if (fParent.getSite().getSelectionProvider() != null)
			selection = fParent.getSite().getSelectionProvider().getSelection();
		
		IMemoryBlock memoryBlock = null;
		if (selection != null)
		{
			memoryBlock = getMemoryBlock(selection);
		}
		
		if (memoryBlock == null)
		{	
			// get selection from this view
			selection = fSelectionProvider.getSelection();
			
			if (MemoryViewUtil.isValidSelection(selection))
			{	
				Object elem = ((IStructuredSelection)selection).getFirstElement();

				if (!(elem instanceof IMemoryBlock))
					return;
				
				memoryBlock = (IMemoryBlock)elem;								
			}
		}
		
		if (memoryBlock == null)
		{
			// get a memory block from current debug context
			IAdaptable context = DebugUITools.getDebugContext();
			if (context != null)
			{
				IMemoryBlockRetrieval retrieval = MemoryViewUtil.getMemoryBlockRetrieval(context);

				if (retrieval != null)
				{
					IMemoryBlock[] blocks = DebugPlugin.getDefault().getMemoryBlockManager().getMemoryBlocks(retrieval);
					if (blocks.length > 0)
						memoryBlock = blocks[0];
				}
			}
		}
		
		if (memoryBlock != null)
		{	
			if (!fTabFolderForMemoryBlock.containsKey(memoryBlock))
			{
				// create tab folder if a tab folder does not already exist
				// for the memory block
				CTabFolder folder = createTabFolder(fViewPaneCanvas);
				
				fTabFolderForMemoryBlock.put(memoryBlock, folder);
				fMemoryBlockFromTabFolder.put(folder, memoryBlock);
				setTabFolder((CTabFolder)fTabFolderForMemoryBlock.get(memoryBlock));
				IMemoryBlockRetrieval retrieval = MemoryViewUtil.getMemoryBlockRetrieval(memoryBlock);
				if (retrieval != null)
					fTabFolderForDebugView.put(retrieval, fTabFolderForMemoryBlock.get(memoryBlock));
				else
					DebugUIPlugin.logErrorMessage("Memory block retrieval for memory block is null."); //$NON-NLS-1$
				
				fViewPaneCanvas.layout();
				fAddedMemoryBlocks.add(memoryBlock);
				
				// every time we create a folder, we have to create a CreateRendering
				newCreateRenderingForFolder(memoryBlock, folder);
			}
			
			if (fTabFolderForMemoryBlock.containsKey(memoryBlock))
			{
				CTabFolder toDisplay = (CTabFolder)fTabFolderForMemoryBlock.get(memoryBlock);
				
				if (toDisplay != null)
				{
					setTabFolder(toDisplay);
					IMemoryBlockRetrieval retrieval = MemoryViewUtil.getMemoryBlockRetrieval(memoryBlock);
					
					if (retrieval != null)
						fTabFolderForDebugView.put(retrieval, toDisplay);
					else
						DebugUIPlugin.logErrorMessage("Memory block retrieval is null for memory block."); //$NON-NLS-1$
					
					fViewPaneCanvas.layout();
					
					// restore view tabs
					IMemoryRendering[] renderings = fRenderingMgr.getRenderingsFromMemoryBlock(memoryBlock);
					
					// if only CreateRendering is present, restore renderings
					if (isRestoreViewTabs(toDisplay))
					{
						restoreViewTabs(renderings);
					}
				}
			}
			
			// disable current storag block
		
			IMemoryViewTab top = getTopMemoryTab();
		
			if (top != null)
				top.setEnabled(fVisible);
		}
	}
	
	public void dispose() {
		fIsDisposed = true;
		
		super.dispose();
		
		fTabFolderForMemoryBlock.clear();
		fTabFolderForMemoryBlock = null;
		
		fMemoryBlockFromTabFolder.clear();
		fMemoryBlockFromTabFolder = null;
		
		fRenderingMgr.dispose();
		fRenderingMgr = null;
		
		fAddedMemoryBlocks.clear();
		fAddedRenderings.clear();
	}	
	
	public Control createViewPane(Composite parent, String paneId, String label, boolean canAddRendering, boolean canRemoveRendering) {
		return doCreateViewPane(parent, paneId, label, canAddRendering, canRemoveRendering);
	}
	
	public Control createViewPane(Composite parent, String paneId, String label) {		
		return doCreateViewPane(parent, paneId, label, true, true);
	}

	/**
	 * @param parent
	 * @param paneId
	 * @param label
	 * @param canAddRendering
	 * @param canRemoveRendering
	 * @return
	 */
	private Control doCreateViewPane(Composite parent, String paneId, String label, boolean canAddRendering,
			boolean canRemoveRendering) {
		Control control =  super.createViewPane(parent, paneId, label);
		fCanAddRendering = canAddRendering;
		fCanRemoveRendering = canRemoveRendering;
		fRenderingMgr = new ViewPaneRenderingMgr(this);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IDebugUIConstants.PLUGIN_ID + ".MemoryRenderingView_context"); //$NON-NLS-1$
		return control;
	}
	
	public IAction[] getActions() {
		return new IAction[0];
	}
	
	// enable/disable toolbar action 
	protected void updateToolBarActionsEnablement()
	{	
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.views.memory.AbstractMemoryViewPane#emptyFolder()
	 */
	protected void emptyFolder() {
		super.emptyFolder();
		updateToolBarActionsEnablement();
		fSelectionProvider.setSelection(AbstractMemoryViewPane.EMPTY);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.views.memory.IRenderingViewPane#addMemoryRendering(org.eclipse.debug.internal.ui.views.memory.IMemoryRendering)
	 */
	public void addMemoryRendering(IMemoryRendering rendering) {
		
		if (rendering == null)
			return;

		memoryBlockRenderingAdded(rendering);
		fRenderingMgr.addMemoryBlockRendering(rendering);
		
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.views.memory.IRenderingViewPane#removeMemoryRendering(org.eclipse.debug.internal.ui.views.memory.IMemoryRendering)
	 */
	public void removeMemoryRendering(IMemoryRendering rendering) {
		
		if (rendering == null)
			return;
		
		memoryBlockRenderingRemoved(rendering);
		
		if (fRenderingMgr != null)
			fRenderingMgr.removeMemoryBlockRendering(rendering);
		
	}
	
	private RenderingViewPane getInstance()
	{
		return this;
	}
	
	private IMemoryBlock getMemoryBlock(ISelection selection)
	{
		if (!(selection instanceof IStructuredSelection))
			return null;

		//only single selection of PICLDebugElements is allowed for this action
		if (selection.isEmpty() || ((IStructuredSelection)selection).size() > 1)
		{
			return null;
		}

		Object elem = ((IStructuredSelection)selection).getFirstElement();
		
		if (elem instanceof IMemoryBlock)
			return (IMemoryBlock)elem;
		else if (elem instanceof IMemoryRendering)
			return ((IMemoryRendering)elem).getMemoryBlock();
		else
			return null;
	}
	
	private void deactivateRendering(IMemoryViewTab viewTab)
	{
		if (viewTab == null)
			return;

		if (!viewTab.isDisposed())
		{		
			viewTab.getRendering().deactivated();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.views.memory.IRenderingViewPane#getMemoryRenderingSite()
	 */
	public IMemoryRenderingSite getMemoryRenderingSite() {
		return fRenderingSite;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.memory.IMemoryRenderingContainer#getId()
	 */
	public String getId() {
		return getPaneId();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.memory.IMemoryRenderingContainer#getRenderings()
	 */
	public IMemoryRendering[] getRenderings() {
		return fRenderingMgr.getRenderings();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.memory.IMemoryRenderingContainer#getActiveRendering()
	 */
	public IMemoryRendering getActiveRendering() {
		if (getTopMemoryTab() == null)
			return null;
		return getTopMemoryTab().getRendering();
	}
	
	/**
	 * Reset the memory renderings within this view pane.
	 * @param memoryBlock - reset renderings associated with the given memory block
	 * @param resetVisible - reset what's currently visible if the parameter is true.
	 * Otherwise, the view pane will reset all renderings associated with the given 
	 * memory block.
	 */
	public void resetRenderings(IMemoryBlock memoryBlock, boolean resetVisible)
	{
		// if we only reset what's visible and the view pane is not visible
		// do nothing.
		if (resetVisible && !isVisible())
			return;
		
		if(resetVisible)
		{
			IMemoryRendering rendering = getActiveRendering();
			if (rendering != null)
			{
				if (rendering.getMemoryBlock() == memoryBlock)
				{
					if (rendering instanceof IResettableMemoryRendering)
					{
						IResettableMemoryRendering resettableRendering = (IResettableMemoryRendering)rendering;
						try {
							resettableRendering.resetRendering();
						} catch (DebugException e) {
							// do not pop up error message
							// error message is annoying where there are multiple rendering
							// panes and renderings to reset
						}
					}
				}
			}
		}
		else
		{
			// get all renderings associated with the given memory block
			IMemoryRendering[] renderings = fRenderingMgr.getRenderingsFromMemoryBlock(memoryBlock);
			
			// back up current synchronization provider
			IMemoryRendering originalProvider = null;
			IMemoryRenderingSynchronizationService service = getMemoryRenderingSite().getSynchronizationService();
			if (service != null)
				originalProvider = service.getSynchronizationProvider();
			
			for (int i=0; i<renderings.length; i++)
			{
				if (renderings[i] instanceof IResettableMemoryRendering)
				{
					try {
						
						// This is done to allow user to select multiple memory monitors and 
						// reset their renderings.
						// In this case, a hidden rendering will not be the sync provider to the sync
						// service.  When the reset happens, the top visible address and selected
						// address is not updated in the sync service.  When the rendering later
						// becomes visible, the rendering gets the sync info from the sync service
						// and will try to sync up with old information, giving user the impression
						// that the rendering was never reset.  By forcing the rendering that we
						// are trying to reset as the synchronization provider, we ensure that
						// the rendering is able to update its sync info even though the rendering
						// is currently hidden.
						if (service != null)
							service.setSynchronizationProvider(renderings[i]);
						((IResettableMemoryRendering)renderings[i]).resetRendering();
					} catch (DebugException e) {
						// do not pop up error message
						// error message is annoying where there are multiple rendering
						// panes and renderings to reset
					}
				}
			}
			
			// restore synchronization provider
			if (service != null)
				service.setSynchronizationProvider(originalProvider);
		}
	}
	
	private boolean isDisposed()
	{
		return fIsDisposed;
	}
	
	public void showCreateRenderingTab()
	{
		IMemoryRendering activeRendering = RenderingViewPane.this.getActiveRendering();
		if(activeRendering == null)
			return;
		
		IMemoryBlock memoryblk = activeRendering.getMemoryBlock();

		final CTabFolder tabFolder = (CTabFolder) fTabFolderForMemoryBlock.get(memoryblk);
		if (tabFolder != null)
		{
			Display.getDefault().asyncExec(new Runnable() {
				public void run()
				{
					int index = getIndexOfCreateRenderingTab(tabFolder);
					if (index >= 0)
						tabFolder.setSelection(index);
				}
			});
		}
	}

	public void contextActivated(final ISelection selection) {
		
		UIJob job = new UIJob("contextActivated"){ //$NON-NLS-1$
			public IStatus runInUIThread(IProgressMonitor monitor) 
			{
				if (isDisposed())
					return Status.OK_STATUS;
				
				IMemoryViewTab lastViewTab = getTopMemoryTab();
				
				if (MemoryViewUtil.isValidSelection(selection))
				{
					if (!(selection instanceof IStructuredSelection))
						return Status.OK_STATUS;

					Object elem = ((IStructuredSelection)selection).getFirstElement();
					
					if (elem instanceof IAdaptable)
					{	
						handleDebugElementSelection(lastViewTab, (IAdaptable)elem);
					}
				}
				else
				{
					if (lastViewTab != null)
						lastViewTab.setEnabled(false);
					
					if (fStackLayout.topControl != fEmptyTabFolder)
						emptyFolder();
					
				}
				return Status.OK_STATUS;
			}};
		job.setSystem(true);
		job.schedule();
	}

	/**
	 * @param memory
	 */
	private CTabFolder createFolderForMemoryBlock(IMemoryBlock memory) {
			CTabFolder folder = createTabFolder(fViewPaneCanvas);
			
			fTabFolderForMemoryBlock.put(memory, folder);
			fMemoryBlockFromTabFolder.put(folder, memory);
			
			IMemoryBlockRetrieval retrieval = MemoryViewUtil.getMemoryBlockRetrieval(memory);
			if (retrieval != null)
			{
				fTabFolderForDebugView.put(retrieval, folder);
			}
			else {
				DebugUIPlugin.logErrorMessage("Memory block retrieval for memory block is null"); //$NON-NLS-1$
			}
						
			newCreateRenderingForFolder(memory, folder);
			
			return folder;
	}

	private void newCreateRenderingForFolder(IMemoryBlock memory,
			CTabFolder folder) {
		
		if (!canAddRendering())
			return;
		
		CTabItem newItem = new CTabItem(folder, SWT.NONE);
		CreateRendering rendering = new CreateRendering(getInstance());
		rendering.init(getInstance(), memory);
		new MemoryViewTab(newItem, rendering, getInstance());
		folder.setSelection(0);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.contexts.provisional.IDebugContextListener#contextEvent(org.eclipse.debug.internal.ui.contexts.provisional.DebugContextEvent)
	 */
	public void debugContextChanged(DebugContextEvent event) {
		if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
			contextActivated(event.getContext());
		}		
	}
	
	/**
	 * @return whether this container allows user to add rendering manually
	 * @since 3.4
	 */
	public boolean canAddRendering()
	{
		return fCanAddRendering;
	}
	
	/**
	 * @return whether this container allows user to remove rendering manually
	 * @since 3.4
	 */
	public boolean canRemoveRendering()
	{
		return fCanRemoveRendering;
	}

	/**
	 * @param tabFolder
	 * @param index
	 * @return
	 */
	private CTabItem createTab(CTabFolder tabFolder, int index) {
		int swtStyle = SWT.CLOSE;
		if (!canRemoveRendering())
			swtStyle = SWT.NONE;
		CTabItem tab = new CTabItem(tabFolder, swtStyle, index);
		return tab;
	}
}
