/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;

import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;

import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.internal.registry.IViewDescriptor;
import org.eclipse.ui.internal.registry.IViewRegistry;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ShowInContext;

/**
 * A <code>ShowInMenu</code> is used to populate a menu manager with
 * Show In actions.  The items to show are determined from the active perspective
 * and active part. 
 */
public class ShowInMenu extends ContributionItem {
	
private static final String NO_TARGETS_MSG = WorkbenchMessages.getString("Workbench.showInNoTargets"); //$NON-NLS-1$
 
private IWorkbenchWindow window;
	
private Map actions = new HashMap(21);

private boolean dirty = true;
private IMenuListener menuListener = new IMenuListener() {
	public void menuAboutToShow(IMenuManager manager) {
		manager.markDirty();
		dirty = true;
	}
};

/**
 * Creates a Show In menu.
 *
 * @param window the window containing the menu
 */
public ShowInMenu(IWorkbenchWindow window, String id) {
	super(id);
	this.window = window;
}

protected IWorkbenchWindow getWindow() {
	return window;
}

public boolean isDirty() {
	return dirty;
}	
/**
 * Overridden to always return true and force dynamic menu building.
 */
public boolean isDynamic() {
	return true;
}

public void fill(Menu menu, int index) {
	if (getParent() instanceof MenuManager)
		((MenuManager)getParent()).addMenuListener(menuListener);

	if (!dirty)
		return;

	MenuManager manager = new MenuManager();
	fillMenu(manager);
	IContributionItem[] items = manager.getItems();
	if (items.length == 0) {
		MenuItem item = new MenuItem(menu, SWT.NONE, index++);
		item.setText(NO_TARGETS_MSG);
		item.setEnabled(false);
	}
	else {
		for (int i = 0; i < items.length; i++) {
			items[i].fill(menu,index++);
		}
	}
	dirty = false;
}

/**
 * Fills the menu with Show In actions.
 */
private void fillMenu(IMenuManager innerMgr) {
	// Remove all.
	innerMgr.removeAll();

	IWorkbenchPart sourcePart = getSourcePart();
	if (sourcePart == null) {
		return;
	}
	ShowInContext context = getContext(sourcePart);
	if (context == null) {
		return;
	}
	if (context.getInput() == null && (context.getSelection() == null || context.getSelection().isEmpty())) {
		return;
	}
	
	IViewDescriptor[] viewDescs = getViewDescriptors(sourcePart);
	if (viewDescs.length == 0) {
		return;
	}

	for (int i = 0; i < viewDescs.length; ++i) {
		IAction action = getAction(viewDescs[i]);
		if (action != null) {
			innerMgr.add(action);
		}
	}
}

/**
 * Returns the action for the given view id, or null if not found.
 */
private IAction getAction(IViewDescriptor desc) {
	// Keep a cache, rather than creating a new action each time,
	// so that image caching in ActionContributionItem works.
	IAction action = (IAction) actions.get(desc.getId());
	if (action == null) {
		if (desc != null) {
			action = new ShowInAction(window, desc);
			actions.put(desc.getId(), action);
		}
	}
	return action;
}

/**
 * Returns the Show In... target part ids for the given source part.  
 * Merges the contributions from the current perspective and the source part.
 */
private ArrayList getShowInPartIds(IWorkbenchPart sourcePart) {
	ArrayList targetIds = new ArrayList();
	WorkbenchPage page = (WorkbenchPage) getWindow().getActivePage();
	if (page != null) {
		targetIds.addAll(page.getShowInPartIds());
	}
	IShowInTargetList targetList = getShowInTargetList(sourcePart);
	if (targetList != null) {
		String[] partIds = targetList.getShowInTargetIds();
		if (partIds != null) {
			for (int i = 0; i < partIds.length; ++i) {
				if (!targetIds.contains(partIds[i])) {
					targetIds.add(partIds[i]);
				}
			}
		}
	}
	page.sortShowInPartIds(targetIds);
	return targetIds;
}
	
/**
 * Returns the source part, or <code>null</code> if there is no applicable
 * source part
 * <p>
 * This implementation returns the current part in the window.
 * Subclasses may extend or reimplement.
 * 
 * @return the source part or <code>null</code>
 */
private IWorkbenchPart getSourcePart() {
	IWorkbenchPage page = getWindow().getActivePage();
	if (page != null) {
		return page.getActivePart();
	}
	return null;
}
	
/**
 * Returns the <code>IShowInSource</code> provided by the source part,
 * or <code>null</code> if it does not provide one.
 * 
 * @param sourcePart the source part
 * @return an <code>IShowInSource</code> or <code>null</code>
 */
private IShowInSource getShowInSource(IWorkbenchPart sourcePart) {
	if (sourcePart instanceof IShowInSource) {
		return (IShowInSource) sourcePart;
	}
	Object o = sourcePart.getAdapter(IShowInSource.class);
	if (o instanceof IShowInSource) {
		return (IShowInSource) o;
	}
	return null;
}

/**
 * Returns the <code>IShowInTargetList</code> for the given source part,
 * or <code>null</code> if it does not provide one.
 * 
 * @param sourcePart the source part
 * @return the <code>IShowInTargetList</code> or <code>null</code>
 */
private IShowInTargetList getShowInTargetList(IWorkbenchPart sourcePart) {
	if (sourcePart instanceof IShowInTargetList) {
		return (IShowInTargetList) sourcePart;
	}
	Object o = sourcePart.getAdapter(IShowInTargetList.class);
	if (o instanceof IShowInTargetList) {
		return (IShowInTargetList) o;
	}
	return null;
}

/**
 * Returns the <code>ShowInContext</code> to show in the selected target,
 * or <code>null</code> if there is no valid context to show.
 * <p>
 * This implementation obtains the context from the <code>IShowInSource</code>
 * of the source part (if provided), or, if the source part is an editor,
 * it creates the context from the editor's input and selection.
 * <p>
 * Subclasses may extend or reimplement.
 * 
 * @return the <code>ShowInContext</code> to show or <code>null</code>
 */
private ShowInContext getContext(IWorkbenchPart sourcePart) {
	IShowInSource source = getShowInSource(sourcePart);
	if (source != null) {
		ShowInContext context = source.getShowInContext();
		if (context != null) {
			return context;
		}
	}
	else if (sourcePart instanceof IEditorPart) {
		Object input = ((IEditorPart) sourcePart).getEditorInput();
		ISelectionProvider sp = sourcePart.getSite().getSelectionProvider();
		ISelection sel = sp == null ? null : sp.getSelection();
		return new ShowInContext(input, sel);
	}
	return null;
}
	
/**
 * Returns the view descriptors to show in the dialog.
 */
private IViewDescriptor[] getViewDescriptors(IWorkbenchPart sourcePart) {
	String srcId = sourcePart.getSite().getId();
	ArrayList ids = getShowInPartIds(sourcePart);
	ArrayList descs = new ArrayList();
	IViewRegistry reg = WorkbenchPlugin.getDefault().getViewRegistry();
	for (Iterator i = ids.iterator(); i.hasNext();) {
		String id = (String) i.next();
		if (!id.equals(srcId)) {
			IViewDescriptor desc = reg.find(id);
			if (desc != null) {
				descs.add(desc);
			}
		}
	}
	return (IViewDescriptor[]) descs.toArray(new IViewDescriptor[descs.size()]);
}
	

	
}
