/******************************************************************************* * Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved. * This program and the accompanying materials are made available under the terms * of the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.ui.forms.parts; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.tcf.te.ui.forms.FormLayoutFactory; import org.eclipse.tcf.te.ui.forms.activator.UIPlugin; import org.eclipse.tcf.te.ui.forms.interfaces.tracing.ITraceIds; import org.eclipse.tcf.te.ui.jface.interfaces.IValidatable; import org.eclipse.tcf.te.ui.jface.interfaces.IValidatingContainer; import org.eclipse.tcf.te.ui.swt.SWTControlUtil; import org.eclipse.ui.forms.AbstractFormPart; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.SectionPart; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Section; /** * Abstract section implementation. */ public abstract class AbstractSection extends SectionPart implements IAdaptable, IValidatable { // The message text private String message = null; // The message type. See IMessageProvider private int messageType = NONE; // Flag to mark if the controls of the sections are currently updated. // While the control are updated, validation and similar logic should // not be processed. // <p> // <b>Note:</b> This flag default to <code>true</code> on instantiation. private boolean updating = true; // Flag to remember the read-only state /* default */ boolean readOnly = false; // Flag to remember the enabled state /* default */ boolean enabled = true; /** * Constructor. * * @param form The parent managed form. Must not be <code>null</code>. * @param parent The parent composite. Must not be <code>null</code>. * @param style The section style. */ public AbstractSection(IManagedForm form, Composite parent, int style) { this(form, parent, style, true); } /** * Constructor. * * @param form The parent managed form. Must not be <code>null</code>. * @param parent The parent composite. Must not be <code>null</code>. * @param style The section style. * @param titleBar If <code>true</code>, the title bar style bit is added to <code>style</code>. */ public AbstractSection(IManagedForm form, Composite parent, int style, boolean titleBar) { super(parent, form.getToolkit(), titleBar ? (ExpandableComposite.TITLE_BAR | style) : style); initialize(form); configureSection(getSection()); } /** * Get the validation container. * @return The validation container. */ protected IValidatingContainer getValidatingContainer() { Object container = getManagedForm().getContainer(); return container instanceof IValidatingContainer ? (IValidatingContainer)container : null; } /** * Configure the section. * * @param section The section. Must not be <code>null</code>. */ protected void configureSection(Section section) { Assert.isNotNull(section); section.clientVerticalSpacing = FormLayoutFactory.SECTION_HEADER_VERTICAL_SPACING; section.setData("part", this); //$NON-NLS-1$ // Adjust the background Color bg = section.getParent().getBackground(); if (bg != null && !bg.equals(section.getBackground())) { section.setBackground(bg); } } /** * Creates the section client. * * @param section The parent section. Must not be <code>null</code>. * @param toolkit The form toolkit. Must not be <code>null</code>. */ protected abstract void createClient(Section section, FormToolkit toolkit); /** * Creates the client container composite. * * @param parent The parent composite. Must not be <code>null</code>. * @param numColumns The number of columns. * @param toolkit The form toolkit or <code>null</code>. * * @return The client container composite. */ protected Composite createClientContainer(Composite parent, int numColumns, FormToolkit toolkit) { return createClientContainer(parent, numColumns, false, toolkit); } /** * Creates the client container composite. * * @param parent The parent composite. Must not be <code>null</code>. * @param numColumns The number of columns. * @param makeColumnsEqualWith * @param toolkit The form toolkit or <code>null</code>. * * @return The client container composite. */ protected Composite createClientContainer(Composite parent, int numColumns, boolean makeColumnsEqualWith, FormToolkit toolkit) { Composite container = toolkit != null ? toolkit.createComposite(parent) : new Composite(parent, SWT.NONE); container.setLayout(FormLayoutFactory.createSectionClientGridLayout(makeColumnsEqualWith, numColumns)); // Adjust the background Color bg = parent.getBackground(); if (bg != null && !bg.equals(container.getBackground())) { container.setBackground(bg); } return container; } /** * Convenience method to create a "invisible" label for creating an * empty space between controls. * * @param parent The parent composite. Must not be <code>null</code>. * @param span The horizontal span. * @param toolkit The form toolkit or <code>null</code>. * * @return */ protected Label createEmptySpace(Composite parent, int span, FormToolkit toolkit) { Assert.isNotNull(parent); Label emptySpace = toolkit != null ? toolkit.createLabel(parent, null) : new Label(parent, SWT.NONE); GridData layoutData = new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false); layoutData.horizontalSpan = span; layoutData.widthHint = 0; layoutData.heightHint = SWTControlUtil.convertHeightInCharsToPixels(emptySpace, 1) / 2; emptySpace.setLayoutData(layoutData); return emptySpace; } /** * Convenience method to create a section toolbar. * * @param section The section. Must not be <code>null</code>. * @param toolkit The form toolkit or <code>null</code>. */ protected void createSectionToolbar(Section section, FormToolkit toolkit) { Assert.isNotNull(section); // Create the toolbar manager and the toolbar control ToolBarManager tlbMgr = new ToolBarManager(SWT.FLAT); ToolBar tlb = tlbMgr.createControl(section); // If the user moves over the toolbar area, change the cursor to become a hand final Cursor cursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); tlb.setCursor(cursor); // Cursor needs to be explicitly disposed tlb.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (cursor.isDisposed() == false) { cursor.dispose(); } } }); // Create the toolbar items createSectionToolbarItems(section, toolkit, tlbMgr); // Update the toolbar manager tlbMgr.update(true); // Associate the toolbar control with the section section.setTextClient(tlb); } /** * Convenience method to create section toolbar items. * <p> * This method is called from {@link #createSectionToolbar(Section, FormToolkit)}. * * @param section The section. Must not be <code>null</code>. * @param toolkit The form toolkit or <code>null</code>. * @param tlbMgr The toolbar manager. Must not be <code>null</code>. */ protected void createSectionToolbarItems(Section section, FormToolkit toolkit, ToolBarManager tlbMgr) { Assert.isNotNull(section); Assert.isNotNull(tlbMgr); } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @Override public Object getAdapter(Class adapter) { return Platform.getAdapterManager().getAdapter(this, adapter); } /** * Marks the section dirty or reset the dirty state. * * @param dirty <code>True</code> to mark the section dirty, <code>false</code> otherwise. */ public final void markDirty(boolean dirty) { if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_SECTIONS_DIRTY_STATE)) { UIPlugin.getTraceHandler().trace("markDirty: Set dirty state to " + dirty, //$NON-NLS-1$ ITraceIds.TRACE_SECTIONS_DIRTY_STATE, this); } if (dirty) markDirty(); else { // For now, there is no direct way to reset the dirty state, // and the refresh() method is setting back both flags (stale and dirty). // Plus, refresh() might be overwritten to refresh the widget content // from the data itself, what will trigger an stack overflow after all. try { final Field f = AbstractFormPart.class.getDeclaredField("dirty"); //$NON-NLS-1$ AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { f.setAccessible(true); return null; } }); f.setBoolean(this, dirty); if (getManagedForm() != null) getManagedForm().dirtyStateChanged(); } catch (Exception e) { /* ignored on purpose */ } } } /** * Marks the section stale or reset the stale state. * * @param stale <code>True</code> to mark the section stale, <code>false</code> otherwise. */ public final void markStale(boolean stale) { if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_SECTIONS_STALE_STATE)) { UIPlugin.getTraceHandler().trace("markStale: Set stale state to " + stale, //$NON-NLS-1$ ITraceIds.TRACE_SECTIONS_STALE_STATE, this); } if (stale) markStale(); else { // For now, there is no direct way to reset the stale state, // and the refresh() method is setting back both flags (stale and dirty). // Plus, refresh() might be overwritten to refresh the widget content // from the data itself, what will trigger an stack overflow after all. try { final Field f = AbstractFormPart.class.getDeclaredField("stale"); //$NON-NLS-1$ AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { f.setAccessible(true); return null; } }); f.setBoolean(this, stale); if (getManagedForm() != null) getManagedForm().staleStateChanged(); } catch (Exception e) { /* ignored on purpose */ } } } /* (non-Javadoc) * @see org.eclipse.ui.forms.AbstractFormPart#commit(boolean) */ @Override public void commit(boolean onSave) { if (onSave) { if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_SECTIONS_DIRTY_STATE)) { UIPlugin.getTraceHandler().trace("Commit(" + onSave + ") reseted the dirty state to false.", //$NON-NLS-1$ //$NON-NLS-2$ ITraceIds.TRACE_SECTIONS_DIRTY_STATE, this); } super.commit(onSave); getManagedForm().dirtyStateChanged(); } } /* (non-Javadoc) * @see org.eclipse.ui.forms.AbstractFormPart#refresh() */ @Override public void refresh() { // refresh is reseting both the stale and the dirty state boolean hasBeenStale = isStale(); boolean hasBeenDirty = isDirty(); super.refresh(); if (hasBeenStale) { if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_SECTIONS_STALE_STATE)) { UIPlugin.getTraceHandler().trace("refresh() reseted the stale state to false.", //$NON-NLS-1$ ITraceIds.TRACE_SECTIONS_STALE_STATE, this); } getManagedForm().staleStateChanged(); } if (hasBeenDirty) { if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_SECTIONS_DIRTY_STATE)) { UIPlugin.getTraceHandler().trace("refresh() reseted the dirty state to false.", //$NON-NLS-1$ ITraceIds.TRACE_SECTIONS_DIRTY_STATE, this); } getManagedForm().dirtyStateChanged(); } } /** * Set the sections read only state. * @param readOnly <code>true</code> if the sections controls should be read only. */ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } /** * Get the read only state of this section. * @return The read onyly state. */ public boolean isReadOnly() { return readOnly; } /** * Set the sections enabled state. * @param readOnly <code>true</code> if the sections controls should be enabled. */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * Get the enabled state of this section. * @return The enabled state. */ public boolean isEnabled() { return enabled; } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.jface.interfaces.IValidatable#isValid() */ @Override public boolean isValid() { setMessage(null, IMessageProvider.NONE); return true; } /** * Sets the message text and type. * * @param message The message or <code>null</code>. * @param messageType The message type. See {@link IMessageProvider}. */ protected final void setMessage(String message, int messageType) { this.message = message; this.messageType = messageType; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage() */ @Override public final String getMessage() { return message; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType() */ @Override public final int getMessageType() { return messageType; } /** * Marks if or if not the controls of the section are currently updated. */ protected final void setIsUpdating(boolean updating) { this.updating = updating; } /** * Returns if or if not the controls of the section are currently updated. * * @return <code>True</code> if the controls are currently updated, <code>false</code> otherwise. */ protected final boolean isUpdating() { return updating; } /** * Saves the widget history of all UI elements of the section. * * @param settings The dialog settings or <code>null</code>. */ public void saveWidgetValues(IDialogSettings settings) { } /** * Restores the widget history of all UI elements of the section. * * @param settings The dialog settings or <code>null</code>. */ public void restoreWidgetValues(IDialogSettings settings) { } }