001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.editor.build;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.Set;
015: import java.util.TreeSet;
016: import java.util.Vector;
017:
018: import org.eclipse.core.resources.IFile;
019: import org.eclipse.core.resources.IFolder;
020: import org.eclipse.core.resources.IProject;
021: import org.eclipse.core.resources.IResource;
022: import org.eclipse.core.resources.IResourceChangeEvent;
023: import org.eclipse.core.resources.IResourceChangeListener;
024: import org.eclipse.core.resources.IResourceDelta;
025: import org.eclipse.core.resources.IResourceDeltaVisitor;
026: import org.eclipse.core.runtime.CoreException;
027: import org.eclipse.core.runtime.IPath;
028: import org.eclipse.jface.action.IMenuListener;
029: import org.eclipse.jface.action.IMenuManager;
030: import org.eclipse.jface.action.MenuManager;
031: import org.eclipse.jface.viewers.CheckStateChangedEvent;
032: import org.eclipse.jface.viewers.CheckboxTreeViewer;
033: import org.eclipse.jface.viewers.ICheckStateListener;
034: import org.eclipse.jface.viewers.ISelection;
035: import org.eclipse.jface.viewers.IStructuredSelection;
036: import org.eclipse.jface.viewers.ITreeContentProvider;
037: import org.eclipse.pde.core.IModelChangedEvent;
038: import org.eclipse.pde.core.IModelChangedListener;
039: import org.eclipse.pde.core.build.IBuild;
040: import org.eclipse.pde.core.build.IBuildEntry;
041: import org.eclipse.pde.core.build.IBuildModel;
042: import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
043: import org.eclipse.pde.internal.ui.PDEPlugin;
044: import org.eclipse.pde.internal.ui.editor.FormLayoutFactory;
045: import org.eclipse.pde.internal.ui.editor.PDEFormPage;
046: import org.eclipse.pde.internal.ui.editor.TableSection;
047: import org.eclipse.pde.internal.ui.editor.context.InputContext;
048: import org.eclipse.pde.internal.ui.elements.DefaultContentProvider;
049: import org.eclipse.swt.SWT;
050: import org.eclipse.swt.custom.BusyIndicator;
051: import org.eclipse.swt.layout.GridData;
052: import org.eclipse.swt.widgets.Composite;
053: import org.eclipse.swt.widgets.Control;
054: import org.eclipse.swt.widgets.Display;
055: import org.eclipse.swt.widgets.Menu;
056: import org.eclipse.ui.forms.widgets.FormToolkit;
057: import org.eclipse.ui.forms.widgets.Section;
058: import org.eclipse.ui.model.WorkbenchLabelProvider;
059:
060: public abstract class BuildContentsSection extends TableSection
061: implements IModelChangedListener, IResourceChangeListener,
062: IResourceDeltaVisitor {
063:
064: protected CheckboxTreeViewer fTreeViewer;
065: private boolean fDoRefresh = false;
066: protected IProject fProject;
067: protected IBuildModel fBuildModel;
068: protected IResource fOriginalResource, fParentResource;
069: protected boolean isChecked;
070:
071: public class TreeContentProvider extends DefaultContentProvider
072: implements ITreeContentProvider {
073:
074: public Object[] getElements(Object parent) {
075: if (parent instanceof IProject) {
076: try {
077: return ((IProject) parent).members();
078: } catch (CoreException e) {
079: PDEPlugin.logException(e);
080: }
081: }
082: return new Object[0];
083: }
084:
085: /**
086: * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
087: */
088: public Object[] getChildren(Object parent) {
089: try {
090: if (parent instanceof IFolder)
091: return ((IFolder) parent).members();
092: } catch (CoreException e) {
093: PDEPlugin.logException(e);
094: }
095: return new Object[0];
096: }
097:
098: public Object[] getFolderChildren(Object parent) {
099: IResource[] members = null;
100: try {
101: if (!(parent instanceof IFolder))
102: return new Object[0];
103: members = ((IFolder) parent).members();
104: ArrayList results = new ArrayList();
105: for (int i = 0; i < members.length; i++) {
106: if ((members[i].getType() == IResource.FOLDER)) {
107: results.add(members[i]);
108: }
109: }
110: return results.toArray();
111: } catch (CoreException e) {
112: PDEPlugin.logException(e);
113: }
114: return new Object[0];
115: }
116:
117: /**
118: * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
119: */
120: public Object getParent(Object element) {
121: if (element != null && element instanceof IResource) {
122: return ((IResource) element).getParent();
123: }
124: return null;
125: }
126:
127: /**
128: * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
129: */
130: public boolean hasChildren(Object element) {
131: if (element instanceof IFolder)
132: return getChildren(element).length > 0;
133: return false;
134: }
135: }
136:
137: protected void createViewerPartControl(Composite parent, int style,
138: int span, FormToolkit toolkit) {
139: MenuManager popupMenuManager = new MenuManager();
140: IMenuListener listener = new IMenuListener() {
141: public void menuAboutToShow(IMenuManager mng) {
142: fillContextMenu(mng);
143: }
144: };
145: popupMenuManager.addMenuListener(listener);
146: popupMenuManager.setRemoveAllWhenShown(true);
147: Control control = fTreeViewer.getControl();
148: Menu menu = popupMenuManager.createContextMenu(control);
149: control.setMenu(menu);
150: }
151:
152: /* (non-Javadoc)
153: * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#fillContextMenu(org.eclipse.jface.action.IMenuManager)
154: */
155: protected void fillContextMenu(IMenuManager manager) {
156: manager.add(getPage().getPDEEditor().getContributor()
157: .getRevertAction());
158: getPage().getPDEEditor().getContributor()
159: .contextMenuAboutToShow(manager, false);
160: }
161:
162: private IBuildModel getBuildModel() {
163: InputContext context = getPage().getPDEEditor()
164: .getContextManager().findContext(
165: BuildInputContext.CONTEXT_ID);
166: return (IBuildModel) context.getModel();
167: }
168:
169: public BuildContentsSection(PDEFormPage page, Composite parent) {
170: super (page, parent, Section.DESCRIPTION, new String[0]);
171: PDEPlugin.getWorkspace().addResourceChangeListener(this );
172: }
173:
174: public void createClient(final Section section, FormToolkit toolkit) {
175: Composite container = createClientContainer(section, 2, toolkit);
176: fBuildModel = getBuildModel();
177: if (fBuildModel.getUnderlyingResource() != null)
178: fProject = fBuildModel.getUnderlyingResource().getProject();
179:
180: fTreeViewer = new CheckboxTreeViewer(toolkit.createTree(
181: container, SWT.CHECK));
182: fTreeViewer.setContentProvider(new TreeContentProvider());
183: fTreeViewer.setLabelProvider(new WorkbenchLabelProvider());
184: fTreeViewer.setAutoExpandLevel(0);
185: fTreeViewer.addCheckStateListener(new ICheckStateListener() {
186:
187: public void checkStateChanged(
188: final CheckStateChangedEvent event) {
189: final Object element = event.getElement();
190: BusyIndicator.showWhile(section.getDisplay(),
191: new Runnable() {
192:
193: public void run() {
194: if (element instanceof IFile) {
195: IFile file = (IFile) event
196: .getElement();
197: handleCheckStateChanged(file, event
198: .getChecked());
199: } else if (element instanceof IFolder) {
200: IFolder folder = (IFolder) event
201: .getElement();
202: handleCheckStateChanged(folder,
203: event.getChecked());
204: }
205: }
206: });
207: }
208: });
209: GridData gd = new GridData(GridData.FILL_BOTH);
210: gd.heightHint = 100;
211: gd.widthHint = 100;
212: fTreeViewer.getTree().setLayoutData(gd);
213: initialize();
214: toolkit.paintBordersFor(container);
215: createViewerPartControl(container, SWT.FULL_SELECTION, 2,
216: toolkit);
217: section.setLayout(FormLayoutFactory.createClearGridLayout(
218: false, 1));
219: section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
220: section.setClient(container);
221: }
222:
223: public void enableSection(boolean enable) {
224: fTreeViewer.getTree().setEnabled(enable);
225: }
226:
227: protected void handleCheckStateChanged(IResource resource,
228: boolean checked) {
229: fOriginalResource = resource;
230: isChecked = checked;
231: boolean wasTopParentChecked = fTreeViewer
232: .getChecked(fOriginalResource.getParent());
233: if (!isChecked) {
234: resource = handleAllUnselected(resource, resource.getName());
235: }
236: fParentResource = resource;
237: handleBuildCheckStateChange(wasTopParentChecked);
238: }
239:
240: protected IResource handleAllUnselected(IResource resource,
241: String name) {
242: IResource parent = resource.getParent();
243: if (parent == resource.getProject()) {
244: return resource;
245: }
246: try {
247: boolean uncheck = true;
248: IResource[] members = ((IFolder) parent).members();
249: for (int i = 0; i < members.length; i++) {
250: if (fTreeViewer.getChecked(members[i])
251: && !members[i].getName().equals(name))
252: uncheck = false;
253: }
254: if (uncheck) {
255: return handleAllUnselected(parent, parent.getName());
256: }
257: return resource;
258: } catch (CoreException e) {
259: PDEPlugin.logException(e);
260: return null;
261: }
262: }
263:
264: protected void setChildrenGrayed(IResource folder, boolean isGray) {
265: fTreeViewer.setGrayed(folder, isGray);
266: if (((TreeContentProvider) fTreeViewer.getContentProvider())
267: .hasChildren(folder)) {
268: Object[] members = ((TreeContentProvider) fTreeViewer
269: .getContentProvider()).getFolderChildren(folder);
270: for (int i = 0; i < members.length; i++) {
271: setChildrenGrayed((IFolder) members[i], isGray);
272: }
273: }
274: }
275:
276: protected void setParentsChecked(IResource resource) {
277: if (resource.getParent() != resource.getProject()) {
278: fTreeViewer.setChecked(resource.getParent(), true);
279: setParentsChecked(resource.getParent());
280: }
281: }
282:
283: /**
284: * removes all child resources of the specified folder from build entries
285: *
286: * @param folder -
287: * current folder being modified in tree
288: *
289: * note: does not remove folder itself
290: */
291: protected abstract void deleteFolderChildrenFromEntries(
292: IFolder folder);
293:
294: protected void initializeCheckState() {
295: uncheckAll();
296: }
297:
298: protected void initializeCheckState(final IBuildEntry includes,
299: final IBuildEntry excludes) {
300: fTreeViewer.getTree().getDisplay().asyncExec(new Runnable() {
301:
302: public void run() {
303: // found slight improvements using Display.getCurrent() instead of fTreeViewer.getTree().getDisplay()
304: BusyIndicator.showWhile(Display.getCurrent(),
305: new Runnable() {
306:
307: public void run() {
308: if (fTreeViewer.getTree().isDisposed())
309: return;
310: Vector fileExt = new Vector();
311: String[] inclTokens, exclTokens = new String[0];
312: if (fProject == null
313: || includes == null)
314: return;
315: inclTokens = includes.getTokens();
316: if (excludes != null)
317: exclTokens = excludes.getTokens();
318: Set temp = new TreeSet();
319: for (int i = 0; i < inclTokens.length; i++)
320: temp.add(inclTokens[i]);
321: for (int i = 0; i < exclTokens.length; i++)
322: temp.add(exclTokens[i]);
323: Iterator iter = temp.iterator();
324: while (iter.hasNext()) {
325: String resource = iter.next()
326: .toString();
327: boolean isIncluded = includes
328: .contains(resource);
329: if (resource.equals(".") || resource.equals("./") || resource.equals(".\\")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
330: // ignore - should be root directory
331: } else if (resource
332: .lastIndexOf(IPath.SEPARATOR) == resource
333: .length() - 1) {
334: IFolder folder = fProject
335: .getFolder(resource);
336: if (!folder.exists())
337: continue;
338: fTreeViewer.setSubtreeChecked(
339: folder, isIncluded);
340: fTreeViewer.setParentsGrayed(
341: folder, true);
342: if (isIncluded) {
343: setParentsChecked(folder);
344: fTreeViewer.setGrayed(
345: folder, false);
346: }
347: } else if (resource
348: .startsWith("*.")) { //$NON-NLS-1$
349: if (isIncluded)
350: fileExt.add(resource
351: .substring(2));
352: } else {
353: IFile file = fProject
354: .getFile(resource);
355: if (!file.exists())
356: continue;
357: fTreeViewer.setChecked(file,
358: isIncluded);
359: fTreeViewer.setParentsGrayed(
360: file, true);
361: if (isIncluded) {
362: fTreeViewer.setGrayed(file,
363: false);
364: setParentsChecked(file);
365: }
366: }
367: }
368: if (fileExt.size() == 0)
369: return;
370: try {
371: IResource[] members = fProject
372: .members();
373: for (int i = 0; i < members.length; i++) {
374: if (!(members[i] instanceof IFolder)
375: && (fileExt
376: .contains(members[i]
377: .getFileExtension()))) {
378: fTreeViewer
379: .setChecked(
380: members[i],
381: includes
382: .contains("*." //$NON-NLS-1$
383: + members[i]
384: .getFileExtension()));
385: }
386: }
387: } catch (CoreException e) {
388: PDEPlugin.logException(e);
389: }
390: }
391: });
392: }
393: });
394: }
395:
396: protected abstract void handleBuildCheckStateChange(
397: boolean wasTopParentChecked);
398:
399: protected void handleCheck(IBuildEntry includes,
400: IBuildEntry excludes, String resourceName,
401: IResource resource, boolean wasTopParentChecked,
402: String PROPERTY_INCLUDES) {
403:
404: try {
405: if (includes == null) {
406: includes = fBuildModel.getFactory().createEntry(
407: PROPERTY_INCLUDES);
408: IBuild build = fBuildModel.getBuild();
409: build.add(includes);
410: }
411: if ((!wasTopParentChecked && !includes
412: .contains(resourceName))
413: || isValidIncludeEntry(includes, excludes,
414: resource, resourceName)) {
415: includes.addToken(resourceName);
416: }
417: if (excludes != null && excludes.contains(resourceName))
418: excludes.removeToken(resourceName);
419: } catch (CoreException e) {
420: PDEPlugin.logException(e);
421: }
422: }
423:
424: protected boolean isValidIncludeEntry(IBuildEntry includes,
425: IBuildEntry excludes, IResource resource,
426: String resourceName) {
427: if (excludes == null)
428: return true;
429: IPath resPath = resource.getProjectRelativePath();
430: while (resPath.segmentCount() > 1) {
431: resPath = resPath.removeLastSegments(1);
432: if (includes.contains(resPath.toString() + IPath.SEPARATOR))
433: return false;
434: else if (excludes != null
435: && excludes.contains(resPath.toString()
436: + IPath.SEPARATOR))
437: return true;
438: }
439: return !excludes.contains(resourceName);
440: }
441:
442: protected void handleUncheck(IBuildEntry includes,
443: IBuildEntry excludes, String resourceName,
444: IResource resource, String PROPERTY_EXCLUDES) {
445:
446: try {
447: if (fTreeViewer.getChecked(resource.getParent())) {
448: if (excludes == null) {
449: excludes = fBuildModel.getFactory().createEntry(
450: PROPERTY_EXCLUDES);
451: IBuild build = fBuildModel.getBuild();
452: build.add(excludes);
453: }
454: if (!excludes.contains(resourceName)
455: && (includes != null ? !includes
456: .contains(resourceName) : true))
457: excludes.addToken(resourceName);
458: }
459: if (includes != null) {
460: if (includes.contains(resourceName))
461: includes.removeToken(resourceName);
462: if (includes
463: .contains("*." + resource.getFileExtension())) { //$NON-NLS-1$
464: IResource[] members = fProject.members();
465: for (int i = 0; i < members.length; i++) {
466: if (!(members[i] instanceof IFolder)
467: && !members[i].getName().equals(
468: resource.getName())
469: && (resource.getFileExtension()
470: .equals(members[i]
471: .getFileExtension()))) {
472: includes.addToken(members[i].getName());
473: }
474: IBuildEntry[] libraries = BuildUtil
475: .getBuildLibraries(fBuildModel
476: .getBuild().getBuildEntries());
477: if (resource.getFileExtension().equals("jar") //$NON-NLS-1$
478: && libraries.length != 0) {
479: for (int j = 0; j < libraries.length; j++) {
480: String libName = libraries[j].getName()
481: .substring(7);
482: IPath path = fProject.getFile(libName)
483: .getProjectRelativePath();
484: if (path.segmentCount() == 1
485: && !includes.contains(libName)
486: && !libName.equals(resource
487: .getName()))
488: includes.addToken(libName);
489: }
490: }
491: }
492: includes
493: .removeToken("*." + resource.getFileExtension()); //$NON-NLS-1$
494: }
495: }
496: } catch (CoreException e) {
497: PDEPlugin.logException(e);
498: }
499: }
500:
501: protected String getResourceFolderName(String resourceName) {
502: return resourceName + IPath.SEPARATOR;
503: }
504:
505: /**
506: * @param resource -
507: * file/folder being modified in tree
508: * @param resourceName -
509: * name file/folder
510: * @return relative path of folder if resource is folder, otherwise, return
511: * resourceName
512: */
513: protected String handleResourceFolder(IResource resource,
514: String resourceName) {
515: if (resource instanceof IFolder) {
516: deleteFolderChildrenFromEntries((IFolder) resource);
517: return getResourceFolderName(resourceName);
518: }
519: return resourceName;
520: }
521:
522: public void initialize() {
523: if (fTreeViewer.getInput() == null) {
524: fTreeViewer.setUseHashlookup(true);
525: fTreeViewer.setInput(fProject);
526: }
527: fBuildModel.addModelChangedListener(this );
528: }
529:
530: public void dispose() {
531: fBuildModel.removeModelChangedListener(this );
532: PDEPlugin.getWorkspace().removeResourceChangeListener(this );
533: super .dispose();
534: }
535:
536: protected void deleteEmptyEntries() {
537: IBuild build = fBuildModel.getBuild();
538: IBuildEntry[] entries = {
539: build
540: .getEntry(IBuildPropertiesConstants.PROPERTY_BIN_EXCLUDES),
541: build
542: .getEntry(IBuildPropertiesConstants.PROPERTY_BIN_INCLUDES),
543: build
544: .getEntry(IBuildPropertiesConstants.PROPERTY_SRC_EXCLUDES),
545: build
546: .getEntry(IBuildPropertiesConstants.PROPERTY_SRC_INCLUDES) };
547: try {
548: for (int i = 0; i < entries.length; i++) {
549: if (entries[i] != null
550: && entries[i].getTokens().length == 0)
551: build.remove(entries[i]);
552: }
553: } catch (CoreException e) {
554: PDEPlugin.logException(e);
555: }
556: }
557:
558: public CheckboxTreeViewer getTreeViewer() {
559: return fTreeViewer;
560: }
561:
562: protected ISelection getViewerSelection() {
563: return getTreeViewer().getSelection();
564: }
565:
566: public void refresh() {
567: initializeCheckState();
568: super .refresh();
569: }
570:
571: public void uncheckAll() {
572: fTreeViewer.setCheckedElements(new Object[0]);
573: }
574:
575: protected void removeChildren(IBuildEntry entry, String parentFolder) {
576: try {
577: if (entry != null) {
578: String[] tokens = entry.getTokens();
579: for (int i = 0; i < tokens.length; i++) {
580: if (tokens[i].indexOf(IPath.SEPARATOR) != -1
581: && tokens[i].startsWith(parentFolder)
582: && !tokens[i].equals(parentFolder)) {
583: entry.removeToken(tokens[i]);
584: }
585: }
586: }
587: } catch (CoreException e) {
588: PDEPlugin.logException(e);
589: }
590: }
591:
592: public void resourceChanged(IResourceChangeEvent event) {
593: if (fTreeViewer.getControl().isDisposed())
594: return;
595: fDoRefresh = false;
596: IResourceDelta delta = event.getDelta();
597: try {
598: if (delta != null)
599: delta.accept(this );
600: if (fDoRefresh) {
601: asyncRefresh();
602: fDoRefresh = false;
603: }
604: } catch (CoreException e) {
605: }
606: }
607:
608: public boolean visit(IResourceDelta delta) throws CoreException {
609: IResource resource = delta.getResource();
610: IProject project = fBuildModel.getUnderlyingResource()
611: .getProject();
612:
613: if ((resource instanceof IFile || resource instanceof IFolder)
614: && resource.getProject().equals(project)) {
615: if (delta.getKind() == IResourceDelta.ADDED
616: || delta.getKind() == IResourceDelta.REMOVED) {
617: fDoRefresh = true;
618: return false;
619: }
620: } else if (resource instanceof IProject
621: && ((IProject) resource).equals(project)) {
622: return delta.getKind() != IResourceDelta.REMOVED;
623: }
624: return true;
625: }
626:
627: private void asyncRefresh() {
628: Control control = fTreeViewer.getControl();
629: if (!control.isDisposed()) {
630: control.getDisplay().asyncExec(new Runnable() {
631:
632: public void run() {
633: if (!fTreeViewer.getControl().isDisposed()) {
634: fTreeViewer.refresh(true);
635: initializeCheckState();
636: }
637: }
638: });
639: }
640: }
641:
642: /*
643: * (non-Javadoc)
644: *
645: * @see org.eclipse.pde.internal.ui.editor.TableSection#selectionChanged(org.eclipse.jface.viewers.IStructuredSelection)
646: */
647: protected void selectionChanged(IStructuredSelection selection) {
648: getPage().getPDEEditor().setSelection(selection);
649:
650: }
651:
652: public void modelChanged(IModelChangedEvent event) {
653:
654: if (event.getChangeType() == IModelChangedEvent.WORLD_CHANGED) {
655: markStale();
656: }
657: Object changeObject = event.getChangedObjects()[0];
658:
659: if (!(changeObject instanceof IBuildEntry && (((IBuildEntry) changeObject)
660: .getName()
661: .equals(IBuildPropertiesConstants.PROPERTY_BIN_EXCLUDES)
662: || ((IBuildEntry) changeObject)
663: .getName()
664: .equals(
665: IBuildPropertiesConstants.PROPERTY_BIN_INCLUDES)
666: || ((IBuildEntry) changeObject)
667: .getName()
668: .equals(
669: IBuildPropertiesConstants.PROPERTY_SRC_EXCLUDES) || ((IBuildEntry) changeObject)
670: .getName()
671: .equals(IBuildPropertiesConstants.PROPERTY_SRC_INCLUDES))))
672: return;
673:
674: if ((fParentResource == null && fOriginalResource != null)
675: || (fOriginalResource == null && fParentResource != null)) {
676: initializeCheckState();
677: return;
678: }
679: if ((fParentResource == null && fOriginalResource == null)
680: || (event.getChangedProperty() != null && event
681: .getChangedProperty()
682: .equals(
683: IBuildPropertiesConstants.PROPERTY_BIN_INCLUDES))) {
684:
685: return;
686: }
687:
688: fTreeViewer.setChecked(fParentResource, isChecked);
689: fTreeViewer.setGrayed(fOriginalResource, false);
690: fTreeViewer.setParentsGrayed(fParentResource, true);
691: setParentsChecked(fParentResource);
692: fTreeViewer.setGrayed(fParentResource, false);
693: if (fParentResource instanceof IFolder) {
694: fTreeViewer.setSubtreeChecked(fParentResource, isChecked);
695: setChildrenGrayed(fParentResource, false);
696: }
697: while (!fOriginalResource.equals(fParentResource)) {
698: fTreeViewer.setChecked(fOriginalResource, isChecked);
699: fOriginalResource = fOriginalResource.getParent();
700: }
701: fParentResource = null;
702: fOriginalResource = null;
703: }
704: }
|