001: /*******************************************************************************
002: * Copyright (c) 2006, 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.cheatsheet.comp.details;
011:
012: import java.util.StringTokenizer;
013:
014: import org.eclipse.core.resources.IFile;
015: import org.eclipse.core.resources.IResource;
016: import org.eclipse.core.resources.IWorkspaceRoot;
017: import org.eclipse.core.runtime.Path;
018: import org.eclipse.jface.viewers.ISelection;
019: import org.eclipse.jface.viewers.StructuredSelection;
020: import org.eclipse.jface.window.Window;
021: import org.eclipse.jface.wizard.IWizardPage;
022: import org.eclipse.jface.wizard.WizardDialog;
023: import org.eclipse.pde.internal.core.icheatsheet.comp.ICompCSConstants;
024: import org.eclipse.pde.internal.core.icheatsheet.comp.ICompCSModelFactory;
025: import org.eclipse.pde.internal.core.icheatsheet.comp.ICompCSParam;
026: import org.eclipse.pde.internal.core.icheatsheet.comp.ICompCSTask;
027: import org.eclipse.pde.internal.ui.PDEPlugin;
028: import org.eclipse.pde.internal.ui.PDEUIMessages;
029: import org.eclipse.pde.internal.ui.editor.FormEntryAdapter;
030: import org.eclipse.pde.internal.ui.editor.cheatsheet.CSAbstractDetails;
031: import org.eclipse.pde.internal.ui.editor.cheatsheet.ICSMaster;
032: import org.eclipse.pde.internal.ui.editor.cheatsheet.comp.CompCSFileValidator;
033: import org.eclipse.pde.internal.ui.editor.cheatsheet.comp.CompCSInputContext;
034: import org.eclipse.pde.internal.ui.parts.FormEntry;
035: import org.eclipse.pde.internal.ui.util.FileExtensionFilter;
036: import org.eclipse.pde.internal.ui.wizards.cheatsheet.NewSimpleCSFileWizard;
037: import org.eclipse.pde.internal.ui.wizards.cheatsheet.SimpleCSFileWizardPage;
038: import org.eclipse.swt.SWT;
039: import org.eclipse.swt.events.SelectionAdapter;
040: import org.eclipse.swt.events.SelectionEvent;
041: import org.eclipse.swt.graphics.Color;
042: import org.eclipse.swt.layout.GridData;
043: import org.eclipse.swt.widgets.Button;
044: import org.eclipse.swt.widgets.Composite;
045: import org.eclipse.ui.PartInitException;
046: import org.eclipse.ui.PlatformUI;
047: import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
048: import org.eclipse.ui.forms.IFormColors;
049: import org.eclipse.ui.forms.IFormPart;
050: import org.eclipse.ui.forms.IManagedForm;
051: import org.eclipse.ui.forms.events.HyperlinkEvent;
052: import org.eclipse.ui.forms.widgets.ExpandableComposite;
053: import org.eclipse.ui.forms.widgets.Section;
054: import org.eclipse.ui.ide.IDE;
055: import org.eclipse.ui.model.WorkbenchContentProvider;
056: import org.eclipse.ui.model.WorkbenchLabelProvider;
057:
058: /**
059: * CompCSTaskDetails
060: *
061: */
062: public class CompCSTaskDetails extends CSAbstractDetails {
063:
064: private Section fDefinitionSection;
065:
066: private FormEntry fNameEntry;
067:
068: private FormEntry fPathEntry;
069:
070: private Button fSkip;
071:
072: private ICompCSTask fDataTask;
073:
074: private CompCSEnclosingTextDetails fEnclosingTextSection;
075:
076: private final static String F_PATH_SEPARATOR = "/"; //$NON-NLS-1$
077:
078: private final static String F_DOT_DOT = ".."; //$NON-NLS-1$
079:
080: /**
081: * @param section
082: */
083: public CompCSTaskDetails(ICSMaster section) {
084: super (section, CompCSInputContext.CONTEXT_ID);
085: fDataTask = null;
086:
087: fNameEntry = null;
088: fPathEntry = null;
089: fSkip = null;
090:
091: fDefinitionSection = null;
092: fEnclosingTextSection = new CompCSEnclosingTextDetails(
093: ICompCSConstants.TYPE_TASK, section);
094: }
095:
096: /**
097: * @param object
098: */
099: public void setData(ICompCSTask object) {
100: // Set data
101: fDataTask = object;
102: // Set data on the enclosing text section
103: fEnclosingTextSection.setData(object);
104: }
105:
106: /* (non-Javadoc)
107: * @see org.eclipse.ui.forms.AbstractFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
108: */
109: public void initialize(IManagedForm form) {
110: super .initialize(form);
111: // Unfortunately this has to be explicitly called for sub detail
112: // sections through its main section parent; since, it never is
113: // registered directly.
114: // Initialize managed form for enclosing text section
115: fEnclosingTextSection.initialize(form);
116: }
117:
118: /* (non-Javadoc)
119: * @see org.eclipse.pde.internal.ui.editor.cheatsheet.CSAbstractDetails#createDetails(org.eclipse.swt.widgets.Composite)
120: */
121: public void createDetails(Composite parent) {
122:
123: // Create the main section
124: int style = Section.DESCRIPTION | ExpandableComposite.TITLE_BAR;
125: fDefinitionSection = getPage().createUISection(parent,
126: PDEUIMessages.SimpleCSDetails_3,
127: PDEUIMessages.CompCSTaskDetails_SectionDescription,
128: style);
129: // Align the master and details section headers (misalignment caused
130: // by section toolbar icons)
131: getPage().alignSectionHeaders(getMasterSection().getSection(),
132: fDefinitionSection);
133: // Create the container for the main section
134: Composite sectionClient = getPage().createUISectionContainer(
135: fDefinitionSection, 3);
136: // Create the name entry
137: createUINameEntry(sectionClient);
138: // Create the kind combo
139: createUIPathEntry(sectionClient);
140: // Create the skip button
141: createUISkipButton(sectionClient);
142: // Create the enclosing text section
143: fEnclosingTextSection.createDetails(parent);
144: // Bind widgets
145: getManagedForm().getToolkit().paintBordersFor(sectionClient);
146: fDefinitionSection.setClient(sectionClient);
147: markDetailsPart(fDefinitionSection);
148: }
149:
150: /**
151: * @param parent
152: */
153: private void createUINameEntry(Composite parent) {
154: fNameEntry = new FormEntry(parent, getManagedForm()
155: .getToolkit(), PDEUIMessages.CompCSTaskDetails_Name,
156: SWT.NONE);
157: }
158:
159: /**
160: * @param parent
161: */
162: private void createUIPathEntry(Composite parent) {
163: fPathEntry = new FormEntry(parent, getManagedForm()
164: .getToolkit(), PDEUIMessages.CompCSTaskDetails_Path,
165: PDEUIMessages.GeneralInfoSection_browse, isEditable());
166: }
167:
168: /**
169: * @param parent
170: */
171: private void createUISkipButton(Composite parent) {
172: Color foreground = getToolkit().getColors().getColor(
173: IFormColors.TITLE);
174: fSkip = getToolkit().createButton(parent,
175: PDEUIMessages.CompCSTaskDetails_SkipLabel, SWT.CHECK);
176: GridData data = new GridData(GridData.FILL_HORIZONTAL);
177: data.horizontalSpan = 3;
178: fSkip.setLayoutData(data);
179: fSkip.setForeground(foreground);
180: }
181:
182: /* (non-Javadoc)
183: * @see org.eclipse.pde.internal.ui.editor.cheatsheet.CSAbstractDetails#hookListeners()
184: */
185: public void hookListeners() {
186: // Create listeners for the name entry
187: createListenersNameEntry();
188: // Create listeners for the path entry
189: createListenersPathEntry();
190: // Create listeners for the skip button
191: createListenersSkipButton();
192: // Create listeners within the enclosing text section
193: fEnclosingTextSection.hookListeners();
194: }
195:
196: /**
197: *
198: */
199: private void createListenersNameEntry() {
200: fNameEntry.setFormEntryListener(new FormEntryAdapter(this ) {
201: public void textValueChanged(FormEntry entry) {
202: // Ensure data object is defined
203: if (fDataTask == null) {
204: return;
205: }
206: fDataTask.setFieldName(fNameEntry.getValue());
207: }
208: });
209: }
210:
211: /**
212: *
213: */
214: private void createListenersPathEntry() {
215: fPathEntry.setFormEntryListener(new FormEntryAdapter(this ) {
216: public void browseButtonSelected(FormEntry entry) {
217: // Ensure data object is defined
218: if (fDataTask == null) {
219: return;
220: }
221: handleButtonEventPathEntry(entry);
222: }
223:
224: public void linkActivated(HyperlinkEvent e) {
225: // Ensure data object is defined
226: if (fDataTask == null) {
227: return;
228: }
229: handleLinkEventPathEntry(convertPathRelativeToAbs(
230: fPathEntry.getValue(), fDataTask.getModel()
231: .getUnderlyingResource().getFullPath()
232: .toPortableString()));
233: }
234:
235: public void textValueChanged(FormEntry entry) {
236: // Ensure data object is defined
237: if (fDataTask == null) {
238: return;
239: }
240: // TODO: MP: LOW: CompCS: Could validate manual input
241: handleTextEventPathEntry(entry.getValue());
242: }
243: });
244: }
245:
246: /**
247: * @param entry
248: */
249: private void handleButtonEventPathEntry(FormEntry entry) {
250: // Create the dialog
251: ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(
252: getManagedForm().getForm().getShell(),
253: new WorkbenchLabelProvider(),
254: new WorkbenchContentProvider());
255:
256: dialog.setValidator(new CompCSFileValidator());
257: dialog.setAllowMultiple(false);
258: dialog
259: .setTitle(PDEUIMessages.CompCSTaskDetails_simpleCSWizardTitle);
260: dialog
261: .setMessage(PDEUIMessages.CompCSTaskDetails_simpleCSWizardDescription);
262: dialog.addFilter(new FileExtensionFilter("xml")); //$NON-NLS-1$
263: dialog.setInput(PDEPlugin.getWorkspace().getRoot().getProject(
264: fDataTask.getModel().getUnderlyingResource()
265: .getProject().getName()));
266:
267: if (dialog.open() == Window.OK) {
268: IFile file = (IFile) dialog.getFirstResult();
269: String newValue = convertPathAbsToRelative(file
270: .getFullPath().toPortableString(), fDataTask
271: .getModel().getUnderlyingResource().getFullPath()
272: .toPortableString());
273: entry.setValue(newValue);
274: handleTextEventPathEntry(newValue);
275: }
276: }
277:
278: /**
279: * @param path
280: * @return
281: */
282: private String extractFileName(String path) {
283: StringTokenizer tokenizer = new StringTokenizer(path,
284: F_PATH_SEPARATOR);
285: while (tokenizer.countTokens() > 1) {
286: tokenizer.nextToken();
287: }
288: return tokenizer.nextToken();
289: }
290:
291: /**
292: * @param path
293: * @return
294: */
295: private String convertPathAbsToRelative(String relativePath,
296: String basePath) {
297: StringTokenizer convertPathTokenizer = new StringTokenizer(
298: relativePath, F_PATH_SEPARATOR);
299: StringTokenizer basePathTokenizer = new StringTokenizer(
300: basePath, F_PATH_SEPARATOR);
301: // First entry is the project name
302: String convertPathToken = convertPathTokenizer.nextToken();
303: String basePathToken = basePathTokenizer.nextToken();
304: // If the project names don't match, then we cannot make a relative
305: // path
306: if (convertPathToken.equals(basePathToken) == false) {
307: return ""; //$NON-NLS-1$
308: }
309: // Process base and convert path segments to make a relative path
310: while (basePathTokenizer.hasMoreTokens()
311: && convertPathTokenizer.hasMoreTokens()) {
312:
313: if (basePathTokenizer.countTokens() == 1) {
314: // Only the base file name is left
315: // No ".." required
316: // No last base path segment is required since we did not
317: // get the next base path segment token
318: return createRelativePath(0, null, convertPathTokenizer);
319: } else if (convertPathTokenizer.countTokens() == 1) {
320: // Only the convert file name is left
321: // Calculate required ".."
322: // No last base path segment is required since we did not
323: // get the next base path segment token
324: return createRelativePath(basePathTokenizer
325: .countTokens() - 1, null, convertPathTokenizer);
326: } else {
327: // Compare the next path segment
328: convertPathToken = convertPathTokenizer.nextToken();
329: basePathToken = basePathTokenizer.nextToken();
330: if (convertPathToken.equals(basePathToken) == false) {
331: // The path segments are not equal
332: // Calculate required ".."
333: // Last base path segment needs to be included in the
334: // relative path
335: return createRelativePath(basePathTokenizer
336: .countTokens(), convertPathToken,
337: convertPathTokenizer);
338: }
339: }
340: }
341: // This should never happen
342: return ""; //$NON-NLS-1$
343: }
344:
345: /**
346: * @param dotDotCount
347: * @param tokenizer
348: * @return
349: */
350: private String createRelativePath(int dotDotCount,
351: String lastToken, StringTokenizer tokenizer) {
352: StringBuffer relativePath = new StringBuffer();
353: // Prepend with the number of specified ".."
354: for (int i = 0; i < dotDotCount; i++) {
355: relativePath.append(F_DOT_DOT);
356: relativePath.append(F_PATH_SEPARATOR);
357: }
358: // Append the last token if specified
359: if (lastToken != null) {
360: relativePath.append(lastToken);
361: relativePath.append(F_PATH_SEPARATOR);
362: }
363: // Append all the path segments excluding the file itself
364: for (int i = 0; i < (tokenizer.countTokens() - 1); i++) {
365: relativePath.append(tokenizer.nextToken());
366: relativePath.append(F_PATH_SEPARATOR);
367: }
368: // Append the file itself
369: relativePath.append(tokenizer.nextToken());
370:
371: return relativePath.toString();
372: }
373:
374: /**
375: * @param absolutePath
376: */
377: private void handleLinkEventPathEntry(String absolutePath) {
378: IWorkspaceRoot root = PDEPlugin.getWorkspace().getRoot();
379: Path path = new Path(absolutePath);
380: // If the path is empty open the new simple cheat sheet wizard
381: if (path.isEmpty()) {
382: handleLinkWizardPathEntry();
383: return;
384: }
385: // Try to find the simple cheat sheet in the workspace
386: IResource resource = root.findMember(path);
387: // If the simple cheat sheet is found open the simple cheat sheet
388: // editor using it as input; otherwise, opne the simple cheat sheet
389: // wizard
390: if ((resource != null) && (resource instanceof IFile)) {
391: try {
392: IDE.openEditor(PDEPlugin.getActivePage(),
393: (IFile) resource, true);
394: } catch (PartInitException e) {
395: // Ignore
396: }
397: } else {
398: handleLinkWizardPathEntry();
399: }
400: }
401:
402: /**
403: *
404: */
405: private void handleLinkWizardPathEntry() {
406: NewSimpleCSFileWizard wizard = new NewSimpleCSFileWizard();
407: // Select in the tree view the directory this composite cheat sheet is
408: // stored in
409: wizard.init(PlatformUI.getWorkbench(), new StructuredSelection(
410: fDataTask.getModel().getUnderlyingResource()));
411: // Create the dialog for the wizard
412: WizardDialog dialog = new WizardDialog(PDEPlugin
413: .getActiveWorkbenchShell(), wizard);
414: dialog.create();
415: // Get the wizard page
416: IWizardPage wizardPage = wizard
417: .getPage(SimpleCSFileWizardPage.F_PAGE_NAME);
418: if ((wizardPage instanceof SimpleCSFileWizardPage) == false) {
419: return;
420: }
421: SimpleCSFileWizardPage page = (SimpleCSFileWizardPage) wizardPage;
422: // Set the initial file name
423: String initialValue = fPathEntry.getValue().trim();
424: if (initialValue.length() > 0) {
425: // It is a relative file name
426: page.setFileName(extractFileName(initialValue));
427: }
428: // Restrict user choices of where to store the new simple cheat sheet
429: // to only the project name this composite cheat sheet is stored in
430: page.setProjectName(fDataTask.getModel()
431: .getUnderlyingResource().getProject().getName());
432: // Check the result
433: if (dialog.open() == Window.OK) {
434: String newValue = convertPathAbsToRelative(page
435: .getAbsoluteFileName(), fDataTask.getModel()
436: .getUnderlyingResource().getFullPath()
437: .toPortableString());
438: fPathEntry.setValue(newValue, true);
439: handleTextEventPathEntry(newValue);
440: }
441: }
442:
443: /**
444: * @param relativePath
445: * @return
446: */
447: private String convertPathRelativeToAbs(String relativePath,
448: String basePath) {
449: StringTokenizer convertPathTokenizer = new StringTokenizer(
450: relativePath, F_PATH_SEPARATOR);
451: StringTokenizer basePathTokenizer = new StringTokenizer(
452: basePath, F_PATH_SEPARATOR);
453: // Accumulate the non ".." path segments excluding the file name
454: // and count the number of ".." path segments
455: StringBuffer endPath = new StringBuffer();
456: int dotDotCount = 0;
457: if (convertPathTokenizer.hasMoreTokens()) {
458: while (convertPathTokenizer.countTokens() > 1) {
459: String token = convertPathTokenizer.nextToken();
460: if (token.equals(F_DOT_DOT)) {
461: dotDotCount++;
462: } else {
463: endPath.append(token);
464: endPath.append(F_PATH_SEPARATOR);
465: }
466: }
467: // Append the file name
468: endPath.append(convertPathTokenizer.nextToken());
469: }
470: // Calculate the number of base path segments to accumulate
471: int baseSegementCount = basePathTokenizer.countTokens()
472: - dotDotCount - 1;
473: // Check to see if the relative path is bogus
474: if (baseSegementCount < 0) {
475: return ""; //$NON-NLS-1$
476: }
477: // Accumulate the initial path segments making up the absolute path
478: StringBuffer startPath = new StringBuffer(F_PATH_SEPARATOR);
479: for (int i = 0; i < baseSegementCount; i++) {
480: startPath.append(basePathTokenizer.nextToken());
481: startPath.append(F_PATH_SEPARATOR);
482: }
483: // Concatenate the start and end paths together to get the absolute
484: // paths
485: return startPath.toString() + endPath.toString();
486: }
487:
488: /**
489: * @param newValue
490: */
491: private void handleTextEventPathEntry(String newValue) {
492: // Check for existing parameters
493: if (fDataTask.hasFieldParams()) {
494: // There are existing parameters
495: // Check for an existing "path" parameter
496: ICompCSParam parameter = fDataTask
497: .getFieldParam(ICompCSConstants.ATTRIBUTE_VALUE_PATH);
498: if (parameter != null) {
499: parameter.setFieldValue(newValue);
500: } else {
501: // No suitable parameter found
502: // Create a new "path" parameter
503: createTaskParamPathEntry(newValue);
504: }
505: } else {
506: // No existing parameters
507: // Create a new "path" parameter
508: createTaskParamPathEntry(newValue);
509: }
510: }
511:
512: /**
513: * @param newValue
514: */
515: private void createTaskParamPathEntry(String newValue) {
516: ICompCSModelFactory factory = fDataTask.getModel().getFactory();
517: // Create parameter
518: ICompCSParam parameter = factory.createCompCSParam(fDataTask);
519: // Configure parameter
520: parameter.setFieldName(ICompCSConstants.ATTRIBUTE_VALUE_PATH);
521: parameter.setFieldValue(newValue);
522: // Add parameter to the task
523: fDataTask.addFieldParam(parameter);
524: }
525:
526: /**
527: *
528: */
529: private void createListenersSkipButton() {
530: fSkip.addSelectionListener(new SelectionAdapter() {
531: public void widgetSelected(SelectionEvent e) {
532: // Ensure data object is defined
533: if (fDataTask == null) {
534: return;
535: }
536: fDataTask.setFieldSkip(fSkip.getSelection());
537: }
538: });
539: }
540:
541: /* (non-Javadoc)
542: * @see org.eclipse.pde.internal.ui.editor.cheatsheet.CSAbstractDetails#updateFields()
543: */
544: public void updateFields() {
545: // Ensure data object is defined
546: if (fDataTask == null) {
547: return;
548: }
549: boolean editable = isEditableElement();
550: // Update name entry
551: updateNameEntry(editable);
552: // Update kind combo
553: updatePathEntry(editable);
554: // Update skip button
555: updateSkipButton(editable);
556: // Update fields within enclosing text section
557: fEnclosingTextSection.updateFields();
558: }
559:
560: /**
561: * @param editable
562: */
563: private void updateNameEntry(boolean editable) {
564: fNameEntry.setValue(fDataTask.getFieldName(), true);
565: fNameEntry.setEditable(editable);
566: }
567:
568: /**
569: * @param editable
570: */
571: private void updatePathEntry(boolean editable) {
572: ICompCSParam parameter = fDataTask
573: .getFieldParam(ICompCSConstants.ATTRIBUTE_VALUE_PATH);
574: if (parameter != null) {
575: fPathEntry.setValue(parameter.getFieldValue(), true);
576: } else {
577: fPathEntry.setValue("", true); //$NON-NLS-1$
578: }
579: }
580:
581: /**
582: * @param editable
583: */
584: private void updateSkipButton(boolean editable) {
585: fSkip.setSelection(fDataTask.getFieldSkip());
586: fSkip.setEnabled(editable);
587: }
588:
589: /* (non-Javadoc)
590: * @see org.eclipse.ui.forms.AbstractFormPart#commit(boolean)
591: */
592: public void commit(boolean onSave) {
593: super .commit(onSave);
594: // Only required for form entries
595: fNameEntry.commit();
596: fPathEntry.commit();
597: // No need to call for sub details, because they contain no form entries
598: }
599:
600: /* (non-Javadoc)
601: * @see org.eclipse.ui.forms.IPartSelectionListener#selectionChanged(org.eclipse.ui.forms.IFormPart, org.eclipse.jface.viewers.ISelection)
602: */
603: public void selectionChanged(IFormPart part, ISelection selection) {
604: // Get the first selected object
605: Object object = getFirstSelectedObject(selection);
606: // Ensure we have the right type
607: if ((object == null)
608: || (object instanceof ICompCSTask) == false) {
609: return;
610: }
611: // Set data
612: setData((ICompCSTask) object);
613: // Update the UI given the new data
614: updateFields();
615: }
616:
617: /* (non-Javadoc)
618: * @see org.eclipse.ui.forms.AbstractFormPart#dispose()
619: */
620: public void dispose() {
621: // Dispose of the enclosing text section
622: if (fEnclosingTextSection != null) {
623: fEnclosingTextSection.dispose();
624: fEnclosingTextSection = null;
625: }
626: super.dispose();
627: }
628:
629: }
|