001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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: * Sebastian Davids <sdavids@gmx.de> - bug 77332 - [Markers] Add task dialog improvements
011: *******************************************************************************/package org.eclipse.ui.views.markers.internal;
012:
013: import java.util.HashMap;
014: import java.util.Map;
015:
016: import org.eclipse.core.commands.ExecutionException;
017: import org.eclipse.core.commands.operations.IUndoableOperation;
018: import org.eclipse.core.resources.IMarker;
019: import org.eclipse.core.resources.IResource;
020: import org.eclipse.core.resources.ResourcesPlugin;
021: import org.eclipse.core.runtime.CoreException;
022: import org.eclipse.core.runtime.IPath;
023: import org.eclipse.jface.dialogs.Dialog;
024: import org.eclipse.jface.dialogs.ErrorDialog;
025: import org.eclipse.jface.dialogs.IDialogConstants;
026: import org.eclipse.jface.dialogs.IDialogSettings;
027: import org.eclipse.jface.dialogs.TrayDialog;
028: import org.eclipse.osgi.util.NLS;
029: import org.eclipse.swt.SWT;
030: import org.eclipse.swt.events.ModifyEvent;
031: import org.eclipse.swt.events.ModifyListener;
032: import org.eclipse.swt.layout.GridData;
033: import org.eclipse.swt.layout.GridLayout;
034: import org.eclipse.swt.widgets.Composite;
035: import org.eclipse.swt.widgets.Control;
036: import org.eclipse.swt.widgets.Label;
037: import org.eclipse.swt.widgets.Shell;
038: import org.eclipse.swt.widgets.Text;
039: import org.eclipse.ui.PlatformUI;
040: import org.eclipse.ui.ide.undo.CreateMarkersOperation;
041: import org.eclipse.ui.ide.undo.UpdateMarkersOperation;
042: import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
043: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
044:
045: /**
046: * Shows the properties of a new or existing marker
047: *
048: * In 3.3, this class was refactored to allow pre-existing public dialog classes
049: * to share the implementation. Note that certain methods are exposed as API
050: * in public subclasses, so changes to the methods in this class should be
051: * treated carefully as they may affect API methods in subclasses. The specific
052: * methods affected are documented in the method comment.
053: */
054: public class DialogMarkerProperties extends TrayDialog {
055:
056: private static final String DIALOG_SETTINGS_SECTION = "DialogMarkerPropertiesDialogSettings"; //$NON-NLS-1$
057:
058: /**
059: * The marker being shown, or <code>null</code> for a new marker
060: */
061: private IMarker marker = null;
062:
063: /**
064: * The resource on which to create a new marker
065: */
066: private IResource resource = null;
067:
068: /**
069: * The type of marker to be created
070: */
071: private String type = IMarker.MARKER;
072:
073: /**
074: * The initial attributes to use when creating a new marker
075: */
076: private Map initialAttributes = null;
077:
078: /**
079: * The text control for the Description field.
080: */
081: private Text descriptionText;
082:
083: /**
084: * The control for the Creation Time field.
085: */
086: private Label creationTime;
087:
088: /**
089: * The text control for the Resource field.
090: */
091: private Text resourceText;
092:
093: /**
094: * The text control for the Folder field.
095: */
096: private Text folderText;
097:
098: /**
099: * The text control for the Location field.
100: */
101: private Text locationText;
102:
103: /**
104: * Dirty flag. True if any changes have been made.
105: */
106: private boolean dirty;
107:
108: private String title;
109:
110: /**
111: * The name used to describe the specific kind of marker. Used when
112: * creating an undo command for the dialog, so that a specific name such
113: * as "Undo Create Task" or "Undo Modify Bookmark" can be used.
114: */
115: private String markerName;
116:
117: /**
118: * Creates the dialog. By default this dialog creates a new marker. To set
119: * the resource and initial attributes for the new marker, use
120: * <code>setResource</code> and <code>setInitialAttributes</code>. To
121: * show or modify an existing marker, use <code>setMarker</code>.
122: *
123: * @param parentShell
124: * the parent shell
125: */
126: public DialogMarkerProperties(Shell parentShell) {
127: super (parentShell);
128: }
129:
130: /**
131: * Creates the dialog. By default this dialog creates a new marker. To set
132: * the resource and initial attributes for the new marker, use
133: * <code>setResource</code> and <code>setInitialAttributes</code>. To
134: * show or modify an existing marker, use <code>setMarker</code>.
135: *
136: * @param parentShell
137: * the parent shell
138: * @param title
139: * the title of the dialog
140: */
141: public DialogMarkerProperties(Shell parentShell, String title) {
142: super (parentShell);
143: this .title = title;
144: }
145:
146: /**
147: * Creates the dialog. By default this dialog creates a new marker. To set
148: * the resource and initial attributes for the new marker, use
149: * <code>setResource</code> and <code>setInitialAttributes</code>. To
150: * show or modify an existing marker, use <code>setMarker</code>.
151: *
152: * @param parentShell
153: * the parent shell
154: * @param title
155: * the title of the dialog
156: * @param markerName
157: * the name used to describe the specific kind of marker shown
158: *
159: * @since 3.3
160: */
161: public DialogMarkerProperties(Shell parentShell, String title,
162: String markerName) {
163: super (parentShell);
164: this .title = title;
165: this .markerName = markerName;
166: }
167:
168: /**
169: * Sets the marker to show or modify.
170: * <p>IMPORTANT: Although this class is internal, there are public
171: * subclasses that expose this method as API. Changes in
172: * this implementation should be treated as API changes.
173: *
174: * @param marker the marker, or <code>null</code> to create a new marker
175: *
176: * @since 3.3
177: */
178: public void setMarker(IMarker marker) {
179: this .marker = marker;
180: if (marker != null) {
181: try {
182: type = marker.getType();
183: } catch (CoreException e) {
184: }
185: }
186: }
187:
188: /**
189: * Returns the marker being created or modified.
190: * For a new marker, this returns <code>null</code> until
191: * the dialog returns, but is non-null after.
192: * <p>IMPORTANT: Although this method is protected and the class is
193: * internal, there are public subclasses that expose this method as API.
194: * Changes in this implementation should be treated as API changes.
195: *
196: * @return the marker
197: *
198: * @since 3.3
199: */
200: protected IMarker getMarker() {
201: return marker;
202: }
203:
204: /**
205: * Sets the resource to use when creating a new task.
206: * If not set, the new task is created on the workspace root.
207: * <p>IMPORTANT: Although this class is internal, there are public
208: * subclasses that expose this method as API. Changes in
209: * this implementation should be treated as API changes.
210: *
211: * @param resource the resource
212: */
213: public void setResource(IResource resource) {
214: this .resource = resource;
215: }
216:
217: /**
218: * Returns the resource to use when creating a new task,
219: * or <code>null</code> if none has been set.
220: * If not set, the new task is created on the workspace root.
221: * <p>IMPORTANT: Although this method is protected and the class is
222: * internal, there are public subclasses that expose this method as API.
223: * Changes in this implementation should be treated as API changes.
224: *
225: * @return the resource
226: *
227: * @since 3.3
228: */
229: protected IResource getResource() {
230: return resource;
231: }
232:
233: /**
234: * Sets initial attributes to use when creating a new task.
235: * If not set, the new task is created with default attributes.
236: * <p>IMPORTANT: Although this method is protected and the class is
237: * internal, there are public subclasses that expose this method as API.
238: * Changes in this implementation should be treated as API changes.
239: *
240: * @param initialAttributes the initial attributes
241: *
242: * @since 3.3
243: */
244: protected void setInitialAttributes(Map initialAttributes) {
245: this .initialAttributes = initialAttributes;
246: }
247:
248: /**
249: * Returns the initial attributes to use when creating a new task,
250: * or <code>null</code> if not set.
251: * If not set, the new task is created with default attributes.
252: * <p>IMPORTANT: Although this method is protected and the class is
253: * internal, there are public subclasses that expose this method as API.
254: * Changes in this implementation should be treated as API changes.
255: *
256: * @return the initial attributes
257: *
258: * @since 3.3
259: */
260: protected Map getInitialAttributes() {
261: if (initialAttributes == null) {
262: initialAttributes = new HashMap();
263: }
264: return initialAttributes;
265: }
266:
267: /**
268: * Method declared on Window.
269: */
270: protected void configureShell(Shell newShell) {
271: super .configureShell(newShell);
272: if (title == null) {
273: newShell.setText(MarkerMessages.propertiesDialog_title);
274: } else {
275: newShell.setText(title);
276: }
277: }
278:
279: /**
280: * Method declared on Dialog.
281: */
282: protected Control createDialogArea(Composite parent) {
283: // initialize resources/properties
284: if (marker != null) {
285: resource = marker.getResource();
286: try {
287: initialAttributes = marker.getAttributes();
288: } catch (CoreException e) {
289: }
290: } else if (resource == null) {
291: resource = ResourcesPlugin.getWorkspace().getRoot();
292: }
293:
294: Composite comp = (Composite) super .createDialogArea(parent);
295: Composite composite = new Composite(comp, SWT.NULL);
296: GridLayout layout = new GridLayout(2, false);
297: layout.marginWidth = 0;
298: layout.marginHeight = 0;
299: composite.setLayout(layout);
300: GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
301: composite.setLayoutData(gridData);
302:
303: initializeDialogUnits(composite);
304: createDescriptionArea(composite);
305: if (marker != null) {
306: createSeperator(composite);
307: createCreationTimeArea(composite);
308: }
309: createAttributesArea(composite);
310: if (resource != null) {
311: createSeperator(composite);
312: createResourceArea(composite);
313: }
314: updateDialogFromMarker();
315: updateEnablement();
316:
317: Dialog.applyDialogFont(composite);
318:
319: return composite;
320: }
321:
322: /**
323: * Creates a seperator.
324: */
325: protected void createSeperator(Composite parent) {
326: Label seperator = new Label(parent, SWT.NULL);
327: GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
328: gridData.horizontalSpan = 2;
329: seperator.setLayoutData(gridData);
330: }
331:
332: /**
333: * Method createCreationTimeArea.
334: * @param parent
335: */
336: private void createCreationTimeArea(Composite parent) {
337: Label label = new Label(parent, SWT.NONE);
338: label
339: .setText(MarkerMessages.propertiesDialog_creationTime_text);
340:
341: creationTime = new Label(parent, SWT.NONE);
342: }
343:
344: /**
345: * Creates the OK and Cancel buttons.
346: */
347: protected void createButtonsForButtonBar(Composite parent) {
348: createButton(parent, IDialogConstants.OK_ID,
349: IDialogConstants.OK_LABEL, true);
350: createButton(parent, IDialogConstants.CANCEL_ID,
351: IDialogConstants.CANCEL_LABEL, false);
352: }
353:
354: /**
355: * Creates the area for the Description field.
356: */
357: private void createDescriptionArea(Composite parent) {
358: Label label = new Label(parent, SWT.NONE);
359: label.setText(MarkerMessages.propertiesDialog_description_text);
360: descriptionText = new Text(parent, (SWT.SINGLE | SWT.BORDER));
361: GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
362: gridData.widthHint = convertHorizontalDLUsToPixels(400);
363: descriptionText.setLayoutData(gridData);
364:
365: descriptionText.addModifyListener(new ModifyListener() {
366: public void modifyText(ModifyEvent e) {
367: markDirty();
368: }
369: });
370: }
371:
372: /**
373: * This method is intended to be overridden by subclasses. The attributes
374: * area is created between the creation time area and the resource area.
375: *
376: * @param parent
377: * the parent composite
378: */
379: protected void createAttributesArea(Composite parent) {
380: }
381:
382: /**
383: * Creates the area for the Resource field.
384: */
385: private void createResourceArea(Composite parent) {
386: Label resourceLabel = new Label(parent, SWT.NONE);
387: resourceLabel
388: .setText(MarkerMessages.propertiesDialog_resource_text);
389: resourceText = new Text(parent, SWT.SINGLE | SWT.WRAP
390: | SWT.READ_ONLY | SWT.BORDER);
391: GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
392: resourceText.setLayoutData(gridData);
393:
394: Label folderLabel = new Label(parent, SWT.NONE);
395: folderLabel
396: .setText(MarkerMessages.propertiesDialog_folder_text);
397: folderText = new Text(parent, SWT.SINGLE | SWT.WRAP
398: | SWT.READ_ONLY | SWT.BORDER);
399: gridData = new GridData(GridData.FILL_HORIZONTAL);
400: folderText.setLayoutData(gridData);
401:
402: Label locationLabel = new Label(parent, SWT.NONE);
403: locationLabel
404: .setText(MarkerMessages.propertiesDialog_location_text);
405: locationText = new Text(parent, SWT.SINGLE | SWT.WRAP
406: | SWT.READ_ONLY | SWT.BORDER);
407: gridData = new GridData(GridData.FILL_HORIZONTAL);
408: locationText.setLayoutData(gridData);
409: }
410:
411: /**
412: * Updates the dialog from the marker state.
413: */
414: protected void updateDialogFromMarker() {
415: if (marker == null) {
416: updateDialogForNewMarker();
417: return;
418: }
419: descriptionText.setText(Util.getProperty(IMarker.MESSAGE,
420: marker));
421: if (creationTime != null) {
422: creationTime.setText(Util.getCreationTime(marker));
423: }
424: if (resourceText != null) {
425: resourceText.setText(Util.getResourceName(marker));
426: }
427: if (folderText != null) {
428: folderText.setText(Util.getContainerName(marker));
429: }
430: if (locationText != null) {
431: String line = Util.getProperty(IMarker.LINE_NUMBER, marker);
432: if (line.equals("")) { //$NON-NLS-1$
433: locationText.setText(""); //$NON-NLS-1$
434: } else {
435: locationText.setText(NLS.bind(
436: MarkerMessages.label_lineNumber, line));
437: }
438: }
439:
440: descriptionText.selectAll();
441: }
442:
443: /**
444: * Updates the dialog from the predefined attributes.
445: */
446: protected void updateDialogForNewMarker() {
447: if (resource != null && resourceText != null
448: && folderText != null) {
449: resourceText.setText(resource.getName());
450:
451: IPath path = resource.getFullPath();
452: int n = path.segmentCount() - 1; // n is the number of segments in container, not path
453: if (n > 0) {
454: int len = 0;
455: for (int i = 0; i < n; ++i) {
456: len += path.segment(i).length();
457: }
458: // account for /'s
459: if (n > 1) {
460: len += n - 1;
461: }
462: StringBuffer sb = new StringBuffer(len);
463: for (int i = 0; i < n; ++i) {
464: if (i != 0) {
465: sb.append('/');
466: }
467: sb.append(path.segment(i));
468: }
469: folderText.setText(sb.toString());
470: }
471: }
472:
473: if (initialAttributes != null) {
474: Object description = initialAttributes.get(IMarker.MESSAGE);
475: if (description != null && description instanceof String) {
476: descriptionText.setText((String) description);
477: }
478: descriptionText.selectAll();
479:
480: Object line = initialAttributes.get(IMarker.LINE_NUMBER);
481: if (line != null && line instanceof Integer
482: && locationText != null) {
483: locationText.setText(NLS.bind(
484: MarkerMessages.label_lineNumber, line));
485: }
486: }
487: }
488:
489: /**
490: * Method declared on Dialog
491: */
492: protected void okPressed() {
493: if (marker == null || Util.isEditable(marker)) {
494: saveChanges();
495: }
496: super .okPressed();
497: }
498:
499: /**
500: * Sets the dialog's dirty flag to <code>true</code>
501: */
502: protected void markDirty() {
503: dirty = true;
504: }
505:
506: /**
507: * @return
508: * <ul>
509: * <li><code>true</code> if the dirty flag has been set to true.</li>
510: * <li><code>false</code> otherwise.</li>
511: * </ul>
512: */
513: protected boolean isDirty() {
514: return dirty;
515: }
516:
517: /**
518: * Saves the changes made in the dialog if needed. Creates a new marker if
519: * needed. Updates the existing marker only if there have been changes.
520: */
521: private void saveChanges() {
522: Map attrs = getMarkerAttributes();
523: IUndoableOperation op = null;
524: if (marker == null) {
525: if (resource == null)
526: return;
527: op = new CreateMarkersOperation(type, attrs, resource,
528: getCreateOperationTitle());
529: } else {
530: if (isDirty()) {
531: op = new UpdateMarkersOperation(marker, attrs,
532: getModifyOperationTitle(), true);
533: }
534: }
535: if (op != null) {
536: try {
537: PlatformUI.getWorkbench().getOperationSupport()
538: .getOperationHistory().execute(
539: op,
540: null,
541: WorkspaceUndoUtil
542: .getUIInfoAdapter(getShell()));
543: } catch (ExecutionException e) {
544: if (e.getCause() instanceof CoreException) {
545: ErrorDialog.openError(getShell(),
546: MarkerMessages.Error, null,
547: ((CoreException) e.getCause()).getStatus());
548: } else
549: IDEWorkbenchPlugin.log(e.getMessage(), e);
550: }
551: }
552: }
553:
554: /**
555: * Returns the marker attributes to save back to the marker, based on the
556: * current dialog fields.
557: */
558: protected Map getMarkerAttributes() {
559: Map attrs = getInitialAttributes();
560: attrs.put(IMarker.MESSAGE, descriptionText.getText());
561: return attrs;
562: }
563:
564: /**
565: * Updates widget enablement for the dialog. Should be overridden by
566: * subclasses.
567: */
568: protected void updateEnablement() {
569: descriptionText.setEditable(isEditable());
570: }
571:
572: /**
573: * @return
574: * <ul>
575: * <li><code>true</code> if the marker is editable or the dialog is
576: * creating a new marker.</li>
577: * <li><code>false</code> if the marker is not editable.</li>
578: * </ul>
579: */
580: protected boolean isEditable() {
581: if (marker == null) {
582: return true;
583: }
584: return Util.isEditable(marker);
585: }
586:
587: /**
588: * Sets the marker type when creating a new marker.
589: *
590: * @param type
591: * the marker type
592: *
593: * @since 3.3 this method is protected.
594: */
595: protected void setType(String type) {
596: this .type = type;
597: }
598:
599: /* (non-Javadoc)
600: * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
601: *
602: * @since 3.2
603: */
604: protected IDialogSettings getDialogBoundsSettings() {
605: IDialogSettings settings = IDEWorkbenchPlugin.getDefault()
606: .getDialogSettings();
607: IDialogSettings section = settings
608: .getSection(DIALOG_SETTINGS_SECTION);
609: if (section == null) {
610: section = settings.addNewSection(DIALOG_SETTINGS_SECTION);
611: }
612: return section;
613: }
614:
615: /**
616: * Return the string that describes a modify marker operation.
617: * Subclasses may override to more specifically describe the marker.
618: *
619: * @since 3.3
620: */
621: protected String getModifyOperationTitle() {
622: if (markerName == null) {
623: // we don't know what kind of marker is being modified
624: return MarkerMessages.DialogMarkerProperties_ModifyMarker;
625: }
626: return NLS.bind(MarkerMessages.qualifiedMarkerCommand_title,
627: MarkerMessages.DialogMarkerProperties_Modify,
628: markerName);
629: }
630:
631: /**
632: * Return the string that describes a create marker operation.
633: * Subclasses may override to more specifically describe the marker.
634: *
635: * @since 3.3
636: */
637: protected String getCreateOperationTitle() {
638: if (markerName == null) {
639: // we don't know what kind of marker is being created
640: return MarkerMessages.DialogMarkerProperties_CreateMarker;
641: }
642: return NLS.bind(MarkerMessages.qualifiedMarkerCommand_title,
643: MarkerMessages.DialogMarkerProperties_Create,
644: markerName);
645:
646: }
647:
648: /*
649: * (non-Javadoc)
650: * @see org.eclipse.jface.dialogs.Dialog#isResizable()
651: */
652: protected boolean isResizable() {
653: return true;
654: }
655:
656: }
|