/*
* � Copyright IBM Corp. 2014
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.ibm.xsp.extlib.designer.tooling.palette.applicationlayout;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import com.ibm.commons.util.StringUtil;
import com.ibm.designer.domino.navigator.NavigatorPlugin;
import com.ibm.designer.domino.xsp.registry.DesignerExtension;
import com.ibm.designer.domino.xsp.registry.DesignerExtensionUtil;
import com.ibm.xsp.extlib.designer.tooling.ExtLibToolingPlugin;
import com.ibm.xsp.extlib.designer.tooling.annotation.ExtLibLayoutExtension;
import com.ibm.xsp.extlib.designer.tooling.utils.ExtLibRegistryUtil;
import com.ibm.xsp.extlib.designer.tooling.utils.ExtLibToolingLogger;
import com.ibm.xsp.extlib.designer.tooling.utils.WizardUtils;
import com.ibm.xsp.library.StandardRegistryMaintainer;
import com.ibm.xsp.registry.FacesDefinition;
import static com.ibm.xsp.extlib.designer.tooling.constants.IExtLibRegistry.*;
/**
* @author Gary Marjoram
*
*/
public class AlwStartPage extends WizardPage implements ControlListener, Listener, SelectionListener {
private static final int MARGIN = 10;
private static final int SPACER = 3;
private static final int DEFAULT_ROWS = 3;
private static final String SAMPLE_TEXT = "Sample"; // $NLX-AlwStartPage.Sample-1$
private int _textMargin = 0;
private List<LayoutConfig> _configList = new ArrayList<LayoutConfig>();
private Table _table;
private Button _allRadio;
private Button _responsiveRadio;
private Button _nonResponsiveRadio;
private final Image _defImage;
private final Image _responsiveImage;
private final Cursor _handCursor;
private final Font _titleFont;
private final Color _hyperlinkColor;
private final boolean _showResponsiveIcon = false;
/*
* Enum for different configuration types
*/
private static enum ConfigurationType {
NON_RESPONSIVE,
RESPONSIVE,
ALL
};
/*
* Constructor
*/
protected AlwStartPage() {
super("");
setTitle("Application Layout"); // $NLX-AlwStartPage.ApplicationLayout-1$
setMessage("Choose the configuration for this control.", IMessageProvider.INFORMATION); // $NLX-AlwStartPage.Choosetheconfigurationforthiscont-1$
// Setup the title font
_titleFont = JFaceResources.getBannerFont();
// Setup hand cursor
_handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
// Load images - Do not have to be disposed later - plugin maintains a list
_defImage = ExtLibToolingPlugin.getImage("app_layout.jpg"); // $NON-NLS-1$
if (_showResponsiveIcon) {
_responsiveImage = NavigatorPlugin.getImage("navigatorIcons/navigatorChild.png"); // $NON-NLS-1$
} else {
_responsiveImage = null;
}
// Setup hyperlink color
_hyperlinkColor = new Color(Display.getCurrent(), 0, 0, 255);
}
/*
* Creates the UI for this wizard page
*/
@Override
public void createControl(final Composite parent) {
// Setup the initial data
populateConfigurationList();
Composite container = new Composite(parent, SWT.NONE);
GridLayout layout = WizardUtils.createGridLayout(1, 5);
container.setLayout(layout);
// Create the radio group
Group group = WizardUtils.createGroup(container, 1, 3);
(_allRadio = WizardUtils.createRadio(group, "&All", 1, this)).setSelection(true); // $NLX-AlwStartPage.All-1$
_responsiveRadio = WizardUtils.createRadio(group, "&Responsive", 1, this); // $NLX-AlwStartPage.Responsive-1$
_nonResponsiveRadio = WizardUtils.createRadio(group, "N&on-Responsive", 1, this); // $NLX-AlwStartPage.NonResponsive-1$
// Create the table
_table = new Table(container, SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.NO_SCROLL);
_table.setHeaderVisible(false);
_table.setLinesVisible(false);
GridData gd = new GridData(SWT.DEFAULT);
gd.horizontalSpan = 1;
gd.verticalAlignment = GridData.FILL;
gd.grabExcessVerticalSpace = true;
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = true;
// Set the preferred height (3 rows)
gd.heightHint = (_defImage.getBounds().height + (MARGIN * 2)) * DEFAULT_ROWS;
_table.setLayoutData(gd);
// Add one column
new TableColumn(_table, SWT.NONE);
// Add the rows
refreshTable(ConfigurationType.ALL);
setControl(container);
setPageComplete(false);
}
/*
* Reads the registry for app layout configurations
*/
private void populateConfigurationList() {
// Find all the app layout configurations
List<FacesDefinition> list = ExtLibRegistryUtil.getConfigNodes(StandardRegistryMaintainer.getStandardRegistry());
for (FacesDefinition def:list) {
// Create a new LayoutConfig
LayoutConfig lc = new LayoutConfig();
lc.tagName = def.getTagName();
lc.facesDef = def;
DesignerExtension de = DesignerExtensionUtil.getExtension(def);
if (de != null) {
// There's a <designer-extension>
lc.title = de.getDisplayName();
lc.description = de.getDescription();
}
ExtLibLayoutExtension le = (ExtLibLayoutExtension) def.getExtension(LAYOUT_EXTENSION);
if (le != null) {
// There is a <layout-extension>
lc.responsive = le.isResponsive();
lc.sampleURL = le.getSampleURL();
if (le.getImage() != null) {
// Get the image if any
ImageDescriptor id = ImageDescriptor.createFromURL(le.getImage());
lc.image = id.createImage();
}
}
if (lc.image == null) {
// If there's no image add a default one
lc.image = _defImage;
}
if (StringUtil.isEmpty(lc.title)) {
// Use the tagName if there's no title
lc.title = lc.facesDef.getFirstDefaultPrefix() + ":" + lc.tagName;
}
// Add this configuration to the list
_configList.add(lc);
}
// Sort the list
Collections.sort(_configList, new Comparator<LayoutConfig>() {
@Override
public int compare(LayoutConfig lc1, LayoutConfig lc2) {
return lc1.title.compareToIgnoreCase(lc2.title);
}
});
}
/*
* Refreshes the table rows for a particular configuration type
*/
private void refreshTable(final ConfigurationType ct) {
_table.setRedraw(false);
_table.removeAll();
for (LayoutConfig lc : _configList) {
boolean addItem = true;
switch (ct) {
case RESPONSIVE:
addItem = lc.responsive;
break;
case NON_RESPONSIVE:
addItem = !lc.responsive;
break;
default:
break;
}
if (addItem) {
new TableItem(_table, SWT.NONE).setData(lc);
_textMargin = Math.max(_textMargin, getImageWidth(lc.image) + (MARGIN * 2));
}
}
_table.setRedraw(true);
setPageComplete(false);
}
/*
* Function invoked when the page is about to be displayed or hidden
*/
@Override
public void setVisible(final boolean visible) {
super.setVisible(visible);
if (visible) {
// Add the listeners
_table.addControlListener(this);
_table.addListener(SWT.MeasureItem, this);
_table.addListener(SWT.PaintItem, this);
_table.addListener(SWT.EraseItem, this);
_table.addListener(SWT.MouseMove, this);
_table.addListener(SWT.MouseUp, this);
_table.addSelectionListener(this);
}
else {
// Remove the listeners
_table.removeSelectionListener(this);
_table.removeListener(SWT.MouseUp, this);
_table.removeListener(SWT.MouseMove, this);
_table.removeListener(SWT.EraseItem, this);
_table.removeListener(SWT.PaintItem, this);
_table.removeListener(SWT.MeasureItem, this);
_table.removeControlListener(this);
}
}
@Override
public void controlMoved(final ControlEvent event) {
}
/*
* Function invoked when the dialog is being resized
*/
@Override
public void controlResized(final ControlEvent event) {
// Make the colum width the full width of the table
_table.getColumn(0).setWidth(_table.getClientArea().width);
}
/*
* Handles specified table events
*/
@Override
public void handleEvent(final Event event) {
LayoutConfig lc;
Point pt;
TableItem item;
int fullWidth = _table.getClientArea().width;
switch (event.type) {
case SWT.MeasureItem:
lc = (LayoutConfig) event.item.getData();
// Height is the max of image and text height
event.height = Math.max(getTextAreaHeight(event.gc, fullWidth, lc), getImageHeight(lc.image)) + (MARGIN * 2);
break;
case SWT.EraseItem:
event.detail &= ~SWT.FOREGROUND;
// Ensure that our only column is dimensioned correctly ->
// Have to do this here or the table will not display correctly
// when the wizard is first opened
if (_table.getColumn(0).getWidth() != _table.getClientArea().width) {
_table.getColumn(0).setWidth(_table.getClientArea().width);
}
break;
case SWT.PaintItem:
lc = (LayoutConfig) event.item.getData();
int textHeight = getTextAreaHeight(event.gc, fullWidth, lc);
// Draw the image
if (lc.image != null) {
int imageOffset = (_textMargin - getImageWidth(lc.image)) / 2;
event.gc.drawImage(lc.image, imageOffset, event.y + ((event.height - getImageHeight(lc.image)) / 2));
Rectangle rect = lc.image.getBounds();
rect.x = imageOffset;
rect.y = event.y + ((event.height - getImageHeight(lc.image)) / 2);
event.gc.drawRectangle(rect);
}
// Draw the title
Font origFont = event.gc.getFont();
event.gc.setFont(_titleFont);
int y = event.y + ((event.height - textHeight) / 2);
event.gc.drawText(lc.title, _textMargin, y, true);
// Draw the rssponsive thumbnail
if (lc.responsive && _showResponsiveIcon) {
event.gc.drawImage(_responsiveImage, _textMargin + getTextWidth(event.gc, lc.title) + SPACER, y);
y += Math.max(getTextHeight(event.gc, "X") + SPACER, getImageHeight(_responsiveImage) + SPACER);
} else {
y += getTextHeight(event.gc, "X") + SPACER;
}
// Draw the description
event.gc.setFont(origFont);
for (String line : getLines(event.gc, lc.description, fullWidth - _textMargin - MARGIN)) {
event.gc.drawText(line, _textMargin, y, true);
y += getTextHeight(event.gc, "X");
}
y += SPACER;
int x = _textMargin;
// Draw the sample-url
if (lc.sampleURL != null) {
Color origColor = _table.getForeground();
event.gc.setForeground(_hyperlinkColor);
Point extent = event.gc.textExtent(SAMPLE_TEXT);
event.gc.drawText(SAMPLE_TEXT, x, y, true);
event.gc.drawLine(x, y + extent.y - 1, x + extent.x, y + extent.y - 1);
event.gc.setForeground(origColor);
lc.hyperlinkRect = new Rectangle(x, y, extent.x, extent.y);
}
break;
case SWT.MouseMove:
pt = new Point(event.x, event.y);
item = _table.getItem(pt);
if (item != null) {
lc = (LayoutConfig) item.getData();
// Check is mouse over a hyperlink
if (lc.hyperlinkRect != null) {
if (lc.hyperlinkRect.contains(pt)) {
// Change the cursor
_table.setCursor(_handCursor);
return;
}
}
}
// Not over hyperlink, reset the cursor if required
if (_table.getCursor() == _handCursor) {
_table.setCursor(null);
}
break;
case SWT.MouseUp:
// If the cursor is over a hyperlink then launch the browser
if (_table.getCursor() == _handCursor) {
pt = new Point(event.x, event.y);
item = _table.getItem(pt);
if (item != null) {
lc = (LayoutConfig) item.getData();
openSampleUrl(lc.sampleURL);
}
}
break;
}
}
/*
* Calculates the height of the text are for each table row
*/
protected int getTextAreaHeight(final GC gc, final int fullWidth, final LayoutConfig lc) {
Font origFont = gc.getFont();
// Add the title height
gc.setFont(_titleFont);
int textHeight = getTextHeight(gc, "X") + SPACER;
gc.setFont(origFont);
if (lc.responsive && _showResponsiveIcon) {
textHeight = Math.max(textHeight, getImageHeight(_responsiveImage) + SPACER);
}
// Add the description height
int lineCount = getLines(gc, lc.description, fullWidth - _textMargin - MARGIN).length;
textHeight += (lineCount * getTextHeight(gc, "X"));
// Add in the footer height
if (lc.sampleURL != null) {
textHeight += getTextHeight(gc, "X") + SPACER;
}
return textHeight;
}
/*
* Utility function to get text width
*/
public static int getTextWidth(final GC gc, final String txt) {
return gc.textExtent(txt).x;
}
/*
* Utility function to get text height
*/
public static int getTextHeight(final GC gc, final String txt) {
return gc.textExtent(txt).y;
}
/*
* Utility function to get and image width
*/
public static int getImageWidth(final Image img) {
if (img != null) {
return img.getBounds().width;
}
return 0;
}
/*
* Utility function to get an image height
*/
public static int getImageHeight(final Image img) {
if (img != null) {
return img.getBounds().height;
}
return 0;
}
/*
* Splits text into a number of lnes based on the specified width
*/
protected static String[] getLines(final GC gc, final String text, final int width) {
if (text == null) {
return new String[]{};
}
char[] chars = text.toCharArray();
List<String> lines = new ArrayList<String>();
StringBuffer line = new StringBuffer();
StringBuffer word = new StringBuffer();
for (int i = 0; i < chars.length; i++) {
// Handle the newline character
if (chars[i] == '\n') {
line.append(word);
word.delete(0, word.length());
lines.add(line.toString());
line.delete(0, line.length());
continue;
}
word.append(chars[i]);
// Check for word boundary
if (Character.isWhitespace(chars[i])) {
// Do we need a new line for this word ?
if (getTextWidth(gc, line.toString() + word.toString()) > width) {
// Yes
lines.add(line.toString());
line.delete(0, line.length());
}
line.append(word);
word.delete(0, word.length());
}
}
// Handle remaining word if any
if (word.length() > 0) {
// Do we need a new line for this word
if (getTextWidth(gc, line.toString() + word.toString()) > width) {
// Yes
lines.add(line.toString());
line.delete(0, line.length());
}
line.append(word);
}
// Handle remaining line if any
if (line.length() > 0) {
lines.add(line.toString());
}
return lines.toArray(new String[lines.size()]);
}
@Override
public void widgetDefaultSelected(final SelectionEvent event) {
}
/*
* Handles widget clicks
*/
@Override
public void widgetSelected(final SelectionEvent event) {
if (event.widget == _allRadio) {
refreshTable(ConfigurationType.ALL);
} else if (event.widget == _responsiveRadio) {
refreshTable(ConfigurationType.RESPONSIVE);
} else if (event.widget == _nonResponsiveRadio) {
refreshTable(ConfigurationType.NON_RESPONSIVE);
} else if (event.widget == _table) {
// User has clicked on a table item, enable the next button
setPageComplete(true);
}
getWizard().getContainer().updateButtons();
}
/*
* Returns the selected layout configuration if any
*/
public LayoutConfig getSelectedLayoutConfig() {
TableItem items[] = _table.getSelection();
if (items.length > 0) {
return (LayoutConfig) items[0].getData();
}
return null;
}
/*
* Opens a URL in the default browser
*/
public void openSampleUrl(URL url) {
IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
try {
IWebBrowser browser = support.getExternalBrowser();
browser.openURL(url);
}
catch (PartInitException e) {
if(ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isErrorEnabled()){
ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.errorp(this, "openSampleUrl", e, "Failed to initialize browser part"); // $NON-NLS-1$ $NLE-AlwStartPage.Failedtoinitializebrowserpart-2$
}
}
catch (Exception e) {
if(ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isErrorEnabled()){
ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.errorp(this, "openSampleUrl", e, "Failed to launch browser"); // $NON-NLS-1$ $NLE-AlwStartPage.Failedtolaunchbrowser-2$
}
}
}
@Override
public void dispose() {
super.dispose();
// Dispose all Images
for (LayoutConfig lc:_configList) {
if ((lc.image != null) && (!lc.image.isDisposed()) && (lc.image != _defImage)) {
lc.image.dispose();
}
}
}
/*
* Utility class for holding layout configuration info
*/
public class LayoutConfig {
public Image image = null;
public String description = "";
public URL sampleURL = null;
public String title = "";
public String tagName = "";
public boolean responsive = false;
public FacesDefinition facesDef = null;
public Rectangle hyperlinkRect = null;
}
}