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 com.aelitis.azureus.ui.swt.subscriptions;
20 
21 import java.util.Arrays;
22 import java.util.Comparator;
23 
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.custom.StackLayout;
26 import org.eclipse.swt.layout.FillLayout;
27 import org.eclipse.swt.layout.FormAttachment;
28 import org.eclipse.swt.layout.FormData;
29 import org.eclipse.swt.layout.FormLayout;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Display;
33 import org.eclipse.swt.widgets.Event;
34 import org.eclipse.swt.widgets.Label;
35 import org.eclipse.swt.widgets.Listener;
36 import org.eclipse.swt.widgets.ProgressBar;
37 import org.eclipse.swt.widgets.Shell;
38 import org.eclipse.swt.widgets.Table;
39 import org.eclipse.swt.widgets.TableColumn;
40 import org.eclipse.swt.widgets.TableItem;
41 import org.gudy.azureus2.core3.config.COConfigurationManager;
42 import org.gudy.azureus2.core3.internat.MessageText;
43 import org.gudy.azureus2.core3.util.AEThread2;
44 import org.gudy.azureus2.ui.swt.Utils;
45 import org.gudy.azureus2.ui.swt.components.shell.ShellFactory;
46 
47 import com.aelitis.azureus.core.subs.Subscription;
48 import com.aelitis.azureus.core.subs.SubscriptionAssociationLookup;
49 import com.aelitis.azureus.core.subs.SubscriptionException;
50 import com.aelitis.azureus.core.subs.SubscriptionLookupListener;
51 import com.aelitis.azureus.core.subs.SubscriptionManager;
52 import com.aelitis.azureus.core.subs.SubscriptionManagerFactory;
53 import com.aelitis.azureus.core.subs.SubscriptionPopularityListener;
54 import com.aelitis.azureus.ui.swt.widgets.AnimatedImage;
55 
56 public class SubscriptionListWindow implements SubscriptionLookupListener {
57 
58 	final private byte[]			torrent_hash;
59 	final private String[]			networks;
60 	final private boolean			useCachedSubs;
61 
62 	private Display display;
63 	private Shell shell;
64 
65 	AnimatedImage animatedImage;
66 
67 	Button action;
68 	Label loadingText;
69 	ProgressBar loadingProgress;
70 	boolean loadingDone = false;
71 
72 	SubscriptionAssociationLookup lookup = null;
73 
74 	Composite mainComposite;
75 	Composite loadingPanel;
76 	Composite listPanel;
77 	Table subscriptionsList;
78 	StackLayout mainLayout;
79 
80 	private class SubscriptionItemModel {
81 		String name;
82 		long popularity;
83 		String popularityDisplay;
84 		Subscription subscription;
85 		boolean selected;
86 	}
87 
88 	SubscriptionItemModel subscriptionItems[];
89 
90 
91 
92 	public
SubscriptionListWindow( Shell parent, String display_name, byte[] torrent_hash, String[] networks, boolean useCachedSubs )93 	SubscriptionListWindow(
94 		Shell		parent,
95 		String		display_name,
96 		byte[]		torrent_hash,
97 		String[]	networks,
98 		boolean 	useCachedSubs )
99 	{
100 		this.torrent_hash 		= torrent_hash;
101 		this.networks			= networks;
102 		this.useCachedSubs		= useCachedSubs;
103 
104 		shell = ShellFactory.createShell( parent, SWT.DIALOG_TRIM | SWT.RESIZE );
105 		Utils.setShellIcon(shell);
106 		shell.setSize(400,300);
107 		Utils.centerWindowRelativeTo( shell, parent );
108 
109 		String networks_str = "";
110 
111 		for ( String net: networks ){
112 
113 			networks_str +=
114 					(networks_str.length()==0?"":", ") +
115 					MessageText.getString( "ConfigView.section.connection.networks." + net );
116 		}
117 
118 		if ( networks_str.length() == 0 ){
119 
120 			networks_str = MessageText.getString( "PeersView.uniquepiece.none" );
121 		}
122 
123 		display = shell.getDisplay();
124 		shell.setText(MessageText.getString("subscriptions.listwindow.title") + " [" + networks_str + "]" );
125 
126 		shell.setLayout(new FormLayout());
127 
128 		mainComposite = new Composite(shell,SWT.NONE);
129 		Label separator = new Label(shell,SWT.SEPARATOR | SWT.HORIZONTAL);
130 		Button cancel = new Button(shell,SWT.PUSH);
131 		action = new Button(shell,SWT.PUSH);
132 		cancel.setText(MessageText.getString("Button.cancel"));
133 
134 		FormData data;
135 
136 		data = new FormData();
137 		data.left = new FormAttachment(0,0);
138 		data.right = new FormAttachment(100,0);
139 		data.top = new FormAttachment(0,0);
140 		data.bottom = new FormAttachment(separator,0);
141 		Utils.setLayoutData(mainComposite, data);
142 
143 		data = new FormData();
144 		data.left = new FormAttachment(0,0);
145 		data.right = new FormAttachment(100,0);
146 		data.bottom = new FormAttachment(cancel,-2);
147 		Utils.setLayoutData(separator, data);
148 
149 		data = new FormData();
150 		data.right = new FormAttachment(action);
151 		data.width = 100;
152 		data.bottom = new FormAttachment(100,-5);
153 		Utils.setLayoutData(cancel, data);
154 
155 		data = new FormData();
156 		data.right = new FormAttachment(100,-5);
157 		data.width = 100;
158 		data.bottom = new FormAttachment(100,-5);
159 		Utils.setLayoutData(action, data);
160 
161 		cancel.addListener(SWT.Selection, new Listener() {
162 			public void handleEvent(Event arg0) {
163 				if(lookup != null) {
164 					lookup.cancel();
165 				}
166 				if(!shell.isDisposed()) {
167 					shell.dispose();
168 				}
169 			}
170 		});
171 
172 		mainLayout = new StackLayout();
173 		mainComposite.setLayout(mainLayout);
174 
175 		loadingPanel = new Composite(mainComposite,SWT.NONE);
176 		loadingPanel.setLayout(new FormLayout());
177 
178 		listPanel = new Composite(mainComposite,SWT.NONE);
179 		listPanel.setLayout(new FillLayout());
180 
181 		subscriptionsList = new Table(listPanel,SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION | SWT.VIRTUAL);
182 		subscriptionsList.setHeaderVisible(true);
183 
184 		TableColumn name = new TableColumn(subscriptionsList,SWT.NONE);
185 		name.setText(MessageText.getString("subscriptions.listwindow.name"));
186 		name.setWidth(Utils.adjustPXForDPI(310));
187 		name.setResizable(false);
188 
189 		TableColumn popularity = new TableColumn(subscriptionsList,SWT.NONE);
190 		popularity.setText(MessageText.getString("subscriptions.listwindow.popularity"));
191 		popularity.setWidth(Utils.adjustPXForDPI(70));
192 		popularity.setResizable(false);
193 
194 		subscriptionsList.addListener(SWT.SetData, new Listener() {
195 			public void handleEvent(Event e) {
196 				TableItem item = (TableItem) e.item;
197 				int index = subscriptionsList.indexOf(item);
198 				if(index >= 0 && index < subscriptionItems.length) {
199 					SubscriptionItemModel subscriptionItem = subscriptionItems[index];
200 					item.setText(0,subscriptionItem.name);
201 					item.setText(1,subscriptionItem.popularityDisplay);
202 				}
203 			}
204 		});
205 
206 		subscriptionsList.setSortColumn(popularity);
207 		subscriptionsList.setSortDirection(SWT.DOWN);
208 
209 		subscriptionsList.addListener(SWT.Selection, new Listener() {
210 			public void handleEvent(Event arg0) {
211 				action.setEnabled(subscriptionsList.getSelectionIndex() != -1);
212 			}
213 		});
214 
215 		Listener sortListener = new Listener() {
216 			public void handleEvent(Event e) {
217 				// determine new sort column and direction
218 				TableColumn sortColumn = subscriptionsList.getSortColumn();
219 				TableColumn currentColumn = (TableColumn) e.widget;
220 				int dir = subscriptionsList.getSortDirection();
221 				if (sortColumn == currentColumn) {
222 					dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
223 				} else {
224 					subscriptionsList.setSortColumn(currentColumn);
225 					dir = SWT.DOWN;
226 				}
227 				subscriptionsList.setSortDirection(dir);
228 				sortAndRefresh();
229 			}
230 		};
231 		name.addListener(SWT.Selection, sortListener);
232 		popularity.addListener(SWT.Selection, sortListener);
233 
234 		animatedImage = new AnimatedImage(loadingPanel);
235 		loadingText = new Label(loadingPanel,SWT.WRAP | SWT.CENTER);
236 		loadingProgress = new ProgressBar(loadingPanel,SWT.HORIZONTAL);
237 
238 		animatedImage.setImageFromName("spinner_big");
239 
240 		loadingText.setText(MessageText.getString("subscriptions.listwindow.loadingtext", new String[] { display_name }));
241 
242 		loadingProgress.setMinimum(0);
243 		loadingProgress.setMaximum(300);
244 		loadingProgress.setSelection(0);
245 
246 		data = new FormData();
247 		data.left = new FormAttachment(1,2,-16);
248 		data.top = new FormAttachment(1,2,-32);
249 		data.width = 32;
250 		data.height = 32;
251 		animatedImage.setLayoutData(data);
252 
253 		data = new FormData();
254 		data.left = new FormAttachment(0,5);
255 		data.right = new FormAttachment(100,-5);
256 		data.top = new FormAttachment(animatedImage.getControl(),10);
257 		data.height = 50;
258 		Utils.setLayoutData(loadingText, data);
259 
260 		data = new FormData();
261 		data.left = new FormAttachment(0,5);
262 		data.right = new FormAttachment(100,-5);
263 		data.top = new FormAttachment(loadingText,5);
264 		Utils.setLayoutData(loadingProgress, data);
265 
266 		boolean autoCheck = COConfigurationManager.getBooleanParameter("subscriptions.autocheck");
267 
268 		if(autoCheck) {
269 			startChecking();
270 		} else {
271 			action.setText(MessageText.getString("Button.yes"));
272 			Composite acceptPanel = new Composite(mainComposite,SWT.NONE);
273 			acceptPanel.setLayout(new FormLayout());
274 
275 			Label acceptLabel = new Label(acceptPanel,SWT.WRAP | SWT.CENTER);
276 
277 			acceptLabel.setText(MessageText.getString("subscriptions.listwindow.autochecktext"));
278 
279 			data = new FormData();
280 			data.left = new FormAttachment(0,5);
281 			data.right = new FormAttachment(100,-5);
282 			data.top = new FormAttachment(1,3,0);
283 			Utils.setLayoutData(acceptLabel, data);
284 
285 			action.addListener(SWT.Selection, new Listener() {
286 				public void handleEvent(Event event) {
287 					action.removeListener(SWT.Selection,this);
288 					COConfigurationManager.setParameter("subscriptions.autocheck",true);
289 					startChecking();
290 					mainComposite.layout();
291 				}
292 			});
293 			mainLayout.topControl = acceptPanel;
294 		}
295 
296 
297 		//shell.setSize(400,300);
298 		shell.open();
299 
300 	}
301 
startChecking()302 	private void startChecking() {
303 		action.setText(MessageText.getString("subscriptions.listwindow.subscribe"));
304 		action.setEnabled(false);
305 		try {
306 
307 
308 			SubscriptionManager subs_man = SubscriptionManagerFactory.getSingleton();
309 			if ( useCachedSubs ){
310 				Subscription[] subs = subs_man.getKnownSubscriptions( torrent_hash );
311 				complete(torrent_hash,subs);
312 			}else{
313 				lookup = subs_man.lookupAssociations( torrent_hash, networks, this);
314 
315 				lookup.setTimeout( 1*60*1000 );
316 			}
317 
318 
319 			loadingDone = false;
320 			AEThread2 progressMover = new AEThread2("progressMover",true) {
321 				public void run() {
322 					final int[] waitTime = new int[1];
323 					waitTime[0]= 100;
324 					while(!loadingDone) {
325 						if(display != null && ! display.isDisposed()) {
326 							display.asyncExec(new Runnable() {
327 								public void run() {
328 									if(loadingProgress != null && !loadingProgress.isDisposed()) {
329 										int currentSelection = loadingProgress.getSelection() +1;
330 										loadingProgress.setSelection(currentSelection);
331 										if(currentSelection > (loadingProgress.getMaximum()) * 80 / 100) {
332 											waitTime[0] = 300;
333 										}
334 										if (currentSelection > (loadingProgress.getMaximum()) * 90 / 100) {
335 											waitTime[0] = 1000;
336 										}
337 									} else {
338 										loadingDone = true;
339 									}
340 								}
341 							});
342 						}
343 						try {
344 							Thread.sleep(waitTime[0]);
345 							//Thread.sleep(100);
346 						} catch (Exception e) {
347 							loadingDone = true;
348 						}
349 					}
350 				}
351 			};
352 			progressMover.start();
353 
354 		} catch(Exception e) {
355 			failed(null,null);
356 		}
357 		animatedImage.start();
358 		mainLayout.topControl = loadingPanel;
359 	}
360 
361 	/*private void populateSubscription(final Subscription subscription) {
362 		final TableItem item = new TableItem(subscriptionsList,SWT.NONE);
363 		item.setData("subscription",subscription);
364 		item.setText(0,subscription.getName());
365 		try {
366 			item.setText(1,MessageText.getString("subscriptions.listwindow.popularity.reading"));
367 
368 
369 
370 
371 
372 		action.setEnabled(true);
373 	}*/
374 
found(byte[] hash, Subscription subscription)375 	public void found(byte[] hash, Subscription subscription) {
376 		// TODO Auto-generated method stub
377 
378 	}
379 
complete(final byte[] hash,final Subscription[] subscriptions)380 	public void complete(final byte[] hash,final Subscription[] subscriptions) {
381 		if( ! (subscriptions.length > 0) ) {
382 			failed(hash, null);
383 		} else {
384 
385 			if(display != null && !display.isDisposed()) {
386 				display.asyncExec(new Runnable() {
387 					public void run() {
388 						subscriptionItems = new SubscriptionItemModel[subscriptions.length];
389 						for(int i = 0 ; i < subscriptions.length ; i++) {
390 							final SubscriptionItemModel subscriptionItem = new SubscriptionItemModel();
391 							subscriptionItems[i] = subscriptionItem;
392 							subscriptionItem.name = subscriptions[i].getName();
393 							subscriptionItem.popularity = -1;
394 							subscriptionItem.popularityDisplay = MessageText.getString("subscriptions.listwindow.popularity.reading");
395 							subscriptionItem.subscription = subscriptions[i];
396 
397 							try {
398 							subscriptions[i].getPopularity(
399 									new SubscriptionPopularityListener()
400 									{
401 										public void
402 										gotPopularity(
403 											long		popularity )
404 										{
405 											update(subscriptionItem,popularity, popularity + "" );
406 										}
407 
408 										public void
409 										failed(
410 											SubscriptionException		error )
411 										{
412 											update(subscriptionItem,-2,MessageText.getString("subscriptions.listwindow.popularity.unknown"));
413 										}
414 
415 
416 									});
417 							} catch(SubscriptionException e) {
418 
419 								update(subscriptionItem,-2,MessageText.getString("subscriptions.listwindow.popularity.unknown"));
420 
421 							}
422 
423 						}
424 
425 						animatedImage.stop();
426 
427 						mainLayout.topControl = listPanel;
428 						mainComposite.layout();
429 
430 						sortAndRefresh();
431 						subscriptionsList.setSelection(0);
432 
433 						action.addListener(SWT.Selection, new Listener() {
434 							public void handleEvent(Event arg0) {
435 								if(subscriptionsList != null && !subscriptionsList.isDisposed()) {
436 									int selectedIndex = subscriptionsList.getSelectionIndex();
437 									if(selectedIndex >= 0 && selectedIndex < subscriptionItems.length) {
438 										Subscription subscription = (Subscription) subscriptionItems[selectedIndex].subscription;
439 										if(subscription != null) {
440 											subscription.setSubscribed(true);
441 											subscription.requestAttention();
442 										}
443 									}
444 								}
445 							}
446 						});
447 					}
448 
449 				});
450 			}
451 		}
452 	}
453 
454 	protected void
update( final SubscriptionItemModel subscriptionItem, final long popularity, final String text )455 	update(
456 		final SubscriptionItemModel subscriptionItem,
457 		final long		popularity,
458 		final String	text )
459 	{
460 		display.asyncExec(
461 			new Runnable()
462 			{
463 				public void
464 				run()
465 				{
466 					subscriptionItem.popularity = popularity;
467 					subscriptionItem.popularityDisplay = text;
468 
469 					sortAndRefresh();
470 				}
471 			});
472 	}
473 
sortAndRefresh()474 	private void sortAndRefresh() {
475 
476 		if ( subscriptionsList.isDisposed()){
477 
478 			return;
479 		}
480 
481 		for(int i = 0 ; i < subscriptionItems.length ; i++) {
482 			subscriptionItems[i].selected = false;
483 		}
484 
485 		int currentSelection = subscriptionsList.getSelectionIndex();
486 		if(currentSelection >= 0 && currentSelection < subscriptionItems.length) {
487 			subscriptionItems[currentSelection].selected = true;
488 		}
489 
490 		final int dir = subscriptionsList.getSortDirection() == SWT.DOWN ? 1 : -1;
491 		final boolean nameSort = subscriptionsList.getColumn(0) == subscriptionsList.getSortColumn();
492 		Arrays.sort(subscriptionItems,new Comparator() {
493 			public int compare(Object arg0, Object arg1) {
494 				SubscriptionItemModel item0 = (SubscriptionItemModel) arg0;
495 				SubscriptionItemModel item1 = (SubscriptionItemModel) arg1;
496 				if(nameSort) {
497 					return dir * item0.name.compareTo(item1.name);
498 				} else {
499 					return dir * (int) (item1.popularity - item0.popularity);
500 				}
501 			}
502 		});
503 		subscriptionsList.setItemCount(subscriptionItems.length);
504 		subscriptionsList.clearAll();
505 		if(currentSelection >= 0 && currentSelection < subscriptionItems.length) {
506 			for(int i = 0 ; i < subscriptionItems.length ; i++) {
507 				if(subscriptionItems[i].selected) {
508 					subscriptionsList.setSelection(i);
509 				}
510 			}
511 		}
512 	}
513 
failed(byte[] hash,SubscriptionException error)514 	public void failed(byte[] hash,SubscriptionException error) {
515 		if(display != null && !display.isDisposed()) {
516 			display.asyncExec(new Runnable() {
517 				public void run() {
518 					animatedImage.stop();
519 					animatedImage.dispose();
520 					loadingProgress.dispose();
521 					if ( !loadingText.isDisposed()){
522 						loadingText.setText(MessageText.getString("subscriptions.listwindow.failed"));
523 					}
524 				}
525 			});
526 		}
527 	}
528 }
529