/******************************************************************************* * Copyright (c) 2004, 2008 Alternative Ideas Corporation. All rights reserved. * This program and the accompanying materials are protected by United * States and International copyright laws. They may not be inspected, * copied, modified, transmitted, executed, or used in any way without * the express written permission of the copyright holder. *******************************************************************************/ package com.deltopia.ui.tests.internal; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; public class FolderTree extends Composite { private static final boolean REDRAW_WHILE_CREATING_TREE_ITEMS = false; private static final int STATS_CREATE_TREE_ITEMS_INVOCATIONS = 1; private static final int STATS_CREATE_TREE_ITEMS_TIME = 0; private static final int STATS_EXPANDED_FALSE = 2; private static final int STATS_EXPANDED_TIME = 0; private static final int STATS_EXPANDED_TRUE = 1; private static final int STATS_SET_IMAGE_INVOCATIONS = 2; /* * Two TreeColumns reproduce our scenario of using TreeItem#setImage. * However, a greater number of TreeColumns better depicts the effect of the * optimization of Tree#imageIndex because it easily increases the number of * TreeItem#setImage(int,Image) calls for TreeColumns with non-zero index. */ private static final int TREE_COLUMN_COUNT = 2; private Text browseText; /** * The Image to be used for file and folders that don't have * an association through Program. *

* Allows close reproduction of a specific number * TreeItem#setImage calls that doesn't vary on the * Program associations of the machine running the test. *

*/ private final Image defaultImage; private final Map extension2Image = new HashMap(); private final Tree tree; public FolderTree(Composite parent, int style) { super(parent, style); Control inputControls = createInputControls(this, style); if (inputControls != null) { inputControls.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); } Control commandControls = createCommandControls(this, style); if (commandControls != null) { commandControls.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); } tree = new Tree(this, SWT.BORDER); tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); for (int columnIx = 0; columnIx < TREE_COLUMN_COUNT; columnIx++) { TreeColumn column = new TreeColumn(tree, SWT.NONE); column.setText(Integer.toString(tree.indexOf(column))); } if (tree.getColumnCount() > 0) { tree.setHeaderVisible(true); } setLayout(new GridLayout(1, false)); defaultImage = new Image(getDisplay(), 16, 16); } private Control createCommandControls(Composite parent, int style) { Composite commandControls = new Composite(parent, SWT.NONE); int numColumns = 0; final Button expandAllButton = new Button(commandControls, SWT.PUSH); expandAllButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); expandAllButton.setText("Expand All"); numColumns++; final Button collapseAllButton = new Button(commandControls, SWT.PUSH); collapseAllButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); collapseAllButton.setText("Collapse All"); numColumns++; final Button getExpandedButton = new Button(commandControls, SWT.PUSH); getExpandedButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); getExpandedButton.setText("TreeItem.getExpanded()"); numColumns++; final Button setTopItemButton = new Button(commandControls, SWT.PUSH); setTopItemButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); setTopItemButton.setText("Tree.setTopItem(TreeItem)"); numColumns++; final Listener listener = new Listener() { public void handleEvent(Event e) { final Widget widget = e.widget; if (widget == expandAllButton) { setExpanded(null, true); } else if (widget == collapseAllButton) { setExpanded(null, false); } else if (widget == getExpandedButton) { final long[] stats = new long[3]; getExpanded(null, stats); System.out .println("TreeItem.getExpanded()= { invocations= { true= " + stats[STATS_EXPANDED_TRUE] + ", false= " + stats[STATS_EXPANDED_FALSE] + " }, time= " + stats[STATS_EXPANDED_TIME] + " }"); } else if (widget == setTopItemButton) { handleSetTopItemButtonSelection(); } } }; expandAllButton.addListener(SWT.Selection, listener); collapseAllButton.addListener(SWT.Selection, listener); getExpandedButton.addListener(SWT.Selection, listener); setTopItemButton.addListener(SWT.Selection, listener); if (numColumns > 0) { GridLayout commandControlsLayout = new GridLayout(); commandControlsLayout.makeColumnsEqualWidth = false; commandControlsLayout.marginHeight = 0; commandControlsLayout.marginWidth = 0; commandControlsLayout.numColumns = numColumns; commandControls.setLayout(commandControlsLayout); } else { commandControls.dispose(); commandControls = null; } return commandControls; } private Control createInputControls(Composite parent, int style) { final Composite inputControls = new Composite(parent, SWT.NONE); browseText = new Text(inputControls, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.WRAP); browseText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); final Button browseButton = new Button(inputControls, SWT.PUSH); browseButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false)); browseButton.setText("Browse..."); final Listener listener = new Listener() { public void handleEvent(Event e) { final Widget widget = e.widget; if (widget == browseText) { switch (e.type) { case SWT.Modify: setFolder(browseText.getText()); break; default: throw new IllegalArgumentException("e.type"); //$NON-NLS-1$ } } else if (widget == browseButton) { switch (e.type) { case SWT.Selection: DirectoryDialog dialog = new DirectoryDialog( browseButton.getShell()); String selection = dialog.open(); if (selection != null) { browseText.setText(selection); } break; default: throw new IllegalArgumentException("e.type"); //$NON-NLS-1$ } } else { throw new IllegalArgumentException("e.widget"); //$NON-NLS-1$ } } }; browseText.addListener(SWT.Modify, listener); browseButton.addListener(SWT.Selection, listener); GridLayout inputControlsLayout = new GridLayout(); inputControlsLayout.makeColumnsEqualWidth = false; inputControlsLayout.marginHeight = 0; inputControlsLayout.marginWidth = 0; inputControlsLayout.numColumns = 2; inputControls.setLayout(inputControlsLayout); return inputControls; } private void createTreeItems(TreeItem parentItem, File file, long[] stats) { if (parentItem == null) { tree.removeAll(); } else { parentItem.removeAll(); } File[] files = file.listFiles(); if (files != null) { final int style = SWT.NONE; for (int fileIx = 0; fileIx < files.length; fileIx++) { final long start = System.currentTimeMillis(); TreeItem item = (parentItem == null) ? new TreeItem(tree, style) : new TreeItem(parentItem, style); final long time = System.currentTimeMillis() - start; stats[STATS_CREATE_TREE_ITEMS_TIME] += time; stats[STATS_CREATE_TREE_ITEMS_INVOCATIONS]++; file = files[fileIx]; item.setText(file.getName()); setImage(item, file, stats); if (file.isDirectory()) { createTreeItems(item, file, stats); } } } } private void getExpanded(TreeItem item, long[] stats) { TreeItem[] items; if (item == null) { items = tree.getItems(); } else { items = item.getItems(); } for (int itemIx = 0; itemIx < items.length; itemIx++) { item = items[itemIx]; final long start = System.currentTimeMillis(); boolean expanded = item.getExpanded(); final long time = System.currentTimeMillis() - start; stats[STATS_EXPANDED_TIME] += time; final int index = expanded ? STATS_EXPANDED_TRUE : STATS_EXPANDED_FALSE; stats[index]++; getExpanded(item, stats); } } private void getViewableTreeItems(TreeItem item, List result) { TreeItem[] items; if (item == null) { items = tree.getItems(); } else { items = item.getItems(); } for (int itemIx = 0; itemIx < items.length; itemIx++) { item = items[itemIx]; result.add(item); if (item.getExpanded()) { getViewableTreeItems(item, result); } } } private void handleSetTopItemButtonSelection() { ScrollBar verticalBar = tree.getVerticalBar(); List items = new ArrayList(); getViewableTreeItems(null, items); int itemCount = items.size(); int minimum = verticalBar.getMinimum(); int range = verticalBar.getMaximum() - minimum; int newSelection = verticalBar.getSelection() + range / 10; int topItemIndex = (int) ((newSelection - minimum) * (itemCount / (double) range)); if ((0 <= topItemIndex) && (topItemIndex < itemCount)) { tree.setTopItem((TreeItem) items.get(topItemIndex)); } } private void setExpanded(TreeItem item, boolean expanded) { TreeItem[] items; if (item == null) { items = tree.getItems(); } else { items = item.getItems(); } for (int itemIx = 0; itemIx < items.length; itemIx++) { item = items[itemIx]; if (expanded) { item.setExpanded(expanded); setExpanded(item, expanded); } else { setExpanded(item, expanded); item.setExpanded(expanded); } } } public void setFolder(String folder) { if (folder == null) { folder = ""; //$NON-NLS-1$ } if ((browseText != null) && (browseText.isDisposed() == false) && (browseText.getText().equals(folder) == false)) { browseText.setText(folder); } if (REDRAW_WHILE_CREATING_TREE_ITEMS == false) { tree.setRedraw(false); } try { final long[] stats = new long[3]; TreeItem.setImage_invocations = 0; TreeItem.setImage_time = 0; createTreeItems(null, new File(folder), stats); System.out.println("new TreeItem= { invocations= " + stats[STATS_CREATE_TREE_ITEMS_INVOCATIONS] + ", time= " + stats[STATS_CREATE_TREE_ITEMS_TIME] + " }"); System.out.println("TreeItem#setImage invocations in FolderTree= " + stats[STATS_SET_IMAGE_INVOCATIONS]); System.out.println("TreeItem#setImage= { invocations= " + TreeItem.setImage_invocations + ", time= " + TreeItem.setImage_time + " }"); /* * Size the TreeColumns not for the sake of performance testing but * in the name of visibility. */ final int columnCount = tree.getColumnCount(); if (columnCount > 0) { int width = tree.computeTrim(0, 0, 0, 0).width; for (int columnIx = columnCount - 1; columnIx > 0; columnIx--) { TreeColumn column = tree.getColumn(columnIx); column.pack(); width += column.getWidth(); } width = tree.getClientArea().width - width; TreeColumn column = tree.getColumn(0); if (width > 0) { column.setWidth(width); } else { column.pack(); } } } finally { if (REDRAW_WHILE_CREATING_TREE_ITEMS == false) { tree.setRedraw(true); } } } private void setImage(TreeItem item, File file, long[] stats) { /* * The image of a TreeItem representing a specific File is the image * associated with the File extension. */ String fileName = file.getName(); int extensionPos = fileName.lastIndexOf('.'); if (-1 != extensionPos) { String extension = fileName.substring(extensionPos).toLowerCase(); /* * Not that it's used in the tests but cache the images in order to * get better performance and not run out of Image handles. */ Image image = (Image) extension2Image.get(extension); if (image == null) { Program program = Program.findProgram(extension); if (program != null) { ImageData imageData = program.getImageData(); if (imageData != null) { image = new Image(item.getDisplay(), imageData); extension2Image.put(extension, image); } } if (image == null) { image = defaultImage; } } /* * Invoke TreeItem#setImage in order to benchmark it. The column * with index 0 is intentionally special-cased in order to invoke * TreeItem#setImage(Image) and not TreeItem#setImage(int,Image) and * thus have Widget#checkWidget() called twice per a * TreeItem#setImage call (in case the situation is of interest). */ final int columnCount = tree.getColumnCount(); for (int columnIx = 0; columnIx < columnCount; columnIx++) { if (columnIx == 0) { item.setImage(image); } else { item.setImage(columnIx, image); } stats[STATS_SET_IMAGE_INVOCATIONS]++; } } } }