/**
 * Created on Jun 23, 2008
 *
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA 
 */

package com.aelitis.azureus.ui.swt.views.skin.sidebar;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.ui.*;
import org.gudy.azureus2.plugins.ui.menus.MenuItem;
import org.gudy.azureus2.plugins.ui.menus.MenuItemListener;
import org.gudy.azureus2.pluginsimpl.local.PluginInitializer;
import org.gudy.azureus2.pluginsimpl.local.ui.config.ConfigSectionHolder;
import org.gudy.azureus2.pluginsimpl.local.ui.config.ConfigSectionRepository;
import org.gudy.azureus2.ui.common.util.MenuItemManager;
import org.gudy.azureus2.ui.swt.MenuBuildUtils;
import org.gudy.azureus2.ui.swt.URLTransfer;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.debug.ObfusticateImage;
import org.gudy.azureus2.ui.swt.mainwindow.TorrentOpener;
import org.gudy.azureus2.ui.swt.plugins.*;
import org.gudy.azureus2.ui.swt.pluginsimpl.*;
import org.gudy.azureus2.ui.swt.pluginsimpl.UISWTInstanceImpl.SWTViewListener;
import org.gudy.azureus2.ui.swt.views.IViewAlwaysInitialize;

import com.aelitis.azureus.ui.UIFunctions;
import com.aelitis.azureus.ui.UIFunctionsManager;
import com.aelitis.azureus.ui.common.updater.UIUpdater;
import com.aelitis.azureus.ui.common.viewtitleinfo.ViewTitleInfo;
import com.aelitis.azureus.ui.mdi.MdiEntry;
import com.aelitis.azureus.ui.mdi.MdiEntryVitalityImage;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.mdi.*;
import com.aelitis.azureus.ui.swt.skin.*;
import com.aelitis.azureus.ui.swt.skin.SWTSkinButtonUtility.ButtonListenerAdapter;
import com.aelitis.azureus.ui.swt.utils.FontUtils;

/**
 * @author TuxPaper
 * @created Jun 23, 2008
 *
 */
public class SideBar
	extends BaseMDI
	implements ObfusticateImage, AEDiagnosticsEvidenceGenerator
{
	protected static final boolean END_INDENT = Constants.isLinux
			|| Constants.isWindows2000 || Constants.isWindows9598ME;

	private static final boolean USE_PAINTITEM = !Utils.isCarbon;

	// Need to use paint even on Cocoa, because there's cases where an area
	// will become invalidated and we don't get a paintitem :(
	private static final boolean USE_PAINT = !Constants.isWindows && !Utils.isGTK;

	protected static final boolean USE_NATIVE_EXPANDER = Utils.isGTK;

	private static final int GAP_BETWEEN_LEVEL_1;
	
	
	static{
		GAP_BETWEEN_LEVEL_1 = Math.min( 5, Math.max( 0, COConfigurationManager.getIntParameter( "Side Bar Top Level Gap", 1 )));
	}

	protected static final int SIDEBAR_ATTENTION_PERIOD	 	= 500;
	protected static final int SIDEBAR_ATTENTION_DURATION 	= 5000;
	
	private SWTSkin skin;

	private SWTSkinObjectContainer soSideBarContents;

	private SWTSkinObject soSideBarList;

	private Tree tree;

	private Font fontHeader;

	private Font font;

	private SWTSkinObject soSideBarPopout;

	private SelectionListener dropDownSelectionListener;

	private DropTarget dropTarget;

	protected SideBarEntrySWT draggingOver;

	private Color fg;

	private Color bg;

	private List<MdiSWTMenuHackListener> listMenuHackListners;

	private List<SideBarEntrySWT> 	attention_seekers = new ArrayList<SideBarEntrySWT>();
	private TimerEventPeriodic		attention_event;

	private Composite cPluginsArea;

	public static SideBar instance = null;

	private List<UISWTViewCore> pluginViews = new ArrayList<UISWTViewCore>();

	
	public SideBar() {
		super();
		if (instance == null) {
			instance = this;
		}
		AEDiagnostics.addEvidenceGenerator(this);
	}

	// @see com.aelitis.azureus.ui.swt.skin.SWTSkinObjectAdapter#skinObjectCreated(com.aelitis.azureus.ui.swt.skin.SWTSkinObject, java.lang.Object)
	public Object skinObjectCreated(SWTSkinObject skinObject, Object params) {
		super.skinObjectCreated(skinObject, params);

		skin = skinObject.getSkin();

		soSideBarContents = (SWTSkinObjectContainer) skin.getSkinObject("sidebar-contents");
		soSideBarList = skin.getSkinObject("sidebar-list");
		soSideBarPopout = skin.getSkinObject("sidebar-pop");

		SWTSkinObjectContainer soSideBarPluginsArea = (SWTSkinObjectContainer) skin.getSkinObject("sidebar-plugins");
		if (soSideBarPluginsArea != null) {
			Composite composite = soSideBarPluginsArea.getComposite();
			cPluginsArea = new Composite(composite, SWT.NONE);
			GridLayout layout = new GridLayout();
			layout.marginHeight = layout.marginWidth = 0;
			layout.verticalSpacing = layout.horizontalSpacing = 0;
			cPluginsArea.setLayout(layout);
			cPluginsArea.setLayoutData(Utils.getFilledFormData());
		}

		// addTestMenus();

		createSideBar();

		try {
				// don't think this is required as the SideBar constructor (well SkinView) registers it
			
			UIUpdater updater = UIFunctionsManager.getUIFunctions().getUIUpdater();
			
			if ( !updater.isAdded( this )){
				
				updater.addUpdater(this);
			}
		} catch ( Throwable  e) {
			Debug.out(e);
		}

		Display.getDefault().addFilter(SWT.KeyDown, new Listener() {
			public void handleEvent(Event event) {
				// F9 is standard Seamonkey, but doesn't work on OSX
				// Command Option T is standard on OSX
				// F7 works on both
				if (event.keyCode == SWT.F9
						|| event.keyCode == SWT.F7
						|| (event.keyCode == 116 && event.stateMask == (SWT.COMMAND | SWT.ALT))) {
					event.doit = false;
					event.keyCode = 0;
					event.character = '\0';
					flipSideBarVisibility();
				}
			}
		});

		return null;
	}

	/**
	 * 
	 *
	 * @since 3.1.0.1
	 */
	/*
	private void addTestMenus() {
		// Add some test menus
		PluginManager pm = AzureusCoreFactory.getSingleton().getPluginManager();
		PluginInterface pi = pm.getDefaultPluginInterface();
		UIManager uim = pi.getUIManager();
		MenuManager menuManager = uim.getMenuManager();
		MenuItem menuItem = menuManager.addMenuItem("sidebar", "test menu");
		menuItem.addListener(new MenuItemListener() {
			public void selected(MenuItem menu, Object target) {
				ToolBarView tb = (ToolBarView) SkinViewManager.getByClass(ToolBarView.class);
				if (tb != null) {
					System.out.println("Found download Toolbar");
					ToolBarItem dlItem = tb.getToolBarItem("download");
					System.out.println("Download ToolBar Item is " + dlItem);
					if (dlItem != null) {
						System.out.println(dlItem.getSkinButton().getSkinObject());
					}
					dlItem.setEnabled(!dlItem.isEnabled());
				}

				if (target instanceof SideBarEntry) {
					SideBarEntry info = (SideBarEntry) target;
					System.err.println(info.getId() + " of " + info.getParentID()
							+ ";ds=" + info.getDatasource());
				}
			}
		});

		menuItem = menuManager.addMenuItem("sidebar." + SIDEBAR_SECTION_ACTIVITIES,
				"Activity Only Menu");
		menuItem.addListener(new MenuItemListener() {
			public void selected(MenuItem menu, Object target) {
				if (target instanceof SideBarEntry) {
					SideBarEntry info = (SideBarEntry) target;
					System.err.println(info.getId() + " of " + info.getParentID()
							+ ";ds=" + info.getDatasource());
				}
			}
		});
	}
	*/

	/**
	 * 
	 *
	 * @since 3.1.1.1
	 */
	public void flipSideBarVisibility() {
		final SWTSkinObjectSash soSash = (SWTSkinObjectSash) skin.getSkinObject("sidebar-sash");
		if (soSash == null) {
			return;
		}
		Utils.execSWTThreadLater(0, new AERunnable() {
			public void runSupport() {
				boolean visible = !soSash.isAboveVisible();
				
				soSash.setAboveVisible( visible );
				updateSidebarVisibility();
				
				COConfigurationManager.setParameter( "Show Side Bar", visible );
			}
		});
	}

	private void updateSidebarVisibility() {
		Utils.execSWTThread(new AERunnable() {
			public void runSupport() {
				final SWTSkinObjectSash soSash = (SWTSkinObjectSash) skin.getSkinObject("sidebar-sash");
				if (soSash == null) {
					return;
				}
				if (soSash.isAboveVisible()) {
					if (soSideBarPopout != null) {
						Object ld = soSideBarPopout.getControl().getLayoutData();
						if (ld instanceof FormData) {
							FormData fd = (FormData) ld;
							fd.width = 0;
						}
						soSideBarPopout.setVisible(false);

						Utils.relayout(soSideBarPopout.getControl());
					}
				} else {
					if (soSideBarPopout != null) {
						Object ld = soSideBarPopout.getControl().getLayoutData();
						if (ld instanceof FormData) {
							FormData fd = (FormData) ld;
							fd.width = 24;
						}
						soSideBarPopout.setVisible(true);
						soSideBarPopout.getControl().moveAbove(null);
						Utils.relayout(soSideBarPopout.getControl());
					}
				}
			}
		});
	}

	public boolean isVisible() {
		SWTSkinObjectSash soSash = (SWTSkinObjectSash) skin.getSkinObject("sidebar-sash");
		if (soSash == null) {
			return false;
		}
		return soSash.isAboveVisible();
	}

	// @see com.aelitis.azureus.ui.swt.views.skin.SkinView#showSupport(com.aelitis.azureus.ui.swt.skin.SWTSkinObject, java.lang.Object)
	public Object skinObjectInitialShow(SWTSkinObject skinObject, Object params) {

		UIManager ui_manager = PluginInitializer.getDefaultInterface().getUIManager();
		ui_manager.addUIListener(new UIManagerListener() {
			public void UIDetached(UIInstance instance) {
			}
			
			public void UIAttached(UIInstance instance) {
				if (instance instanceof UISWTInstance) {
					final AESemaphore wait_sem = new AESemaphore( "SideBar:wait" );
					
					Utils.execSWTThread(new AERunnable() {
						public void runSupport() {
							try{
								try {
									loadCloseables();
								} catch (Throwable t) {
									Debug.out(t);
								}
	
								setupPluginViews();
								
								createSideBarPluginViews();
							}finally{
								
								wait_sem.release();
							}
						}
					});
					
						// we need to wait for the loadCloseables to complete as there is code in MainMDISetup that runs on the 'UIAttachedComplete'
						// callback that needs the closables to be loaded (when setting 'start tab') otherwise the order gets broken
					
					if ( !wait_sem.reserve(10*1000)){
						
						Debug.out( "eh?");
					}
				}
			}
		});

		COConfigurationManager.addParameterListener(
			"Show Side Bar",
			new ParameterListener()
			{
				public void 
				parameterChanged(
					String name )
				{
					boolean visible = COConfigurationManager.getBooleanParameter( name );
					
					if ( visible != isVisible()){
						
						flipSideBarVisibility();
					}
				}
			});
		
		updateSidebarVisibility();
		
		return null;
	}

	public Object skinObjectDestroyed(SWTSkinObject skinObject, Object params) {
		try {
			UIFunctionsManager.getUIFunctions().getUIUpdater().removeUpdater(this);
		} catch (Exception e) {
			Debug.out(e);
		}

		return super.skinObjectDestroyed(skinObject, params);
	}

	private void createSideBar() {
		if (soSideBarList == null) {
			return;
		}
		Composite parent = (Composite) soSideBarList.getControl();

		tree = new Tree(parent, SWT.FULL_SELECTION | SWT.V_SCROLL
				| SWT.DOUBLE_BUFFERED | SWT.NO_SCROLL);
		tree.setHeaderVisible(false);

		new SideBarToolTips(this, tree);

		tree.setLayoutData(Utils.getFilledFormData());

		SWTSkinProperties skinProperties = skin.getSkinProperties();
		bg = skinProperties.getColor("color.sidebar.bg");
		fg = skinProperties.getColor("color.sidebar.fg");

		tree.setBackground(bg);
		tree.setForeground(fg);
		FontData[] fontData = tree.getFont().getFontData();

		int fontHeight = (Constants.isOSX ? 11 : 12)
				+ (tree.getItemHeight() > 18 ? tree.getItemHeight() - 18 : 0);
		
		if (Constants.isLinux && tree.getItemHeight() >= 38) {
			fontHeight = 13;
		}

		fontData[0].setStyle(SWT.BOLD);
		FontUtils.getFontHeightFromPX(tree.getDisplay(), fontData, null, fontHeight);
		fontHeader = new Font(tree.getDisplay(), fontData);
		font = FontUtils.getFontWithHeight(tree.getFont(), null, fontHeight);

		tree.setFont(font);
		
			// after a scroll we need to recalculate the hit areas as they will have moved!
		
		tree.getVerticalBar().addSelectionListener( 
			new SelectionAdapter()
			{
				public void widgetSelected(SelectionEvent e) {
					if ( e.detail == SWT.None ){
						SideBarEntrySWT[] sideBarEntries = mapIdToEntry.values().toArray(
								new SideBarEntrySWT[0]);
						swt_updateSideBarHitAreasY(sideBarEntries);
					}
				}
			});
		
		Listener treeListener = new Listener() {
			TreeItem lastTopItem = null;

			boolean mouseDowned = false;

			private boolean wasExpanded;

			public void handleEvent(final Event event) {
				TreeItem treeItem = (TreeItem) event.item;
				Tree tree = getTree();

				try {
					switch (event.type) {
						case SWT.MeasureItem: {
							int clientWidth = tree.getClientArea().width;
							String text = treeItem.getText(event.index);
							Point size = event.gc.textExtent(text);
							if (event.x + event.width < clientWidth) {
								event.width = size.x + event.x; // tree.getClientArea().width;
								event.x = 0;
							}

							if (Constants.isWindows) {
								event.width = clientWidth - event.x;
							}

							event.height = 20;

							break;
						}
						case SWT.PaintItem: {
							if (USE_PAINTITEM) {
								SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
								//System.out.println("PaintItem: " + event.item + ";" + event.index + ";" + event.detail + ";" + id + ";" + event.getBounds() + ";" + event.gc.getClipping());
								if (entry != null) {
									boolean selected = currentEntry == entry
											&& entry.isSelectable();

									if (!selected) {
										event.detail &= ~SWT.SELECTED;
									} else {
										event.detail |= SWT.SELECTED;
									}
									entry.swt_paintSideBar(event);
								}
							}
							break;
						}

						case SWT.Paint: {
							//System.out.println("Paint: " + event.getBounds() + ";" + event.detail + ";" + event.index + ";" + event.gc.getClipping() + "  " + Debug.getCompressedStackTrace());
							if (!USE_PAINT) {
								return;
							}
							Rectangle bounds = event.getBounds();
							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
							int y = event.y + 1;
							treeItem = tree.getItem(new Point(indent, y));

							while (treeItem != null) {
								SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
								Rectangle itemBounds = entry == null ? null
										: entry.swt_getBounds();

								// null itemBounds is weird, the entry must be disposed. it 
								// happened once, so let's check..
								if (itemBounds != null && entry != null) {
									event.item = treeItem;

									boolean selected = currentEntry == entry
											&& entry.isSelectable();
									event.detail = selected ? SWT.SELECTED : SWT.NONE;

									Rectangle newClip = bounds.intersection(itemBounds);
									//System.out.println("Paint " + id + " @ " + newClip);
									event.setBounds(newClip);
									event.gc.setClipping(newClip);

									entry.swt_paintSideBar(event);

									y = itemBounds.y + itemBounds.height + 1;
								} else {
									y += tree.getItemHeight();
								}

								if (y > bounds.y + bounds.height) {
									break;
								}
								TreeItem oldTreeItem = treeItem;
								treeItem = tree.getItem(new Point(indent, y));
								if (oldTreeItem == treeItem) {
									break;
								}
							}

							if (tree.getTopItem() != lastTopItem) {
								lastTopItem = tree.getTopItem();
								SideBarEntrySWT[] sideBarEntries = mapIdToEntry.values().toArray(
										new SideBarEntrySWT[0]);
								swt_updateSideBarHitAreasY(sideBarEntries);
							}

							break;
						}

						case SWT.EraseItem: {
							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
							if (entry == null) {
								event.detail = 0;
							}
							//event.detail &= ~SWT.FOREGROUND;
							//event.detail &= ~(SWT.FOREGROUND | SWT.BACKGROUND);
							event.doit = true;
							break;
						}

						case SWT.Resize: {
							tree.redraw();
							break;
						}

						case SWT.Selection: {
							if (treeItem == null) {
								return;
							}
							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
							if (entry != null && entry.isSelectable()) {
								showEntry(entry);
							} else if (currentEntry != null) {
								TreeItem topItem = tree.getTopItem();

								// prevent "jumping" in the case where selection is off screen
								// setSelection would jump the item on screen, and then
								// showItem would jump back to where the user was.
								tree.setRedraw(false);
								TreeItem ti = ((SideBarEntrySWT) currentEntry).getTreeItem();
								if (ti != null) {
									tree.setSelection(ti);
								}

								tree.setTopItem(topItem);
								tree.setRedraw(true);

								event.doit = false;
							}
							break;
						}

						case SWT.MouseMove: {
							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
							treeItem = tree.getItem(new Point(indent, event.y));
							SideBarEntrySWT entry = (SideBarEntrySWT) ((treeItem == null || treeItem.isDisposed())
									? null : treeItem.getData("MdiEntry"));

							int cursorNo = SWT.CURSOR_ARROW;
							if (treeItem != null) {
								Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
								if (closeArea != null && closeArea.contains(event.x, event.y)) {
									cursorNo = SWT.CURSOR_HAND;
								} else if (entry != null && !entry.isCollapseDisabled()
										&& treeItem.getItemCount() > 0) {
									cursorNo = SWT.CURSOR_HAND;
								}
							}

							Cursor cursor = event.display.getSystemCursor(cursorNo);
							if (tree.getCursor() != cursor) {
								tree.setCursor(cursor);
							}

							if (treeItem != null) {
								wasExpanded = entry != null && entry.isExpanded();
							} else {
								wasExpanded = false;
							}
							break;
						}

						case SWT.MouseDown: {
							mouseDowned = true;
							break;
						}

						case SWT.MouseUp: {
							if (!mouseDowned) {
								return;
							}
							mouseDowned = false;
							if (tree.getItemCount() == 0 || event.button != 1) {
								return;
							}
							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
							treeItem = tree.getItem(new Point(indent, event.y));
							if (treeItem == null) {
								return;
							}
							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");

							Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
							if (closeArea != null && closeArea.contains(event.x, event.y)) {
								treeItem.dispose();
								return;
							} else if (currentEntry != entry && Constants.isOSX) {
								showEntry(entry);
							}

							if (entry != null) {
								MdiEntryVitalityImage[] vitalityImages = entry.getVitalityImages();
								for (int i = 0; i < vitalityImages.length; i++) {
									SideBarVitalityImageSWT vitalityImage = (SideBarVitalityImageSWT) vitalityImages[i];
									if (vitalityImage == null || !vitalityImage.isVisible()) {
										continue;
									}
									Rectangle hitArea = vitalityImage.getHitArea();
									if (hitArea == null) {
										continue;
									}
									// setHitArea needs it relative to entry
									Rectangle itemBounds = entry.swt_getBounds();
									int relY = event.y - (itemBounds == null ? 0 : itemBounds.y);

									if (hitArea.contains(event.x, relY)) {
										vitalityImage.triggerClickedListeners(event.x, relY);
										return;
									}
								}

								if (!entry.isCollapseDisabled() && treeItem.getItemCount() > 0) {
									if (!entry.isSelectable() || event.x < 20) {
										// Note: On Windows, user can expand row by clicking the invisible area where the OS twisty would be
  									MdiEntry currentEntry = getCurrentEntry();
  									if (currentEntry != null
  											&& entry.getId().equals(currentEntry.getParentID())) {
  										showEntryByID(SIDEBAR_SECTION_LIBRARY);
  									}
  									entry.setExpanded(!wasExpanded);
  									wasExpanded = !wasExpanded;
									}
								}
							}

							break;
						}

						case SWT.Dispose: {
							fontHeader.dispose();
							font.dispose();
							if (dropTarget != null && !dropTarget.isDisposed()) {
								dropTarget.dispose();
							}
							saveCloseables();

							break;
						}

						case SWT.Collapse: {
							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");

							if (entry.isCollapseDisabled()) {
								tree.setRedraw(false);
								Display.getDefault().asyncExec(new Runnable() {
									public void run() {
										((TreeItem) event.item).setExpanded(true);
										getTree().setRedraw(true);
									}
								});
							} else {
								MdiEntry currentEntry = getCurrentEntry();
								if (currentEntry != null
										&& entry.getId().equals(currentEntry.getParentID())) {
									showEntryByID(SIDEBAR_SECTION_LIBRARY);
								}
							}
							break;
						}

					}
				} catch (Exception e) {
					Debug.out(e);
				}
			}
		};
		tree.addListener(SWT.MeasureItem, treeListener);
		tree.addListener(SWT.Resize, treeListener);
		tree.addListener(SWT.Paint, treeListener);
		if (USE_PAINTITEM) {
			tree.addListener(SWT.PaintItem, treeListener);
			tree.addListener(SWT.EraseItem, treeListener);
		}

		tree.addListener(SWT.Selection, treeListener);
		tree.addListener(SWT.Dispose, treeListener);

		// For icons
		tree.addListener(SWT.MouseUp, treeListener);
		tree.addListener(SWT.MouseDown, treeListener);

		// For cursor
		tree.addListener(SWT.MouseMove, treeListener);

		// to disable collapsing
		tree.addListener(SWT.Collapse, treeListener);

		dropTarget = new DropTarget(tree, DND.DROP_COPY);
		dropTarget.setTransfer(new Transfer[] {
			URLTransfer.getInstance(),
			FileTransfer.getInstance(),
			TextTransfer.getInstance(),
		});

		dropTarget.addDropListener(new DropTargetAdapter() {
			public void dropAccept(DropTargetEvent event) {
				event.currentDataType = URLTransfer.pickBestType(event.dataTypes,
						event.currentDataType);
			}

			public void dragEnter(DropTargetEvent event) {
			}

			public void dragOperationChanged(DropTargetEvent event) {
			}

			// @see org.eclipse.swt.dnd.DropTargetAdapter#dragOver(org.eclipse.swt.dnd.DropTargetEvent)
			public void dragOver(DropTargetEvent event) {
				TreeItem treeItem = (event.item instanceof TreeItem)
						? (TreeItem) event.item : null;

				if (treeItem != null) {
					SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");

					draggingOver = entry;
				} else {
					draggingOver = null;
				}
				if (draggingOver == null || !draggingOver.hasDropListeners()) {

					boolean isTorrent = TorrentOpener.doesDropHaveTorrents(event);

					if (isTorrent) {
						event.detail = DND.DROP_COPY;
					} else {
						event.detail = DND.DROP_NONE;
					}
					draggingOver = null;
				} else if ((event.operations & DND.DROP_LINK) > 0)
					event.detail = DND.DROP_LINK;
				else if ((event.operations & DND.DROP_COPY) > 0)
					event.detail = DND.DROP_COPY;
				else if ((event.operations & DND.DROP_DEFAULT) > 0)
					event.detail = DND.DROP_COPY;

				if (Constants.isOSX) {
					tree.redraw();
				}
				
				event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND;
			}

			// @see org.eclipse.swt.dnd.DropTargetAdapter#dragLeave(org.eclipse.swt.dnd.DropTargetEvent)
			public void dragLeave(DropTargetEvent event) {
				draggingOver = null;
				tree.redraw();
			}

			public void drop(DropTargetEvent event) {
				draggingOver = null;
				tree.redraw();
				if (!(event.item instanceof TreeItem)) {
					defaultDrop(event);
					return;
				}
				TreeItem treeItem = (TreeItem) event.item;

				SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");

				boolean handled = entry.triggerDropListeners(event.data);
				if (!handled) {
					defaultDrop(event);
				}
			}
		});

		final Menu menuTree = new Menu(tree);
		tree.setMenu(menuTree);

		menuTree.addMenuListener(new MenuListener() {
			boolean bShown = false;

			public void menuHidden(MenuEvent e) {
				bShown = false;

				if (Constants.isOSX) {
					return;
				}

				// Must dispose in an asyncExec, otherwise SWT.Selection doesn't
				// get fired (async workaround provided by Eclipse Bug #87678)
				Utils.execSWTThreadLater(0, new AERunnable() {
					public void runSupport() {
						if (bShown || menuTree.isDisposed()) {
							return;
						}
						Utils.disposeSWTObjects(menuTree.getItems());
					}
				});
			}

			public void menuShown(MenuEvent e) {
				Utils.disposeSWTObjects(menuTree.getItems());

				bShown = true;

				Point ptMouse = tree.toControl(e.display.getCursorLocation());

				int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
				TreeItem treeItem = tree.getItem(new Point(indent, ptMouse.y));
				if (treeItem == null) {
					return;
				}
				SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");

				fillMenu(menuTree, entry);

				if (menuTree.getItemCount() == 0) {
					Utils.execSWTThreadLater(0, new AERunnable() {
						public void runSupport() {
							menuTree.setVisible(false);
						}
					});
				}
			}
		});

		if (soSideBarPopout != null) {
			SWTSkinObject soDropDown = skin.getSkinObject("sidebar-dropdown");
			if (soDropDown != null) {

				final Menu menuDropDown = new Menu(soDropDown.getControl());

				menuDropDown.addMenuListener(new MenuListener() {
					boolean bShown = false;

					public void menuHidden(MenuEvent e) {
						bShown = false;

						if (Constants.isOSX) {
							return;
						}

						// Must dispose in an asyncExec, otherwise SWT.Selection doesn't
						// get fired (async workaround provided by Eclipse Bug #87678)
						Utils.execSWTThreadLater(0, new AERunnable() {
							public void runSupport() {
								if (bShown || menuDropDown.isDisposed()) {
									return;
								}
								Utils.disposeSWTObjects(menuDropDown.getItems());
							}
						});
					}

					public void menuShown(MenuEvent e) {
						Utils.disposeSWTObjects(menuDropDown.getItems());

						bShown = true;

						fillDropDownMenu(menuDropDown, tree.getItems(), 0);
					}
				});

				dropDownSelectionListener = new SelectionListener() {
					public void widgetSelected(SelectionEvent e) {
						String id = (String) e.widget.getData("Plugin.viewID");
						showEntryByID(id);
					}

					public void widgetDefaultSelected(SelectionEvent e) {
					}
				};

				SWTSkinButtonUtility btnDropDown = new SWTSkinButtonUtility(soDropDown);
				btnDropDown.addSelectionListener(new ButtonListenerAdapter() {
					public void pressed(SWTSkinButtonUtility buttonUtility,
							SWTSkinObject skinObject, int stateMask) {
						Control c = buttonUtility.getSkinObject().getControl();
						menuDropDown.setLocation(c.getDisplay().getCursorLocation());
						menuDropDown.setVisible(!menuDropDown.getVisible());
					}
				});
			}

			SWTSkinObject soExpand = skin.getSkinObject("sidebar-expand");
			if (soExpand != null) {
				SWTSkinButtonUtility btnExpand = new SWTSkinButtonUtility(soExpand);
				btnExpand.addSelectionListener(new ButtonListenerAdapter() {
					public void pressed(SWTSkinButtonUtility buttonUtility,
							SWTSkinObject skinObject, int stateMask) {
						flipSideBarVisibility();
					}
				});
			}

		}
	}

	private void createSideBarPluginViews() {
		if (cPluginsArea == null) {
			return;
		}
		UISWTInstanceImpl uiSWTinstance = (UISWTInstanceImpl) UIFunctionsManagerSWT.getUIFunctionsSWT().getUISWTInstance();

		if (uiSWTinstance == null) {
			return;
		}

		UISWTViewEventListenerHolder[] pluginViews = uiSWTinstance.getViewListeners(UISWTInstance.VIEW_SIDEBAR_AREA);
		for (UISWTViewEventListenerHolder l : pluginViews) {
			if (l != null) {
				try {
					UISWTViewImpl view = new UISWTViewImpl(UISWTInstance.VIEW_SIDEBAR_AREA,
							l.getViewID(), l, null);
					addSideBarView(view, cPluginsArea);
					cPluginsArea.getParent().getParent().layout(true, true);
				} catch (Exception e) {
					e.printStackTrace();
					// skip, plugin probably specifically asked to not be added
				}
			}
		}
		
		uiSWTinstance.addSWTViewListener(new SWTViewListener() {

			public void setViewAdded(final String parent, final String id,
					final UISWTViewEventListener l) {
				if (!parent.equals(UISWTInstance.VIEW_SIDEBAR_AREA)) {
					return;
				}
				Utils.execSWTThread(new AERunnable() {

					public void runSupport() {
						try {
							UISWTViewImpl view = new UISWTViewImpl(parent, id, l, null);
							addSideBarView(view, cPluginsArea);
						} catch (Exception e) {
							e.printStackTrace();
							// skip, plugin probably specifically asked to not be added
						}
					}
				});
			}

			public void setViewRemoved(final String parent, final String id,
					final UISWTViewEventListener l) {
				if (!parent.equals(UISWTInstance.VIEW_SIDEBAR_AREA)) {
					return;
				}
				Utils.execSWTThread(new AERunnable() {

					public void runSupport() {
						try {
							for (UISWTViewCore view : SideBar.this.pluginViews) {
								if (l.equals(view.getEventListener())) {
									view.closeView();
								}
							}
						} catch (Exception e) {
							e.printStackTrace();
							// skip, plugin probably specifically asked to not be added
						}
					}
				});
			}
		});
		
		cPluginsArea.getParent().getParent().layout(true, true);
	}
	
	private void addSideBarView(UISWTViewImpl view, Composite cPluginsArea) {
		Composite parent = new Composite(cPluginsArea, SWT.NONE);
		GridData gridData = new GridData();
		gridData.grabExcessHorizontalSpace = true;
		gridData.horizontalAlignment = SWT.FILL;
		parent.setLayoutData(gridData);
		parent.setLayout(new FormLayout());
		//parent.setBackground(ColorCache.getRandomColor());
		//cPluginsArea.setBackground(ColorCache.getRandomColor());

		view.initialize(parent);
		parent.setVisible(true);

		Control[] children = parent.getChildren();
		for (int i = 0; i < children.length; i++) {
			Control control = children[i];
			Object ld = control.getLayoutData();
			boolean useGridLayout = ld != null && (ld instanceof GridData);
			if (useGridLayout) {
				GridLayout gridLayout = new GridLayout();
				gridLayout.horizontalSpacing = 0;
				gridLayout.marginHeight = 0;
				gridLayout.marginWidth = 0;
				gridLayout.verticalSpacing = 0;
				parent.setLayout(gridLayout);
				break;
			} else if (ld == null) {
				control.setLayoutData(Utils.getFilledFormData());
			}
		}
		
		pluginViews.add(view);
	}


	/**
	 * @param event
	 */
	protected void defaultDrop(DropTargetEvent event) {
		TorrentOpener.openDroppedTorrents(event, false);
	}

	/**
	 * @param menuDropDown
	 *
	 * @since 3.1.1.1
	 */
	protected void fillDropDownMenu(Menu menuDropDown, TreeItem[] items,
			int indent) {
		String s = "";
		for (int i = 0; i < indent; i++) {
			s += "   ";
		}
		for (int i = 0; i < items.length; i++) {
			TreeItem treeItem = items[i];
			
			SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
			if (entry == null) {
				continue;
			}
			org.eclipse.swt.widgets.MenuItem menuItem = new org.eclipse.swt.widgets.MenuItem(
					menuDropDown, entry.isSelectable() ? SWT.RADIO : SWT.CASCADE);
			
			String id = entry.getId();
			menuItem.setData("Plugin.viewID", id);
			ViewTitleInfo titleInfo = entry.getViewTitleInfo();
			String ind = "";
			if (titleInfo != null) {
				String o = (String) titleInfo.getTitleInfoProperty(ViewTitleInfo.TITLE_INDICATOR_TEXT);
				if (o != null) {
					ind = "  (" + o + ")";
					//ind = "\t" + o;
				}
			}
			menuItem.setText(s + entry.getTitle() + ind);
			menuItem.addSelectionListener(dropDownSelectionListener);
			if (currentEntry != null && currentEntry.getId().equals(id)) {
				menuItem.setSelection(true);
			}

			TreeItem[] subItems = treeItem.getItems();
			if (subItems.length > 0) {
				Menu parent = menuDropDown;
				if (!entry.isSelectable()) {
					parent = new Menu(menuDropDown.getParent().getShell(), SWT.DROP_DOWN);
					menuItem.setMenu(parent);
				}
				

				fillDropDownMenu(parent, subItems, indent + 1);
			}
		}
	}

	/**
	 * @param menuTree
	 *
	 * @since 3.1.0.1
	 */
	protected void fillMenu(Menu menuTree, final MdiEntry entry) {
		org.gudy.azureus2.plugins.ui.menus.MenuItem[] menu_items;

		menu_items = MenuItemManager.getInstance().getAllAsArray("sidebar");

		MenuBuildUtils.addPluginMenuItems(menu_items, menuTree, false, true,
				new MenuBuildUtils.MenuItemPluginMenuControllerImpl(new Object[] {
					entry
				}));

		if (entry != null) {

			menu_items = MenuItemManager.getInstance().getAllAsArray(
					"sidebar." + entry.getId());

			if (menu_items.length == 0) {

				UIPluginView view = entry.getView();

				if (view instanceof UISWTView) {

					PluginInterface pi = ((UISWTView) view).getPluginInterface();

					if (pi != null) {

						final List<String> relevant_sections = new ArrayList<String>();

						List<ConfigSectionHolder> sections = ConfigSectionRepository.getInstance().getHolderList();

						for (ConfigSectionHolder cs : sections) {

							if (pi == cs.getPluginInterface()) {

								relevant_sections.add(cs.configSectionGetName());
							}
						}

						if (relevant_sections.size() > 0) {

							MenuItem mi = pi.getUIManager().getMenuManager().addMenuItem(
									"sidebar." + entry.getId(),
									"MainWindow.menu.view.configuration");

							mi.addListener(new MenuItemListener() {
								public void selected(MenuItem menu, Object target) {
									UIFunctions uif = UIFunctionsManager.getUIFunctions();

									if (uif != null) {

										for (String s : relevant_sections) {

											uif.openView(UIFunctions.VIEW_CONFIG, s);
										}
									}
								}
							});

							menu_items = MenuItemManager.getInstance().getAllAsArray(
									"sidebar." + entry.getId());
						}
					}
				}
			}

			MenuBuildUtils.addPluginMenuItems(menu_items, menuTree, false, true,
					new MenuBuildUtils.MenuItemPluginMenuControllerImpl(new Object[] {
						entry
					}));

			MdiSWTMenuHackListener[] menuHackListeners = getMenuHackListeners();
			for (MdiSWTMenuHackListener l : menuHackListeners) {
				try {
					l.menuWillBeShown(entry, menuTree);
				} catch (Exception e) {
					Debug.out(e);
				}
			}
			if (currentEntry instanceof SideBarEntrySWT) {
				menuHackListeners = ((SideBarEntrySWT) entry).getMenuHackListeners();
				for (MdiSWTMenuHackListener l : menuHackListeners) {
					try {
						l.menuWillBeShown(entry, menuTree);
					} catch (Exception e) {
						Debug.out(e);
					}
				}
			}
		}
	}

	public void addListener(MdiSWTMenuHackListener l) {
		synchronized (this) {
			if (listMenuHackListners == null) {
				listMenuHackListners = new ArrayList<MdiSWTMenuHackListener>(1);
			}
			if (!listMenuHackListners.contains(l)) {
				listMenuHackListners.add(l);
			}
		}
	}

	public void removeListener(MdiSWTMenuHackListener l) {
		synchronized (this) {
			if (listMenuHackListners == null) {
				listMenuHackListners = new ArrayList<MdiSWTMenuHackListener>(1);
			}
			listMenuHackListners.remove(l);
		}
	}

	public MdiSWTMenuHackListener[] getMenuHackListeners() {
		synchronized (this) {
			if (listMenuHackListners == null) {
				return new MdiSWTMenuHackListener[0];
			}
			return listMenuHackListners.toArray(new MdiSWTMenuHackListener[0]);
		}
	}

	/**
	 * 
	 *
	 * @since 3.1.1.1
	 */
	private void swt_updateSideBarHitAreasY(SideBarEntrySWT[] entries) {
		for (int x = 0; x < entries.length; x++) {
			SideBarEntrySWT entry = entries[x];
			TreeItem treeItem = entry.getTreeItem();
			if (treeItem == null || treeItem.isDisposed()) {
				continue;
			}
			Rectangle itemBounds = entry.swt_getBounds();

			if ( itemBounds != null ){
				if (entry.isCloseable()) {
					Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
					if (closeArea != null) {
						closeArea.y = itemBounds.y + (itemBounds.height - closeArea.height)
								/ 2;
					}
				}
	
				MdiEntryVitalityImage[] vitalityImages = entry.getVitalityImages();
				for (int i = 0; i < vitalityImages.length; i++) {
					SideBarVitalityImageSWT vitalityImage = (SideBarVitalityImageSWT) vitalityImages[i];
					if (!vitalityImage.isVisible()) {
						continue;
					}
					Image image = vitalityImage.getImage();
					if (image != null) {
						Rectangle bounds = vitalityImage.getHitArea();
						if (bounds == null) {
							continue;
						}
						bounds.y = (itemBounds.height - bounds.height) / 2;
					}
				}
			}
		}
	}

	protected int indexOf(final MdiEntry entryLibrary) {
		Object o = Utils.execSWTThreadWithObject("indexOf", new AERunnableObject() {
			public Object runSupport() {
				TreeItem treeItem = ((SideBarEntrySWT) entryLibrary).getTreeItem();
				if (treeItem == null) {
					return -1;
				}
				TreeItem parentItem = treeItem.getParentItem();
				if (parentItem != null) {
					return parentItem.indexOf(treeItem);
				}
				return tree.indexOf(treeItem);
			}
		}, 500);
		if (o instanceof Number) {
			return ((Number) o).intValue();
		}
		return -1;
	}

	public MdiEntry createHeader(String id, String titleID, String preferredAfterID) {
		MdiEntry oldEntry = getEntry(id);
		if (oldEntry != null) {
			return oldEntry;
		}

		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id);
		entry.setSelectable(false);
		entry.setPreferredAfterID(preferredAfterID);
		entry.setTitleID(titleID);

		setupNewEntry(entry, id, true, false);

		return entry;
	}

	public MdiEntry createEntryFromView(String parentID, UISWTViewCore iview, String id,
			Object datasource, boolean closeable, boolean show, boolean expand) {
		if (id == null) {
			id = iview.getClass().getName();
			int i = id.lastIndexOf('.');
			if (i > 0) {
				id = id.substring(i + 1);
			}
		}

		MdiEntry oldEntry = getEntry(id);
		if (oldEntry != null) {
			if (show) {
				showEntry(oldEntry);
			}
			return oldEntry;
		}

		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id);

		entry.setCoreView(iview);
		entry.setDatasource(datasource);
		entry.setParentID(parentID);

		setupNewEntry(entry, id, expand, closeable);

		if (iview instanceof IViewAlwaysInitialize) {
			entry.build();
		}

		if (show) {
			showEntry(entry);
		}

		return entry;
	}

	private void setupNewEntry(final SideBarEntrySWT entry, final String id,
			final boolean expandParent, final boolean closeable) {
		//System.out.println("createItem " + id + ";" + entry.getParentID() + ";" + Debug.getCompressedStackTrace());
		synchronized (mapIdToEntry) {
			mapIdToEntry.put(id, entry);
		}

		entry.setCloseable(closeable);
		entry.setParentSkinObject(soSideBarContents);

		if (SIDEBAR_HEADER_PLUGINS.equals(entry.getParentID())
				&& entry.getImageLeftID() == null) {
			entry.setImageLeftID("image.sidebar.plugin");
		}

		Utils.execSWTThreadLater(0, new AERunnable() {
			public void runSupport() {
				_setupNewEntry(entry, id, expandParent, closeable);
			}
		});
	}

	protected void _setupNewEntry(SideBarEntrySWT entry, String id,
			boolean expandParent, boolean closeable) {
		String parentID = entry.getParentID();
		MdiEntry parent = getEntry(parentID);
		TreeItem parentTreeItem = null;
		if (parent instanceof SideBarEntrySWT) {
			SideBarEntrySWT parentSWT = (SideBarEntrySWT) parent;
			parentTreeItem = parentSWT.getTreeItem();
			if (expandParent) {
				parentTreeItem.setExpanded(true);
			}
		}
		int index = -1;
		String preferredAfterID = entry.getPreferredAfterID();
		if (preferredAfterID != null) {
			if (preferredAfterID.length() == 0) {
				index = 0;
			} else {
				boolean hack_it = preferredAfterID.startsWith( "~" );
				
				if ( hack_it ){
					
						//hack - this means preferred BEFORE ID...
					
					preferredAfterID = preferredAfterID.substring(1);
				}
				
				MdiEntry entryAbove = getEntry(preferredAfterID);
				if (entryAbove != null) {
					index = indexOf(entryAbove);
					if ( hack_it ){
				
					}else{
						if (index >= 0) {
							index++;
						}
					}
					//System.out.println("ENTRY " + id + " is going to go below " + entryAbove.getId() + " at " + index);
				}
			}
		}

		if (index == -1 && parent == null) {
			index = 0;
			String[] order = getPreferredOrder();
			for (int i = 0; i < order.length; i++) {
				String orderID = order[i];
				if (orderID.equals(id)) {
					break;
				}
				MdiEntry entry2 = getEntry(orderID);
				if (entry2 != null) {
					int i2 = indexOf(entry2);
					if (i2 >= 0) {
						index = i2 + 1;
					}
				}
			}
		}

		if (GAP_BETWEEN_LEVEL_1 > 0 && parentTreeItem == null
				&& tree.getItemCount() > 0 && index != 0) {
			for (int i=0;i<GAP_BETWEEN_LEVEL_1;i++){
				createTreeItem(null, index);
				if (index >= 0) {
					index++;
				}
			}
		}
		TreeItem treeItem = createTreeItem(parentTreeItem, index);
		if (treeItem != null) {
			treeItem.setData("MdiEntry", entry);
			entry.setTreeItem(treeItem);

			triggerEntryLoadedListeners(entry);
		}
		if (GAP_BETWEEN_LEVEL_1 > 0 && parentTreeItem == null
				&& tree.getItemCount() > 1 && index == 0) {
			for (int i=0;i<GAP_BETWEEN_LEVEL_1;i++){
				createTreeItem(null, ++index);
			}
		}
	}

	private TreeItem createTreeItem(Object parentSwtItem, int index) {
		TreeItem treeItem;

		if (parentSwtItem == null) {
			parentSwtItem = tree;
		}

		if (parentSwtItem instanceof Tree) {
			Tree tree = (Tree) parentSwtItem;
			if (tree.isDisposed()) {
				return null;
			}
			if (index >= 0 && index < tree.getItemCount()) {
				treeItem = new TreeItem(tree, SWT.NONE, index);
			} else {
				treeItem = new TreeItem(tree, SWT.NONE);
			}
		} else {
			if (((TreeItem) parentSwtItem).isDisposed()) {
				return null;
			}
			if (index >= 0 && index < ((TreeItem) parentSwtItem).getItemCount()) {
				treeItem = new TreeItem((TreeItem) parentSwtItem, SWT.NONE, index);
			} else {
				treeItem = new TreeItem((TreeItem) parentSwtItem, SWT.NONE);
			}
		}

		return treeItem;
	}

	public void showEntry(MdiEntry newEntry) {
		if (tree.isDisposed()) {
			return;
		}

		if (newEntry == null || !newEntry.isSelectable()) {
			return;
		}

		final SideBarEntrySWT oldEntry = (SideBarEntrySWT) currentEntry;

		//System.out.println("showEntry " + newEntry.getId() + "; was " + (oldEntry == null ? "null" : oldEntry.getId()) + " via " + Debug.getCompressedStackTrace());
		if (currentEntry == newEntry) {
			triggerSelectionListener(newEntry, newEntry);
			return;
		}

		// show new
		currentEntry = (MdiEntrySWT) newEntry;

		if (oldEntry != null && oldEntry != newEntry) {
			oldEntry.redraw();
		}

		if (currentEntry != null) {
			((BaseMdiEntry) currentEntry).show();
		}

		// hide old
		if (oldEntry != null && oldEntry != newEntry) {
			oldEntry.hide();
			oldEntry.redraw();
		}

		newEntry.redraw();

		triggerSelectionListener(newEntry, oldEntry);
	}

	/**
	 *  @see com.aelitis.azureus.ui.swt.mdi.BaseMDI#createEntryFromEventListener(java.lang.String, org.gudy.azureus2.ui.swt.plugins.UISWTViewEventListener, java.lang.String, boolean, java.lang.Object)
	 */
	public MdiEntry createEntryFromEventListener(String parentID,
			UISWTViewEventListener l, String id, boolean closeable, Object datasource, String preferredAfterID) {

		MdiEntry oldEntry = getEntry(id);
		if (oldEntry != null) {
			return oldEntry;
		}

		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id);
		try {
			// hack: setEventListner will create the UISWTView.
			// We need to have the entry available for the view to use
			// if it wants
			synchronized (mapIdToEntry) {
				mapIdToEntry.put(id, entry);
			}

			entry.setParentID(parentID);
			entry.setDatasource(datasource);
			entry.setPreferredAfterID(preferredAfterID);
			setupNewEntry(entry, id, false, closeable);

			entry.setEventListener(l);

			if (l instanceof IViewAlwaysInitialize) {
				entry.build();
			}
		} catch (Exception e) {
			Debug.out(e);
			entry.close(true);
		}

		return entry;
	}

	// @see com.aelitis.azureus.ui.swt.mdi.BaseMDI#createEntryFromSkinRef(java.lang.String, java.lang.String, java.lang.String, java.lang.String, com.aelitis.azureus.ui.common.viewtitleinfo.ViewTitleInfo, java.lang.Object, boolean, java.lang.String)
	public MdiEntry createEntryFromSkinRef(String parentID, String id,
			String configID, String title, ViewTitleInfo titleInfo, Object params,
			boolean closeable, String preferredAfterID) {

		MdiEntry oldEntry = getEntry(id);
		if (oldEntry != null) {
			return oldEntry;
		}

		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id);

		entry.setTitle(title);
		entry.setSkinRef(configID, params);
		entry.setParentID(parentID);
		entry.setViewTitleInfo(titleInfo);
		entry.setPreferredAfterID(preferredAfterID);

		setupNewEntry(entry, id, false, closeable);

		return entry;
	}

	// @see com.aelitis.azureus.ui.swt.utils.UIUpdatable#updateUI()
	public void updateUI() {
		Object[] views = pluginViews.toArray();
		for (int i = 0; i < views.length; i++) {
			try {
				UISWTViewCore view = (UISWTViewCore) views[i];
				Composite composite = view.getComposite();
				if (composite.isDisposed()) {
					pluginViews.remove(view);
					continue;
				}
				if (composite.isVisible()) {
					view.triggerEvent(UISWTViewEvent.TYPE_REFRESH, null);
				}
			} catch (Exception e) {
				Debug.out(e);
			}
		}

		if (currentEntry == null || currentEntry.getView() == null
				|| tree.getSelectionCount() == 0) {
			return;
		}
		currentEntry.updateUI();
	}

	protected boolean wasEntryLoadedOnce(String id) {
		@SuppressWarnings("deprecation")
		boolean loadedOnce = COConfigurationManager.getBooleanParameter("sb.once."
				+ id, false);
		return loadedOnce;
	}
	
	protected void setEntryLoadedOnce(String id) {
		COConfigurationManager.setParameter("sb.once." + id, true);
	}

	public Font getHeaderFont() {
		return fontHeader;
	}

	protected Tree getTree() {
		return tree;
	}

	// @see com.aelitis.azureus.ui.swt.mdi.MultipleDocumentInterfaceSWT#getEntryFromSkinObject(org.gudy.azureus2.ui.swt.plugins.PluginUISWTSkinObject)
	public MdiEntrySWT getEntryFromSkinObject(
			PluginUISWTSkinObject pluginSkinObject) {
		if (pluginSkinObject instanceof SWTSkinObject) {
			Control control = ((SWTSkinObject) pluginSkinObject).getControl();
			while (control != null && !control.isDisposed()) {
				Object entry = control.getData("BaseMDIEntry");
				if (entry instanceof BaseMdiEntry) {
					BaseMdiEntry mdiEntry = (BaseMdiEntry) entry;
					return mdiEntry;
				}
				control = control.getParent();
			}
		}
		return null;
	}
	
	protected void
	requestAttention(
		SideBarEntrySWT	entry )
	{
		synchronized( attention_seekers ){
	
			if ( !attention_seekers.contains( entry )){
				
				attention_seekers.add( entry );
			}
			
			if ( attention_event == null ){
				
				attention_event = 
					SimpleTimer.addPeriodicEvent(
						"SideBar:attention",
						SIDEBAR_ATTENTION_PERIOD,
						new TimerEventPerformer()
						{
							int	tick_count = 0;
							
							public void 
							perform(
								TimerEvent event ) 
							{
								tick_count++;
								
								final List<SideBarEntrySWT>	repaints = new ArrayList<SideBarEntrySWT>();
								
								synchronized( attention_seekers ){
									
									Iterator<SideBarEntrySWT> it = attention_seekers.iterator();
									
									while ( it.hasNext()){
										
										SideBarEntrySWT entry = it.next();
										
										if ( entry.isDisposed()){
											
											it.remove();
											
										}else{
										
											if ( !entry.attentionUpdate( tick_count )){
																																				
												it.remove();
											}
											
											repaints.add( entry );
										}
									}
									
									if ( attention_seekers.size() == 0 ){
										
										TimerEventPeriodic ev = attention_event;
										
										if ( ev != null ){
											
											ev.cancel();
											
											attention_event = null;
										}
									}
								}
								
								if ( repaints.size() > 0 ){
									
									Utils.execSWTThread(
										new AERunnable() 
										{
											@Override
											public void 
											runSupport() 
											{
												for ( SideBarEntrySWT entry: repaints ){
													
													entry.redraw();
												}
											}
										});
								}
							}
						});
						
			}
		}
	}
	
	public void generate(IndentWriter writer) {
		MdiEntrySWT[] entries = getEntriesSWT();
		for (MdiEntrySWT entry : entries) {
			if (entry == null) {
				continue;
			}

			UISWTViewCore view = entry.getCoreView();

			if (!(view instanceof AEDiagnosticsEvidenceGenerator)) {
				writer.println("Sidebar View (No Generator): " + entry.getId());
				try {
					writer.indent();

					writer.println("Parent: " + entry.getParentID());
					writer.println("Title: " + entry.getTitle());
				} catch (Exception e) {

				} finally {

					writer.exdent();
				}
			}
			
		}
	}

	// @see org.gudy.azureus2.ui.swt.debug.ObfusticateImage#obfusticatedImage(org.eclipse.swt.graphics.Image)
	public Image obfusticatedImage(Image image) {

		Rectangle treeBounds = tree.getBounds();
		SideBarEntrySWT[] sideBarEntries = mapIdToEntry.values().toArray(
				new SideBarEntrySWT[0]);
		for (SideBarEntrySWT entry : sideBarEntries) {
			Rectangle entryBounds = entry.swt_getBounds();
			if (entryBounds != null && treeBounds.intersects(entryBounds)) {
				entry.obfusticatedImage(image);
			}
		}
		return image;
	}
}
