1 /**
2  * Created on Jun 23, 2008
3  *
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  */
20 
21 package com.aelitis.azureus.ui.swt.views.skin.sidebar;
22 
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Stack;
27 
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.dnd.*;
30 import org.eclipse.swt.events.*;
31 import org.eclipse.swt.graphics.*;
32 import org.eclipse.swt.layout.*;
33 import org.eclipse.swt.widgets.*;
34 import org.gudy.azureus2.core3.config.COConfigurationManager;
35 import org.gudy.azureus2.core3.config.ParameterListener;
36 import org.gudy.azureus2.core3.util.*;
37 import org.gudy.azureus2.plugins.PluginInterface;
38 import org.gudy.azureus2.plugins.PluginManager;
39 import org.gudy.azureus2.plugins.ui.*;
40 import org.gudy.azureus2.plugins.ui.menus.MenuItem;
41 import org.gudy.azureus2.plugins.ui.menus.MenuItemFillListener;
42 import org.gudy.azureus2.plugins.ui.menus.MenuItemListener;
43 import org.gudy.azureus2.plugins.ui.menus.MenuManager;
44 import org.gudy.azureus2.ui.swt.URLTransfer;
45 import org.gudy.azureus2.ui.swt.Utils;
46 import org.gudy.azureus2.ui.swt.debug.ObfusticateImage;
47 import org.gudy.azureus2.ui.swt.mainwindow.TorrentOpener;
48 import org.gudy.azureus2.ui.swt.plugins.*;
49 import org.gudy.azureus2.ui.swt.pluginsimpl.*;
50 import org.gudy.azureus2.ui.swt.pluginsimpl.UISWTInstanceImpl.SWTViewListener;
51 import org.gudy.azureus2.ui.swt.views.IViewAlwaysInitialize;
52 
53 import com.aelitis.azureus.core.AzureusCoreFactory;
54 import com.aelitis.azureus.ui.UIFunctionsManager;
55 import com.aelitis.azureus.ui.common.updater.UIUpdater;
56 import com.aelitis.azureus.ui.common.viewtitleinfo.ViewTitleInfo;
57 import com.aelitis.azureus.ui.mdi.MdiEntry;
58 import com.aelitis.azureus.ui.mdi.MdiEntryVitalityImage;
59 import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
60 import com.aelitis.azureus.ui.swt.mdi.*;
61 import com.aelitis.azureus.ui.swt.skin.*;
62 import com.aelitis.azureus.ui.swt.skin.SWTSkinButtonUtility.ButtonListenerAdapter;
63 import com.aelitis.azureus.ui.swt.utils.FontUtils;
64 import com.aelitis.azureus.ui.swt.views.skin.SkinnedDialog;
65 
66 /**
67  * @author TuxPaper
68  * @created Jun 23, 2008
69  *
70  */
71 public class SideBar
72 	extends BaseMDI
73 	implements ObfusticateImage, AEDiagnosticsEvidenceGenerator
74 {
75 	protected static final boolean isGTK3 = Utils.isGTK
76 			&& System.getProperty("org.eclipse.swt.internal.gtk.version", "2").startsWith("3");
77 
78 	protected static final boolean END_INDENT = Constants.isLinux
79 			|| Constants.isWindows2000 || Constants.isWindows9598ME;
80 
81 	private static final boolean USE_PAINTITEM = !Utils.isCarbon;
82 
83 	// Need to use paint even on Cocoa, because there's cases where an area
84 	// will become invalidated and we don't get a paintitem :(
85 	private static final boolean USE_PAINT = !Constants.isWindows && !Utils.isGTK;
86 
87 	protected static final boolean USE_NATIVE_EXPANDER = Utils.isGTK;
88 
89 	private static final int GAP_BETWEEN_LEVEL_1;
90 
91 
92 	static{
93 		GAP_BETWEEN_LEVEL_1 = Math.min( 5, Math.max( 0, COConfigurationManager.getIntParameter( "Side Bar Top Level Gap", 1 )));
94 	}
95 
96 	protected static final int SIDEBAR_ATTENTION_PERIOD	 	= 500;
97 	protected static final int SIDEBAR_ATTENTION_DURATION 	= 5000;
98 
99 	private SWTSkin skin;
100 
101 	private SWTSkinObjectContainer soSideBarContents;
102 
103 	private SWTSkinObject soSideBarList;
104 
105 	private Tree tree;
106 
107 	private Font fontHeader;
108 
109 	private Font font;
110 
111 	private SWTSkinObject soSideBarPopout;
112 
113 	private SelectionListener dropDownSelectionListener;
114 
115 	private DropTarget dropTarget;
116 
117 	protected SideBarEntrySWT draggingOver;
118 
119 	private Color fg;
120 
121 	private Color bg;
122 
123 	private List<SideBarEntrySWT> 	attention_seekers = new ArrayList<SideBarEntrySWT>();
124 	private TimerEventPeriodic		attention_event;
125 
126 	private Composite cPluginsArea;
127 
128 	public static SideBar instance = null;
129 
130 	private List<UISWTViewCore> pluginViews = new ArrayList<UISWTViewCore>();
131 
132 
SideBar()133 	public SideBar() {
134 		super();
135 		if (instance == null) {
136 			instance = this;
137 		}
138 		AEDiagnostics.addEvidenceGenerator(this);
139 	}
140 
141 	// @see com.aelitis.azureus.ui.swt.skin.SWTSkinObjectAdapter#skinObjectCreated(com.aelitis.azureus.ui.swt.skin.SWTSkinObject, java.lang.Object)
skinObjectCreated(SWTSkinObject skinObject, Object params)142 	public Object skinObjectCreated(SWTSkinObject skinObject, Object params) {
143 		super.skinObjectCreated(skinObject, params);
144 
145 		skin = skinObject.getSkin();
146 
147 		soSideBarContents = (SWTSkinObjectContainer) skin.getSkinObject("sidebar-contents");
148 		soSideBarList = skin.getSkinObject("sidebar-list");
149 		soSideBarPopout = skin.getSkinObject("sidebar-pop");
150 
151 		SWTSkinObjectContainer soSideBarPluginsArea = (SWTSkinObjectContainer) skin.getSkinObject("sidebar-plugins");
152 		if (soSideBarPluginsArea != null) {
153 			Composite composite = soSideBarPluginsArea.getComposite();
154 			cPluginsArea = new Composite(composite, SWT.NONE);
155 			GridLayout layout = new GridLayout();
156 			layout.marginHeight = layout.marginWidth = 0;
157 			layout.verticalSpacing = layout.horizontalSpacing = 0;
158 			cPluginsArea.setLayout(layout);
159 			cPluginsArea.setLayoutData(Utils.getFilledFormData());
160 		}
161 
162 		addGeneralMenus();
163 
164 		createSideBar();
165 
166 		try {
167 				// don't think this is required as the SideBar constructor (well SkinView) registers it
168 
169 			UIUpdater updater = UIFunctionsManager.getUIFunctions().getUIUpdater();
170 
171 			if ( !updater.isAdded( this )){
172 
173 				updater.addUpdater(this);
174 			}
175 		} catch ( Throwable  e) {
176 			Debug.out(e);
177 		}
178 
179 		Display.getDefault().addFilter(SWT.KeyDown, new Listener() {
180 			public void handleEvent(Event event) {
181 				// F9 is standard Seamonkey, but doesn't work on OSX
182 				// Command Option T is standard on OSX
183 				// F7 works on both
184 				if (event.keyCode == SWT.F9
185 						|| event.keyCode == SWT.F7
186 						|| (event.keyCode == 116 && event.stateMask == (SWT.COMMAND | SWT.ALT))) {
187 					event.doit = false;
188 					event.keyCode = 0;
189 					event.character = '\0';
190 					flipSideBarVisibility();
191 				}else if (event.keyCode == SWT.F4 && event.stateMask == SWT.CTRL ){
192 					MdiEntry entry = getCurrentEntry();
193 
194 					if ( entry instanceof SideBarEntrySWT && entry.isCloseable()){
195 
196 						((SideBarEntrySWT)entry).getTreeItem().dispose();
197 					}
198 				}
199 			}
200 		});
201 
202 		return null;
203 	}
204 
205 	/**
206 	 *
207 	 *
208 	 * @since 3.1.0.1
209 	 */
210 
addGeneralMenus()211 	private void addGeneralMenus() {
212 
213 		PluginManager pm = AzureusCoreFactory.getSingleton().getPluginManager();
214 		PluginInterface pi = pm.getDefaultPluginInterface();
215 		UIManager uim = pi.getUIManager();
216 		MenuManager menuManager = uim.getMenuManager();
217 		MenuItem menuItem = menuManager.addMenuItem("sidebar._end_", "menu.pop.out");
218 
219 		menuItem.addFillListener(
220 			new MenuItemFillListener() {
221 
222 				public void menuWillBeShown(MenuItem menu, Object data) {
223 					SideBarEntrySWT sbe = (SideBarEntrySWT)currentEntry;
224 
225 					menu.setVisible( sbe != null && sbe.canBuildStandAlone());
226 				}
227 			});
228 
229 		menuItem.addListener(new MenuItemListener() {
230 			public void selected(MenuItem menu, Object target) {
231 				SideBarEntrySWT sbe = (SideBarEntrySWT)currentEntry;
232 
233 				if ( sbe != null ){
234 					SkinnedDialog skinnedDialog =
235 							new SkinnedDialog(
236 									"skin3_dlg_sidebar_popout",
237 									"shell",
238 									null,	// standalone
239 									SWT.RESIZE | SWT.MAX | SWT.DIALOG_TRIM);
240 
241 					SWTSkin skin = skinnedDialog.getSkin();
242 
243 					SWTSkinObjectContainer cont = sbe.buildStandAlone((SWTSkinObjectContainer)skin.getSkinObject( "content-area" ));
244 
245 					if ( cont != null ){
246 
247 						skinnedDialog.setTitle( sbe.getTitle());
248 
249 						skinnedDialog.open();
250 
251 					}else{
252 
253 						skinnedDialog.close();
254 					}
255 				}
256 			}
257 		});
258 
259 	}
260 
261 
262 	/**
263 	 *
264 	 *
265 	 * @since 3.1.1.1
266 	 */
flipSideBarVisibility()267 	public void flipSideBarVisibility() {
268 		final SWTSkinObjectSash soSash = (SWTSkinObjectSash) skin.getSkinObject("sidebar-sash");
269 		if (soSash == null) {
270 			return;
271 		}
272 		Utils.execSWTThreadLater(0, new AERunnable() {
273 			public void runSupport() {
274 				boolean visible = !soSash.isAboveVisible();
275 
276 				soSash.setAboveVisible( visible );
277 				updateSidebarVisibility();
278 
279 				COConfigurationManager.setParameter( "Show Side Bar", visible );
280 			}
281 		});
282 	}
283 
updateSidebarVisibility()284 	private void updateSidebarVisibility() {
285 		Utils.execSWTThread(new AERunnable() {
286 			public void runSupport() {
287 				final SWTSkinObjectSash soSash = (SWTSkinObjectSash) skin.getSkinObject("sidebar-sash");
288 				if (soSash == null) {
289 					return;
290 				}
291 				if (soSash.isAboveVisible()) {
292 					if (soSideBarPopout != null) {
293 						Object ld = soSideBarPopout.getControl().getLayoutData();
294 						if (ld instanceof FormData) {
295 							FormData fd = (FormData) ld;
296 							fd.width = 0;
297 						}
298 						soSideBarPopout.setVisible(false);
299 
300 						Utils.relayout(soSideBarPopout.getControl());
301 					}
302 				} else {
303 					if (soSideBarPopout != null) {
304 						Object ld = soSideBarPopout.getControl().getLayoutData();
305 						if (ld instanceof FormData) {
306 							FormData fd = (FormData) ld;
307 							fd.width = 24;
308 						}
309 						soSideBarPopout.setVisible(true);
310 						soSideBarPopout.getControl().moveAbove(null);
311 						Utils.relayout(soSideBarPopout.getControl());
312 					}
313 				}
314 			}
315 		});
316 	}
317 
isVisible()318 	public boolean isVisible() {
319 		SWTSkinObjectSash soSash = (SWTSkinObjectSash) skin.getSkinObject("sidebar-sash");
320 		if (soSash == null) {
321 			return false;
322 		}
323 		return soSash.isAboveVisible();
324 	}
325 
326 	// @see com.aelitis.azureus.ui.swt.views.skin.SkinView#showSupport(com.aelitis.azureus.ui.swt.skin.SWTSkinObject, java.lang.Object)
skinObjectInitialShow(SWTSkinObject skinObject, Object params)327 	public Object skinObjectInitialShow(SWTSkinObject skinObject, Object params) {
328 
329 		super.skinObjectInitialShow(skinObject, params);
330 
331 		COConfigurationManager.addParameterListener(
332 			"Show Side Bar",
333 			new ParameterListener()
334 			{
335 				public void
336 				parameterChanged(
337 					String name )
338 				{
339 					boolean visible = COConfigurationManager.getBooleanParameter( name );
340 
341 					if ( visible != isVisible()){
342 
343 						flipSideBarVisibility();
344 					}
345 				}
346 			});
347 
348 		updateSidebarVisibility();
349 
350 		return null;
351 	}
352 
353 	/* (non-Javadoc)
354 	 * @see com.aelitis.azureus.ui.swt.mdi.BaseMDI#setupPluginViews()
355 	 */
356 	@Override
setupPluginViews()357 	protected void setupPluginViews() {
358 		super.setupPluginViews();
359 		createSideBarPluginViews();
360 	}
361 
skinObjectDestroyed(SWTSkinObject skinObject, Object params)362 	public Object skinObjectDestroyed(SWTSkinObject skinObject, Object params) {
363 		try {
364 			UIFunctionsManager.getUIFunctions().getUIUpdater().removeUpdater(this);
365 		} catch (Exception e) {
366 			Debug.out(e);
367 		}
368 
369 		return super.skinObjectDestroyed(skinObject, params);
370 	}
371 
createSideBar()372 	private void createSideBar() {
373 		if (soSideBarList == null) {
374 			return;
375 		}
376 		Composite parent = (Composite) soSideBarList.getControl();
377 
378 		tree = new Tree(parent, SWT.FULL_SELECTION | SWT.V_SCROLL
379 				| SWT.DOUBLE_BUFFERED | SWT.NO_SCROLL);
380 		tree.setHeaderVisible(false);
381 
382 		new SideBarToolTips(this, tree);
383 
384 		tree.setLayoutData(Utils.getFilledFormData());
385 
386 		SWTSkinProperties skinProperties = skin.getSkinProperties();
387 		bg = skinProperties.getColor("color.sidebar.bg");
388 		fg = skinProperties.getColor("color.sidebar.fg");
389 
390 		COConfigurationManager.addParameterListener(
391 			"config.skin.color.sidebar.bg",
392 			new ParameterListener() {
393 
394 				public void parameterChanged(String parameterName) {
395 
396 					Utils.execSWTThread(
397 						new Runnable()
398 						{
399 							public void
400 							run()
401 							{
402 								swt_updateSideBarColors();
403 							}
404 						});
405 				}
406 			});
407 
408 		tree.setBackground(bg);
409 		tree.setForeground(fg);
410 		FontData[] fontData = tree.getFont().getFontData();
411 
412 		int fontHeight;
413 
414 		if (isGTK3) {
415 			fontHeight = fontData[0].getHeight();
416 		} else {
417 			fontHeight = (Constants.isOSX ? 11 : 12)
418 				+ (tree.getItemHeight() > 18 ? tree.getItemHeight() - 18 : 0);
419 
420 			if (Constants.isLinux && tree.getItemHeight() >= 38) {
421 				fontHeight = 13;
422 			}
423 		}
424 
425 		fontData[0].setStyle(SWT.BOLD);
426 		FontUtils.getFontHeightFromPX(tree.getDisplay(), fontData, null, fontHeight);
427 		fontHeader = new Font(tree.getDisplay(), fontData);
428 		font = FontUtils.getFontWithHeight(tree.getFont(), null, fontHeight);
429 
430 		tree.setFont(font);
431 
432 			// after a scroll we need to recalculate the hit areas as they will have moved!
433 
434 		tree.getVerticalBar().addSelectionListener(
435 			new SelectionAdapter()
436 			{
437 				public void widgetSelected(SelectionEvent e) {
438 					if ( e.detail == SWT.None ){
439 						SideBarEntrySWT[] sideBarEntries = getEntries( new SideBarEntrySWT[0]);
440 						swt_updateSideBarHitAreasY(sideBarEntries);
441 					}
442 				}
443 			});
444 
445 		Listener treeListener = new Listener() {
446 			TreeItem lastTopItem = null;
447 
448 			boolean mouseDowned = false;
449 
450 			Rectangle lastCloseAreaClicked = null;
451 
452 			private boolean wasExpanded;
453 
454 			public void handleEvent(final Event event) {
455 				TreeItem treeItem = (TreeItem) event.item;
456 				Tree tree = getTree();
457 
458 				try {
459 					switch (event.type) {
460 						case SWT.MeasureItem: {
461 							int clientWidth = tree.getClientArea().width;
462 							String text = treeItem.getText(event.index);
463 							Point size = event.gc.textExtent(text);
464 							if (event.x + event.width < clientWidth) {
465 								event.width = size.x + event.x; // tree.getClientArea().width;
466 								event.x = 0;
467 							}
468 
469 							if (Constants.isWindows) {
470 								event.width = clientWidth - event.x;
471 							}
472 
473 							event.height = 20;
474 
475 							break;
476 						}
477 						case SWT.PaintItem: {
478 							if (USE_PAINTITEM) {
479 								SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
480 								//System.out.println("PaintItem: " + event.item + ";" + event.index + ";" + event.detail + ";" + id + ";" + event.getBounds() + ";" + event.gc.getClipping());
481 								if (entry != null) {
482 									boolean selected = currentEntry == entry
483 											&& entry.isSelectable();
484 
485 									if (!selected) {
486 										event.detail &= ~SWT.SELECTED;
487 									} else {
488 										event.detail |= SWT.SELECTED;
489 									}
490 									entry.swt_paintSideBar(event);
491 								}
492 							}
493 							break;
494 						}
495 
496 						case SWT.Paint: {
497 							//System.out.println("Paint: " + event.getBounds() + ";" + event.detail + ";" + event.index + ";" + event.gc.getClipping() + "  " + Debug.getCompressedStackTrace());
498 							if (!USE_PAINT) {
499 								return;
500 							}
501 							Rectangle bounds = event.getBounds();
502 							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
503 							int y = event.y + 1;
504 							treeItem = tree.getItem(new Point(indent, y));
505 
506 							while (treeItem != null) {
507 								SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
508 								Rectangle itemBounds = entry == null ? null
509 										: entry.swt_getBounds();
510 
511 								// null itemBounds is weird, the entry must be disposed. it
512 								// happened once, so let's check..
513 								if (itemBounds != null && entry != null) {
514 									event.item = treeItem;
515 
516 									boolean selected = currentEntry == entry
517 											&& entry.isSelectable();
518 									event.detail = selected ? SWT.SELECTED : SWT.NONE;
519 
520 									Rectangle newClip = bounds.intersection(itemBounds);
521 									//System.out.println("Paint " + id + " @ " + newClip);
522 									event.setBounds(newClip);
523 									Utils.setClipping(event.gc, newClip);
524 
525 									entry.swt_paintSideBar(event);
526 
527 									y = itemBounds.y + itemBounds.height + 1;
528 								} else {
529 									y += tree.getItemHeight();
530 								}
531 
532 								if (y > bounds.y + bounds.height) {
533 									break;
534 								}
535 								TreeItem oldTreeItem = treeItem;
536 								treeItem = tree.getItem(new Point(indent, y));
537 								if (oldTreeItem == treeItem) {
538 									break;
539 								}
540 							}
541 
542 							if (tree.getTopItem() != lastTopItem) {
543 								lastTopItem = tree.getTopItem();
544 								SideBarEntrySWT[] sideBarEntries = getEntries( new SideBarEntrySWT[0]);
545 								swt_updateSideBarHitAreasY(sideBarEntries);
546 							}
547 
548 							break;
549 						}
550 
551 						case SWT.EraseItem: {
552 							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
553 							if (entry == null) {
554 								event.detail = 0;
555 							}
556 							//event.detail &= ~SWT.FOREGROUND;
557 							//event.detail &= ~(SWT.FOREGROUND | SWT.BACKGROUND);
558 							event.doit = true;
559 							break;
560 						}
561 
562 						case SWT.Resize: {
563 							tree.redraw();
564 							break;
565 						}
566 
567 						case SWT.Selection: {
568 							if (treeItem == null) {
569 								return;
570 							}
571 							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
572 							if (entry != null && entry.isSelectable()) {
573 								Point cursorLocation = tree.toControl(event.display.getCursorLocation());
574 								if (lastCloseAreaClicked != null && lastCloseAreaClicked.contains(cursorLocation.x, cursorLocation.y)) {
575 									return;
576 								}
577 
578 								showEntry(entry);
579 							} else if (currentEntry != null) {
580 								TreeItem topItem = tree.getTopItem();
581 
582 								// prevent "jumping" in the case where selection is off screen
583 								// setSelection would jump the item on screen, and then
584 								// showItem would jump back to where the user was.
585 								tree.setRedraw(false);
586 								TreeItem ti = ((SideBarEntrySWT) currentEntry).getTreeItem();
587 								if (ti != null) {
588 									tree.setSelection(ti);
589 								}
590 
591 								tree.setTopItem(topItem);
592 								tree.setRedraw(true);
593 
594 								event.doit = false;
595 							}
596 							break;
597 						}
598 
599 						case SWT.MouseMove: {
600 							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
601 							treeItem = tree.getItem(new Point(indent, event.y));
602 							SideBarEntrySWT entry = (SideBarEntrySWT) ((treeItem == null || treeItem.isDisposed())
603 									? null : treeItem.getData("MdiEntry"));
604 
605 							int cursorNo = SWT.CURSOR_ARROW;
606 							if (treeItem != null) {
607 								Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
608 								if (closeArea != null && closeArea.contains(event.x, event.y)) {
609 									cursorNo = SWT.CURSOR_HAND;
610 								} else if (entry != null && !entry.isCollapseDisabled()
611 										&& treeItem.getItemCount() > 0) {
612 									cursorNo = SWT.CURSOR_HAND;
613 								}
614 							}
615 
616 							Cursor cursor = event.display.getSystemCursor(cursorNo);
617 							if (tree.getCursor() != cursor) {
618 								tree.setCursor(cursor);
619 							}
620 
621 							if (treeItem != null) {
622 								wasExpanded = entry != null && entry.isExpanded();
623 							} else {
624 								wasExpanded = false;
625 							}
626 							break;
627 						}
628 
629 						case SWT.MouseDown: {
630 							mouseDowned = true;
631 							lastCloseAreaClicked  = null;
632 							if (tree.getItemCount() == 0 || event.button != 1) {
633 								return;
634 							}
635 							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
636 							treeItem = tree.getItem(new Point(indent, event.y));
637 							if (treeItem == null) {
638 								return;
639 							}
640 							Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
641 							if (closeArea != null && closeArea.contains(event.x, event.y)) {
642 								lastCloseAreaClicked = closeArea;
643 								treeItem.dispose();
644 								// pretend we don't have a mouse down, so we don't process a showEntry
645 								mouseDowned = false;
646 							}
647 							break;
648 						}
649 
650 						case SWT.MouseUp: {
651 							if (!mouseDowned) {
652 								return;
653 							}
654 							mouseDowned = false;
655 							if (tree.getItemCount() == 0 || event.button != 1) {
656 								return;
657 							}
658 							int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
659 							treeItem = tree.getItem(new Point(indent, event.y));
660 							if (treeItem == null) {
661 								return;
662 							}
663 							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
664 
665 							Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
666 							if (closeArea != null && closeArea.contains(event.x, event.y)) {
667 								//treeItem.dispose();
668 								return;
669 							} else if (currentEntry != entry && Constants.isOSX) {
670 								showEntry(entry);
671 							}
672 
673 							if (entry != null) {
674 								MdiEntryVitalityImage[] vitalityImages = entry.getVitalityImages();
675 								for (int i = 0; i < vitalityImages.length; i++) {
676 									SideBarVitalityImageSWT vitalityImage = (SideBarVitalityImageSWT) vitalityImages[i];
677 									if (vitalityImage == null || !vitalityImage.isVisible()) {
678 										continue;
679 									}
680 									Rectangle hitArea = vitalityImage.getHitArea();
681 									if (hitArea == null) {
682 										continue;
683 									}
684 									// setHitArea needs it relative to entry
685 									Rectangle itemBounds = entry.swt_getBounds();
686 									int relY = event.y - (itemBounds == null ? 0 : itemBounds.y);
687 
688 									if (hitArea.contains(event.x, relY)) {
689 										vitalityImage.triggerClickedListeners(event.x, relY);
690 										return;
691 									}
692 								}
693 
694 								if (!entry.isCollapseDisabled() && treeItem.getItemCount() > 0) {
695 									if (!entry.isSelectable() || event.x < 20) {
696 										// Note: On Windows, user can expand row by clicking the invisible area where the OS twisty would be
697   									MdiEntry currentEntry = getCurrentEntry();
698   									if (currentEntry != null
699   											&& entry.getId().equals(currentEntry.getParentID())) {
700   										showEntryByID(SIDEBAR_SECTION_LIBRARY);
701   									}
702   									entry.setExpanded(!wasExpanded);
703   									wasExpanded = !wasExpanded;
704 									}
705 								}
706 							}
707 
708 							break;
709 						}
710 
711 						case SWT.Dispose: {
712 							fontHeader.dispose();
713 							font.dispose();
714 							if (dropTarget != null && !dropTarget.isDisposed()) {
715 								dropTarget.dispose();
716 							}
717 							saveCloseables();
718 
719 							break;
720 						}
721 
722 						case SWT.Collapse: {
723 							SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
724 
725 							if (entry.isCollapseDisabled()) {
726 								tree.setRedraw(false);
727 								Display.getDefault().asyncExec(new Runnable() {
728 									public void run() {
729 										((TreeItem) event.item).setExpanded(true);
730 										getTree().setRedraw(true);
731 									}
732 								});
733 							} else {
734 								MdiEntry currentEntry = getCurrentEntry();
735 								if (currentEntry != null
736 										&& entry.getId().equals(currentEntry.getParentID())) {
737 									showEntryByID(SIDEBAR_SECTION_LIBRARY);
738 								}
739 							}
740 							break;
741 						}
742 
743 					}
744 				} catch (Exception e) {
745 					Debug.out(e);
746 				}
747 			}
748 		};
749 		tree.addListener(SWT.MeasureItem, treeListener);
750 		tree.addListener(SWT.Resize, treeListener);
751 		tree.addListener(SWT.Paint, treeListener);
752 		if (USE_PAINTITEM) {
753 			tree.addListener(SWT.PaintItem, treeListener);
754 			tree.addListener(SWT.EraseItem, treeListener);
755 		}
756 
757 		tree.addListener(SWT.Selection, treeListener);
758 		tree.addListener(SWT.Dispose, treeListener);
759 
760 		// For icons
761 		tree.addListener(SWT.MouseUp, treeListener);
762 		tree.addListener(SWT.MouseDown, treeListener);
763 
764 		// For cursor
765 		tree.addListener(SWT.MouseMove, treeListener);
766 
767 		// to disable collapsing
768 		tree.addListener(SWT.Collapse, treeListener);
769 
770 		dropTarget = new DropTarget(tree, DND.DROP_COPY);
771 		dropTarget.setTransfer(new Transfer[] {
772 			URLTransfer.getInstance(),
773 			FileTransfer.getInstance(),
774 			TextTransfer.getInstance(),
775 		});
776 
777 		dropTarget.addDropListener(new DropTargetAdapter() {
778 			public void dropAccept(DropTargetEvent event) {
779 				event.currentDataType = URLTransfer.pickBestType(event.dataTypes,
780 						event.currentDataType);
781 			}
782 
783 			public void dragEnter(DropTargetEvent event) {
784 			}
785 
786 			public void dragOperationChanged(DropTargetEvent event) {
787 			}
788 
789 			// @see org.eclipse.swt.dnd.DropTargetAdapter#dragOver(org.eclipse.swt.dnd.DropTargetEvent)
790 			public void dragOver(DropTargetEvent event) {
791 				TreeItem treeItem = (event.item instanceof TreeItem)
792 						? (TreeItem) event.item : null;
793 
794 				if (treeItem != null) {
795 					SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
796 
797 					draggingOver = entry;
798 				} else {
799 					draggingOver = null;
800 				}
801 				if (draggingOver == null || !draggingOver.hasDropListeners()) {
802 
803 					boolean isTorrent = TorrentOpener.doesDropHaveTorrents(event);
804 
805 					if (isTorrent) {
806 						event.detail = DND.DROP_COPY;
807 					} else {
808 						event.detail = DND.DROP_NONE;
809 					}
810 					draggingOver = null;
811 				} else if ((event.operations & DND.DROP_LINK) > 0)
812 					event.detail = DND.DROP_LINK;
813 				else if ((event.operations & DND.DROP_COPY) > 0)
814 					event.detail = DND.DROP_COPY;
815 				else if ((event.operations & DND.DROP_DEFAULT) > 0)
816 					event.detail = DND.DROP_COPY;
817 
818 				if (Constants.isOSX) {
819 					tree.redraw();
820 				}
821 
822 				event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND;
823 			}
824 
825 			// @see org.eclipse.swt.dnd.DropTargetAdapter#dragLeave(org.eclipse.swt.dnd.DropTargetEvent)
826 			public void dragLeave(DropTargetEvent event) {
827 				draggingOver = null;
828 				tree.redraw();
829 			}
830 
831 			public void drop(DropTargetEvent event) {
832 				draggingOver = null;
833 				tree.redraw();
834 				if (!(event.item instanceof TreeItem)) {
835 					defaultDrop(event);
836 					return;
837 				}
838 				TreeItem treeItem = (TreeItem) event.item;
839 
840 				SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
841 
842 				boolean handled = entry != null && entry.triggerDropListeners(event.data);
843 				if (!handled) {
844 					defaultDrop(event);
845 				}
846 			}
847 		});
848 
849 		final Menu menuTree = new Menu(tree);
850 		tree.setMenu(menuTree);
851 
852 		menuTree.addMenuListener(new MenuListener() {
853 			boolean bShown = false;
854 
855 			public void menuHidden(MenuEvent e) {
856 				bShown = false;
857 
858 				if (Constants.isOSX) {
859 					return;
860 				}
861 
862 				// Must dispose in an asyncExec, otherwise SWT.Selection doesn't
863 				// get fired (async workaround provided by Eclipse Bug #87678)
864 				Utils.execSWTThreadLater(0, new AERunnable() {
865 					public void runSupport() {
866 						if (bShown || menuTree.isDisposed()) {
867 							return;
868 						}
869 						Utils.disposeSWTObjects(menuTree.getItems());
870 					}
871 				});
872 			}
873 
874 			public void menuShown(MenuEvent e) {
875 				Utils.disposeSWTObjects(menuTree.getItems());
876 
877 				bShown = true;
878 
879 				Point ptMouse = tree.toControl(e.display.getCursorLocation());
880 
881 				int indent = END_INDENT ? tree.getClientArea().width - 1 : 0;
882 				TreeItem treeItem = tree.getItem(new Point(indent, ptMouse.y));
883 				if (treeItem == null) {
884 					return;
885 				}
886 				SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
887 
888 				fillMenu(menuTree, entry, "sidebar");
889 
890 				if (menuTree.getItemCount() == 0) {
891 					Utils.execSWTThreadLater(0, new AERunnable() {
892 						public void runSupport() {
893 							menuTree.setVisible(false);
894 						}
895 					});
896 				}
897 			}
898 		});
899 
900 		if (soSideBarPopout != null) {
901 			SWTSkinObject soDropDown = skin.getSkinObject("sidebar-dropdown");
902 			if (soDropDown != null) {
903 
904 				final Menu menuDropDown = new Menu(soDropDown.getControl());
905 
906 				menuDropDown.addMenuListener(new MenuListener() {
907 					boolean bShown = false;
908 
909 					public void menuHidden(MenuEvent e) {
910 						bShown = false;
911 
912 						if (Constants.isOSX) {
913 							return;
914 						}
915 
916 						// Must dispose in an asyncExec, otherwise SWT.Selection doesn't
917 						// get fired (async workaround provided by Eclipse Bug #87678)
918 						Utils.execSWTThreadLater(0, new AERunnable() {
919 							public void runSupport() {
920 								if (bShown || menuDropDown.isDisposed()) {
921 									return;
922 								}
923 								Utils.disposeSWTObjects(menuDropDown.getItems());
924 							}
925 						});
926 					}
927 
928 					public void menuShown(MenuEvent e) {
929 						Utils.disposeSWTObjects(menuDropDown.getItems());
930 
931 						bShown = true;
932 
933 						fillDropDownMenu(menuDropDown, tree.getItems(), 0);
934 					}
935 				});
936 
937 				dropDownSelectionListener = new SelectionListener() {
938 					public void widgetSelected(SelectionEvent e) {
939 						String id = (String) e.widget.getData("Plugin.viewID");
940 						showEntryByID(id);
941 					}
942 
943 					public void widgetDefaultSelected(SelectionEvent e) {
944 					}
945 				};
946 
947 				SWTSkinButtonUtility btnDropDown = new SWTSkinButtonUtility(soDropDown);
948 				btnDropDown.addSelectionListener(new ButtonListenerAdapter() {
949 					public void pressed(SWTSkinButtonUtility buttonUtility,
950 							SWTSkinObject skinObject, int stateMask) {
951 						Control c = buttonUtility.getSkinObject().getControl();
952 						menuDropDown.setLocation(c.getDisplay().getCursorLocation());
953 						menuDropDown.setVisible(!menuDropDown.getVisible());
954 					}
955 				});
956 			}
957 
958 			SWTSkinObject soExpand = skin.getSkinObject("sidebar-expand");
959 			if (soExpand != null) {
960 				SWTSkinButtonUtility btnExpand = new SWTSkinButtonUtility(soExpand);
961 				btnExpand.addSelectionListener(new ButtonListenerAdapter() {
962 					public void pressed(SWTSkinButtonUtility buttonUtility,
963 							SWTSkinObject skinObject, int stateMask) {
964 						flipSideBarVisibility();
965 					}
966 				});
967 			}
968 
969 		}
970 	}
971 
createSideBarPluginViews()972 	private void createSideBarPluginViews() {
973 		if (cPluginsArea == null) {
974 			return;
975 		}
976 		UISWTInstanceImpl uiSWTinstance = (UISWTInstanceImpl) UIFunctionsManagerSWT.getUIFunctionsSWT().getUISWTInstance();
977 
978 		if (uiSWTinstance == null) {
979 			return;
980 		}
981 
982 		UISWTViewEventListenerHolder[] pluginViews = uiSWTinstance.getViewListeners(UISWTInstance.VIEW_SIDEBAR_AREA);
983 		for (UISWTViewEventListenerHolder l : pluginViews) {
984 			if (l != null) {
985 				try {
986 					UISWTViewImpl view = new UISWTViewImpl(l.getViewID(), UISWTInstance.VIEW_SIDEBAR_AREA, false);
987 					view.setEventListener(l, true);
988 					addSideBarView(view, cPluginsArea);
989 					cPluginsArea.getParent().getParent().layout(true, true);
990 				} catch (Exception e) {
991 					e.printStackTrace();
992 					// skip, plugin probably specifically asked to not be added
993 				}
994 			}
995 		}
996 
997 		uiSWTinstance.addSWTViewListener(new SWTViewListener() {
998 
999 			public void setViewAdded(final String parent, final String id,
1000 					final UISWTViewEventListener l) {
1001 				if (!parent.equals(UISWTInstance.VIEW_SIDEBAR_AREA)) {
1002 					return;
1003 				}
1004 				Utils.execSWTThread(new AERunnable() {
1005 
1006 					public void runSupport() {
1007 						try {
1008 							UISWTViewImpl view = new UISWTViewImpl(id, parent, false);
1009 							view.setEventListener(l, true);
1010 							addSideBarView(view, cPluginsArea);
1011 						} catch (Exception e) {
1012 							e.printStackTrace();
1013 							// skip, plugin probably specifically asked to not be added
1014 						}
1015 					}
1016 				});
1017 			}
1018 
1019 			public void setViewRemoved(final String parent, final String id,
1020 					final UISWTViewEventListener l) {
1021 				if (!parent.equals(UISWTInstance.VIEW_SIDEBAR_AREA)) {
1022 					return;
1023 				}
1024 				Utils.execSWTThread(new AERunnable() {
1025 
1026 					public void runSupport() {
1027 						try {
1028 							for (UISWTViewCore view : SideBar.this.pluginViews) {
1029 								if (l.equals(view.getEventListener())) {
1030 									view.closeView();
1031 								}else{
1032 									if ( l instanceof UISWTViewEventListenerHolder ){
1033 										UISWTViewEventListener l2 = ((UISWTViewEventListenerHolder) l).getDelegatedEventListener(view);
1034 										if ( l2 != null && l2.equals(view.getEventListener())) {
1035 											view.closeView();
1036 										}
1037 									}
1038 								}
1039 							}
1040 						} catch (Exception e) {
1041 							e.printStackTrace();
1042 							// skip, plugin probably specifically asked to not be added
1043 						}
1044 					}
1045 				});
1046 			}
1047 		});
1048 
1049 		cPluginsArea.getParent().getParent().layout(true, true);
1050 	}
1051 
addSideBarView(UISWTViewImpl view, Composite cPluginsArea)1052 	private void addSideBarView(UISWTViewImpl view, Composite cPluginsArea) {
1053 		Composite parent = new Composite(cPluginsArea, SWT.NONE);
1054 		GridData gridData = new GridData();
1055 		gridData.grabExcessHorizontalSpace = true;
1056 		gridData.horizontalAlignment = SWT.FILL;
1057 		parent.setLayoutData(gridData);
1058 		parent.setLayout(new FormLayout());
1059 		//parent.setBackground(ColorCache.getRandomColor());
1060 		//cPluginsArea.setBackground(ColorCache.getRandomColor());
1061 
1062 		view.initialize(parent);
1063 		parent.setVisible(true);
1064 
1065 		Control[] children = parent.getChildren();
1066 		for (int i = 0; i < children.length; i++) {
1067 			Control control = children[i];
1068 			Object ld = control.getLayoutData();
1069 			boolean useGridLayout = ld != null && (ld instanceof GridData);
1070 			if (useGridLayout) {
1071 				GridLayout gridLayout = new GridLayout();
1072 				gridLayout.horizontalSpacing = 0;
1073 				gridLayout.marginHeight = 0;
1074 				gridLayout.marginWidth = 0;
1075 				gridLayout.verticalSpacing = 0;
1076 				parent.setLayout(gridLayout);
1077 				break;
1078 			} else if (ld == null) {
1079 				control.setLayoutData(Utils.getFilledFormData());
1080 			}
1081 		}
1082 
1083 		pluginViews.add(view);
1084 	}
1085 
1086 
1087 	/**
1088 	 * @param event
1089 	 */
defaultDrop(DropTargetEvent event)1090 	protected void defaultDrop(DropTargetEvent event) {
1091 		TorrentOpener.openDroppedTorrents(event, false);
1092 	}
1093 
1094 	/**
1095 	 * @param menuDropDown
1096 	 *
1097 	 * @since 3.1.1.1
1098 	 */
fillDropDownMenu(Menu menuDropDown, TreeItem[] items, int indent)1099 	protected void fillDropDownMenu(Menu menuDropDown, TreeItem[] items,
1100 			int indent) {
1101 		String s = "";
1102 		for (int i = 0; i < indent; i++) {
1103 			s += "   ";
1104 		}
1105 		for (int i = 0; i < items.length; i++) {
1106 			TreeItem treeItem = items[i];
1107 
1108 			SideBarEntrySWT entry = (SideBarEntrySWT) treeItem.getData("MdiEntry");
1109 			if (entry == null) {
1110 				continue;
1111 			}
1112 			org.eclipse.swt.widgets.MenuItem menuItem = new org.eclipse.swt.widgets.MenuItem(
1113 					menuDropDown, entry.isSelectable() ? SWT.RADIO : SWT.CASCADE);
1114 
1115 			String id = entry.getId();
1116 			menuItem.setData("Plugin.viewID", id);
1117 			ViewTitleInfo titleInfo = entry.getViewTitleInfo();
1118 			String ind = "";
1119 			if (titleInfo != null) {
1120 				String o = (String) titleInfo.getTitleInfoProperty(ViewTitleInfo.TITLE_INDICATOR_TEXT);
1121 				if (o != null) {
1122 					ind = "  (" + o + ")";
1123 					//ind = "\t" + o;
1124 				}
1125 			}
1126 			menuItem.setText(s + entry.getTitle() + ind);
1127 			menuItem.addSelectionListener(dropDownSelectionListener);
1128 			if (currentEntry != null && currentEntry.getId().equals(id)) {
1129 				menuItem.setSelection(true);
1130 			}
1131 
1132 			TreeItem[] subItems = treeItem.getItems();
1133 			if (subItems.length > 0) {
1134 				Menu parent = menuDropDown;
1135 				if (!entry.isSelectable()) {
1136 					parent = new Menu(menuDropDown.getParent().getShell(), SWT.DROP_DOWN);
1137 					menuItem.setMenu(parent);
1138 				}
1139 
1140 
1141 				fillDropDownMenu(parent, subItems, indent + 1);
1142 			}
1143 		}
1144 	}
1145 
1146 	/**
1147 	 *
1148 	 *
1149 	 * @since 3.1.1.1
1150 	 */
swt_updateSideBarHitAreasY(SideBarEntrySWT[] entries)1151 	private void swt_updateSideBarHitAreasY(SideBarEntrySWT[] entries) {
1152 		for (int x = 0; x < entries.length; x++) {
1153 			SideBarEntrySWT entry = entries[x];
1154 			TreeItem treeItem = entry.getTreeItem();
1155 			if (treeItem == null || treeItem.isDisposed()) {
1156 				continue;
1157 			}
1158 			Rectangle itemBounds = entry.swt_getBounds();
1159 
1160 			if ( itemBounds != null ){
1161 				if (entry.isCloseable()) {
1162 					Rectangle closeArea = (Rectangle) treeItem.getData("closeArea");
1163 					if (closeArea != null) {
1164 						closeArea.y = itemBounds.y + (itemBounds.height - closeArea.height)
1165 								/ 2;
1166 					}
1167 				}
1168 
1169 				MdiEntryVitalityImage[] vitalityImages = entry.getVitalityImages();
1170 				for (int i = 0; i < vitalityImages.length; i++) {
1171 					SideBarVitalityImageSWT vitalityImage = (SideBarVitalityImageSWT) vitalityImages[i];
1172 					if (!vitalityImage.isVisible()) {
1173 						continue;
1174 					}
1175 					Image image = vitalityImage.getImage();
1176 					if (image != null) {
1177 						Rectangle bounds = vitalityImage.getHitArea();
1178 						if (bounds == null) {
1179 							continue;
1180 						}
1181 						bounds.y = (itemBounds.height - bounds.height) / 2;
1182 					}
1183 				}
1184 			}
1185 		}
1186 	}
1187 
1188 	private void
swt_updateSideBarColors()1189 	swt_updateSideBarColors()
1190 	{
1191 		SWTSkinProperties skinProperties = skin.getSkinProperties();
1192 
1193 		skinProperties.clearCache();
1194 
1195 		bg = skinProperties.getColor("color.sidebar.bg");
1196 
1197 		tree.setBackground(bg);
1198 
1199 		tree.redraw();
1200 
1201 		swt_updateSideBarColors( tree.getItems());
1202 	}
1203 
1204 	private void
swt_updateSideBarColors( TreeItem[] items )1205 	swt_updateSideBarColors(
1206 		TreeItem[]	items )
1207 	{
1208 		for ( TreeItem ti: items){
1209 
1210 			SideBarEntrySWT entry = (SideBarEntrySWT) ti.getData("MdiEntry");
1211 
1212 			if ( entry != null ){
1213 
1214 				entry.updateColors();
1215 
1216 				entry.redraw();
1217 			}
1218 
1219 			swt_updateSideBarColors( ti.getItems());
1220 		}
1221 	}
1222 
indexOf(final MdiEntry entry)1223 	protected int indexOf(final MdiEntry entry) {
1224 		Object o = Utils.execSWTThreadWithObject("indexOf", new AERunnableObject() {
1225 			public Object runSupport() {
1226 				TreeItem treeItem = ((SideBarEntrySWT) entry).getTreeItem();
1227 				if (treeItem == null) {
1228 					return -1;
1229 				}
1230 				TreeItem parentItem = treeItem.getParentItem();
1231 				if (parentItem != null) {
1232 					return parentItem.indexOf(treeItem);
1233 				}
1234 				return tree.indexOf(treeItem);
1235 			}
1236 		}, 500);
1237 		if (o instanceof Number) {
1238 			return ((Number) o).intValue();
1239 		}
1240 		return -1;
1241 	}
1242 
createHeader(String id, String titleID, String preferredAfterID)1243 	public MdiEntry createHeader(String id, String titleID, String preferredAfterID) {
1244 		MdiEntry oldEntry = getEntry(id);
1245 		if (oldEntry != null) {
1246 			return oldEntry;
1247 		}
1248 
1249 		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id, null);
1250 		entry.setSelectable(false);
1251 		entry.setPreferredAfterID(preferredAfterID);
1252 		entry.setTitleID(titleID);
1253 
1254 		setupNewEntry(entry, id, true, false);
1255 
1256 		return entry;
1257 	}
1258 
setupNewEntry(final SideBarEntrySWT entry, final String id, final boolean expandParent, final boolean closeable)1259 	private void setupNewEntry(final SideBarEntrySWT entry, final String id,
1260 			final boolean expandParent, final boolean closeable) {
1261 		//System.out.println("createItem " + id + ";" + entry.getParentID() + ";" + Debug.getCompressedStackTrace());
1262 		addItem( entry );
1263 
1264 		entry.setCloseable(closeable);
1265 		entry.setParentSkinObject(soSideBarContents);
1266 		entry.setDestroyOnDeactivate(false);
1267 
1268 		if (SIDEBAR_HEADER_PLUGINS.equals(entry.getParentID())
1269 				&& entry.getImageLeftID() == null) {
1270 			entry.setImageLeftID("image.sidebar.plugin");
1271 		}
1272 
1273 		Utils.execSWTThreadLater(0, new AERunnable() {
1274 			public void runSupport() {
1275 				_setupNewEntry(entry, id, expandParent, closeable);
1276 			}
1277 		});
1278 	}
1279 
_setupNewEntry(SideBarEntrySWT entry, String id, boolean expandParent, boolean closeable)1280 	protected void _setupNewEntry(SideBarEntrySWT entry, String id,
1281 			boolean expandParent, boolean closeable) {
1282 		String parentID = entry.getParentID();
1283 		MdiEntry parent = getEntry(parentID);
1284 		TreeItem parentTreeItem = null;
1285 		if (parent instanceof SideBarEntrySWT) {
1286 			SideBarEntrySWT parentSWT = (SideBarEntrySWT) parent;
1287 			parentTreeItem = parentSWT.getTreeItem();
1288 			if (expandParent) {
1289 				parentTreeItem.setExpanded(true);
1290 			}
1291 		}
1292 		int index = -1;
1293 		String preferredAfterID = entry.getPreferredAfterID();
1294 		if (preferredAfterID != null) {
1295 			if (preferredAfterID.length() == 0) {
1296 				index = 0;
1297 			} else {
1298 				boolean hack_it = preferredAfterID.startsWith( "~" );
1299 
1300 				if ( hack_it ){
1301 
1302 						//hack - this means preferred BEFORE ID...
1303 
1304 					preferredAfterID = preferredAfterID.substring(1);
1305 				}
1306 
1307 				MdiEntry entryAbove = getEntry(preferredAfterID);
1308 				if (entryAbove != null) {
1309 					index = indexOf(entryAbove);
1310 					if ( hack_it ){
1311 
1312 					}else{
1313 						if (index >= 0) {
1314 							index++;
1315 						}
1316 					}
1317 					//System.out.println("ENTRY " + id + " is going to go below " + entryAbove.getId() + " at " + index);
1318 				}
1319 			}
1320 		}
1321 
1322 		if (index == -1 && parent == null) {
1323 			index = 0;
1324 			String[] order = getPreferredOrder();
1325 			for (int i = 0; i < order.length; i++) {
1326 				String orderID = order[i];
1327 				if (orderID.equals(id)) {
1328 					break;
1329 				}
1330 				MdiEntry entry2 = getEntry(orderID);
1331 				if (entry2 != null) {
1332 					int i2 = indexOf(entry2);
1333 					if (i2 >= 0) {
1334 						index = i2 + 1;
1335 					}
1336 				}
1337 			}
1338 		}
1339 
1340 		if (GAP_BETWEEN_LEVEL_1 > 0 && parentTreeItem == null
1341 				&& tree.getItemCount() > 0 && index != 0) {
1342 			for (int i=0;i<GAP_BETWEEN_LEVEL_1;i++){
1343 				createTreeItem(null, index);
1344 				if (index >= 0) {
1345 					index++;
1346 				}
1347 			}
1348 		}
1349 		TreeItem treeItem = createTreeItem(parentTreeItem, index);
1350 		if (treeItem != null) {
1351 			treeItem.setData("MdiEntry", entry);
1352 			entry.setTreeItem(treeItem);
1353 
1354 			triggerEntryLoadedListeners(entry);
1355 		}
1356 		if (GAP_BETWEEN_LEVEL_1 > 0 && parentTreeItem == null
1357 				&& tree.getItemCount() > 1 && index == 0) {
1358 			for (int i=0;i<GAP_BETWEEN_LEVEL_1;i++){
1359 				createTreeItem(null, ++index);
1360 			}
1361 		}
1362 	}
1363 
createTreeItem(Object parentSwtItem, int index)1364 	private TreeItem createTreeItem(Object parentSwtItem, int index) {
1365 		TreeItem treeItem;
1366 
1367 		if (parentSwtItem == null) {
1368 			parentSwtItem = tree;
1369 		}
1370 
1371 		if (parentSwtItem instanceof Tree) {
1372 			Tree tree = (Tree) parentSwtItem;
1373 			if (tree.isDisposed()) {
1374 				return null;
1375 			}
1376 			if (index >= 0 && index < tree.getItemCount()) {
1377 				treeItem = new TreeItem(tree, SWT.NONE, index);
1378 			} else {
1379 				treeItem = new TreeItem(tree, SWT.NONE);
1380 			}
1381 		} else {
1382 			if (((TreeItem) parentSwtItem).isDisposed()) {
1383 				return null;
1384 			}
1385 			if (index >= 0 && index < ((TreeItem) parentSwtItem).getItemCount()) {
1386 				treeItem = new TreeItem((TreeItem) parentSwtItem, SWT.NONE, index);
1387 			} else {
1388 				treeItem = new TreeItem((TreeItem) parentSwtItem, SWT.NONE);
1389 			}
1390 		}
1391 
1392 		return treeItem;
1393 	}
1394 
showEntry(MdiEntry newEntry)1395 	public void showEntry(MdiEntry newEntry) {
1396 		if (tree.isDisposed()) {
1397 			return;
1398 		}
1399 
1400 		if (newEntry == null || !newEntry.isSelectable()) {
1401 			return;
1402 		}
1403 
1404 		final SideBarEntrySWT oldEntry = (SideBarEntrySWT) currentEntry;
1405 
1406 		//System.out.println("showEntry " + newEntry.getId() + "; was " + (oldEntry == null ? "null" : oldEntry.getId()) + " via " + Debug.getCompressedStackTrace());
1407 		if (currentEntry == newEntry) {
1408 			triggerSelectionListener(newEntry, newEntry);
1409 			return;
1410 		}
1411 
1412 		// show new
1413 		currentEntry = (MdiEntrySWT) newEntry;
1414 
1415 		if (oldEntry != null && oldEntry != newEntry) {
1416 			oldEntry.redraw();
1417 		}
1418 
1419 		if (currentEntry != null) {
1420 			((BaseMdiEntry) currentEntry).show();
1421 		}
1422 
1423 		// hide old
1424 		if (oldEntry != null && oldEntry != newEntry) {
1425 			oldEntry.hide();
1426 			oldEntry.redraw();
1427 		}
1428 
1429 			// as this code isn't thread safe there is a chance we end up with multiple entries visible
1430 			// (well actually it happens fairly frequently) - this results in other views being rendered
1431 			// during switching which is nasty - hide anything that shouldn't be visible for the moment
1432 
1433 		MdiEntrySWT[] entries = getEntriesSWT();
1434 		for (MdiEntrySWT entry : entries) {
1435 			if (entry == null) {
1436 				continue;
1437 			}
1438 			if ( entry != currentEntry ){
1439 
1440 				SWTSkinObject obj = ((SideBarEntrySWT)entry).getSkinObjectMaster();
1441 
1442 				if ( obj != null && obj.isVisible()){
1443 
1444 					entry.hide();
1445 					entry.redraw();
1446 				}
1447 			}
1448 		}
1449 
1450 		newEntry.redraw();
1451 
1452 		triggerSelectionListener(newEntry, oldEntry);
1453 	}
1454 
1455 	/**
1456 	 *  @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)
1457 	 */
createEntryFromEventListener(String parentEntryID, String parentViewID, UISWTViewEventListener l, String id, boolean closeable, Object datasource, String preferredAfterID)1458 	public MdiEntry createEntryFromEventListener(String parentEntryID, String parentViewID,
1459 			UISWTViewEventListener l, String id, boolean closeable, Object datasource, String preferredAfterID) {
1460 
1461 		MdiEntry oldEntry = getEntry(id);
1462 		if (oldEntry != null) {
1463 			return oldEntry;
1464 		}
1465 
1466 		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id, parentViewID);
1467 		try {
1468 			// hack: setEventListner will create the UISWTView.
1469 			// We need to have the entry available for the view to use
1470 			// if it wants
1471 
1472 			addItem( entry );
1473 
1474 			entry.setEventListener(l, true);
1475 			entry.setParentID(parentEntryID);
1476 			entry.setDatasource(datasource);
1477 			entry.setPreferredAfterID(preferredAfterID);
1478 			setupNewEntry(entry, id, false, closeable);
1479 
1480 
1481 			if (l instanceof IViewAlwaysInitialize) {
1482 				entry.build();
1483 			}
1484 		} catch (Exception e) {
1485 			Debug.out(e);
1486 			entry.close(true);
1487 			entry = null;
1488 		}
1489 
1490 		return entry;
1491 	}
1492 
1493 	// @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)
createEntryFromSkinRef(String parentID, String id, String configID, String title, ViewTitleInfo titleInfo, Object params, boolean closeable, String preferredAfterID)1494 	public MdiEntry createEntryFromSkinRef(String parentID, String id,
1495 			String configID, String title, ViewTitleInfo titleInfo, Object params,
1496 			boolean closeable, String preferredAfterID) {
1497 
1498 		MdiEntry oldEntry = getEntry(id);
1499 		if (oldEntry != null) {
1500 			return oldEntry;
1501 		}
1502 
1503 		SideBarEntrySWT entry = new SideBarEntrySWT(this, skin, id, null);
1504 
1505 		entry.setTitle(title);
1506 		entry.setSkinRef(configID, params);
1507 		entry.setParentID(parentID);
1508 		entry.setViewTitleInfo(titleInfo);
1509 		entry.setPreferredAfterID(preferredAfterID);
1510 
1511 		setupNewEntry(entry, id, false, closeable);
1512 
1513 		return entry;
1514 	}
1515 
1516 	// @see com.aelitis.azureus.ui.swt.utils.UIUpdatable#updateUI()
updateUI()1517 	public void updateUI() {
1518 		Object[] views = pluginViews.toArray();
1519 		for (int i = 0; i < views.length; i++) {
1520 			try {
1521 				UISWTViewCore view = (UISWTViewCore) views[i];
1522 				Composite composite = view.getComposite();
1523 				if ( composite == null ){
1524 					continue;
1525 				}
1526 				if (composite.isDisposed()) {
1527 					pluginViews.remove(view);
1528 					continue;
1529 				}
1530 				if (composite.isVisible()) {
1531 					view.triggerEvent(UISWTViewEvent.TYPE_REFRESH, null);
1532 				}
1533 			} catch (Exception e) {
1534 				Debug.out(e);
1535 			}
1536 		}
1537 
1538 		if (tree.getSelectionCount() == 0) {
1539 			return;
1540 		}
1541 		super.updateUI();
1542 	}
1543 
wasEntryLoadedOnce(String id)1544 	protected boolean wasEntryLoadedOnce(String id) {
1545 		@SuppressWarnings("deprecation")
1546 		boolean loadedOnce = COConfigurationManager.getBooleanParameter("sb.once."
1547 				+ id, false);
1548 		return loadedOnce;
1549 	}
1550 
setEntryLoadedOnce(String id)1551 	protected void setEntryLoadedOnce(String id) {
1552 		COConfigurationManager.setParameter("sb.once." + id, true);
1553 	}
1554 
getHeaderFont()1555 	public Font getHeaderFont() {
1556 		return fontHeader;
1557 	}
1558 
getTree()1559 	protected Tree getTree() {
1560 		return tree;
1561 	}
1562 
1563 	// @see com.aelitis.azureus.ui.swt.mdi.MultipleDocumentInterfaceSWT#getEntryFromSkinObject(org.gudy.azureus2.ui.swt.plugins.PluginUISWTSkinObject)
getEntryFromSkinObject( PluginUISWTSkinObject pluginSkinObject)1564 	public MdiEntrySWT getEntryFromSkinObject(
1565 			PluginUISWTSkinObject pluginSkinObject) {
1566 		if (pluginSkinObject instanceof SWTSkinObject) {
1567 			Control control = ((SWTSkinObject) pluginSkinObject).getControl();
1568 			while (control != null && !control.isDisposed()) {
1569 				Object entry = control.getData("BaseMDIEntry");
1570 				if (entry instanceof BaseMdiEntry) {
1571 					BaseMdiEntry mdiEntry = (BaseMdiEntry) entry;
1572 					return mdiEntry;
1573 				}
1574 				control = control.getParent();
1575 			}
1576 		}
1577 		return null;
1578 	}
1579 
1580 	protected void
requestAttention( SideBarEntrySWT entry )1581 	requestAttention(
1582 		SideBarEntrySWT	entry )
1583 	{
1584 		synchronized( attention_seekers ){
1585 
1586 			if ( !attention_seekers.contains( entry )){
1587 
1588 				attention_seekers.add( entry );
1589 			}
1590 
1591 			if ( attention_event == null ){
1592 
1593 				attention_event =
1594 					SimpleTimer.addPeriodicEvent(
1595 						"SideBar:attention",
1596 						SIDEBAR_ATTENTION_PERIOD,
1597 						new TimerEventPerformer()
1598 						{
1599 							int	tick_count = 0;
1600 
1601 							public void
1602 							perform(
1603 								TimerEvent event )
1604 							{
1605 								tick_count++;
1606 
1607 								final List<SideBarEntrySWT>	repaints = new ArrayList<SideBarEntrySWT>();
1608 
1609 								synchronized( attention_seekers ){
1610 
1611 									Iterator<SideBarEntrySWT> it = attention_seekers.iterator();
1612 
1613 									while ( it.hasNext()){
1614 
1615 										SideBarEntrySWT entry = it.next();
1616 
1617 										if ( entry.isDisposed()){
1618 
1619 											it.remove();
1620 
1621 										}else{
1622 
1623 											if ( !entry.attentionUpdate( tick_count )){
1624 
1625 												it.remove();
1626 											}
1627 
1628 											repaints.add( entry );
1629 										}
1630 									}
1631 
1632 									if ( attention_seekers.size() == 0 ){
1633 
1634 										TimerEventPeriodic ev = attention_event;
1635 
1636 										if ( ev != null ){
1637 
1638 											ev.cancel();
1639 
1640 											attention_event = null;
1641 										}
1642 									}
1643 								}
1644 
1645 								if ( repaints.size() > 0 ){
1646 
1647 									Utils.execSWTThread(
1648 										new AERunnable()
1649 										{
1650 											@Override
1651 											public void
1652 											runSupport()
1653 											{
1654 												for ( SideBarEntrySWT entry: repaints ){
1655 
1656 													entry.redraw();
1657 												}
1658 											}
1659 										});
1660 								}
1661 							}
1662 						});
1663 
1664 			}
1665 		}
1666 	}
1667 
1668 		// track entry additions and selection so we can switch to previous entry when one is closed
1669 
1670 	private Stack<SideBarEntrySWT>	stack = new Stack<SideBarEntrySWT>();
1671 
addItem(MdiEntry entry)1672 	public void addItem(MdiEntry entry) {
1673 		super.addItem( entry );
1674 		if ( entry instanceof SideBarEntrySWT ){
1675 			synchronized( stack ){
1676 				stack.remove( entry );
1677 				if ( entry.isSelectable()){
1678 					stack.push( (SideBarEntrySWT)entry );
1679 				}
1680 			}
1681 		}
1682 	}
1683 
1684 	protected void
itemSelected(MdiEntry entry )1685 	itemSelected(MdiEntry entry ){
1686 		super.itemSelected( entry );
1687 		if ( entry instanceof SideBarEntrySWT ){
1688 			synchronized( stack ){
1689 				stack.remove( entry );
1690 				if ( entry.isSelectable()){
1691 					stack.push( (SideBarEntrySWT)entry );
1692 				}
1693 			}
1694 		}
1695 	}
1696 
removeItem(MdiEntry entry)1697 	public void removeItem(MdiEntry entry) {
1698 		super.removeItem( entry );
1699 		if ( entry instanceof SideBarEntrySWT ){
1700 
1701 			MdiEntry current = getCurrentEntry();
1702 
1703 			SideBarEntrySWT next = null;
1704 
1705 			synchronized( stack ){
1706 
1707 				stack.remove( entry );
1708 
1709 				if ( 	current == null ||
1710 						current == entry ){
1711 
1712 					while( !stack.isEmpty()){
1713 						next = stack.pop();
1714 						if ( next.isDisposed()){
1715 							next = null;
1716 						}else{
1717 							break;
1718 						}
1719 					}
1720 				}
1721 			}
1722 			if ( next != null ){
1723 				showEntry( next );
1724 			}
1725 		}
1726 	}
1727 
generate(IndentWriter writer)1728 	public void generate(IndentWriter writer) {
1729 		MdiEntrySWT[] entries = getEntriesSWT();
1730 		for (MdiEntrySWT entry : entries) {
1731 			if (entry == null) {
1732 				continue;
1733 			}
1734 
1735 			if (!(entry instanceof AEDiagnosticsEvidenceGenerator)) {
1736 				writer.println("Sidebar View (No Generator): " + entry.getId());
1737 				try {
1738 					writer.indent();
1739 
1740 					writer.println("Parent: " + entry.getParentID());
1741 					writer.println("Title: " + entry.getTitle());
1742 				} catch (Exception e) {
1743 
1744 				} finally {
1745 
1746 					writer.exdent();
1747 				}
1748 			}
1749 
1750 		}
1751 	}
1752 
1753 	// @see org.gudy.azureus2.ui.swt.debug.ObfusticateImage#obfusticatedImage(org.eclipse.swt.graphics.Image)
obfusticatedImage(Image image)1754 	public Image obfusticatedImage(Image image) {
1755 
1756 		Rectangle treeBounds = tree.getBounds();
1757 		SideBarEntrySWT[] sideBarEntries = getEntries(
1758 				new SideBarEntrySWT[0]);
1759 		for (SideBarEntrySWT entry : sideBarEntries) {
1760 			Rectangle entryBounds = entry.swt_getBounds();
1761 			if (entryBounds != null && treeBounds.intersects(entryBounds)) {
1762 				entry.obfusticatedImage(image);
1763 			}
1764 		}
1765 		return image;
1766 	}
1767 }
1768