Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.editors.ui;
     18 
     19 import com.android.ide.eclipse.adt.AdtPlugin;
     20 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     21 
     22 import org.eclipse.jface.text.DefaultInformationControl;
     23 import org.eclipse.swt.events.DisposeEvent;
     24 import org.eclipse.swt.events.DisposeListener;
     25 import org.eclipse.swt.events.MouseEvent;
     26 import org.eclipse.swt.events.MouseTrackListener;
     27 import org.eclipse.swt.graphics.Point;
     28 import org.eclipse.swt.layout.GridLayout;
     29 import org.eclipse.swt.widgets.Button;
     30 import org.eclipse.swt.widgets.Composite;
     31 import org.eclipse.swt.widgets.Control;
     32 import org.eclipse.swt.widgets.Label;
     33 import org.eclipse.swt.widgets.Text;
     34 import org.eclipse.ui.forms.SectionPart;
     35 import org.eclipse.ui.forms.widgets.FormText;
     36 import org.eclipse.ui.forms.widgets.FormToolkit;
     37 import org.eclipse.ui.forms.widgets.Section;
     38 import org.eclipse.ui.forms.widgets.TableWrapData;
     39 import org.eclipse.ui.forms.widgets.TableWrapLayout;
     40 
     41 import java.lang.reflect.Method;
     42 
     43 /**
     44  * Helper for the AndroidManifest form editor.
     45  *
     46  * Helps create a new SectionPart with sensible default parameters,
     47  * create default layout or add typical widgets.
     48  *
     49  * IMPORTANT: This is NOT a generic class. It makes a lot of assumptions on the
     50  * UI as used by the form editor for the AndroidManifest.
     51  *
     52  * TODO: Consider moving to a common package.
     53  */
     54 public final class SectionHelper {
     55 
     56     /**
     57      * Utility class that derives from SectionPart, constructs the Section with
     58      * sensible defaults (with a title and a description) and provide some shorthand
     59      * methods for creating typically UI (label and text, form text.)
     60      */
     61     static public class ManifestSectionPart extends SectionPart {
     62 
     63         /**
     64          * Construct a SectionPart that uses a title bar and a description.
     65          * It's up to the caller to call setText() and setDescription().
     66          * <p/>
     67          * The section style includes a description and a title bar by default.
     68          *
     69          * @param body The parent (e.g. FormPage body)
     70          * @param toolkit Form Toolkit
     71          */
     72         public ManifestSectionPart(Composite body, FormToolkit toolkit) {
     73             this(body, toolkit, 0, false);
     74         }
     75 
     76         /**
     77          * Construct a SectionPart that uses a title bar and a description.
     78          * It's up to the caller to call setText() and setDescription().
     79          * <p/>
     80          * The section style includes a description and a title bar by default.
     81          * You can add extra styles, like Section.TWISTIE.
     82          *
     83          * @param body The parent (e.g. FormPage body).
     84          * @param toolkit Form Toolkit.
     85          * @param extra_style Extra styles (on top of description and title bar).
     86          * @param use_description True if the Section.DESCRIPTION style should be added.
     87          */
     88         public ManifestSectionPart(Composite body, FormToolkit toolkit,
     89                 int extra_style, boolean use_description) {
     90             super(body, toolkit, extra_style |
     91                     Section.TITLE_BAR |
     92                     (use_description ? Section.DESCRIPTION : 0));
     93         }
     94 
     95         // Create non-static methods of helpers just for convenience
     96 
     97         /**
     98          * Creates a new composite with a TableWrapLayout set with a given number of columns.
     99          *
    100          * If the parent composite is a Section, the new composite is set as a client.
    101          *
    102          * @param toolkit Form Toolkit
    103          * @param numColumns Desired number of columns.
    104          * @return The new composite.
    105          */
    106         public Composite createTableLayout(FormToolkit toolkit, int numColumns) {
    107             return SectionHelper.createTableLayout(getSection(), toolkit, numColumns);
    108         }
    109 
    110         /**
    111          * Creates a label widget.
    112          * If the parent layout if a TableWrapLayout, maximize it to span over all columns.
    113          *
    114          * @param parent The parent (e.g. composite from CreateTableLayout())
    115          * @param toolkit Form Toolkit
    116          * @param label The string for the label.
    117          * @param tooltip An optional tooltip for the label and text. Can be null.
    118          * @return The new created label
    119          */
    120         public Label createLabel(Composite parent, FormToolkit toolkit, String label,
    121                 String tooltip) {
    122             return SectionHelper.createLabel(parent, toolkit, label, tooltip);
    123         }
    124 
    125         /**
    126          * Creates two widgets: a label and a text field.
    127          *
    128          * This expects the parent composite to have a TableWrapLayout with 2 columns.
    129          *
    130          * @param parent The parent (e.g. composite from CreateTableLayout())
    131          * @param toolkit Form Toolkit
    132          * @param label The string for the label.
    133          * @param value The initial value of the text field. Can be null.
    134          * @param tooltip An optional tooltip for the label and text. Can be null.
    135          * @return The new created Text field (the label is not returned)
    136          */
    137         public Text createLabelAndText(Composite parent, FormToolkit toolkit, String label,
    138                 String value, String tooltip) {
    139             return SectionHelper.createLabelAndText(parent, toolkit, label, value, tooltip);
    140         }
    141 
    142         /**
    143          * Creates a FormText widget.
    144          *
    145          * This expects the parent composite to have a TableWrapLayout with 2 columns.
    146          *
    147          * @param parent The parent (e.g. composite from CreateTableLayout())
    148          * @param toolkit Form Toolkit
    149          * @param isHtml True if the form text will contain HTML that must be interpreted as
    150          *               rich text (i.e. parse tags & expand URLs).
    151          * @param label The string for the label.
    152          * @param setupLayoutData indicates whether the created form text receives a TableWrapData
    153          * through the setLayoutData method. In some case, creating it will make the table parent
    154          * huge, which we don't want.
    155          * @return The new created FormText.
    156          */
    157         public FormText createFormText(Composite parent, FormToolkit toolkit, boolean isHtml,
    158                 String label, boolean setupLayoutData) {
    159             return SectionHelper.createFormText(parent, toolkit, isHtml, label, setupLayoutData);
    160         }
    161 
    162         /**
    163          * Forces the section to recompute its layout and redraw.
    164          * This is needed after the content of the section has been changed at runtime.
    165          */
    166         public void layoutChanged() {
    167             Section section = getSection();
    168 
    169             // Calls getSection().reflow(), which is the same that Section calls
    170             // when the expandable state is changed and the height changes.
    171             // Since this is protected, some reflection is needed to invoke it.
    172             try {
    173                 Method reflow;
    174                 reflow = section.getClass().getDeclaredMethod("reflow", (Class<?>[])null);
    175                 reflow.setAccessible(true);
    176                 reflow.invoke(section);
    177             } catch (Exception e) {
    178                 AdtPlugin.log(e, "Error when invoking Section.reflow");
    179             }
    180 
    181             section.layout(true /* changed */, true /* all */);
    182         }
    183     }
    184 
    185     /**
    186      * Creates a new composite with a TableWrapLayout set with a given number of columns.
    187      *
    188      * If the parent composite is a Section, the new composite is set as a client.
    189      *
    190      * @param composite The parent (e.g. a Section or SectionPart)
    191      * @param toolkit Form Toolkit
    192      * @param numColumns Desired number of columns.
    193      * @return The new composite.
    194      */
    195     static public Composite createTableLayout(Composite composite, FormToolkit toolkit,
    196             int numColumns) {
    197         Composite table = toolkit.createComposite(composite);
    198         TableWrapLayout layout = new TableWrapLayout();
    199         layout.numColumns = numColumns;
    200         table.setLayout(layout);
    201         toolkit.paintBordersFor(table);
    202         if (composite instanceof Section) {
    203             ((Section) composite).setClient(table);
    204         }
    205         return table;
    206     }
    207 
    208     /**
    209      * Creates a new composite with a GridLayout set with a given number of columns.
    210      *
    211      * If the parent composite is a Section, the new composite is set as a client.
    212      *
    213      * @param composite The parent (e.g. a Section or SectionPart)
    214      * @param toolkit Form Toolkit
    215      * @param numColumns Desired number of columns.
    216      * @return The new composite.
    217      */
    218     static public Composite createGridLayout(Composite composite, FormToolkit toolkit,
    219             int numColumns) {
    220         Composite grid = toolkit.createComposite(composite);
    221         GridLayout layout = new GridLayout();
    222         layout.numColumns = numColumns;
    223         grid.setLayout(layout);
    224         toolkit.paintBordersFor(grid);
    225         if (composite instanceof Section) {
    226             ((Section) composite).setClient(grid);
    227         }
    228         return grid;
    229     }
    230 
    231     /**
    232      * Creates two widgets: a label and a text field.
    233      *
    234      * This expects the parent composite to have a TableWrapLayout with 2 columns.
    235      *
    236      * @param parent The parent (e.g. composite from CreateTableLayout())
    237      * @param toolkit Form Toolkit
    238      * @param label_text The string for the label.
    239      * @param value The initial value of the text field. Can be null.
    240      * @param tooltip An optional tooltip for the label and text. Can be null.
    241      * @return The new created Text field (the label is not returned)
    242      */
    243     static public Text createLabelAndText(Composite parent, FormToolkit toolkit, String label_text,
    244             String value, String tooltip) {
    245         Label label = toolkit.createLabel(parent, label_text);
    246         label.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
    247         Text text = toolkit.createText(parent, value);
    248         text.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
    249 
    250         addControlTooltip(label, tooltip);
    251         return text;
    252     }
    253 
    254     /**
    255      * Creates a label widget.
    256      * If the parent layout if a TableWrapLayout, maximize it to span over all columns.
    257      *
    258      * @param parent The parent (e.g. composite from CreateTableLayout())
    259      * @param toolkit Form Toolkit
    260      * @param label_text The string for the label.
    261      * @param tooltip An optional tooltip for the label and text. Can be null.
    262      * @return The new created label
    263      */
    264     static public Label createLabel(Composite parent, FormToolkit toolkit, String label_text,
    265             String tooltip) {
    266         Label label = toolkit.createLabel(parent, label_text);
    267 
    268         TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB);
    269         if (parent.getLayout() instanceof TableWrapLayout) {
    270             twd.colspan = ((TableWrapLayout) parent.getLayout()).numColumns;
    271         }
    272         label.setLayoutData(twd);
    273 
    274         addControlTooltip(label, tooltip);
    275         return label;
    276     }
    277 
    278     /**
    279      * Associates a tooltip with a control.
    280      *
    281      * This mirrors the behavior from org.eclipse.pde.internal.ui.editor.text.PDETextHover
    282      *
    283      * @param control The control to which associate the tooltip.
    284      * @param tooltip The tooltip string. Can use \n for multi-lines. Will not display if null.
    285      */
    286     static public void addControlTooltip(final Control control, String tooltip) {
    287         if (control == null || tooltip == null || tooltip.length() == 0) {
    288             return;
    289         }
    290 
    291         // Some kinds of controls already properly implement tooltip display.
    292         if (control instanceof Button) {
    293             control.setToolTipText(tooltip);
    294             return;
    295         }
    296 
    297         control.setToolTipText(null);
    298 
    299         final DefaultInformationControl ic = new DefaultInformationControl(control.getShell());
    300         ic.setInformation(tooltip);
    301         Point sz = ic.computeSizeHint();
    302         ic.setSize(sz.x, sz.y);
    303         ic.setVisible(false); // initially hidden
    304 
    305         control.addMouseTrackListener(new MouseTrackListener() {
    306             @Override
    307             public void mouseEnter(MouseEvent e) {
    308             }
    309 
    310             @Override
    311             public void mouseExit(MouseEvent e) {
    312                 ic.setVisible(false);
    313             }
    314 
    315             @Override
    316             public void mouseHover(MouseEvent e) {
    317                 ic.setLocation(control.toDisplay(10, 25));  // same offset as in PDETextHover
    318                 ic.setVisible(true);
    319             }
    320         });
    321         control.addDisposeListener(new DisposeListener() {
    322             @Override
    323             public void widgetDisposed(DisposeEvent e) {
    324                 ic.dispose();
    325             }
    326         });
    327     }
    328 
    329     /**
    330      * Creates a FormText widget.
    331      *
    332      * This expects the parent composite to have a TableWrapLayout with 2 columns.
    333      *
    334      * @param parent The parent (e.g. composite from CreateTableLayout())
    335      * @param toolkit Form Toolkit
    336      * @param isHtml True if the form text will contain HTML that must be interpreted as
    337      *               rich text (i.e. parse tags & expand URLs).
    338      * @param label The string for the label.
    339      * @param setupLayoutData indicates whether the created form text receives a TableWrapData
    340      * through the setLayoutData method. In some case, creating it will make the table parent
    341      * huge, which we don't want.
    342      * @return The new created FormText.
    343      */
    344     static public FormText createFormText(Composite parent, FormToolkit toolkit,
    345             boolean isHtml, String label, boolean setupLayoutData) {
    346         FormText text = toolkit.createFormText(parent, true /* track focus */);
    347         if (setupLayoutData) {
    348             TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB);
    349             twd.maxWidth = AndroidXmlEditor.TEXT_WIDTH_HINT;
    350             if (parent.getLayout() instanceof TableWrapLayout) {
    351                 twd.colspan = ((TableWrapLayout) parent.getLayout()).numColumns;
    352             }
    353             text.setLayoutData(twd);
    354         }
    355         text.setWhitespaceNormalized(true);
    356         if (isHtml && !label.startsWith("<form>")) {          //$NON-NLS-1$
    357             // This assertion is violated, for example by the Class attribute for an activity
    358             //assert label.startsWith("<form>") : "HTML for FormText must be wrapped in <form>...</form>"; //$NON-NLS-1$
    359             label = "<form>" + label + "</form>";   //$NON-NLS-1$ //$NON-NLS-2$
    360         }
    361         text.setText(label, isHtml /* parseTags */, isHtml /* expandURLs */);
    362         return text;
    363     }
    364 }
    365