blob: fdb5d829232e9092e28dfa21a7d82993327d45d9 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.editors.ui;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import java.lang.reflect.Method;
/**
* Helper for the AndroidManifest form editor.
*
* Helps create a new SectionPart with sensible default parameters,
* create default layout or add typical widgets.
*
* IMPORTANT: This is NOT a generic class. It makes a lot of assumptions on the
* UI as used by the form editor for the AndroidManifest.
*
* TODO: Consider moving to a common package.
*/
public final class SectionHelper {
/**
* Utility class that derives from SectionPart, constructs the Section with
* sensible defaults (with a title and a description) and provide some shorthand
* methods for creating typically UI (label and text, form text.)
*/
static public class ManifestSectionPart extends SectionPart {
/**
* Construct a SectionPart that uses a title bar and a description.
* It's up to the caller to call setText() and setDescription().
* <p/>
* The section style includes a description and a title bar by default.
*
* @param body The parent (e.g. FormPage body)
* @param toolkit Form Toolkit
*/
public ManifestSectionPart(Composite body, FormToolkit toolkit) {
this(body, toolkit, 0, false);
}
/**
* Construct a SectionPart that uses a title bar and a description.
* It's up to the caller to call setText() and setDescription().
* <p/>
* The section style includes a description and a title bar by default.
* You can add extra styles, like Section.TWISTIE.
*
* @param body The parent (e.g. FormPage body).
* @param toolkit Form Toolkit.
* @param extra_style Extra styles (on top of description and title bar).
* @param use_description True if the Section.DESCRIPTION style should be added.
*/
public ManifestSectionPart(Composite body, FormToolkit toolkit,
int extra_style, boolean use_description) {
super(body, toolkit, extra_style |
Section.TITLE_BAR |
(use_description ? Section.DESCRIPTION : 0));
}
// Create non-static methods of helpers just for convenience
/**
* Creates a new composite with a TableWrapLayout set with a given number of columns.
*
* If the parent composite is a Section, the new composite is set as a client.
*
* @param toolkit Form Toolkit
* @param numColumns Desired number of columns.
* @return The new composite.
*/
public Composite createTableLayout(FormToolkit toolkit, int numColumns) {
return SectionHelper.createTableLayout(getSection(), toolkit, numColumns);
}
/**
* Creates a label widget.
* If the parent layout if a TableWrapLayout, maximize it to span over all columns.
*
* @param parent The parent (e.g. composite from CreateTableLayout())
* @param toolkit Form Toolkit
* @param label The string for the label.
* @param tooltip An optional tooltip for the label and text. Can be null.
* @return The new created label
*/
public Label createLabel(Composite parent, FormToolkit toolkit, String label,
String tooltip) {
return SectionHelper.createLabel(parent, toolkit, label, tooltip);
}
/**
* Creates two widgets: a label and a text field.
*
* This expects the parent composite to have a TableWrapLayout with 2 columns.
*
* @param parent The parent (e.g. composite from CreateTableLayout())
* @param toolkit Form Toolkit
* @param label The string for the label.
* @param value The initial value of the text field. Can be null.
* @param tooltip An optional tooltip for the label and text. Can be null.
* @return The new created Text field (the label is not returned)
*/
public Text createLabelAndText(Composite parent, FormToolkit toolkit, String label,
String value, String tooltip) {
return SectionHelper.createLabelAndText(parent, toolkit, label, value, tooltip);
}
/**
* Creates a FormText widget.
*
* This expects the parent composite to have a TableWrapLayout with 2 columns.
*
* @param parent The parent (e.g. composite from CreateTableLayout())
* @param toolkit Form Toolkit
* @param isHtml True if the form text will contain HTML that must be interpreted as
* rich text (i.e. parse tags & expand URLs).
* @param label The string for the label.
* @param setupLayoutData indicates whether the created form text receives a TableWrapData
* through the setLayoutData method. In some case, creating it will make the table parent
* huge, which we don't want.
* @return The new created FormText.
*/
public FormText createFormText(Composite parent, FormToolkit toolkit, boolean isHtml,
String label, boolean setupLayoutData) {
return SectionHelper.createFormText(parent, toolkit, isHtml, label, setupLayoutData);
}
/**
* Forces the section to recompute its layout and redraw.
* This is needed after the content of the section has been changed at runtime.
*/
public void layoutChanged() {
Section section = getSection();
// Calls getSection().reflow(), which is the same that Section calls
// when the expandable state is changed and the height changes.
// Since this is protected, some reflection is needed to invoke it.
try {
Method reflow;
reflow = section.getClass().getDeclaredMethod("reflow", (Class<?>[])null);
reflow.setAccessible(true);
reflow.invoke(section);
} catch (Exception e) {
AdtPlugin.log(e, "Error when invoking Section.reflow");
}
section.layout(true /* changed */, true /* all */);
}
}
/**
* Creates a new composite with a TableWrapLayout set with a given number of columns.
*
* If the parent composite is a Section, the new composite is set as a client.
*
* @param composite The parent (e.g. a Section or SectionPart)
* @param toolkit Form Toolkit
* @param numColumns Desired number of columns.
* @return The new composite.
*/
static public Composite createTableLayout(Composite composite, FormToolkit toolkit,
int numColumns) {
Composite table = toolkit.createComposite(composite);
TableWrapLayout layout = new TableWrapLayout();
layout.numColumns = numColumns;
table.setLayout(layout);
toolkit.paintBordersFor(table);
if (composite instanceof Section) {
((Section) composite).setClient(table);
}
return table;
}
/**
* Creates a new composite with a GridLayout set with a given number of columns.
*
* If the parent composite is a Section, the new composite is set as a client.
*
* @param composite The parent (e.g. a Section or SectionPart)
* @param toolkit Form Toolkit
* @param numColumns Desired number of columns.
* @return The new composite.
*/
static public Composite createGridLayout(Composite composite, FormToolkit toolkit,
int numColumns) {
Composite grid = toolkit.createComposite(composite);
GridLayout layout = new GridLayout();
layout.numColumns = numColumns;
grid.setLayout(layout);
toolkit.paintBordersFor(grid);
if (composite instanceof Section) {
((Section) composite).setClient(grid);
}
return grid;
}
/**
* Creates two widgets: a label and a text field.
*
* This expects the parent composite to have a TableWrapLayout with 2 columns.
*
* @param parent The parent (e.g. composite from CreateTableLayout())
* @param toolkit Form Toolkit
* @param label_text The string for the label.
* @param value The initial value of the text field. Can be null.
* @param tooltip An optional tooltip for the label and text. Can be null.
* @return The new created Text field (the label is not returned)
*/
static public Text createLabelAndText(Composite parent, FormToolkit toolkit, String label_text,
String value, String tooltip) {
Label label = toolkit.createLabel(parent, label_text);
label.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
Text text = toolkit.createText(parent, value);
text.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
addControlTooltip(label, tooltip);
return text;
}
/**
* Creates a label widget.
* If the parent layout if a TableWrapLayout, maximize it to span over all columns.
*
* @param parent The parent (e.g. composite from CreateTableLayout())
* @param toolkit Form Toolkit
* @param label_text The string for the label.
* @param tooltip An optional tooltip for the label and text. Can be null.
* @return The new created label
*/
static public Label createLabel(Composite parent, FormToolkit toolkit, String label_text,
String tooltip) {
Label label = toolkit.createLabel(parent, label_text);
TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB);
if (parent.getLayout() instanceof TableWrapLayout) {
twd.colspan = ((TableWrapLayout) parent.getLayout()).numColumns;
}
label.setLayoutData(twd);
addControlTooltip(label, tooltip);
return label;
}
/**
* Associates a tooltip with a control.
*
* This mirrors the behavior from org.eclipse.pde.internal.ui.editor.text.PDETextHover
*
* @param control The control to which associate the tooltip.
* @param tooltip The tooltip string. Can use \n for multi-lines. Will not display if null.
*/
static public void addControlTooltip(final Control control, String tooltip) {
if (control == null || tooltip == null || tooltip.length() == 0) {
return;
}
// Some kinds of controls already properly implement tooltip display.
if (control instanceof Button) {
control.setToolTipText(tooltip);
return;
}
control.setToolTipText(null);
final DefaultInformationControl ic = new DefaultInformationControl(control.getShell());
ic.setInformation(tooltip);
Point sz = ic.computeSizeHint();
ic.setSize(sz.x, sz.y);
ic.setVisible(false); // initially hidden
control.addMouseTrackListener(new MouseTrackListener() {
@Override
public void mouseEnter(MouseEvent e) {
}
@Override
public void mouseExit(MouseEvent e) {
ic.setVisible(false);
}
@Override
public void mouseHover(MouseEvent e) {
ic.setLocation(control.toDisplay(10, 25)); // same offset as in PDETextHover
ic.setVisible(true);
}
});
control.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
ic.dispose();
}
});
}
/**
* Creates a FormText widget.
*
* This expects the parent composite to have a TableWrapLayout with 2 columns.
*
* @param parent The parent (e.g. composite from CreateTableLayout())
* @param toolkit Form Toolkit
* @param isHtml True if the form text will contain HTML that must be interpreted as
* rich text (i.e. parse tags & expand URLs).
* @param label The string for the label.
* @param setupLayoutData indicates whether the created form text receives a TableWrapData
* through the setLayoutData method. In some case, creating it will make the table parent
* huge, which we don't want.
* @return The new created FormText.
*/
static public FormText createFormText(Composite parent, FormToolkit toolkit,
boolean isHtml, String label, boolean setupLayoutData) {
FormText text = toolkit.createFormText(parent, true /* track focus */);
if (setupLayoutData) {
TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB);
twd.maxWidth = AndroidXmlEditor.TEXT_WIDTH_HINT;
if (parent.getLayout() instanceof TableWrapLayout) {
twd.colspan = ((TableWrapLayout) parent.getLayout()).numColumns;
}
text.setLayoutData(twd);
}
text.setWhitespaceNormalized(true);
if (isHtml && !label.startsWith("<form>")) { //$NON-NLS-1$
// This assertion is violated, for example by the Class attribute for an activity
//assert label.startsWith("<form>") : "HTML for FormText must be wrapped in <form>...</form>"; //$NON-NLS-1$
label = "<form>" + label + "</form>"; //$NON-NLS-1$ //$NON-NLS-2$
}
text.setText(label, isHtml /* parseTags */, isHtml /* expandURLs */);
return text;
}
}