1 /*
2  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details ( see the LICENSE file ).
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 package org.gudy.azureus2.ui.swt.views.table.impl;
20 
21 import java.util.Arrays;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25 
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.events.SelectionAdapter;
28 import org.eclipse.swt.events.SelectionEvent;
29 import org.eclipse.swt.graphics.Image;
30 import org.eclipse.swt.graphics.Rectangle;
31 import org.eclipse.swt.layout.*;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Event;
34 import org.eclipse.swt.widgets.Listener;
35 import org.eclipse.swt.widgets.Sash;
36 import org.gudy.azureus2.core3.config.COConfigurationManager;
37 import org.gudy.azureus2.core3.config.impl.ConfigurationManager;
38 import org.gudy.azureus2.core3.download.DownloadManager;
39 import org.gudy.azureus2.core3.util.Constants;
40 import org.gudy.azureus2.core3.util.Debug;
41 import org.gudy.azureus2.core3.util.IndentWriter;
42 import org.gudy.azureus2.plugins.PluginInterface;
43 import org.gudy.azureus2.plugins.ui.UIManager;
44 import org.gudy.azureus2.plugins.ui.menus.MenuItem;
45 import org.gudy.azureus2.plugins.ui.menus.MenuItemFillListener;
46 import org.gudy.azureus2.plugins.ui.menus.MenuItemListener;
47 import org.gudy.azureus2.plugins.ui.menus.MenuManager;
48 import org.gudy.azureus2.pluginsimpl.local.PluginInitializer;
49 import org.gudy.azureus2.ui.swt.Utils;
50 import org.gudy.azureus2.ui.swt.debug.ObfusticateImage;
51 import org.gudy.azureus2.ui.swt.plugins.UISWTInstance;
52 import org.gudy.azureus2.ui.swt.plugins.UISWTInstance.UISWTViewEventListenerWrapper;
53 import org.gudy.azureus2.ui.swt.plugins.UISWTView;
54 import org.gudy.azureus2.ui.swt.plugins.UISWTViewEvent;
55 import org.gudy.azureus2.ui.swt.pluginsimpl.UISWTViewCore;
56 import org.gudy.azureus2.ui.swt.views.table.TableViewSWT;
57 
58 import com.aelitis.azureus.ui.UIFunctions;
59 import com.aelitis.azureus.ui.UIFunctionsManager;
60 import com.aelitis.azureus.ui.common.table.TableView;
61 import com.aelitis.azureus.ui.mdi.MdiEntry;
62 import com.aelitis.azureus.ui.mdi.MdiEntryCreationListener2;
63 import com.aelitis.azureus.ui.mdi.MultipleDocumentInterface;
64 import com.aelitis.azureus.ui.selectedcontent.ISelectedContent;
65 import com.aelitis.azureus.ui.selectedcontent.SelectedContentListener;
66 import com.aelitis.azureus.ui.selectedcontent.SelectedContentManager;
67 import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
68 import com.aelitis.azureus.ui.swt.UIFunctionsSWT;
69 import com.aelitis.azureus.ui.swt.mdi.MdiEntrySWT;
70 import com.aelitis.azureus.ui.swt.mdi.TabbedMdiInterface;
71 import com.aelitis.azureus.ui.swt.mdi.TabbedMdiMaximizeListener;
72 
73 /**
74  *
75  * @author TuxPaper
76  *
77  */
78 public class TableViewSWT_TabsCommon implements SelectedContentListener
79 {
80 	UISWTView parentView;
81 	TableViewSWT<?> tv;
82 
83 	/** Composite that stores the table (sometimes the same as mainComposite) */
84 	public Composite tableComposite;
85 
86 	private TableView<?> tvOverride;
87 	private Sash sash;
88 	private TabbedMdiInterface tabbedMDI;
89 	private Composite cTabsHolder;
90 	private FormData fdHeightChanger;
91 	private MenuItem menuItemShowTabs;
92 
TableViewSWT_TabsCommon(UISWTView parentView, TableViewSWT<?> tv)93 	public TableViewSWT_TabsCommon(UISWTView parentView, TableViewSWT<?> tv) {
94 		this.parentView = parentView;
95 		this.tv = tv;
96 	}
97 
triggerTabViewsDataSourceChanged(TableView<?> tv)98 	public void triggerTabViewsDataSourceChanged(TableView<?> tv) {
99 		if (tabbedMDI == null || tabbedMDI.isDisposed()) {
100 			return;
101 		}
102 		MdiEntry[] entries = tabbedMDI.getEntries();
103 		if (entries == null || entries.length == 0) {
104 			return;
105 		}
106 
107 		Object[] ds = tv.getSelectedDataSources(true);
108 
109 		for (MdiEntry entry : entries) {
110 			if (entry instanceof MdiEntrySWT) {
111 				triggerTabViewDataSourceChanged((MdiEntrySWT) entry, tv, ds);
112 			}
113 		}
114 	}
115 
setTvOverride(TableView<?> tvOverride)116 	public void setTvOverride(TableView<?> tvOverride) {
117 		this.tvOverride = tvOverride;
118 		selectedContent = SelectedContentManager.getCurrentlySelectedContent();
119 	}
120 
triggerTabViewDataSourceChanged(MdiEntrySWT view, TableView<?> tv, Object[] dataSourcesCore)121 	public void triggerTabViewDataSourceChanged(MdiEntrySWT view, TableView<?> tv,
122 			Object[] dataSourcesCore) {
123 		if (tvOverride != null) {
124 			tv = tvOverride;
125 		}
126 		if (view == null) {
127 			return;
128 		}
129 
130 		// When there is not selected datasources in the TableView, send the
131 		// parent's datasource
132 
133 		if (dataSourcesCore == null) {
134 			dataSourcesCore = tv.getSelectedDataSources(true);
135 		}
136 		if (tabbedMDI != null) {
137 			tabbedMDI.setMaximizeVisible(dataSourcesCore != null && dataSourcesCore.length == 1);
138 		}
139 		view.triggerEvent(UISWTViewEvent.TYPE_DATASOURCE_CHANGED,
140 				dataSourcesCore.length == 0 ? tv.getParentDataSource()
141 						: dataSourcesCore);
142 	}
143 
delete()144 	public void delete() {
145 		SelectedContentManager.removeCurrentlySelectedContentListener(this);
146 
147 		if (menuItemShowTabs != null) {
148 			menuItemShowTabs.remove();
149 		}
150 		/* disposal of composite should take care of tabbedMDI
151 		if (tabbedMDI == null || tabbedMDI.isDisposed()) {
152 			return;
153 		}
154 		MdiEntry[] entries = tabbedMDI.getEntries();
155 		if (entries == null || entries.length == 0) {
156 			return;
157 		}
158 		for (MdiEntry entry : entries) {
159 			if (entry instanceof MdiEntrySWT) {
160 				((MdiEntrySWT) entry).triggerEvent(UISWTViewEvent.TYPE_DESTROY, null);
161 			}
162 		}
163 		*/
164 	}
165 
generate(IndentWriter writer)166 	public void generate(IndentWriter writer) {
167 		writer.println("# of SubViews: "
168 				+ (tabbedMDI == null ? "null" : tabbedMDI.getEntriesCount()));
169 	}
170 
localeChanged()171 	public void localeChanged() {
172 		if ( tabbedMDI == null ){
173 			return;
174 		}
175 		MdiEntry[] entries = tabbedMDI.getEntries();
176 		if (entries == null || entries.length == 0) {
177 			return;
178 		}
179 		for (MdiEntry entry : entries) {
180 			if (entry instanceof MdiEntrySWT) {
181 				((MdiEntrySWT) entry).triggerEvent(UISWTViewEvent.TYPE_LANGUAGEUPDATE, null);
182 			}
183 		}
184 	}
185 
getActiveSubView()186 	public MdiEntrySWT getActiveSubView() {
187 		if (!tv.isTabViewsEnabled() || tabbedMDI == null || tabbedMDI.isDisposed()
188 				|| tabbedMDI.getMinimized()) {
189 			return null;
190 		}
191 
192 		return tabbedMDI.getCurrentEntrySWT();
193 	}
194 
195 		// TabViews Functions
196 
addTabView(UISWTViewEventListenerWrapper listener, String afterID)197 	private MdiEntry addTabView(UISWTViewEventListenerWrapper listener,
198 			String afterID) {
199 		UISWTViewCore view = null;
200 		MdiEntrySWT entry = (MdiEntrySWT) tabbedMDI.createEntryFromEventListener(
201 				tv.getTableID(), listener, listener.getViewID(), true, null, afterID);
202 		if (entry instanceof UISWTViewCore) {
203 			view = (UISWTViewCore) entry;
204 
205 		} else {
206 			return entry;
207 		}
208 
209 		try {
210 			if (parentView != null) {
211 				view.setParentView(parentView);
212 			}
213 
214 			triggerTabViewDataSourceChanged(entry, tv, null);
215 
216 		} catch (Throwable e) {
217 
218 			Debug.out(e);
219 		}
220 
221 		return entry;
222 	}
223 
removeTabView(String id)224 	private void removeTabView(String id)
225 	{
226 		boolean exists = tabbedMDI.entryExists(id);
227 		if (!exists) {
228 			return;
229 		}
230 		MdiEntry entry = tabbedMDI.getEntry(id);
231 
232 		// XXX
233 		//removedViews.add(((MdiEntrySWT) entry).getEventListener());
234 		tabbedMDI.removeItem(entry);
235 	}
236 
createSashForm(final Composite composite)237 	public Composite createSashForm(final Composite composite) {
238 		if (!tv.isTabViewsEnabled()) {
239 			tableComposite = tv.createMainPanel(composite);
240 			return tableComposite;
241 		}
242 
243 		SelectedContentManager.addCurrentlySelectedContentListener(this);
244 
245 		ConfigurationManager configMan = ConfigurationManager.getInstance();
246 
247 		int iNumViews = 0;
248 
249 		UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
250 		if (uiFunctions != null) {
251 			UISWTInstance pluginUI = uiFunctions.getUISWTInstance();
252 
253 			if (pluginUI != null) {
254 				iNumViews += pluginUI.getViewListeners(tv.getTableID()).length;
255 			}
256 		}
257 
258 		if (iNumViews == 0) {
259 			tableComposite = tv.createMainPanel(composite);
260 			return tableComposite;
261 		}
262 
263 		final String	props_prefix = tv.getTableID() + "." + tv.getPropertiesPrefix();
264 
265 		FormData formData;
266 
267 		final Composite form = new Composite(composite, SWT.NONE);
268 		FormLayout flayout = new FormLayout();
269 		flayout.marginHeight = 0;
270 		flayout.marginWidth = 0;
271 		form.setLayout(flayout);
272 		GridData gridData;
273 		gridData = new GridData(GridData.FILL_BOTH);
274 		form.setLayoutData(gridData);
275 
276 		// Create them in reverse order, so we can have the table auto-grow, and
277 		// set the tabFolder's height manually
278 
279 
280 		cTabsHolder = new Composite(form, SWT.NONE);
281 		tabbedMDI = uiFunctions.createTabbedMDI(cTabsHolder, props_prefix);
282 		tabbedMDI.setMaximizeVisible(true);
283 		tabbedMDI.setMinimizeVisible(true);
284 
285 		tabbedMDI.setTabbedMdiMaximizeListener(new TabbedMdiMaximizeListener() {
286 			public void maximizePressed() {
287 				TableView tvToUse = tvOverride == null ? tv : tvOverride;
288 				Object[] ds = tvToUse.getSelectedDataSources(true);
289 
290 				if (ds.length == 1 && ds[0] instanceof DownloadManager) {
291 
292 					UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
293 
294 					if (uiFunctions != null) {
295 
296 						uiFunctions.getMDI().showEntryByID(
297 								MultipleDocumentInterface.SIDEBAR_SECTION_TORRENT_DETAILS,
298 								ds);
299 					}
300 				}
301 			}
302 		});
303 
304 		final int SASH_WIDTH = 5;
305 
306 		sash = Utils.createSash( form, SASH_WIDTH );
307 
308 		tableComposite = tv.createMainPanel(form);
309 		Composite cFixLayout = tableComposite;
310 		while (cFixLayout != null && cFixLayout.getParent() != form) {
311 			cFixLayout = cFixLayout.getParent();
312 		}
313 		if (cFixLayout == null) {
314 			cFixLayout = tableComposite;
315 		}
316 		GridLayout layout = new GridLayout();
317 		layout.numColumns = 1;
318 		layout.horizontalSpacing = 0;
319 		layout.verticalSpacing = 0;
320 		layout.marginHeight = 0;
321 		layout.marginWidth = 0;
322 		cFixLayout.setLayout(layout);
323 
324 		// FormData for Folder
325 		formData = new FormData();
326 		formData.left = new FormAttachment(0, 0);
327 		formData.right = new FormAttachment(100, 0);
328 		formData.bottom = new FormAttachment(100, 0);
329 		int iSplitAt = configMan.getIntParameter(props_prefix + ".SplitAt",
330 				3000);
331 		// Was stored at whole
332 		if (iSplitAt < 100) {
333 			iSplitAt *= 100;
334 		}
335 
336 		// pct is % bottom
337 		double pct = iSplitAt / 10000.0;
338 		if (pct < 0.03) {
339 			pct = 0.03;
340 		} else if (pct > 0.97) {
341 			pct = 0.97;
342 		}
343 
344 		// height will be set on first resize call
345 		sash.setData("PCT", new Double(pct));
346 		cTabsHolder.setLayout(new FormLayout());
347 		fdHeightChanger = formData;
348 		cTabsHolder.setLayoutData(formData);
349 
350 		// FormData for Sash
351 		formData = new FormData();
352 		formData.left = new FormAttachment(0, 0);
353 		formData.right = new FormAttachment(100, 0);
354 		formData.bottom = new FormAttachment(cTabsHolder);
355 		formData.height = SASH_WIDTH;
356 		sash.setLayoutData(formData);
357 
358 		// FormData for table Composite
359 		formData = new FormData();
360 		formData.left = new FormAttachment(0, 0);
361 		formData.right = new FormAttachment(100, 0);
362 		formData.top = new FormAttachment(0, 0);
363 		formData.bottom = new FormAttachment(sash);
364 		cFixLayout.setLayoutData(formData);
365 
366 
367 
368 		// Listeners to size the folder
369 		sash.addSelectionListener(new SelectionAdapter() {
370 			public void widgetSelected(SelectionEvent e) {
371 				final boolean FASTDRAG = true;
372 
373 				if (FASTDRAG && e.detail == SWT.DRAG) {
374 					return;
375 				}
376 
377 				Rectangle area = form.getClientArea();
378 
379 				int height = area.height - e.y - e.height;
380 
381 				if ( !Constants.isWindows ){
382 					height -= SASH_WIDTH;
383 				}
384 
385 					// prevent sash from being dragged too far up. In the worst case it ends up
386 					// overlaying the split my-torrents sash and the user can't easily separate the two...
387 
388 				if ( area.height - height < 100 ){
389 					height = area.height - 100;
390 				}
391 
392 				if ( height < 0 ){
393 					height = 0;
394 				}
395 
396 				fdHeightChanger.height = height;
397 
398 				Double l = new Double((double) height / area.height);
399 				sash.setData("PCT", l);
400 				if (e.detail != SWT.DRAG) {
401 					ConfigurationManager configMan = ConfigurationManager.getInstance();
402 					configMan.setParameter(props_prefix + ".SplitAt",
403 							(int) (l.doubleValue() * 10000));
404 				}
405 				form.layout();
406 				// sometimes sash cheese is left
407 				cTabsHolder.redraw();
408 			}
409 		});
410 
411 		buildFolder(form, props_prefix);
412 
413 		return form;
414 	}
415 
buildFolder(final Composite form, final String props_prefix)416 	private void buildFolder(final Composite form, final String props_prefix) {
417 		PluginInterface pi = PluginInitializer.getDefaultInterface();
418 		UIManager uim = pi.getUIManager();
419 		MenuManager menuManager = uim.getMenuManager();
420 
421 
422 		menuItemShowTabs = menuManager.addMenuItem(props_prefix + "._end_",
423 				"ConfigView.section.style.ShowTabsInTorrentView");
424 		menuItemShowTabs.setStyle(MenuItem.STYLE_CHECK);
425 		menuItemShowTabs.addFillListener(new MenuItemFillListener() {
426 			public void menuWillBeShown(MenuItem menu, Object data) {
427 				menu.setData(COConfigurationManager.getBooleanParameter(
428 						"Library.ShowTabsInTorrentView"));
429 			}
430 		});
431 		menuItemShowTabs.addListener(new MenuItemListener() {
432 			public void selected(MenuItem menu, Object target) {
433 				COConfigurationManager.setParameter("Library.ShowTabsInTorrentView",
434 						(Boolean) menu.getData());
435 			}
436 		});
437 
438 		cTabsHolder.addListener(SWT.Resize, new Listener() {
439 			public void handleEvent(Event e) {
440 				if (tabbedMDI.getMinimized()) {
441 					fdHeightChanger.height = tabbedMDI.getFolderHeight();
442 					cTabsHolder.getParent().layout();
443 					return;
444 				}
445 
446 				Double l = (Double) sash.getData("PCT");
447 				if (l != null) {
448 					fdHeightChanger.height = (int) (form.getBounds().height * l.doubleValue());
449 					cTabsHolder.getParent().layout();
450 				}
451 			}
452 		});
453 
454 		String[] restricted_to = tv.getTabViewsRestrictedTo();
455 
456 		Set<String> rt_set = new HashSet<String>();
457 
458 		if ( restricted_to != null ){
459 
460 			rt_set.addAll( Arrays.asList( restricted_to ));
461 		}
462 
463 		// Call plugin listeners
464 
465 		UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
466 		if (uiFunctions != null) {
467 
468 			UISWTInstance pluginUI = uiFunctions.getUISWTInstance();
469 
470 			if (pluginUI != null) {
471 
472 				UISWTViewEventListenerWrapper[] pluginViews = pluginUI.getViewListeners(
473 						tv.getTableID());
474 				if (pluginViews != null) {
475 					for (final UISWTViewEventListenerWrapper l : pluginViews) {
476 						if (l == null) {
477 							continue;
478 						}
479 						try {
480 							String view_id = l.getViewID();
481 
482 							if (restricted_to != null && !rt_set.contains(view_id)) {
483 								continue;
484 							}
485 
486 							tabbedMDI.registerEntry(view_id, new MdiEntryCreationListener2() {
487 								public MdiEntry createMDiEntry(MultipleDocumentInterface mdi, String id,
488 										Object datasource, Map<?, ?> params) {
489 									return addTabView(l, null);
490 								}
491 							});
492 
493 							tabbedMDI.loadEntryByID(view_id, false);
494 
495 						} catch (Exception e) {
496 							// skip, plugin probably specifically asked to not be added
497 						}
498 					}
499 				}
500 			}
501 		}
502 
503 		if (!tabbedMDI.getMinimized()) {
504 			MdiEntry[] entries = tabbedMDI.getEntries();
505 			if (entries.length > 0) {
506 				tabbedMDI.showEntry(entries[0]);
507 			}
508 		}
509 	}
510 
511 	private ISelectedContent[] selectedContent;
512 
swt_refresh()513 	public void swt_refresh() {
514 		if (tv.isTabViewsEnabled() && tabbedMDI != null && !tabbedMDI.isDisposed()
515 				&& !tabbedMDI.getMinimized()){
516 
517 			MdiEntry entry = tabbedMDI.getCurrentEntry();
518 			if (entry != null) {
519 				entry.updateUI();
520 			}
521 		}
522 	}
523 
524 	// @see com.aelitis.azureus.ui.selectedcontent.SelectedContentListener#currentlySelectedContentChanged(com.aelitis.azureus.ui.selectedcontent.ISelectedContent[], java.lang.String)
currentlySelectedContentChanged(ISelectedContent[] currentContent, String viewID)525 	public void currentlySelectedContentChanged(ISelectedContent[] currentContent,
526 			String viewID) {
527 		TableView tvToUse = tvOverride == null ? tv : tvOverride;
528 		if (viewID != null && viewID.equals(tvToUse.getTableID())) {
529 			selectedContent = currentContent;
530 		}
531 		if (currentContent.length == 0 && tv.isVisible() && selectedContent != null
532 				&& selectedContent.length != 0) {
533 			SelectedContentManager.changeCurrentlySelectedContent(
534 					tvToUse.getTableID(), selectedContent, tvToUse);
535 		}
536 	}
537 
obfusticatedImage(Image image)538 	public void obfusticatedImage(Image image) {
539 		if (tabbedMDI instanceof ObfusticateImage) {
540 			ObfusticateImage o = (ObfusticateImage) tabbedMDI;
541 			image = o.obfusticatedImage(image);
542 		}
543 	}
544 }
545