1 /*
2  * Created on 2 juil. 2003
3  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
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.
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16  *
17  */
18 package org.gudy.azureus2.ui.swt.views;
19 
20 import java.util.*;
21 
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.custom.SashForm;
24 import org.eclipse.swt.custom.ScrolledComposite;
25 import org.eclipse.swt.custom.StackLayout;
26 import org.eclipse.swt.events.*;
27 import org.eclipse.swt.graphics.*;
28 import org.eclipse.swt.layout.*;
29 import org.eclipse.swt.widgets.*;
30 import org.eclipse.swt.widgets.List;
31 import org.gudy.azureus2.core3.config.COConfigurationManager;
32 import org.gudy.azureus2.core3.internat.MessageText;
33 import org.gudy.azureus2.core3.logging.LogEvent;
34 import org.gudy.azureus2.core3.logging.LogIDs;
35 import org.gudy.azureus2.core3.logging.Logger;
36 import org.gudy.azureus2.core3.util.*;
37 import org.gudy.azureus2.core3.util.Timer;
38 import org.gudy.azureus2.plugins.ui.UIInputReceiver;
39 import org.gudy.azureus2.plugins.ui.UIInputReceiverListener;
40 import org.gudy.azureus2.plugins.ui.config.ConfigSection;
41 import org.gudy.azureus2.pluginsimpl.local.ui.config.ConfigSectionRepository;
42 import org.gudy.azureus2.ui.swt.Messages;
43 import org.gudy.azureus2.ui.swt.SimpleTextEntryWindow;
44 import org.gudy.azureus2.ui.swt.Utils;
45 import org.gudy.azureus2.ui.swt.plugins.*;
46 import org.gudy.azureus2.ui.swt.views.configsections.*;
47 
48 import com.aelitis.azureus.core.AzureusCore;
49 import com.aelitis.azureus.core.AzureusCoreFactory;
50 import com.aelitis.azureus.core.AzureusCoreRunningListener;
51 import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;
52 
53 @SuppressWarnings("deprecation")
54 public class ConfigView implements UISWTViewEventListener {
55   public static final String VIEW_ID = UISWTInstance.VIEW_CONFIG;
56   private static final LogIDs LOGID = LogIDs.GUI;
57   public static final String sSectionPrefix = "ConfigView.section.";
58 
59   Map<TreeItem, ConfigSection> sections = new HashMap<TreeItem, ConfigSection>();
60   // Only access on SWT Thread
61   java.util.List<ConfigSection> sectionsCreated = new ArrayList<ConfigSection>(1);
62   Composite cConfig;
63   Composite cConfigSection;
64   StackLayout layoutConfigSection;
65   Label lHeader;
66   Label usermodeHint;
67   Font headerFont;
68   Font filterFoundFont;
69   Tree tree;
70   ArrayList<ConfigSection> pluginSections;
71 
72 	private Timer filterDelayTimer;
73 	private String filterText = "";
74 	private Label lblX;
75 	private Listener scResizeListener;
76 
77 	private Image imgSmallX;
78 	private Image imgSmallXGray;
79 	private String startSection;
80 	private UISWTView swtView;
81 
ConfigView()82 	public ConfigView() {
83   }
84 
initialize(final Composite composite)85   private void initialize(final Composite composite) {
86   	// need to initalize composite now, since getComposite can
87   	// be called at any time
88     cConfig = new Composite(composite, SWT.NONE);
89 
90     GridLayout configLayout = new GridLayout();
91     configLayout.marginHeight = 0;
92     configLayout.marginWidth = 0;
93     cConfig.setLayout(configLayout);
94     GridData gridData = new GridData(GridData.FILL_BOTH);
95     Utils.setLayoutData(cConfig, gridData);
96 
97     final Label label = new Label(cConfig, SWT.CENTER);
98     Messages.setLanguageText(label, "view.waiting.core");
99     gridData = new GridData(GridData.FILL_BOTH);
100     Utils.setLayoutData(label, gridData);
101 
102     // Need to delay initialation until core is done so we can guarantee
103     // all config sections are loaded (ie. plugin ones).
104     // TODO: Maybe add them on the fly?
105     AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() {
106 			public void azureusCoreRunning(AzureusCore core) {
107 				Utils.execSWTThread(new AERunnable() {
108 					public void runSupport() {
109 						_initialize(composite);
110 						label.dispose();
111 						composite.layout(true, true);
112 					}
113 				});
114 			}
115 		});
116   }
117 
_initialize(final Composite composite)118   private void _initialize(final Composite composite) {
119 
120     GridData gridData;
121     /*
122     /--cConfig-----------------------------------------------------------\
123     | ###SashForm#form################################################## |
124     | # /--cLeftSide-\ /--cRightSide---------------------------------\ # |
125     | # | ##tree#### | | ***cHeader********************************* | # |
126     | # | #        # | | * lHeader                    usermodeHint * | # |
127     | # | #        # | | ******************************************* | # |
128     | # | #        # | | ###Composite cConfigSection################ | # |
129     | # | #        # | | #                                         # | # |
130     | # | #        # | | #                                         # | # |
131     | # | #        # | | #                                         # | # |
132     | # | ########## | | #                                         # | # |
133     | # |txtFilter X | | ########################################### | # |
134     | # \------------/ \---------------------------------------------/ # |
135     | ################################################################## |
136     |                                                          [Buttons] |
137     \--------------------------------------------------------------------/
138     */
139     try {
140       Display d = composite.getDisplay();
141       GridLayout configLayout;
142 
143       SashForm form = new SashForm(cConfig,SWT.HORIZONTAL);
144       gridData = new GridData(GridData.FILL_BOTH);
145       Utils.setLayoutData(form, gridData);
146 
147       Composite cLeftSide = new Composite(form, SWT.BORDER);
148       gridData = new GridData(GridData.FILL_BOTH);
149       Utils.setLayoutData(cLeftSide, gridData);
150 
151       FormLayout layout = new FormLayout();
152       cLeftSide.setLayout(layout);
153 
154       Composite cFilterArea = new Composite(cLeftSide, SWT.NONE);
155       cFilterArea.setLayout(new FormLayout());
156 
157       final Text txtFilter = new Text(cFilterArea, SWT.BORDER);
158       final String sFilterText = MessageText.getString("ConfigView.filter");
159       txtFilter.setText(sFilterText);
160       txtFilter.selectAll();
161       txtFilter.addModifyListener(new ModifyListener() {
162       	public void modifyText(ModifyEvent e) {
163       		filterTree(txtFilter.getText());
164       	}
165       });
166       txtFilter.addMouseListener(new MouseAdapter() {
167 				public void mouseDown(MouseEvent e) {
168 					if (txtFilter.getText().equals(sFilterText)) {
169 						txtFilter.selectAll();
170 					}
171 				}
172 			});
173       txtFilter.setFocus();
174 
175       ImageLoader imageLoader = ImageLoader.getInstance();
176       imgSmallXGray = imageLoader.getImage("smallx-gray");
177       imgSmallX = imageLoader.getImage("smallx");
178 
179   		lblX = new Label(cFilterArea, SWT.WRAP);
180       Messages.setLanguageTooltip(lblX, "MyTorrentsView.clearFilter.tooltip");
181       lblX.setImage(imgSmallXGray);
182       lblX.addMouseListener(new MouseAdapter() {
183       	public void mouseUp(MouseEvent e) {
184       		txtFilter.setText("");
185       	}
186       });
187 
188       lblX.addDisposeListener(new DisposeListener() {
189 				public void widgetDisposed(DisposeEvent arg0) {
190 			    ImageLoader imageLoader = ImageLoader.getInstance();
191 			    imageLoader.releaseImage("smallx-gray");
192 			    imageLoader.releaseImage("smallx");
193 				}
194 			});
195 
196       Label lblSearch = new Label(cFilterArea, SWT.NONE);
197       imageLoader.setLabelImage(lblSearch, "search");
198 
199       tree = new Tree(cLeftSide, SWT.NONE);
200       FontData[] fontData = tree.getFont().getFontData();
201       fontData[0].setStyle(SWT.BOLD);
202       filterFoundFont = new Font(d, fontData);
203 
204       FormData formData;
205 
206       formData = new FormData();
207       formData.bottom = new FormAttachment(100, -5);
208       formData.left = new FormAttachment(0, 0);
209       formData.right = new FormAttachment(100, 0);
210       Utils.setLayoutData(cFilterArea, formData);
211 
212       formData = new FormData();
213       formData.top = new FormAttachment(0,5);
214       formData.left = new FormAttachment(0, 5);
215       Utils.setLayoutData(lblSearch, formData);
216 
217       formData = new FormData();
218       formData.top = new FormAttachment(0,5);
219       formData.left = new FormAttachment(lblSearch,5);
220       formData.right = new FormAttachment(lblX, -3);
221       Utils.setLayoutData(txtFilter, formData);
222 
223       formData = new FormData();
224       formData.top = new FormAttachment(0,5);
225       formData.right = new FormAttachment(100,-5);
226       Utils.setLayoutData(lblX, formData);
227 
228       formData = new FormData();
229       formData.top = new FormAttachment(0, 0);
230       formData.left = new FormAttachment(0,0);
231       formData.right = new FormAttachment(100,0);
232       formData.bottom = new FormAttachment(cFilterArea,-1);
233       Utils.setLayoutData(tree, formData);
234 
235       Composite cRightSide = new Composite(form, SWT.NULL);
236       configLayout = new GridLayout();
237       configLayout.marginHeight = 3;
238       configLayout.marginWidth = 0;
239       cRightSide.setLayout(configLayout);
240 
241       	// Header
242 
243       Composite cHeader = new Composite(cRightSide, SWT.BORDER);
244 
245       configLayout = new GridLayout();
246       configLayout.marginHeight = 3;
247       configLayout.marginWidth = 0;
248       configLayout.numColumns = 2;
249       configLayout.marginRight = 5;
250       cHeader.setLayout(configLayout);
251       gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER);
252       Utils.setLayoutData(cHeader, gridData);
253 
254       cHeader.setBackground(d.getSystemColor(SWT.COLOR_LIST_SELECTION));
255       cHeader.setForeground(d.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
256 
257       lHeader = new Label(cHeader, SWT.NULL);
258       lHeader.setBackground(d.getSystemColor(SWT.COLOR_LIST_SELECTION));
259       lHeader.setForeground(d.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
260       fontData = lHeader.getFont().getFontData();
261       fontData[0].setStyle(SWT.BOLD);
262       int fontHeight = (int)(fontData[0].getHeight() * 1.2);
263       fontData[0].setHeight(fontHeight);
264       headerFont = new Font(d, fontData);
265       lHeader.setFont(headerFont);
266       gridData = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING);
267       Utils.setLayoutData(lHeader, gridData);
268 
269 
270       usermodeHint = new Label(cHeader, SWT.NULL);
271       usermodeHint.setBackground(d.getSystemColor(SWT.COLOR_LIST_SELECTION));
272       usermodeHint.setForeground(d.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
273       gridData = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END | GridData.GRAB_HORIZONTAL);
274       Utils.setLayoutData(usermodeHint, gridData);
275 
276 	  Menu headerMenu = new Menu(cHeader.getShell(), SWT.POP_UP );
277 
278 	  final MenuItem menuShortCut = new MenuItem(headerMenu, SWT.PUSH);
279 	  Messages.setLanguageText( menuShortCut, "label.set.shortcut" );
280 
281 	  menuShortCut.addSelectionListener(new SelectionAdapter() {
282 		  public void widgetSelected(SelectionEvent e) {
283 
284 			  final TreeItem tree_item = (TreeItem)lHeader.getData( "TreeItem" );
285 
286 			  if ( tree_item != null ){
287 
288 				  final String id = (String)tree_item.getData( "ID" );
289 
290 				  if ( id != null ){
291 
292 						SimpleTextEntryWindow entryWindow = new SimpleTextEntryWindow(
293 								"config.dialog.shortcut.title",
294 								"config.dialog.shortcut.text");
295 						entryWindow.setPreenteredText(COConfigurationManager.getStringParameter( "config.section.shortcut.key." + id, "" ), false);
296 						entryWindow.setTextLimit(1);
297 						entryWindow.prompt(new UIInputReceiverListener() {
298 							public void UIInputReceiverClosed(UIInputReceiver entryWindow) {
299 								if (!entryWindow.hasSubmittedInput()) {
300 									return;
301 								}
302 								String sReturn = entryWindow.getSubmittedInput();
303 								if ( sReturn != null ){
304 
305 									sReturn = sReturn.trim();
306 
307 									if ( sReturn.length() > 1 ){
308 
309 										sReturn = sReturn.substring(0,1);
310 									}
311 
312 									COConfigurationManager.setParameter( "config.section.shortcut.key." + id, sReturn );
313 
314 									updateHeader( tree_item );
315 								}
316 							}
317 						});
318 				  }
319 			  }
320 		  }
321 	  });
322 
323 	  cHeader.setMenu( headerMenu );
324 	  lHeader.setMenu( headerMenu );
325 	  usermodeHint.setMenu( headerMenu );
326 
327       // Config Section
328       cConfigSection = new Composite(cRightSide, SWT.NULL);
329       layoutConfigSection = new StackLayout();
330       cConfigSection.setLayout(layoutConfigSection);
331       gridData = new GridData(GridData.FILL_BOTH);
332       gridData.horizontalIndent = 2;
333       Utils.setLayoutData(cConfigSection, gridData);
334 
335       form.setWeights(new int[] {20,80});
336 
337       tree.addSelectionListener(new SelectionAdapter() {
338         public void widgetSelected(SelectionEvent e) {
339           Tree tree = (Tree)e.getSource();
340           //Check that at least an item is selected
341           //OSX lets you select nothing in the tree for example when a child is selected
342           //and you close its parent.
343           if(tree.getSelection().length > 0)
344     	      showSection(tree.getSelection()[0], false);
345            }
346       });
347       // Double click = expand/contract branch
348       tree.addListener(SWT.DefaultSelection, new Listener() {
349         public void handleEvent(Event e) {
350             TreeItem item = (TreeItem)e.item;
351             if (item != null)
352               item.setExpanded(!item.getExpanded());
353         }
354       });
355     } catch (Exception e) {
356     	Logger.log(new LogEvent(LOGID, "Error initializing ConfigView", e));
357     }
358 
359     scResizeListener = new Listener() {
360 			public void handleEvent(Event event) {
361 				setupSC((ScrolledComposite)event.widget);
362 			}
363 		};
364 
365 
366     // Add sections
367     /** How to add a new section
368      * 1) Create a new implementation of ConfigSectionSWT in a new file
369      *    (Use the ConfigSectionTMP.java as a template if it's still around)
370      * 2) import it into here
371      * 3) add it to the internal sections list
372      */
373     pluginSections = ConfigSectionRepository.getInstance().getList();
374 
375     ConfigSection[] internalSections = {
376                                          new ConfigSectionMode(),
377                                          new ConfigSectionStartShutdown(),
378                                          new ConfigSectionBackupRestore(),
379                                          new ConfigSectionConnection(),
380                                          new ConfigSectionConnectionProxy(),
381                                          new ConfigSectionConnectionAdvanced(),
382                                          new ConfigSectionConnectionEncryption(),
383                                          new ConfigSectionConnectionDNS(),
384                                          new ConfigSectionTransfer(),
385                                          new ConfigSectionTransferAutoSpeedSelect(),
386                                          new ConfigSectionTransferAutoSpeed(),
387                                          new ConfigSectionTransferAutoSpeedBeta(),
388                                          new ConfigSectionTransferLAN(),
389                                          new ConfigSectionFile(),
390                                          new ConfigSectionFileMove(),
391                                          new ConfigSectionFileTorrents(),
392                                          new ConfigSectionFileTorrentsDecoding(),
393                                          new ConfigSectionFilePerformance(),
394                                          new ConfigSectionInterface(),
395                                          new ConfigSectionInterfaceLanguage(),
396                                          new ConfigSectionInterfaceStart(),
397                                          new ConfigSectionInterfaceDisplay(),
398                                          new ConfigSectionInterfaceTables(),
399                                          new ConfigSectionInterfaceColor(),
400                                          new ConfigSectionInterfaceAlerts(),
401                                          new ConfigSectionInterfacePassword(),
402                                          new ConfigSectionInterfaceLegacy(),
403                                          new ConfigSectionIPFilter(),
404                                          new ConfigSectionPlugins(this),
405                                          new ConfigSectionStats(),
406                                          new ConfigSectionTracker(),
407                                          new ConfigSectionTrackerClient(),
408                                          new ConfigSectionTrackerServer(),
409                                          new ConfigSectionSecurity(),
410                                          new ConfigSectionSharing(),
411                                          new ConfigSectionLogging()
412                                         };
413 
414     pluginSections.addAll(0, Arrays.asList(internalSections));
415 
416     for (int i = 0; i < pluginSections.size(); i++) {
417 
418     	// slip the non-standard "plugins" initialisation inbetween the internal ones
419     	// and the plugin ones so plugin ones can be children of it
420 
421       boolean	plugin_section = i >= internalSections.length;
422 
423       ConfigSection section = pluginSections.get(i);
424 
425       if ( section instanceof UISWTConfigSection ) {
426         String name;
427         try {
428           name = section.configSectionGetName();
429          } catch (Exception e) {
430         	 Logger.log(new LogEvent(LOGID, "A ConfigSection plugin caused an "
431 							+ "error while trying to call its "
432 							+ "configSectionGetName function", e));
433           name = "Bad Plugin";
434         }
435 
436          String	section_key = name;
437 
438          if ( plugin_section ){
439          		// if resource exists without prefix then use it as plugins don't
440          		// need to start with the prefix
441 
442          	if ( !MessageText.keyExists(section_key)){
443 
444          		section_key = sSectionPrefix + name;
445          	}
446 
447          }else{
448 
449          	section_key = sSectionPrefix + name;
450          }
451 
452          String	section_name = MessageText.getString( section_key );
453 
454          try {
455           TreeItem treeItem;
456           String location = section.configSectionGetParentSection();
457 
458           if ( location.length() == 0 || location.equalsIgnoreCase(ConfigSection.SECTION_ROOT)){
459         	  //int position = findInsertPointFor(section_name, tree);
460         	  //if ( position == -1 ){
461         		  treeItem = new TreeItem(tree, SWT.NULL);
462         	  // }else{
463         	  //	  treeItem = new TreeItem(tree, SWT.NULL, position);
464         	  //}
465          }else{
466         	  TreeItem treeItemFound = findTreeItem(tree, location);
467         	  if (treeItemFound != null){
468         		  if (location.equalsIgnoreCase(ConfigSection.SECTION_PLUGINS)) {
469         			  // Force ordering by name here.
470         			  int position = findInsertPointFor(section_name, treeItemFound);
471         			  if (position == -1) {
472         				  treeItem = new TreeItem(treeItemFound, SWT.NULL);
473         			  }
474         			  else {
475         				  treeItem = new TreeItem(treeItemFound, SWT.NULL, position);
476         			  }
477         		  }
478         		  else {
479         			  treeItem = new TreeItem(treeItemFound, SWT.NULL);
480         		  }
481         	  }else{
482         		  treeItem = new TreeItem(tree, SWT.NULL);
483         	  }
484           }
485 
486           ScrolledComposite sc = new ScrolledComposite(cConfigSection, SWT.H_SCROLL | SWT.V_SCROLL);
487           sc.setExpandHorizontal(true);
488           sc.setExpandVertical(true);
489           Utils.setLayoutData(sc, new GridData(GridData.FILL_BOTH));
490       		sc.getVerticalBar().setIncrement(16);
491       		sc.addListener(SWT.Resize, scResizeListener);
492 
493           if(i == 0) {
494             Composite c = ((UISWTConfigSection)section).configSectionCreate(sc);
495             sectionsCreated.add(section);
496             sc.setContent(c);
497           }
498 
499           Messages.setLanguageText(treeItem, section_key);
500           treeItem.setData("Panel", sc);
501           treeItem.setData("ID", name);
502           treeItem.setData("ConfigSectionSWT", section);
503 
504           sections.put(treeItem, section);
505 
506           // ConfigSectionPlugins is special because it has to handle the
507           // PluginConfigModel config pages
508           if (section instanceof ConfigSectionPlugins)
509           	((ConfigSectionPlugins)section).initPluginSubSections();
510         } catch (Exception e) {
511         	Logger.log(new LogEvent(LOGID, "ConfigSection plugin '" + name
512 							+ "' caused an error", e));
513         }
514       }
515     }
516 
517     final Display d = composite.getDisplay();
518 
519     final Listener shortcut_listener =
520     	new Listener()
521 		{
522 			public void
523 			handleEvent(
524 				Event e)
525 			{
526 				  if ((e.stateMask & ( SWT.MOD1 | SWT.CONTROL )) != 0 || e.keyCode == SWT.COMMAND ){
527 
528 				  char key = e.character;
529 
530 				  if (key <= 26 && key > 0){
531 					  key += 'a' - 1;
532 				  }
533 
534 				  if ((e.stateMask & SWT.SHIFT )!= 0 ){
535 					  key = Character.toUpperCase(key);
536 				  }
537 				  if ( !Character.isISOControl( key )){
538 
539 					  for ( TreeItem ti: sections.keySet()){
540 
541 						  String id = (String)ti.getData( "ID" );
542 
543 						  if ( id != null ){
544 
545 							  String shortcut = COConfigurationManager.getStringParameter( "config.section.shortcut.key." + id, "" );
546 
547 							  if ( shortcut.equals( String.valueOf( key ))){
548 
549 								  //findFocus( cConfig );
550 
551 								  selectSection( id, true );
552 
553 								  e.doit = false;
554 
555 								  break;
556 							  }
557 						  }
558 					  }
559 				  }
560 			  }
561 			};
562 		};
563 
564     d.addFilter( SWT.KeyDown, shortcut_listener );
565 
566     cConfigSection.addDisposeListener(
567     	new DisposeListener() {
568 
569 			public void widgetDisposed(DisposeEvent e) {
570 				d.removeFilter( SWT.KeyDown, shortcut_listener);
571 			}
572 		});
573 
574     if (composite instanceof Shell) {
575     	initApplyCloseButton();
576     } else {
577     	initSaveButton();
578     }
579 
580     if (startSection != null) {
581     	if (selectSection(startSection,false)) {
582     		return;
583     	}
584     }
585 
586     TreeItem selection = getLatestSelection();
587 
588     TreeItem[] items = { selection };
589 
590     tree.setSelection( items );
591 
592     	// setSelection doesn't trigger a SelectionListener, so..
593 
594     showSection( selection, false );
595   }
596 
597 
598   /*
599   private void
600   findFocus(
601 		Control 	c )
602   {
603 
604 	 if ( c.isFocusControl()){
605 		 System.out.println( "Focus=" + c );
606 	 }
607 
608 	  if ( c instanceof Composite ){
609 
610 		  Control[] kids = ((Composite)c).getChildren();
611 
612 		  for( Control k: kids ){
613 
614 			  findFocus( k );
615 		  }
616 	  }
617   }
618   */
619 
setupSC(ScrolledComposite sc)620 	private void setupSC(ScrolledComposite sc) {
621 		Composite c = (Composite) sc.getContent();
622 		if (c != null) {
623 			Point size1 = c.computeSize(sc.getClientArea().width, SWT.DEFAULT);
624 			Point size = c.computeSize(SWT.DEFAULT, size1.y);
625 			sc.setMinSize(size);
626 		}
627 		sc.getVerticalBar().setPageIncrement(sc.getSize().y);
628 	}
629 
630 
631   /**
632 	 * @param text
633 	 */
filterTree(String text)634 	protected void filterTree(String text) {
635 		filterText = text;
636 		if (filterDelayTimer != null) {
637 			filterDelayTimer.destroy();
638 		}
639 
640 		if (lblX != null && !lblX.isDisposed()) {
641 			Image img = filterText.length() > 0 ? imgSmallX : imgSmallXGray;
642 
643 			lblX.setImage(img);
644 		}
645 
646 
647 		filterDelayTimer = new Timer("Filter");
648 		filterDelayTimer.addEvent(SystemTime.getCurrentTime() + 300,
649 				new TimerEventPerformer() {
650 					public void perform(TimerEvent event) {
651 						filterDelayTimer.destroy();
652 						filterDelayTimer = null;
653 
654 						Utils.execSWTThread(new AERunnable() {
655 							public void runSupport() {
656 								if (filterDelayTimer != null) {
657 									return;
658 								}
659 								if ( tree.isDisposed()){
660 									return;
661 								}
662 
663 								Shell shell = tree.getShell();
664 								if (shell != null) {
665 									shell.setCursor(shell.getDisplay().getSystemCursor(
666 											SWT.CURSOR_WAIT));
667 								}
668 								try {
669 									ArrayList<TreeItem> foundItems = new ArrayList<TreeItem>();
670 									TreeItem[] items = tree.getItems();
671 									try {
672 										tree.setRedraw(false);
673 										for (int i = 0; i < items.length; i++) {
674 											items[i].setExpanded(false);
675 										}
676 
677 										filterTree(items, filterText, foundItems);
678 									} finally {
679 										tree.setRedraw(true);
680 									}
681 								} finally {
682 									if (shell != null) {
683 										shell.setCursor(null);
684 									}
685 									TreeItem[] selection = tree.getSelection();
686 									if (selection != null && selection.length > 0) {
687 										showSection(selection[0],false);
688 									}
689 								}
690 							}
691 						});
692 					}
693 				});
694 	}
695 
filterTree(TreeItem[] items, String text, ArrayList<TreeItem> foundItems)696 	protected void filterTree(TreeItem[] items, String text,
697 			ArrayList<TreeItem> foundItems) {
698 		text = text.toLowerCase();
699 		for (int i = 0; i < items.length; i++) {
700 			ensureSectionBuilt(items[i], false);
701 			ScrolledComposite composite = (ScrolledComposite) items[i].getData("Panel");
702 
703 			if (text.length() > 0
704 					&& (items[i].getText().toLowerCase().indexOf(text) >= 0 || compositeHasText(
705 							composite, text))) {
706 				foundItems.add(items[i]);
707 
708 				ensureExpandedTo(items[i]);
709 				items[i].setFont(filterFoundFont);
710 			} else {
711 				items[i].setFont(null);
712 			}
713 			filterTree(items[i].getItems(), text, foundItems);
714 		}
715 	}
716 
ensureExpandedTo(TreeItem item)717 	private void ensureExpandedTo(TreeItem item) {
718     TreeItem itemParent = item.getParentItem();
719   	if (itemParent != null) {
720   		itemParent.setExpanded(true);
721   		ensureExpandedTo(itemParent);
722   	}
723 	}
724 
725 	/**
726 	 * @param composite
727 	 * @param text
728 	 * @return
729 	 */
compositeHasText(Composite composite, String text)730 	private boolean compositeHasText(Composite composite, String text) {
731 		Control[] children = composite.getChildren();
732 
733 		for (int i = 0; i < children.length; i++) {
734 			Control child = children[i];
735 			if (child instanceof Label) {
736 				if (((Label)child).getText().toLowerCase().indexOf(text) >= 0) {
737 					return true;
738 				}
739 			} else if (child instanceof Group) {
740 				if (((Group)child).getText().toLowerCase().indexOf(text) >= 0) {
741 					return true;
742 				}
743 			} else if (child instanceof Button) {
744 				if (((Button)child).getText().toLowerCase().indexOf(text) >= 0) {
745 					return true;
746 				}
747 			} else if (child instanceof List) {
748 				String[] items = ((List)child).getItems();
749 				for (String item : items) {
750 					if (item.toLowerCase().indexOf(text) >= 0) {
751 						return true;
752 					}
753 				}
754 			} else if (child instanceof Combo) {
755 				String[] items = ((Combo)child).getItems();
756 				for (String item : items) {
757 					if (item.toLowerCase().indexOf(text) >= 0) {
758 						return true;
759 					}
760 				}
761 			}
762 
763 			if (child instanceof Composite) {
764 				if (compositeHasText((Composite) child, text)) {
765 					return true;
766 				}
767 			}
768 		}
769 
770 		return false;
771 	}
772 
773 	private void
saveLatestSelection( TreeItem item )774 	saveLatestSelection(
775 		TreeItem	item )
776 	{
777 		String path = "";
778 
779 		while( item != null ){
780 
781 			path = item.getText() + (path.length()==0?"":("$" + path ));
782 
783 			item = item.getParentItem();
784 		}
785 
786 		COConfigurationManager.setParameter( "ConfigView.section.last.selection", path );
787 	}
788 
789 	private TreeItem
getLatestSelection()790 	getLatestSelection()
791 	{
792 		String path = COConfigurationManager.getStringParameter( "ConfigView.section.last.selection", "" );
793 
794 		String[] bits = path.split( "\\$" );
795 
796 		TreeItem[]	items = tree.getItems();
797 
798 		TreeItem current = null;
799 
800 		boolean	located = false;
801 
802 		for ( int i=0;i<bits.length;i++ ){
803 
804 			String bit = bits[i];
805 
806 			boolean found = false;
807 
808 			for ( int j=0;j<items.length;j++ ){
809 
810 				if ( items[j].getText().equals( bit )){
811 
812 					current = items[j];
813 
814 					items = current.getItems();
815 
816 					found = true;
817 
818 					if ( i == bits.length - 1 ){
819 
820 						located = true;
821 					}
822 
823 					break;
824 				}
825 			}
826 
827 			if ( !found ){
828 
829 				break;
830 			}
831 		}
832 
833 		TreeItem result = located?current:tree.getItems()[0];
834 
835 		return( result );
836 	}
837 
838 	private void
showSection( TreeItem section, boolean focus )839 	showSection(
840 		TreeItem 	section,
841 		boolean		focus )
842 	{
843 	saveLatestSelection( section );
844 
845     ScrolledComposite item = (ScrolledComposite)section.getData("Panel");
846 
847     if (item != null) {
848 
849     	ensureSectionBuilt(section, true);
850 
851       layoutConfigSection.topControl = item;
852 
853       setupSC(item);
854 
855       if (filterText != null && filterText.length() > 0) {
856       	hilightText(item, filterText);
857         item.layout(true, true);
858       }
859 
860       cConfigSection.layout();
861 
862       updateHeader(section);
863 
864       if ( focus ){
865     	  layoutConfigSection.topControl.traverse( SWT.TRAVERSE_TAB_NEXT);
866       }
867     }
868   }
869 
hilightText(Composite c, String text)870 	private void hilightText(Composite c, String text) {
871 		Control[] children = c.getChildren();
872 		for (Control child : children) {
873 			if (child instanceof Composite) {
874 				hilightText((Composite) child, text);
875 			}
876 
877 			if (child instanceof Label) {
878 				if (((Label)child).getText().toLowerCase().indexOf(text) >= 0) {
879 					hilightControl(child);
880 				}
881 			} else if (child instanceof Group) {
882 				if (((Group)child).getText().toLowerCase().indexOf(text) >= 0) {
883 					hilightControl(child);
884 				}
885 			} else if (child instanceof Button) {
886 				if (((Button)child).getText().toLowerCase().indexOf(text) >= 0) {
887 					hilightControl(child);
888 				}
889 			} else if (child instanceof List) {
890 				String[] items = ((List)child).getItems();
891 				for (String item : items) {
892 					if (item.toLowerCase().indexOf(text) >= 0) {
893 						hilightControl(child);
894 						break;
895 					}
896 				}
897 			} else if (child instanceof Combo) {
898 				String[] items = ((Combo)child).getItems();
899 				for (String item : items) {
900 					if (item.toLowerCase().indexOf(text) >= 0) {
901 						hilightControl(child);
902 						break;
903 					}
904 				}
905 			}
906 
907 		}
908 	}
909 
910 	/**
911 	 * @param child
912 	 *
913 	 * @since 4.5.1.1
914 	 */
hilightControl(Control child)915 	private void hilightControl(Control child) {
916 		child.setFont(headerFont);
917 		child.setBackground(child.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
918 		child.setForeground(child.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
919 	}
920 
ensureSectionBuilt(TreeItem treeSection, boolean recreateIfAlreadyThere)921 	private void ensureSectionBuilt(TreeItem treeSection, boolean recreateIfAlreadyThere) {
922     ScrolledComposite item = (ScrolledComposite)treeSection.getData("Panel");
923 
924     if (item != null) {
925 
926       ConfigSection configSection = (ConfigSection)treeSection.getData("ConfigSectionSWT");
927 
928       if (configSection != null) {
929 
930         Control previous = item.getContent();
931         if (previous instanceof Composite) {
932         	if (!recreateIfAlreadyThere) {
933         		return;
934         	}
935         	configSection.configSectionDelete();
936           sectionsCreated.remove(configSection);
937           Utils.disposeComposite((Composite)previous,true);
938         }
939 
940         Composite c = ((UISWTConfigSection)configSection).configSectionCreate(item);
941 
942         sectionsCreated.add(configSection);
943 
944         item.setContent(c);
945       }
946     }
947 	}
948 
updateHeader(TreeItem section)949   private void updateHeader(TreeItem section) {
950 		if (section == null)
951 			return;
952 
953 		lHeader.setData( "TreeItem", section );
954 
955 		int userMode = COConfigurationManager.getIntParameter("User Mode");
956 		int maxUsermode = 0;
957 		try
958 		{
959 			ConfigSection sect = sections.get(section);
960 			if (sect instanceof UISWTConfigSection)
961 			{
962 				maxUsermode = ((UISWTConfigSection) sect).maxUserMode();
963 			}
964 		} catch (Error e)
965 		{
966 			//Debug.printStackTrace(e);
967 		}
968 
969 		String id = (String)section.getData( "ID" );
970 
971 		String shortcut = COConfigurationManager.getStringParameter( "config.section.shortcut.key." + id, null );
972 
973 		String sc_text;
974 
975 		if ( shortcut != null && shortcut.length() > 0 ){
976 
977 			sc_text = "      (Ctrl+" + shortcut.charAt(0) + ")";
978 		}else{
979 
980 			sc_text = "";
981 		}
982 
983 		if (userMode < maxUsermode)
984 			Messages.setLanguageText(usermodeHint, "ConfigView.higher.mode.available");
985 		else
986 			usermodeHint.setText("");
987 
988 		String sHeader = section.getText();
989 
990 		section = section.getParentItem();
991 		while (section != null)
992 		{
993 			sHeader = section.getText() + " : " + sHeader;
994 			section = section.getParentItem();
995 		}
996 		lHeader.setText(" " + sHeader.replaceAll("&", "&&") + sc_text );
997 		lHeader.getParent().layout(true, true);
998 	}
999 
1000 
createConfigSection(TreeItem treeItemParent, String sNameID, int position, boolean bPrefix)1001   public Composite createConfigSection(TreeItem treeItemParent,
1002                                         String sNameID,
1003                                         int position,
1004                                         boolean bPrefix) {
1005     ScrolledComposite sc = new ScrolledComposite(cConfigSection, SWT.H_SCROLL | SWT.V_SCROLL);
1006     sc.setExpandHorizontal(true);
1007     sc.setExpandVertical(true);
1008     Utils.setLayoutData(sc, new GridData(GridData.FILL_BOTH));
1009 		sc.getVerticalBar().setIncrement(16);
1010 		sc.addListener(SWT.Resize, scResizeListener);
1011 
1012     Composite cConfigSection = new Composite(sc, SWT.NULL);
1013 
1014     String section_key = ((bPrefix) ? sSectionPrefix : "") + sNameID;
1015 
1016     if (position == -2) { // Means "auto-order".
1017     	position = findInsertPointFor(MessageText.getString(section_key), (treeItemParent == null) ? (Object)tree : (Object)treeItemParent);
1018     }
1019 
1020     TreeItem treeItem;
1021     if (treeItemParent == null) {
1022       if (position >= 0)
1023         treeItem = new TreeItem(tree, SWT.NULL, position);
1024       else
1025         treeItem = new TreeItem(tree, SWT.NULL);
1026     } else {
1027       if (position >= 0)
1028         treeItem = new TreeItem(treeItemParent, SWT.NULL, position);
1029       else
1030         treeItem = new TreeItem(treeItemParent, SWT.NULL);
1031     }
1032     Messages.setLanguageText(treeItem, section_key);
1033     treeItem.setData("Panel", sc);
1034     treeItem.setData("ID", sNameID);
1035 
1036     sc.setContent(cConfigSection);
1037     return cConfigSection;
1038   }
1039 
1040   private static Comparator<Object> insert_point_comparator = new Comparator<Object>() {
1041 
1042 	  private String asString(Object o) {
1043 		  if (o instanceof String) {
1044 			  return (String)o;
1045 		  }
1046 		  else if (o instanceof TreeItem) {
1047 			  return ((TreeItem)o).getText();
1048 		  }
1049 		  else {
1050 			  throw new ClassCastException("object is not String or TreeItem: " + o.getClass().getName());
1051 		  }
1052 	  }
1053 
1054 	  public int compare(Object o1, Object o2) {
1055 		  int result = String.CASE_INSENSITIVE_ORDER.compare(asString(o1), asString(o2));
1056 		  return result;
1057 	  }
1058   };
1059 
findInsertPointFor(String name, Object structure)1060   private static int findInsertPointFor(String name, Object structure) {
1061 	  TreeItem[] children = null;
1062 	  if (structure instanceof Tree) {
1063 	      children = ((Tree)structure).getItems();
1064 	  }
1065 	  else {
1066 		  children = ((TreeItem)structure).getItems();
1067 	  }
1068 	  if (children.length == 0) {return -1;}
1069 	  int result =  Arrays.binarySearch(children, name, insert_point_comparator);
1070 	  if (result > 0) {return result;}
1071 	  result = -(result+1);
1072 	  if (result == children.length) {
1073 		  result = -1;
1074 	  }
1075 	  return result;
1076   }
1077 
findTreeItem(String ID)1078   public TreeItem findTreeItem(String ID) {
1079   	return findTreeItem((Tree)null, ID);
1080   }
1081 
findTreeItem(Tree tree, String ID)1082   private TreeItem findTreeItem(Tree tree, String ID) {
1083   	if (tree == null) {
1084   		tree = this.tree;
1085   	}
1086   	if (tree == null) {
1087   		return null;
1088   	}
1089     TreeItem[] items = tree.getItems();
1090     for (int i = 0; i < items.length; i++) {
1091       String itemID = (String)items[i].getData("ID");
1092       if (itemID != null && itemID.equalsIgnoreCase(ID)) {
1093         return items[i];
1094       }
1095       TreeItem itemFound = findTreeItem(items[i], ID);
1096       if (itemFound != null)
1097         return itemFound;
1098     }
1099 	 return null;
1100   }
1101 
findTreeItem(TreeItem item, String ID)1102   private TreeItem findTreeItem(TreeItem item, String ID) {
1103     TreeItem[] subItems = item.getItems();
1104     for (int i = 0; i < subItems.length; i++) {
1105       String itemID = (String)subItems[i].getData("ID");
1106       if (itemID != null && itemID.equalsIgnoreCase(ID)) {
1107         return subItems[i];
1108       }
1109 
1110       TreeItem itemFound = findTreeItem(subItems[i], ID);
1111       if (itemFound != null)
1112         return itemFound;
1113     }
1114     return null;
1115   }
1116 
initSaveButton()1117   private void initSaveButton() {
1118     GridData gridData;
1119     final Button save = new Button(cConfig, SWT.PUSH);
1120     Messages.setLanguageText(save, "ConfigView.button.save"); //$NON-NLS-1$
1121     gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
1122     gridData.horizontalSpan = 2;
1123     gridData.widthHint = 80;
1124     Utils.setLayoutData(save, gridData);
1125 
1126     save.addSelectionListener(new SelectionAdapter() {
1127       public void widgetSelected(SelectionEvent event) {
1128 				// force focusout on osx
1129 				save.setFocus();
1130 				save();
1131 			}
1132 		});
1133   }
1134 
initApplyCloseButton()1135   private void initApplyCloseButton() {
1136   	Composite cButtons = new Composite(cConfig, SWT.NONE);
1137   	GridLayout gridLayout = new GridLayout();
1138 		gridLayout.horizontalSpacing = gridLayout.verticalSpacing = gridLayout.marginHeight = gridLayout.marginWidth = 0;
1139 		gridLayout.numColumns = 2;
1140 		cButtons.setLayout(gridLayout);
1141 		Utils.setLayoutData(cButtons, new GridData(GridData.HORIZONTAL_ALIGN_END));
1142 
1143     GridData gridData;
1144     final Button apply = new Button(cButtons, SWT.PUSH);
1145     Messages.setLanguageText(apply, "Button.apply");
1146     gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
1147     gridData.widthHint = 80;
1148     Utils.setLayoutData(apply, gridData);
1149 
1150     apply.addSelectionListener(new SelectionAdapter() {
1151       public void widgetSelected(SelectionEvent event) {
1152 				// force focusout on osx
1153 				apply.setFocus();
1154 				save();
1155 			}
1156 		});
1157 
1158     final Button close = new Button(cButtons, SWT.PUSH);
1159     Messages.setLanguageText(close, "Button.close");
1160     gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
1161     gridData.widthHint = 80;
1162     Utils.setLayoutData(close, gridData);
1163 
1164     close.addSelectionListener(new SelectionAdapter() {
1165       public void widgetSelected(SelectionEvent event) {
1166 				// force focusout on osx
1167 				apply.setFocus();
1168 				save();
1169 				apply.getShell().dispose();
1170 			}
1171 		});
1172   }
1173 
getComposite()1174   private Composite getComposite() {
1175     return cConfig;
1176   }
1177 
updateLanguage()1178   private void updateLanguage() {
1179     updateHeader(tree.getSelection()[0]);
1180     if (swtView != null) {
1181     	swtView.setTitle(getFullTitle());
1182     }
1183 //    cConfig.setSize(cConfig.computeSize(SWT.DEFAULT, SWT.DEFAULT));
1184   }
1185 
delete()1186   private void delete() {
1187   	for (ConfigSection section : sectionsCreated) {
1188     	try {
1189     		section.configSectionDelete();
1190     	} catch (Exception e) {
1191     		Debug.out("Error while deleting config section", e);
1192     	}
1193     }
1194   	sectionsCreated.clear();
1195   	if ( pluginSections != null ){
1196   		pluginSections.clear();
1197   	}
1198   	if ( tree != null ){
1199 	    if(! tree.isDisposed()) {
1200 		    TreeItem[] items = tree.getItems();
1201 		    for (int i = 0; i < items.length; i++) {
1202 		      Composite c = (Composite)items[i].getData("Panel");
1203 		      Utils.disposeComposite(c);
1204 		      items[i].setData("Panel", null);
1205 
1206 		      items[i].setData("ConfigSectionSWT", null);
1207 		    }
1208 	    }
1209   	}
1210     Utils.disposeComposite(cConfig);
1211 
1212   	Utils.disposeSWTObjects(new Object[] { headerFont, filterFoundFont });
1213 		headerFont = null;
1214 		filterFoundFont = null;
1215   }
1216 
getFullTitle()1217   private String getFullTitle() {
1218   	/*
1219   	 * Using resolveLocalizationKey because there are different version for Classic vs. Vuze
1220   	 */
1221     return MessageText.getString(MessageText.resolveLocalizationKey("ConfigView.title.full")); //$NON-NLS-1$
1222   }
1223 
selectSection(String id, boolean focus)1224   public boolean selectSection(String id, boolean focus) {
1225 		TreeItem ti = findTreeItem(id);
1226 		if (ti == null)
1227 			return false;
1228 		tree.setSelection(new TreeItem[] { ti });
1229 		showSection(ti, focus);
1230 		return true;
1231 	}
1232 
1233   public void
selectSection( Class<?> config_section_class )1234   selectSection(
1235   	Class<?>	config_section_class )
1236   {
1237 	  TreeItem[]	items = tree.getItems();
1238 
1239 	  for (int i=0;i<items.length;i++){
1240 
1241 		  TreeItem	item = items[i];
1242 
1243 		  ConfigSection section = (ConfigSection)item.getData("ConfigSectionSWT");
1244 
1245 		  if ( section != null && section.getClass() == config_section_class ){
1246 
1247 			  tree.setSelection( new TreeItem[]{ item });
1248 
1249 			  showSection( item, true );
1250 
1251 			  break;
1252 		  }
1253 	  }
1254   }
1255 
save()1256   public void save() {
1257 		COConfigurationManager.setParameter("updated", 1);
1258 		COConfigurationManager.save();
1259 
1260 		if (null != pluginSections) {
1261 			for (int i = 0; i < pluginSections.size(); i++) {
1262 				pluginSections.get(i).configSectionSave();
1263 			}
1264 		}
1265 	}
1266 
dataSourceChanged(Object newDataSource)1267   private void dataSourceChanged(Object newDataSource) {
1268 
1269   	if (newDataSource instanceof String) {
1270 			String id = (String) newDataSource;
1271 	  	startSection = id;
1272 	  	Utils.execSWTThread(new AERunnable() {
1273 				public void runSupport() {
1274 					selectSection(startSection,false);
1275 				}
1276 			});
1277 		}
1278   }
1279 
eventOccurred(UISWTViewEvent event)1280 	public boolean eventOccurred(UISWTViewEvent event) {
1281     switch (event.getType()) {
1282       case UISWTViewEvent.TYPE_CREATE:
1283       	swtView = (UISWTView)event.getData();
1284       	swtView.setTitle(getFullTitle());
1285         break;
1286 
1287       case UISWTViewEvent.TYPE_DESTROY:
1288         delete();
1289         break;
1290 
1291       case UISWTViewEvent.TYPE_INITIALIZE:
1292         initialize((Composite)event.getData());
1293         break;
1294 
1295       case UISWTViewEvent.TYPE_LANGUAGEUPDATE:
1296       	Messages.updateLanguageForControl(getComposite());
1297       	updateLanguage();
1298         break;
1299 
1300       case UISWTViewEvent.TYPE_DATASOURCE_CHANGED:
1301       	dataSourceChanged(event.getData());
1302         break;
1303 
1304       case UISWTViewEvent.TYPE_FOCUSGAINED:
1305       	break;
1306 
1307       case UISWTViewEvent.TYPE_REFRESH:
1308         break;
1309     }
1310 
1311     return true;
1312   }
1313 
1314 }
1315