# FolderInfoPropertyPage Class After Editing

    package samples.ui.propertypages;

    import org.eclipse.rse.files.ui.propertypages.SystemAbstractRemoteFilePropertyPageExtensionAction;
    import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
    import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile;
    import org.eclipse.rse.ui.SystemWidgetHelpers;
    import org.eclipse.swt.events.SelectionEvent;
    import org.eclipse.swt.events.SelectionListener;
    import org.eclipse.swt.widgets.Button;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.swt.widgets.Control;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Label;

    import samples.RSESamplesPlugin;

    /**
     * A sample property page for a remote object, which in this case is scoped via the
     *  extension point xml to only apply to folder objects.
     */
    public class FolderInfoPropertyPage
    	extends SystemAbstractRemoteFilePropertyPageExtensionAction
    	implements SelectionListener
    {
    	// gui widgets...
    	private Label sizeLabel, filesLabel, foldersLabel;
    	private Button stopButton;
    	// state...
    	private int totalSize = 0;
    	private int totalFolders = 0;
    	private int totalFiles = 0;
    	private boolean stopped = false;
    	private Thread workerThread;
    	private Runnable guiUpdater;

    	/**
    	 * Constructor for FolderInfoPropertyPage.
    	 */
    	public FolderInfoPropertyPage()
    	{
    		super();
    	}

    	// --------------------------
    	// Parent method overrides...
    	// --------------------------

    	/* (non-Javadoc)
    	 * @see org.eclipse.rse.files.ui.propertypages.SystemAbstractRemoteFilePropertyPageExtensionAction#createContentArea(org.eclipse.swt.widgets.Composite)
    	 */
    	protected Control createContentArea(Composite parent)
    	{
    		Composite composite = SystemWidgetHelpers.createComposite(parent, 2);
    		// draw the gui
    		sizeLabel = SystemWidgetHelpers.createLabeledLabel(composite,
    				RSESamplesPlugin.getResourceString("pp.size.label"), //$NON-NLS-1$
    				RSESamplesPlugin.getResourceString("pp.size.tooltip"), //$NON-NLS-1$
    				false);
    		filesLabel = SystemWidgetHelpers.createLabeledLabel(composite,
    				RSESamplesPlugin.getResourceString("pp.files.label"), //$NON-NLS-1$
    				RSESamplesPlugin.getResourceString("pp.files.tooltip"), //$NON-NLS-1$
    				false);
    		foldersLabel = SystemWidgetHelpers.createLabeledLabel(composite,
    				RSESamplesPlugin.getResourceString("pp.folders.label"), //$NON-NLS-1$
    				RSESamplesPlugin.getResourceString("pp.folders.tooltip"), //$NON-NLS-1$
    				false);
    		stopButton = SystemWidgetHelpers.createPushButton(composite, null,
    				RSESamplesPlugin.getResourceString("pp.stopButton.label"), //$NON-NLS-1$
    				RSESamplesPlugin.getResourceString("pp.stopButton.tooltip") //$NON-NLS-1$
    				);
    		stopButton.addSelectionListener(this);

    		setValid(false); // Disable OK button until thread is done

    		// show "Processing..." message
    		setMessage(RSESamplesPlugin.getPluginMessage("RSSG1002")); //$NON-NLS-1$

    		// create instance of Runnable to allow asynchronous GUI updates from background thread
    		guiUpdater = new RunnableGUIClass();
    		// spawn a thread to calculate the information
    		workerThread = new RunnableClass(getRemoteFile());
    		workerThread.start();

    		return composite;
    	}

    	/**
    	 * Intercept from PreferencePage. Called when user presses Cancel button.
    	 * We stop the background thread.
    	 * @see org.eclipse.jface.preference.PreferencePage#performCancel()
    	 */
    	public boolean performCancel()
    	{
    		killThread();
    		return true;
    	}

    	/**
    	 * Intercept from DialogPage. Called when dialog going away.
    	 * If the user presses the X to close this dialog, we
    	 *  need to stop that background thread.
    	 */
    	public void dispose()
    	{
    		killThread();
    		super.dispose();
    	}

    	/**
    	 * Private method to kill our background thread.
    	 * Control doesn't return until it ends.
    	 */
    	private void killThread()
    	{
    		if (!stopped && workerThread.isAlive())
    		{
    		    stopped = true;
    		    try {
    		      workerThread.join(); // wait for thread to end
    		    } catch (InterruptedException exc) {}
    		}
    	}

    	// -------------------------------------------
    	// Methods from SelectionListener interface...
    	// -------------------------------------------

    	/**
    	 * From SelectionListener
    	 * @see SelectionListener#widgetSelected(SelectionEvent)
    	 */
    	public void widgetSelected(SelectionEvent event)
    	{
    		if (event.getSource() == stopButton)
    		{
    			stopped = true;
    			stopButton.setEnabled(false);
    		}
    	}
    	/**
    	 * From SelectionListener
    	 * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
    	 */
    	public void widgetDefaultSelected(SelectionEvent event)
    	{
    	}

    	// ----------------
    	// Inner classes...
    	// ----------------
    	/**
    	 * Inner class encapsulating the background work to be done, so it may be executed
    	 *  in background thread.
    	 */
    	private class RunnableClass extends Thread
    	{
    		IRemoteFile inputFolder;

    		RunnableClass(IRemoteFile inputFolder)
    		{
    			this.inputFolder = inputFolder;
    		}

    		public void run()
    		{
    			if (stopped)
    			  return;
    			walkFolder(inputFolder);
    			updateGUI();
    			if (!stopped)
    			{
    				stopped = true;
    				updateGUI();
    			}
    		}

    		/**
    		 * Recursively walk a folder, updating the running tallies.
    		 * Update the GUI after processing each subfolder.
    		 */
    		private void walkFolder(IRemoteFile currFolder)
    		{
    			try
    			{
    			IRemoteFile[] folders = currFolder.getParentRemoteFileSubSystem().listFoldersAndFiles( currFolder, null);
    			if ((folders != null) && (folders.length>0))
    			{
    				for (int idx=0; !stopped && (idx<folders.length); idx++)
    				{
    					// is this a folder?
    					if (folders[idx].isDirectory())
    					{
    						++totalFolders;
    						walkFolder(folders[idx]);
    						updateGUI();
    					}
    					// is this a file?
    					else
    					{
    						++totalFiles;
    						totalSize += folders[idx].getLength();
    					}
    				}
    			}
    			}
    			catch (SystemMessageException e)
    			{

    			}
    		} // end of walkFolder method

    	} // end of inner class

    	/**
    	 * Inner class encapsulating the GUI work to be done from the
    	 *  background thread.
    	 */
    	private class RunnableGUIClass implements Runnable
    	{
    		public void run()
    		{
    			if (stopButton.isDisposed())
    			  return;
    			if (!stopped)
    			{
    				sizeLabel.setText(Integer.toString(totalSize));
    				filesLabel.setText(Integer.toString(totalFiles));
    				foldersLabel.setText(Integer.toString(totalFolders));
    			}
    			else if (stopped)
    			{
    				setValid(true); // re-enable OK button
    				stopButton.setEnabled(false); // disable Stop button
    				clearMessage(); // clear "Processing..." message
    			}
    		}
    	}

    	/**
    	 * Update the GUI with the current status
    	 */
    	private void updateGUI()
    	{
    		Display.getDefault().asyncExec(guiUpdater);
    	}

    }